diff --git a/lib/community/utils/post_actions.dart b/lib/community/utils/post_actions.dart index 007e9cfe2..f3b94d5de 100644 --- a/lib/community/utils/post_actions.dart +++ b/lib/community/utils/post_actions.dart @@ -54,11 +54,13 @@ void triggerPostAction({ } } -DismissDirection determinePostSwipeDirection(bool isUserLoggedIn, ThunderState state) { +DismissDirection determinePostSwipeDirection(bool isUserLoggedIn, ThunderState state, {bool disableSwiping = false}) { if (!isUserLoggedIn) return DismissDirection.none; if (state.enablePostGestures == false) return DismissDirection.none; + if (disableSwiping) return DismissDirection.none; + // If all of the actions are none, then disable swiping if (state.leftPrimaryPostGesture == SwipeAction.none && state.leftSecondaryPostGesture == SwipeAction.none && diff --git a/lib/community/widgets/post_card.dart b/lib/community/widgets/post_card.dart index a38a15174..f657cbfa4 100644 --- a/lib/community/widgets/post_card.dart +++ b/lib/community/widgets/post_card.dart @@ -37,6 +37,8 @@ class PostCard extends StatefulWidget { final ListingType? listingType; + final bool disableSwiping; + const PostCard({ super.key, required this.postViewMedia, @@ -51,6 +53,7 @@ class PostCard extends StatefulWidget { required this.listingType, required this.indicateRead, required this.isLastTapped, + this.disableSwiping = false, }); @override @@ -135,11 +138,13 @@ class _PostCardState extends State { // We are checking to see if there is a left to right swipe here. If there is a left to right swipe, and LTR swipe actions are disabled, then we disable the DismissDirection temporarily // to allow for the full screen swipe to go back. Otherwise, we retain the default behaviour if (horizontalDragDistance > 0) { - if (determinePostSwipeDirection(isUserLoggedIn, state) == DismissDirection.endToStart && isOverridingSwipeGestureAction == false && dismissThreshold == 0.0) { + if (determinePostSwipeDirection(isUserLoggedIn, state, disableSwiping: widget.disableSwiping) == DismissDirection.endToStart && + isOverridingSwipeGestureAction == false && + dismissThreshold == 0.0) { setState(() => isOverridingSwipeGestureAction = true); } } else { - if (determinePostSwipeDirection(isUserLoggedIn, state) == DismissDirection.endToStart && isOverridingSwipeGestureAction == true) { + if (determinePostSwipeDirection(isUserLoggedIn, state, disableSwiping: widget.disableSwiping) == DismissDirection.endToStart && isOverridingSwipeGestureAction == true) { setState(() => isOverridingSwipeGestureAction = false); } } @@ -147,7 +152,7 @@ class _PostCardState extends State { child: Column( children: [ Dismissible( - direction: isOverridingSwipeGestureAction == true ? DismissDirection.none : determinePostSwipeDirection(isUserLoggedIn, state), + direction: isOverridingSwipeGestureAction == true ? DismissDirection.none : determinePostSwipeDirection(isUserLoggedIn, state, disableSwiping: widget.disableSwiping), key: ObjectKey(widget.postViewMedia.postView.post.id), resizeDuration: Duration.zero, dismissThresholds: const {DismissDirection.endToStart: 1, DismissDirection.startToEnd: 1}, diff --git a/lib/core/enums/local_settings.dart b/lib/core/enums/local_settings.dart index 26962d480..9fcfcb203 100644 --- a/lib/core/enums/local_settings.dart +++ b/lib/core/enums/local_settings.dart @@ -349,6 +349,7 @@ enum LocalSettings { accountBlocks(name: 'account_blocks', key: 'accountBlocks', category: LocalSettingsCategories.account, subCategory: LocalSettingsSubCategories.contentManagement), accountChangePassword(name: 'account_change_password', key: 'accountChangePassword', category: LocalSettingsCategories.account, subCategory: LocalSettingsSubCategories.dangerZone), accountDeleteAccount(name: 'account_delete_account', key: 'accountDeleteAccount', category: LocalSettingsCategories.account, subCategory: LocalSettingsSubCategories.dangerZone), + accountManageMedia(name: 'account_manage_media', key: 'accountManageMedia', category: LocalSettingsCategories.account, subCategory: LocalSettingsSubCategories.dangerZone), debugDeleteLocalPreferences(name: 'debug_delete_local_preferences', key: 'debugDeleteLocalPreferences', category: LocalSettingsCategories.debug, subCategory: LocalSettingsSubCategories.reset), debugDeleteLocalDatabase(name: 'debug_delete_local_database', key: 'debugDeleteLocalDatabase', category: LocalSettingsCategories.debug, subCategory: LocalSettingsSubCategories.reset), debugClearCache(name: 'debug_clear_cache', key: 'debugClearCache', category: LocalSettingsCategories.debug, subCategory: LocalSettingsSubCategories.reset), @@ -542,6 +543,7 @@ extension LocalizationExt on AppLocalizations { 'accountChangePassword': changePassword, 'dangerZone': dangerZone, 'accountDeleteAccount': deleteAccount, + 'accountManageMedia': manageMedia, 'reset': resetPreferencesAndData, 'debugDeleteLocalPreferences': deleteLocalPreferences, 'debugDeleteLocalDatabase': deleteLocalDatabase, diff --git a/lib/core/singletons/lemmy_client.dart b/lib/core/singletons/lemmy_client.dart index 7ea65c1c7..971895704 100644 --- a/lib/core/singletons/lemmy_client.dart +++ b/lib/core/singletons/lemmy_client.dart @@ -93,6 +93,7 @@ enum LemmyFeature { commentSortTypeControversial(0, 19, 0, preRelease: ["rc", "1"]), blockInstance(0, 19, 0, preRelease: ["rc", "1"]), multiRead(0, 19, 0, preRelease: ["rc", "1"]), + listMedia(0, 19, 4, preRelease: ["beta", "4"]), hidePosts(0, 19, 4), customThumbnail(0, 19, 4), commentModLog(0, 19, 4), diff --git a/lib/feed/widgets/feed_post_card_list.dart b/lib/feed/widgets/feed_post_card_list.dart index 5f7448e0b..aa59b5ec4 100644 --- a/lib/feed/widgets/feed_post_card_list.dart +++ b/lib/feed/widgets/feed_post_card_list.dart @@ -30,6 +30,12 @@ class FeedPostCardList extends StatefulWidget { /// Whether or not to dim read posts. This value overrides [dimReadPosts] in [ThunderBloc] final bool? dimReadPosts; + /// Whether to disable swiping of posts + final bool disableSwiping; + + /// Overrides the system setting for whether to indicate read posts + final bool? indicateRead; + const FeedPostCardList({ super.key, required this.postViewMedias, @@ -37,6 +43,8 @@ class FeedPostCardList extends StatefulWidget { required this.markPostReadOnScroll, this.queuedForRemoval, this.dimReadPosts, + this.disableSwiping = false, + this.indicateRead, }); @override @@ -165,8 +173,9 @@ class _FeedPostCardListState extends State { }, onTap: () => setState(() => lastTappedPost = widget.postViewMedias[index].postView.post.id), listingType: state.postListingType, - indicateRead: dimReadPosts, + indicateRead: widget.indicateRead ?? dimReadPosts, isLastTapped: lastTappedPost == widget.postViewMedias[index].postView.post.id, + disableSwiping: widget.disableSwiping, )) : null, ); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 5cb2bf6c8..1cd7224ea 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -661,6 +661,14 @@ "@deleteAccountDescription": { "description": "Description for confirmation action to delete account" }, + "deleteImageConfirmMessage": "Are you sure you want to delete this image?", + "@deleteImageConfirmMessage": { + "description": "Confirmation messages for deleting an image" + }, + "deleteImageConfirmTitle": "Delete?", + "@deleteImageConfirmTitle": { + "description": "Confirmation title message for deleting an image" + }, "deleteLocalDatabase": "Delete Local Database", "@deleteLocalDatabase": { "description": "Label for action to delete local database" @@ -811,6 +819,10 @@ "@endOfComments": {}, "endSearch": "End Search", "@endSearch": {}, + "errorDeletingImage": "There was an error deleting the image: {error}", + "@errorDeletingImage": { + "description": "Error message for deleting an image" + }, "errorDownloadingMedia": "Could not download the media file to share: {errorMessage}", "@errorDownloadingMedia": {}, "errorMarkingReplyRead": "There was an error marking the reply as read.", @@ -1209,6 +1221,10 @@ "@loadMorePlural": {}, "loadMoreSingular": "Load {count} more reply…", "@loadMoreSingular": {}, + "loading": "Loading...", + "@loading": { + "description": "Placeholder text for loading" + }, "local": "Local", "@local": {}, "localNotifications": "Local Notifications", @@ -1257,6 +1273,10 @@ }, "manageAccounts": "Manage Accounts", "@manageAccounts": {}, + "manageMedia": "Manage Media", + "@manageMedia": { + "description": "Setting name and page title for media management" + }, "markAllAsRead": "Mark all as read", "@markAllAsRead": { "description": "The mark all as read action" @@ -1437,6 +1457,10 @@ "@noFavoritedCommunities": { "description": "Message for no favorited communities on the drawer" }, + "noImages": "It looks like you have not uploaded any images.", + "@noImages": { + "description": "Message for no uploaded images" + }, "noInstanceBlocks": "No blocked instances.", "@noInstanceBlocks": {}, "noItems": "No items", @@ -1469,6 +1493,10 @@ "@noProfileBioSet": { "description": "Message for no profile bio set" }, + "noReferencesToImage": "No posts or comments were found containing this image. However, it may be used elsewhere on the internet.", + "@noReferencesToImage": { + "description": "Message indicating media was not found on Lemmy" + }, "noReplies": "No replies", "@noReplies": { "description": "Label for when there are no replies in the list" @@ -2637,6 +2665,10 @@ "@updateReleased": {}, "uploadImage": "Upload image", "@uploadImage": {}, + "uploadedDate": "Uploaded: {date}", + "@uploadedDate": { + "description": "Metadata for image uploads" + }, "upvote": "Upvote", "@upvote": { "description": "Action for upvoting a post/comment" diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index 81b21d056..900382a0c 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -2327,7 +2327,7 @@ "@unableToLoadImage": { "description": "Bir ikili görüntüyü yükleyemediğimizde kullanılacak yer tutucu" }, - "unableToLoadImageFrom": "{Domain} adresinden resim yüklenemiyor", + "unableToLoadImageFrom": "{domain} adresinden resim yüklenemiyor", "@unableToLoadImageFrom": { "description": "URL'den bir resim yükleyemediğimizde kullanılacak yer tutucu" }, diff --git a/lib/user/bloc/user_settings_bloc.dart b/lib/user/bloc/user_settings_bloc.dart index 40a598acd..49c121f64 100644 --- a/lib/user/bloc/user_settings_bloc.dart +++ b/lib/user/bloc/user_settings_bloc.dart @@ -1,13 +1,17 @@ import 'package:bloc/bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:equatable/equatable.dart'; +import 'package:lemmy_api_client/pictrs.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:thunder/account/models/account.dart'; import 'package:thunder/core/auth/helpers/fetch_account.dart'; +import 'package:thunder/core/models/post_view_media.dart'; import 'package:thunder/core/singletons/lemmy_client.dart'; import 'package:thunder/instance/utils/instance.dart'; +import 'package:thunder/post/utils/post.dart'; import 'package:thunder/utils/error_messages.dart'; import 'package:thunder/utils/global_context.dart'; @@ -51,6 +55,17 @@ class UserSettingsBloc extends Bloc { _unblockPersonEvent, transformer: throttleDroppable(throttleDuration), ); + on( + _listMediaEvent, + transformer: throttleDroppable(throttleDuration), + ); + on( + _deleteMediaEvent, + // Do not use any transformer, because a throttleDroppable will only process the first request and restartable will only process the last. + ); + on( + _findMediaUsagesEvent, + ); } Future _resetUserSettingsEvent(ResetUserSettingsEvent event, emit) async { @@ -264,4 +279,100 @@ class UserSettingsBloc extends Bloc { errorMessage: e is LemmyApiException ? getErrorMessage(GlobalContext.context, e.message) : e.toString())); } } + + Future _listMediaEvent(ListMediaEvent event, emit) async { + LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + Account? account = await fetchActiveProfileAccount(); + + emit(state.copyWith(status: UserSettingsStatus.listingMedia)); + + try { + int page = 1; + List images = []; + List? lastResponse; + + while (lastResponse?.isEmpty != true) { + ListMediaResponse listMediaResponse = await lemmy.run(ListMedia(page: page, auth: account?.jwt)); + images.addAll(lastResponse = listMediaResponse.images); + ++page; + } + + return emit(state.copyWith(status: UserSettingsStatus.succeededListingMedia, images: images)); + } catch (e) { + return emit(state.copyWith(status: UserSettingsStatus.failedListingMedia, errorMessage: getExceptionErrorMessage(e))); + } + } + + Future _deleteMediaEvent(DeleteMediaEvent event, emit) async { + emit(state.copyWith(status: UserSettingsStatus.deletingMedia)); + + try { + // Optimistically remove the media from the list + state.images?.removeWhere((localImageView) => localImageView.localImage.pictrsAlias == event.id); + + Account? account = await fetchActiveProfileAccount(); + + if (account?.jwt == null) return; + + await PictrsApi(account!.instance!).delete(PictrsUploadFile(deleteToken: event.deleteToken, file: event.id), account.jwt); + + return emit(state.copyWith(status: UserSettingsStatus.succeededListingMedia, images: state.images)); + } catch (e) { + return emit( + state.copyWith( + status: UserSettingsStatus.failedListingMedia, + errorMessage: AppLocalizations.of(GlobalContext.context)!.errorDeletingImage(getExceptionErrorMessage(e)), + ), + ); + } + } + + Future _findMediaUsagesEvent(FindMediaUsagesEvent event, emit) async { + emit(state.copyWith(status: UserSettingsStatus.searchingMedia)); + + try { + LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + Account? account = await fetchActiveProfileAccount(); + + String url = Uri.https(lemmy.host, 'pictrs/image/${event.id}').toString(); + + List posts = (await lemmy.run(Search( + q: url, + type: SearchType.posts, + auth: account?.jwt, + ))) + .posts + .toList(); // Copy so we can modify + + List postsByUrl = (await lemmy.run(Search( + q: url, + type: SearchType.url, + auth: account?.jwt, + ))) + .posts; + + // De-dup posts found by body and URL + posts.addAll(postsByUrl.where((postViewByUrl) => !posts.any((postView) => postView.post.id == postViewByUrl.post.id))); + + final List comments = (await lemmy.run(Search( + q: url, + type: SearchType.comments, + auth: account?.jwt, + ))) + .comments; + + return emit(state.copyWith( + status: UserSettingsStatus.succeededSearchingMedia, + imageSearchPosts: await parsePostViews(posts), + imageSearchComments: comments, + )); + } catch (e) { + return emit( + state.copyWith( + status: UserSettingsStatus.failedListingMedia, + errorMessage: getExceptionErrorMessage(e), + ), + ); + } + } } diff --git a/lib/user/bloc/user_settings_event.dart b/lib/user/bloc/user_settings_event.dart index 66308a4ef..ab50c3e69 100644 --- a/lib/user/bloc/user_settings_event.dart +++ b/lib/user/bloc/user_settings_event.dart @@ -92,3 +92,20 @@ class UnblockPersonEvent extends UserSettingsEvent { const UnblockPersonEvent({required this.personId, this.unblock = true}); } + +class ListMediaEvent extends UserSettingsEvent { + const ListMediaEvent(); +} + +class DeleteMediaEvent extends UserSettingsEvent { + final String deleteToken; + final String id; + + const DeleteMediaEvent({required this.deleteToken, required this.id}); +} + +class FindMediaUsagesEvent extends UserSettingsEvent { + final String id; + + const FindMediaUsagesEvent({required this.id}); +} diff --git a/lib/user/bloc/user_settings_state.dart b/lib/user/bloc/user_settings_state.dart index e08feba22..0e84120ef 100644 --- a/lib/user/bloc/user_settings_state.dart +++ b/lib/user/bloc/user_settings_state.dart @@ -10,6 +10,12 @@ enum UserSettingsStatus { revert, failedRevert, notLoggedIn, + listingMedia, + failedListingMedia, + succeededListingMedia, + deletingMedia, + searchingMedia, + succeededSearchingMedia, } class UserSettingsState extends Equatable { @@ -23,6 +29,9 @@ class UserSettingsState extends Equatable { this.instanceBeingBlocked = 0, this.getSiteResponse, this.errorMessage = '', + this.images, + this.imageSearchPosts, + this.imageSearchComments, }); final UserSettingsStatus status; @@ -38,6 +47,9 @@ class UserSettingsState extends Equatable { final GetSiteResponse? getSiteResponse; final String? errorMessage; + final List? images; + final List? imageSearchPosts; + final List? imageSearchComments; UserSettingsState copyWith({ required UserSettingsStatus status, @@ -49,6 +61,9 @@ class UserSettingsState extends Equatable { int? instanceBeingBlocked, GetSiteResponse? getSiteResponse, String? errorMessage, + List? images, + List? imageSearchPosts, + List? imageSearchComments, }) { return UserSettingsState( status: status, @@ -60,6 +75,9 @@ class UserSettingsState extends Equatable { instanceBeingBlocked: instanceBeingBlocked ?? this.instanceBeingBlocked, getSiteResponse: getSiteResponse ?? this.getSiteResponse, errorMessage: errorMessage ?? this.errorMessage, + images: images ?? this.images, + imageSearchPosts: imageSearchPosts ?? this.imageSearchPosts, + imageSearchComments: imageSearchComments ?? this.imageSearchComments, ); } @@ -74,5 +92,6 @@ class UserSettingsState extends Equatable { instanceBeingBlocked, getSiteResponse, errorMessage, + images, ]; } diff --git a/lib/user/pages/media_management_page.dart b/lib/user/pages/media_management_page.dart new file mode 100644 index 000000000..05a06176d --- /dev/null +++ b/lib/user/pages/media_management_page.dart @@ -0,0 +1,294 @@ +import 'package:extended_image/extended_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:thunder/comment/widgets/comment_list_entry.dart'; +import 'package:thunder/core/auth/bloc/auth_bloc.dart'; +import 'package:thunder/core/enums/image_caching_mode.dart'; +import 'package:thunder/core/singletons/lemmy_client.dart'; +import 'package:thunder/feed/bloc/feed_bloc.dart'; +import 'package:thunder/feed/widgets/feed_post_card_list.dart'; +import 'package:thunder/shared/dialogs.dart'; +import 'package:thunder/shared/full_name_widgets.dart'; +import 'package:thunder/shared/snackbar.dart'; +import 'package:thunder/shared/text/scalable_text.dart'; +import 'package:thunder/thunder/bloc/thunder_bloc.dart'; +import 'package:thunder/user/bloc/user_settings_bloc.dart'; +import 'package:thunder/utils/media/image.dart'; + +class MediaManagementPage extends StatelessWidget { + const MediaManagementPage({super.key}); + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final AppLocalizations l10n = AppLocalizations.of(context)!; + final ThunderBloc thunderBloc = context.read(); + + return BlocBuilder( + builder: (context, state) { + if (state.status == UserSettingsStatus.failedListingMedia && state.errorMessage?.isNotEmpty == true) { + showSnackbar( + state.errorMessage!, + trailingIcon: Icons.refresh_rounded, + trailingAction: () => context.read().add(const ListMediaEvent()), + ); + } + + return Scaffold( + body: Container( + color: theme.colorScheme.surface, + child: SafeArea( + top: false, + child: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + toolbarHeight: 70.0, + title: ListTile( + title: Text( + l10n.manageMedia, + style: theme.textTheme.titleLarge, + ), + subtitle: UserFullNameWidget( + context, + context.read().state.account?.username, + context.read().state.account?.displayName, + context.read().state.account?.instance, + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 0), + ), + ), + if (state.status == UserSettingsStatus.listingMedia) + const SliverFillRemaining( + child: Center( + child: CircularProgressIndicator(), + ), + ), + if (state.status == UserSettingsStatus.searchingMedia || + state.status == UserSettingsStatus.succeededSearchingMedia || + state.status == UserSettingsStatus.deletingMedia || + state.status == UserSettingsStatus.failedListingMedia || + state.status == UserSettingsStatus.succeededListingMedia) ...[ + if (state.images?.isNotEmpty == true) + SliverList.builder( + addSemanticIndexes: false, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: state.images!.length, + itemBuilder: (context, index) { + String url = 'https://${LemmyClient.instance.lemmyApiV3.host}/pictrs/image/${state.images![index].localImage.pictrsAlias}'; + + return KeepAlive( + keepAlive: true, + child: Card( + elevation: 2, + clipBehavior: Clip.hardEdge, + child: Column( + children: [ + AnimatedSize( + duration: const Duration(milliseconds: 250), + child: Stack( + children: [ + ExtendedImage.network( + url, + cache: true, + clearMemoryCacheWhenDispose: thunderBloc.state.imageCachingMode == ImageCachingMode.relaxed, + loadStateChanged: (state) { + if (state.extendedImageLoadState == LoadState.loading) { + return SizedBox( + width: double.infinity, + child: Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text(l10n.loading), + ), + ), + ); + } + if (state.extendedImageLoadState == LoadState.failed) { + return SizedBox( + width: double.infinity, + child: Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + l10n.unableToLoadImageFrom(LemmyClient.instance.lemmyApiV3.host), + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.textTheme.bodyMedium?.color?.withOpacity(0.5), + ), + ), + ), + ), + ); + } + return null; + }, + ), + Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => showImageViewer(context, url: url), + ), + ), + ), + ], + ), + ), + Row( + children: [ + const SizedBox(width: 12), + Text(l10n.uploadedDate(thunderBloc.state.dateFormat?.format(DateTime.parse(state.images![index].localImage.published).toLocal()) ?? '')), + const Spacer(), + IconButton( + onPressed: () async { + final UserSettingsBloc userSettingsBloc = context.read(); + userSettingsBloc.add(FindMediaUsagesEvent(id: state.images![index].localImage.pictrsAlias)); + + showModalBottomSheet( + context: context, + showDragHandle: true, + isScrollControlled: false, + builder: (context) { + return AnimatedSize( + duration: const Duration(milliseconds: 250), + child: BlocProvider.value( + value: userSettingsBloc, + child: BlocBuilder( + builder: (context, state) { + if (state.status == UserSettingsStatus.failedListingMedia) { + Navigator.of(context).pop(); + } + + return SingleChildScrollView( + child: Column( + children: [ + if (state.status == UserSettingsStatus.searchingMedia) + const SizedBox( + height: 200, + child: Center( + child: CircularProgressIndicator(), + ), + ) + else if (state.status == UserSettingsStatus.succeededSearchingMedia) ...[ + if (state.imageSearchPosts?.isNotEmpty == true) + BlocProvider.value( + value: FeedBloc(lemmyClient: LemmyClient.instance), + child: CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + slivers: [ + FeedPostCardList( + postViewMedias: state.imageSearchPosts!, + tabletMode: false, + markPostReadOnScroll: false, + disableSwiping: true, + indicateRead: false, + ) + ], + ), + ), + if (state.imageSearchComments?.isNotEmpty == true) + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: state.imageSearchComments!.length, + itemBuilder: (context, index) => CommentListEntry(commentView: state.imageSearchComments![index]), + ), + ], + if (state.status == UserSettingsStatus.succeededSearchingMedia && + state.imageSearchComments?.isNotEmpty != true && + state.imageSearchPosts?.isNotEmpty != true) + SizedBox( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.only(bottom: 24), + child: Container( + color: theme.dividerColor.withOpacity(0.1), + padding: const EdgeInsets.symmetric(vertical: 32.0), + child: ScalableText( + l10n.noReferencesToImage, + textAlign: TextAlign.center, + style: theme.textTheme.titleSmall, + fontScale: thunderBloc.state.metadataFontSizeScale, + ), + ), + ), + ), + if (state.status == UserSettingsStatus.succeededSearchingMedia && + (state.imageSearchComments?.isNotEmpty == true || state.imageSearchPosts?.isNotEmpty == true)) + const SizedBox(height: 50), + ], + ), + ); + }, + ), + ), + ); + }, + ); + }, + icon: const Icon(Icons.search_rounded), + ), + IconButton( + onPressed: () async { + bool result = false; + await showThunderDialog( + context: context, + title: l10n.deleteImageConfirmTitle, + contentText: l10n.deleteImageConfirmMessage, + onSecondaryButtonPressed: (dialogContext) { + result = false; + Navigator.of(dialogContext).pop(); + }, + secondaryButtonText: l10n.cancel, + onPrimaryButtonPressed: (dialogContext, _) { + result = true; + Navigator.of(dialogContext).pop(); + }, + primaryButtonText: l10n.delete, + ); + + if (result && context.mounted) { + context + .read() + .add(DeleteMediaEvent(deleteToken: state.images![index].localImage.pictrsDeleteToken, id: state.images![index].localImage.pictrsAlias)); + } + }, + icon: const Icon(Icons.delete_forever), + ), + ], + ), + ], + ), + ), + ); + }, + ), + if (state.images?.isNotEmpty != true) + SliverToBoxAdapter( + child: Container( + color: theme.dividerColor.withOpacity(0.1), + padding: const EdgeInsets.symmetric(vertical: 32.0), + child: ScalableText( + l10n.noImages, + textAlign: TextAlign.center, + style: theme.textTheme.titleSmall, + fontScale: thunderBloc.state.metadataFontSizeScale, + ), + ), + ), + ] + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/user/pages/user_settings_page.dart b/lib/user/pages/user_settings_page.dart index 9ecb1a400..31e5dfabd 100644 --- a/lib/user/pages/user_settings_page.dart +++ b/lib/user/pages/user_settings_page.dart @@ -1,5 +1,7 @@ import "dart:async"; +import "dart:io"; +import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; import "package:flutter_bloc/flutter_bloc.dart"; @@ -24,6 +26,7 @@ import "package:thunder/shared/sort_picker.dart"; import "package:thunder/thunder/bloc/thunder_bloc.dart"; import "package:thunder/thunder/thunder_icons.dart"; import "package:thunder/user/bloc/user_settings_bloc.dart"; +import "package:thunder/user/pages/media_management_page.dart"; import "package:thunder/user/pages/user_settings_block_page.dart"; import "package:thunder/user/widgets/user_indicator.dart"; import "package:thunder/utils/bottom_sheet_list_picker.dart"; @@ -507,6 +510,44 @@ class _UserSettingsPageState extends State { setting: LocalSettings.accountDeleteAccount, highlightedSetting: settingToHighlight, ), + if (LemmyClient.instance.supportsFeature(LemmyFeature.listMedia)) + SettingsListTile( + icon: Icons.hide_image_rounded, + description: l10n.manageMedia, + widget: const SizedBox( + height: 42.0, + child: Icon(Icons.chevron_right_rounded), + ), + onTap: () async { + final ThunderBloc thunderBloc = context.read(); + final UserSettingsBloc userSettingsBloc = context.read(); + final AuthBloc authBloc = context.read(); + + userSettingsBloc.add(const ListMediaEvent()); + + await Navigator.of(context).push( + SwipeablePageRoute( + transitionDuration: thunderBloc.state.reduceAnimations ? const Duration(milliseconds: 100) : null, + backGestureDetectionStartOffset: !kIsWeb && Platform.isAndroid ? 45 : 0, + backGestureDetectionWidth: 45, + canOnlySwipeFromEdge: true, + builder: (otherContext) { + return MultiBlocProvider( + providers: [ + BlocProvider.value(value: userSettingsBloc), + BlocProvider.value(value: thunderBloc), + BlocProvider.value(value: authBloc), + ], + child: const MediaManagementPage(), + ); + }, + ), + ); + }, + highlightKey: settingToHighlightKey, + setting: LocalSettings.accountManageMedia, + highlightedSetting: settingToHighlight, + ), const SizedBox(height: 100.0), ], ), diff --git a/pubspec.lock b/pubspec.lock index d282899cb..c83cf76a9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,14 +22,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.11.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" android_intent_plus: dependency: "direct main" description: name: android_intent_plus - sha256: "53136214d506d3128c9f4e5bfce3d026abe7e8038958629811a8d3223b1757c1" + sha256: "38921ec22ebb3b9a7eb678792cf6fab0b6f458b61b9d327688573449c9b47db3" url: "https://pub.dev" source: hosted - version: "5.2.1" + version: "5.2.0" ansicolor: dependency: transitive description: @@ -74,10 +82,10 @@ packages: dependency: transitive description: name: archive - sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "3.6.1" args: dependency: transitive description: @@ -106,10 +114,10 @@ packages: dependency: "direct main" description: name: back_button_interceptor - sha256: "5102af98f83805f41c1444a705558bf284a909ad35fbad07d457fe9054021cd8" + sha256: "50d775cee2f75e8788b0577b71261c28638f47c4faf04d5aa566f13c6b19baa3" url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "8.0.3" background_fetch: dependency: "direct main" description: @@ -162,50 +170,50 @@ packages: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" url: "https://pub.dev" source: hosted - version: "2.4.14" + version: "2.4.13" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "7.3.2" built_collection: dependency: transitive description: @@ -218,10 +226,10 @@ packages: dependency: transitive description: name: built_value - sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.3" + version: "8.9.2" cached_network_image: dependency: "direct main" description: @@ -258,10 +266,10 @@ packages: dependency: transitive description: name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -274,10 +282,10 @@ packages: dependency: transitive description: name: cli_util - sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.4.1" clock: dependency: transitive description: @@ -290,10 +298,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.10.0" collection: dependency: "direct main" description: @@ -302,14 +310,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.0" + color_models: + dependency: transitive + description: + name: color_models + sha256: "3683f0a461570161ca22b7d3e1765b2ce2bce3534db14e00d5ee458d0287abac" + url: "https://pub.dev" + source: hosted + version: "1.3.3" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - sha256: e0817759ec6d2d8e57eb234e6e57d2173931367a865850c7acea40d4b4f9c27d + sha256: "876849631b0c7dc20f8b471a2a03142841b482438e3b707955464f5ffca3e4c3" url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -370,10 +386,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.3.7" dbus: dependency: transitive description: @@ -402,18 +418,18 @@ packages: dependency: "direct main" description: name: drift - sha256: cc593c8acaccaf4a5750fac034e06289f572038a38391d4343739789591e6b01 + sha256: df027d168a2985a2e9da900adeba2ab0136f0d84436592cf3cd5135f82c8579c url: "https://pub.dev" source: hosted - version: "2.23.0" + version: "2.21.0" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: fde4fcb5776c2048ecdf672de396e3fdad8e93bf5148b69dd401cf6767a16e0d + sha256: "623649abe932fc17bd32e578e7e05f7ac5e7dd0b33e6c8669a0634105d1389bf" url: "https://pub.dev" source: hosted - version: "2.23.0" + version: "2.21.2" drift_flutter: dependency: "direct main" description: @@ -434,10 +450,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.5" executor: dependency: transitive description: @@ -506,10 +522,10 @@ packages: dependency: transitive description: name: file_selector_linux - sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2" url: "https://pub.dev" source: hosted - version: "0.9.3+2" + version: "0.9.3" file_selector_macos: dependency: transitive description: @@ -546,18 +562,18 @@ packages: dependency: "direct main" description: name: flex_color_scheme - sha256: "90f4fe67b9561ae8a4af117df65a8ce9988624025667c54e6d304e65cff77d52" + sha256: "09bea5d776f694c5a67f2229f2aa500cc7cce369322dc6500ab01cf9ad1b4e1a" url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.1.0" flex_seed_scheme: dependency: transitive description: name: flex_seed_scheme - sha256: "7639d2c86268eff84a909026eb169f008064af0fb3696a651b24b0fa24a40334" + sha256: d3ba3c5c92d2d79d45e94b4c6c71d01fac3c15017da1545880c53864da5dfeb0 url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.5.0" flutter: dependency: "direct main" description: flutter @@ -579,6 +595,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" + flutter_color_models: + dependency: transitive + description: + name: flutter_color_models + sha256: "8454198ba9a82e533452d0764f1df3d6c3ccc2b33e18eb1f2fedc7ae4a5c43be" + url: "https://pub.dev" + source: hosted + version: "1.3.3+2" flutter_custom_tabs: dependency: "direct main" description: @@ -663,10 +687,10 @@ packages: dependency: transitive description: name: flutter_inappwebview_internal_annotations - sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.1" flutter_inappwebview_ios: dependency: transitive description: @@ -759,10 +783,10 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5" + sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77" url: "https://pub.dev" source: hosted - version: "0.14.2" + version: "0.14.1" flutter_lints: dependency: "direct dev" description: @@ -804,26 +828,26 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "255b00afa1a7bad19727da6a7780cf3db6c3c12e68d302d85e0ff1fdf173db9e" + sha256: bd9c475d9aae256369edacafc29d1e74c81f78a10cdcdacbbbc9e3c43d009e4a url: "https://pub.dev" source: hosted - version: "0.7.4+3" + version: "0.7.4" flutter_native_splash: dependency: "direct main" description: name: flutter_native_splash - sha256: "1152ab0067ca5a2ebeb862fe0a762057202cceb22b7e62692dcbabf6483891bb" + sha256: ee5c9bd2b74ea8676442fd4ab876b5d41681df49276488854d6c81a5377c0ef1 url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.2" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e" + sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.0.23" flutter_sharing_intent: dependency: "direct main" description: @@ -867,7 +891,7 @@ packages: source: hosted version: "0.9.1" freezed_annotation: - dependency: transitive + dependency: "direct main" description: name: freezed_annotation sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 @@ -894,10 +918,10 @@ packages: dependency: "direct main" description: name: gal - sha256: "2771519c8b29f784d5e27f4efc2667667eef51c6c47cccaa0435a8fe8aa208e4" + sha256: "54c9b72528efce7c66234f3b6dd01cb0304fd8af8196de15571d7bdddb940977" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.0" glob: dependency: transitive description: @@ -966,26 +990,26 @@ packages: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.0.2" image: dependency: transitive description: name: image - sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6" + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.3.0" image_picker: dependency: "direct main" description: @@ -998,10 +1022,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: aa6f1280b670861ac45220cc95adc59bb6ae130259d36f980ccb62220dc5e59f + sha256: d34e0d9e024e81321b2aeed7b202ec6181cc282e6a1c0c0b4e6ad07ef1065d82 url: "https://pub.dev" source: hosted - version: "0.8.12+19" + version: "0.8.12+16" image_picker_for_web: dependency: transitive description: @@ -1062,26 +1086,26 @@ packages: dependency: transitive description: name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" jovial_misc: dependency: transitive description: name: jovial_misc - sha256: "4b10a4cac4f492d9692e97699bff775efa84abdba29909124cbccf3126e31cea" + sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1 url: "https://pub.dev" source: hosted - version: "0.9.0" + version: "0.8.5" jovial_svg: dependency: "direct main" description: name: jovial_svg - sha256: ca14d42956b9949c36333065c9141f100e930c918f57f4bd8dd59d35581bd3fc + sha256: adbc985f89a9e9c601d29aebb9fc17dd0a5db05b67af7e6c21da91eeb13dacb7 url: "https://pub.dev" source: hosted - version: "1.1.24" + version: "1.1.23" js: dependency: transitive description: @@ -1102,10 +1126,10 @@ packages: dependency: "direct main" description: name: l10n_esperanto - sha256: "8bdaf08ac603c6c6255f71ce31aabaea676a722c2936e1a028ef222db6448689" + sha256: db37016f8752a13257ed6e7ef718db575960ae99c7d4c694fbb45f635a583fa2 url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.11" leak_tracker: dependency: transitive description: @@ -1245,6 +1269,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + num_utilities: + dependency: transitive + description: + name: num_utilities + sha256: "170695bcafd17a19ee3a060325e7e10fb35bb878c8bcb3e6f5691dde1462c0ae" + url: "https://pub.dev" + source: hosted + version: "1.0.5" octo_image: dependency: transitive description: @@ -1265,10 +1297,10 @@ packages: dependency: transitive description: name: package_config - sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.0" path: dependency: "direct main" description: @@ -1281,26 +1313,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a url: "https://pub.dev" source: hosted - version: "2.2.15" + version: "2.2.12" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1353,10 +1385,10 @@ packages: dependency: transitive description: name: permission_handler_html - sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 url: "https://pub.dev" source: hosted - version: "0.1.3+5" + version: "0.1.3+2" permission_handler_platform_interface: dependency: transitive description: @@ -1429,6 +1461,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.2+1" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" pool: dependency: transitive description: @@ -1437,14 +1477,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - posix: + powers: dependency: transitive description: - name: posix - sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + name: powers + sha256: "389ba222d4264655ecd3ffa461921bc707a07e4597b98831d21dd516eed6f496" url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "1.0.0+2" provider: dependency: transitive description: @@ -1457,18 +1497,18 @@ packages: dependency: transitive description: name: pub_semver - sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" push: dependency: "direct main" description: @@ -1518,42 +1558,42 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "6327c3f233729374d0abaafd61f6846115b2a481b4feddd8534211dc10659400" + sha256: "334fcdf0ef9c0df0e3b428faebcac9568f35c747d59831474b2fc56e156d244e" url: "https://pub.dev" source: hosted - version: "10.1.3" + version: "10.1.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + sha256: c57c0bbfec7142e3a0f55633be504b796af72e60e3c791b44d5a017b985f7a48 url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.1" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "3c7e73920c694a436afaf65ab60ce3453d91f84208d761fbd83fc21182134d93" + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d" + sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.3.3" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.3" shared_preferences_linux: dependency: transitive description: @@ -1590,18 +1630,18 @@ packages: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -1627,10 +1667,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.5.0" source_span: dependency: transitive description: @@ -1651,10 +1691,10 @@ packages: dependency: "direct dev" description: name: sqflite - sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" + sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" sqflite_android: dependency: transitive description: @@ -1667,26 +1707,26 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" + sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" url: "https://pub.dev" source: hosted - version: "2.5.4+6" + version: "2.5.4+5" sqflite_common_ffi: dependency: "direct dev" description: name: sqflite_common_ffi - sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36" + sha256: d316908f1537725427ff2827a5c5f3b2c1bc311caed985fe3c9b10939c9e11ca url: "https://pub.dev" source: hosted - version: "2.3.4+4" + version: "2.3.4" sqflite_darwin: dependency: transitive description: name: sqflite_darwin - sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474" + sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.1-1" sqflite_platform_interface: dependency: transitive description: @@ -1699,26 +1739,26 @@ packages: dependency: transitive description: name: sqlite3 - sha256: cb7f4e9dc1b52b1fa350f7b3d41c662e75fc3d399555fa4e5efcf267e9a4fbb5 + sha256: bb174b3ec2527f9c5f680f73a89af8149dd99782fbb56ea88ad0807c5638f2ed url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.4.7" sqlite3_flutter_libs: dependency: transitive description: name: sqlite3_flutter_libs - sha256: "73016db8419f019e807b7a5e5fbf2a7bd45c165fed403b8e7681230f3a102785" + sha256: ccd29dd6cf6fb9351fa07cd6f92895809adbf0779c1d986acf5e3d53b3250e33 url: "https://pub.dev" source: hosted - version: "0.5.28" + version: "0.5.25" sqlparser: dependency: transitive description: name: sqlparser - sha256: "4cad4b2c5f63dc9ea1a8dcffb58cf762322bea5dd8836870164a65e913bdae41" + sha256: c5f63dff8677407ddcddfa4744c176ea6dc44286c47ba9e69e76d8071398034d url: "https://pub.dev" source: hosted - version: "0.40.0" + version: "0.39.1" stack_trace: dependency: transitive description: @@ -1739,10 +1779,10 @@ packages: dependency: "direct main" description: name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.0" string_scanner: dependency: transitive description: @@ -1763,10 +1803,10 @@ packages: dependency: "direct main" description: name: swipeable_page_route - sha256: b494bddcd48705b2840d4cf22d3d2197f78d405ae51b08acb2118706e078713b + sha256: e3f709083611298117a96198726b06f12952a7be4a396af8de36899441540656 url: "https://pub.dev" source: hosted - version: "0.4.6" + version: "0.4.4" synchronized: dependency: transitive description: @@ -1795,18 +1835,18 @@ packages: dependency: transitive description: name: timezone - sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.9.4" timing: dependency: transitive description: name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.1" typed_data: dependency: transitive description: @@ -1867,34 +1907,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" + sha256: "8fc3bae0b68c02c47c5c86fa8bfa74471d42687b0eded01b78de87872db745e2" url: "https://pub.dev" source: hosted - version: "6.3.14" + version: "6.3.12" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" url_launcher_platform_interface: dependency: transitive description: @@ -1955,18 +1995,18 @@ packages: dependency: transitive description: name: video_player_android - sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898" + sha256: "2800d68d6d5b4c22da62453568ed68e63c35bea524d4fa42062e53d6bb591433" url: "https://pub.dev" source: hosted - version: "2.7.16" + version: "2.7.13" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "33224c19775fd244be2d6e3dbd8e1826ab162877bd61123bf71890772119a2b7" + sha256: cd5ab8a8bc0eab65ab0cea40304097edc46da574c8c1ecdee96f28cd8ef3792f url: "https://pub.dev" source: hosted - version: "2.6.5" + version: "2.6.2" video_player_platform_interface: dependency: transitive description: @@ -1979,10 +2019,10 @@ packages: dependency: transitive description: name: video_player_web - sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10" + sha256: "6dcdd298136523eaf7dfc31abaf0dfba9aa8a8dbc96670e87e9d42b6f2caf774" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.2" visibility_detector: dependency: "direct main" description: @@ -2003,10 +2043,10 @@ packages: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" web: dependency: transitive description: @@ -2059,10 +2099,10 @@ packages: dependency: transitive description: name: win32 - sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" + sha256: "2735daae5150e8b1dfeb3eb0544b4d3af0061e9e82cef063adcd583bdae4306a" url: "https://pub.dev" source: hosted - version: "5.9.0" + version: "5.7.0" win32_registry: dependency: transitive description: @@ -2100,10 +2140,10 @@ packages: dependency: transitive description: name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" youtube_player_flutter: dependency: "direct main" description: @@ -2124,10 +2164,10 @@ packages: dependency: transitive description: name: youtube_player_iframe_web - sha256: "05222a228937932e7ee7a6171e8020fee4cd23d1c7bf6b4128c569484338c593" + sha256: "73dd7bbbe8a6519b5d58905122153e38591f753ad2df40b5328a9d8474e1587e" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.0" sdks: dart: ">=3.6.0 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 1f0cddecd..85de210b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -92,6 +92,7 @@ dependencies: webview_flutter_wkwebview: "^3.5.0" youtube_player_flutter: "^9.1.0" youtube_player_iframe: "^5.2.0" + freezed_annotation: ^2.4.4 dev_dependencies: flutter_test: