feat: show preview in a separate page

This commit is contained in:
Alex
2026-01-06 12:12:51 -06:00
parent d1fc10c59b
commit 8fce13568f
5 changed files with 87 additions and 74 deletions

View File

@@ -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",

View File

@@ -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<LocalAsset> 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<BaseAsset>(), TimelineOrigin.search);
ref.onDispose(timelineService.dispose);
return timelineService;
}),
],
child: const Timeline(appBar: null, bottomSheet: null, groupBy: GroupAssetsBy.day, readOnly: true),
),
);
}
}

View File

@@ -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: '/'),

View File

@@ -611,6 +611,43 @@ class ChangePasswordRoute extends PageRouteInfo<void> {
);
}
/// generated route for
/// [CleanupPreviewPage]
class CleanupPreviewRoute extends PageRouteInfo<CleanupPreviewRouteArgs> {
CleanupPreviewRoute({
Key? key,
required List<LocalAsset> assets,
List<PageRouteInfo>? 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<CleanupPreviewRouteArgs>();
return CleanupPreviewPage(key: args.key, assets: args.assets);
},
);
}
class CleanupPreviewRouteArgs {
const CleanupPreviewRouteArgs({this.key, required this.assets});
final Key? key;
final List<LocalAsset> assets;
@override
String toString() {
return 'CleanupPreviewRouteArgs{key: $key, assets: $assets}';
}
}
/// generated route for
/// [CreateAlbumPage]
class CreateAlbumRoute extends PageRouteInfo<CreateAlbumRouteArgs> {

View File

@@ -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<FreeUpSpaceSettings> {
void _showAssetsPreview(List<LocalAsset> 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<LocalAsset> 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<BaseAsset>(), 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;