diff --git a/i18n/en.json b/i18n/en.json index 589615ae53..0676046ddc 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -745,6 +745,7 @@ "cleanup_found_assets": "Found {count} backed up assets", "cleanup_icloud_shared_albums_excluded": "iCloud Shared Albums are excluded from the scan", "cleanup_no_assets_found": "No backed up assets found matching your criteria", + "cleanup_preview_title": "Assets to remove ({count})", "cleanup_step3_description": "Scan for photos and videos that have been backed up to the server with the selected cutoff date and filter options", "cleanup_step4_summary": "{count} assets created before {date} are queued for removal from your device", "clear": "Clear", diff --git a/mobile/lib/presentation/pages/cleanup_preview.page.dart b/mobile/lib/presentation/pages/cleanup_preview.page.dart new file mode 100644 index 0000000000..f5965cc54c --- /dev/null +++ b/mobile/lib/presentation/pages/cleanup_preview.page.dart @@ -0,0 +1,44 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +@RoutePage() +class CleanupPreviewPage extends StatelessWidget { + final List assets; + + const CleanupPreviewPage({super.key, required this.assets}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'cleanup_preview_title'.t(context: context, args: {'count': assets.length.toString()}), + ), + centerTitle: true, + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: context.colorScheme.surface, + ), + body: ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref + .watch(timelineFactoryProvider) + .fromAssetsWithBuckets(assets.cast(), TimelineOrigin.search); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: const Timeline(appBar: null, bottomSheet: null, groupBy: GroupAssetsBy.day, readOnly: true), + ), + ); + } +} diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 9c4a193381..9468b105e5 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -88,6 +88,7 @@ import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart'; import 'package:immich_mobile/presentation/pages/drift_archive.page.dart'; import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/drift_asset_troubleshoot.page.dart'; +import 'package:immich_mobile/presentation/pages/cleanup_preview.page.dart'; import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart'; import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; @@ -338,6 +339,7 @@ class AppRouter extends RootStackRouter { AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: ImmichUIShowcaseRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: CleanupPreviewRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 939bf73369..b287d73114 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -611,6 +611,43 @@ class ChangePasswordRoute extends PageRouteInfo { ); } +/// generated route for +/// [CleanupPreviewPage] +class CleanupPreviewRoute extends PageRouteInfo { + CleanupPreviewRoute({ + Key? key, + required List assets, + List? children, + }) : super( + CleanupPreviewRoute.name, + args: CleanupPreviewRouteArgs(key: key, assets: assets), + initialChildren: children, + ); + + static const String name = 'CleanupPreviewRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return CleanupPreviewPage(key: args.key, assets: args.assets); + }, + ); +} + +class CleanupPreviewRouteArgs { + const CleanupPreviewRouteArgs({this.key, required this.assets}); + + final Key? key; + + final List assets; + + @override + String toString() { + return 'CleanupPreviewRouteArgs{key: $key, assets: $assets}'; + } +} + /// generated route for /// [CreateAlbumPage] class CreateAlbumRoute extends PageRouteInfo { diff --git a/mobile/lib/widgets/settings/free_up_space_settings.dart b/mobile/lib/widgets/settings/free_up_space_settings.dart index b16b6b626a..cafc12872d 100644 --- a/mobile/lib/widgets/settings/free_up_space_settings.dart +++ b/mobile/lib/widgets/settings/free_up_space_settings.dart @@ -1,16 +1,14 @@ +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/timeline.model.dart'; -import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/cleanup.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; enum CleanupStep { selectDate, filterOptions, scan, delete } @@ -136,18 +134,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState { void _showAssetsPreview(List assets) { ref.read(hapticFeedbackProvider.notifier).mediumImpact(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => DraggableScrollableSheet( - initialChildSize: 0.9, - minChildSize: 0.5, - maxChildSize: 0.95, - expand: false, - builder: (context, scrollController) => _CleanupAssetsPreview(assets: assets), - ), - ); + context.pushRoute(CleanupPreviewRoute(assets: assets)); } @override @@ -648,64 +635,6 @@ class _DeleteConfirmationDialog extends StatelessWidget { } } -class _CleanupAssetsPreview extends StatelessWidget { - final List assets; - - const _CleanupAssetsPreview({required this.assets}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - const _DragHandle(), - Expanded( - child: ProviderScope( - overrides: [ - timelineServiceProvider.overrideWith((ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .fromAssetsWithBuckets(assets.cast(), TimelineOrigin.search); - ref.onDispose(timelineService.dispose); - return timelineService; - }), - ], - child: const Timeline( - appBar: null, - bottomSheet: null, - withScrubber: false, - groupBy: GroupAssetsBy.day, - readOnly: true, - ), - ), - ), - ], - ); - } -} - -class _DragHandle extends StatelessWidget { - const _DragHandle(); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 38, - child: Center( - child: SizedBox( - width: 32, - height: 6, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(20)), - color: context.colorScheme.onSurfaceVariant.withValues(alpha: 0.4), - ), - ), - ), - ), - ); - } -} - class _DatePresetCard extends StatelessWidget { final String value; final String unit;