mirror of
https://github.com/immich-app/immich.git
synced 2026-01-25 10:54:37 -08:00
chore: refactor
This commit is contained in:
@@ -87,7 +87,7 @@ enum StoreKey<T> {
|
||||
// Free up space
|
||||
cleanupKeepFavorites<bool>._(1008),
|
||||
cleanupKeepMediaType<int>._(1009),
|
||||
cleanupExcludedAlbumIds<String>._(1010),
|
||||
cleanupKeepAlbumIds<String>._(1010),
|
||||
cleanupCutoffDaysAgo<int>._(1011);
|
||||
|
||||
const StoreKey._(this.id);
|
||||
|
||||
@@ -135,7 +135,7 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||
DateTime cutoffDate, {
|
||||
AssetKeepType keepMediaType = AssetKeepType.none,
|
||||
bool keepFavorites = true,
|
||||
Set<String> excludedAlbumIds = const {},
|
||||
Set<String> keepAlbumIds = const {},
|
||||
}) async {
|
||||
final iosSharedAlbumAssets = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
@@ -160,14 +160,13 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||
// Exclude assets that are in iOS shared albums
|
||||
whereClause = whereClause & _db.localAssetEntity.id.isNotInQuery(iosSharedAlbumAssets);
|
||||
|
||||
if (excludedAlbumIds.isNotEmpty) {
|
||||
final excludedAlbumAssets = _db.localAlbumAssetEntity.selectOnly()
|
||||
if (keepAlbumIds.isNotEmpty) {
|
||||
final keepAlbumAssets = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..where(_db.localAlbumAssetEntity.albumId.isIn(excludedAlbumIds));
|
||||
whereClause = whereClause & _db.localAssetEntity.id.isNotInQuery(excludedAlbumAssets);
|
||||
..where(_db.localAlbumAssetEntity.albumId.isIn(keepAlbumIds));
|
||||
whereClause = whereClause & _db.localAssetEntity.id.isNotInQuery(keepAlbumAssets);
|
||||
}
|
||||
|
||||
// keepMediaType specifies what to KEEP, so we filter to DELETE the opposite
|
||||
if (keepMediaType == AssetKeepType.photosOnly) {
|
||||
// Keep photos = delete only videos
|
||||
whereClause = whereClause & _db.localAssetEntity.type.equalsValue(AssetType.video);
|
||||
|
||||
@@ -13,7 +13,7 @@ class CleanupState {
|
||||
final bool isDeleting;
|
||||
final AssetKeepType keepMediaType;
|
||||
final bool keepFavorites;
|
||||
final Set<String> excludedAlbumIds;
|
||||
final Set<String> keepAlbumIds;
|
||||
|
||||
const CleanupState({
|
||||
this.selectedDate,
|
||||
@@ -22,7 +22,7 @@ class CleanupState {
|
||||
this.isDeleting = false,
|
||||
this.keepMediaType = AssetKeepType.none,
|
||||
this.keepFavorites = true,
|
||||
this.excludedAlbumIds = const {},
|
||||
this.keepAlbumIds = const {},
|
||||
});
|
||||
|
||||
CleanupState copyWith({
|
||||
@@ -32,7 +32,7 @@ class CleanupState {
|
||||
bool? isDeleting,
|
||||
AssetKeepType? keepMediaType,
|
||||
bool? keepFavorites,
|
||||
Set<String>? excludedAlbumIds,
|
||||
Set<String>? keepAlbumIds,
|
||||
}) {
|
||||
return CleanupState(
|
||||
selectedDate: selectedDate ?? this.selectedDate,
|
||||
@@ -41,7 +41,7 @@ class CleanupState {
|
||||
isDeleting: isDeleting ?? this.isDeleting,
|
||||
keepMediaType: keepMediaType ?? this.keepMediaType,
|
||||
keepFavorites: keepFavorites ?? this.keepFavorites,
|
||||
excludedAlbumIds: excludedAlbumIds ?? this.excludedAlbumIds,
|
||||
keepAlbumIds: keepAlbumIds ?? this.keepAlbumIds,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,17 +66,17 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
||||
void _loadPersistedSettings() {
|
||||
final keepFavorites = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepFavorites);
|
||||
final keepMediaTypeIndex = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepMediaType);
|
||||
final excludedAlbumIdsString = _appSettingsService.getSetting(AppSettingsEnum.cleanupExcludedAlbumIds);
|
||||
final keepAlbumIdsString = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepAlbumIds);
|
||||
final cutoffDaysAgo = _appSettingsService.getSetting(AppSettingsEnum.cleanupCutoffDaysAgo);
|
||||
|
||||
final keepMediaType = AssetKeepType.values[keepMediaTypeIndex.clamp(0, AssetKeepType.values.length - 1)];
|
||||
final excludedAlbumIds = excludedAlbumIdsString.isEmpty ? <String>{} : excludedAlbumIdsString.split(',').toSet();
|
||||
final keepAlbumIds = keepAlbumIdsString.isEmpty ? <String>{} : keepAlbumIdsString.split(',').toSet();
|
||||
final selectedDate = cutoffDaysAgo > 0 ? DateTime.now().subtract(Duration(days: cutoffDaysAgo)) : null;
|
||||
|
||||
state = state.copyWith(
|
||||
keepFavorites: keepFavorites,
|
||||
keepMediaType: keepMediaType,
|
||||
excludedAlbumIds: excludedAlbumIds,
|
||||
keepAlbumIds: keepAlbumIds,
|
||||
selectedDate: selectedDate,
|
||||
);
|
||||
}
|
||||
@@ -99,24 +99,24 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupKeepFavorites, keepFavorites);
|
||||
}
|
||||
|
||||
void toggleExcludedAlbum(String albumId) {
|
||||
final newExcludedAlbumIds = Set<String>.from(state.excludedAlbumIds);
|
||||
if (newExcludedAlbumIds.contains(albumId)) {
|
||||
newExcludedAlbumIds.remove(albumId);
|
||||
void toggleKeepAlbum(String albumId) {
|
||||
final newKeepAlbumIds = Set<String>.from(state.keepAlbumIds);
|
||||
if (newKeepAlbumIds.contains(albumId)) {
|
||||
newKeepAlbumIds.remove(albumId);
|
||||
} else {
|
||||
newExcludedAlbumIds.add(albumId);
|
||||
newKeepAlbumIds.add(albumId);
|
||||
}
|
||||
state = state.copyWith(excludedAlbumIds: newExcludedAlbumIds, assetsToDelete: []);
|
||||
_persistExcludedAlbumIds(newExcludedAlbumIds);
|
||||
state = state.copyWith(keepAlbumIds: newKeepAlbumIds, assetsToDelete: []);
|
||||
_persistExcludedAlbumIds(newKeepAlbumIds);
|
||||
}
|
||||
|
||||
void setExcludedAlbumIds(Set<String> albumIds) {
|
||||
state = state.copyWith(excludedAlbumIds: albumIds, assetsToDelete: []);
|
||||
state = state.copyWith(keepAlbumIds: albumIds, assetsToDelete: []);
|
||||
_persistExcludedAlbumIds(albumIds);
|
||||
}
|
||||
|
||||
void _persistExcludedAlbumIds(Set<String> albumIds) {
|
||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupExcludedAlbumIds, albumIds.join(','));
|
||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupKeepAlbumIds, albumIds.join(','));
|
||||
}
|
||||
|
||||
Future<void> scanAssets() async {
|
||||
@@ -131,7 +131,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
||||
state.selectedDate!,
|
||||
keepMediaType: state.keepMediaType,
|
||||
keepFavorites: state.keepFavorites,
|
||||
excludedAlbumIds: state.excludedAlbumIds,
|
||||
keepAlbumIds: state.keepAlbumIds,
|
||||
);
|
||||
state = state.copyWith(assetsToDelete: assets, isScanning: false);
|
||||
} catch (e) {
|
||||
|
||||
@@ -57,7 +57,7 @@ enum AppSettingsEnum<T> {
|
||||
backupTriggerDelay<int>(StoreKey.backupTriggerDelay, null, 30),
|
||||
cleanupKeepFavorites<bool>(StoreKey.cleanupKeepFavorites, null, true),
|
||||
cleanupKeepMediaType<int>(StoreKey.cleanupKeepMediaType, null, 0),
|
||||
cleanupExcludedAlbumIds<String>(StoreKey.cleanupExcludedAlbumIds, null, ""),
|
||||
cleanupKeepAlbumIds<String>(StoreKey.cleanupKeepAlbumIds, null, ""),
|
||||
cleanupCutoffDaysAgo<int>(StoreKey.cleanupCutoffDaysAgo, null, 60);
|
||||
|
||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||
|
||||
@@ -20,14 +20,14 @@ class CleanupService {
|
||||
DateTime cutoffDate, {
|
||||
AssetKeepType keepMediaType = AssetKeepType.none,
|
||||
bool keepFavorites = true,
|
||||
Set<String> excludedAlbumIds = const {},
|
||||
Set<String> keepAlbumIds = const {},
|
||||
}) {
|
||||
return _localAssetRepository.getRemovalCandidates(
|
||||
userId,
|
||||
cutoffDate,
|
||||
keepMediaType: keepMediaType,
|
||||
keepFavorites: keepFavorites,
|
||||
excludedAlbumIds: excludedAlbumIds,
|
||||
keepAlbumIds: keepAlbumIds,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
|
||||
final subtitleStyle = context.textTheme.bodyMedium!.copyWith(
|
||||
color: context.textTheme.bodyMedium!.color!.withAlpha(215),
|
||||
);
|
||||
|
||||
StepStyle styleForState(StepState stepState, {bool isDestructive = false}) {
|
||||
switch (stepState) {
|
||||
case StepState.complete:
|
||||
@@ -187,7 +188,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
|
||||
final step3State = hasAssets ? StepState.indexed : StepState.disabled;
|
||||
|
||||
final hasKeepSettings =
|
||||
state.keepFavorites || state.excludedAlbumIds.isNotEmpty || state.keepMediaType != AssetKeepType.none;
|
||||
state.keepFavorites || state.keepAlbumIds.isNotEmpty || state.keepMediaType != AssetKeepType.none;
|
||||
|
||||
String getKeepSettingsSummary() {
|
||||
final parts = <String>[];
|
||||
@@ -202,10 +203,8 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
|
||||
parts.add('favorites'.t(context: context).toLowerCase());
|
||||
}
|
||||
|
||||
if (state.excludedAlbumIds.isNotEmpty) {
|
||||
parts.add(
|
||||
'excluded_albums_count'.t(context: context, args: {'count': state.excludedAlbumIds.length.toString()}),
|
||||
);
|
||||
if (state.keepAlbumIds.isNotEmpty) {
|
||||
parts.add('keep_albums_count'.t(context: context, args: {'count': state.keepAlbumIds.length.toString()}));
|
||||
}
|
||||
|
||||
if (parts.isEmpty) {
|
||||
@@ -286,20 +285,15 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('cleanup_filter_description'.t(context: context), style: subtitleStyle),
|
||||
const SizedBox(height: 16),
|
||||
Text('keep_description'.t(context: context), style: subtitleStyle),
|
||||
const SizedBox(height: 4),
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
'keep_favorites'.t(context: context),
|
||||
style: context.textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w500, height: 1.5),
|
||||
),
|
||||
subtitle: Text(
|
||||
'keep_favorites_description'.t(context: context),
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
color: context.textTheme.bodyMedium!.color!.withAlpha(215),
|
||||
),
|
||||
),
|
||||
|
||||
value: state.keepFavorites,
|
||||
onChanged: (value) {
|
||||
ref.read(cleanupProvider.notifier).setKeepFavorites(value);
|
||||
@@ -307,16 +301,16 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_ExcludedAlbumsSection(
|
||||
excludedAlbumIds: state.excludedAlbumIds,
|
||||
_KeepAlbumsSection(
|
||||
albumIds: state.keepAlbumIds,
|
||||
onAlbumToggled: (albumId) {
|
||||
ref.read(cleanupProvider.notifier).toggleExcludedAlbum(albumId);
|
||||
ref.read(cleanupProvider.notifier).toggleKeepAlbum(albumId);
|
||||
_onKeepSettingsChanged();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'cleanup_keep_all_media_type'.t(context: context),
|
||||
'always_keep'.t(context: context),
|
||||
style: context.textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w500, height: 1.5),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -748,11 +742,11 @@ class _DatePresetCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ExcludedAlbumsSection extends ConsumerWidget {
|
||||
final Set<String> excludedAlbumIds;
|
||||
class _KeepAlbumsSection extends ConsumerWidget {
|
||||
final Set<String> albumIds;
|
||||
final ValueChanged<String> onAlbumToggled;
|
||||
|
||||
const _ExcludedAlbumsSection({required this.excludedAlbumIds, required this.onAlbumToggled});
|
||||
const _KeepAlbumsSection({required this.albumIds, required this.onAlbumToggled});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -762,15 +756,11 @@ class _ExcludedAlbumsSection extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'exclude_albums'.t(context: context),
|
||||
'keep_albums'.t(context: context),
|
||||
style: context.textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w500, height: 1.5),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'exclude_albums_description'.t(context: context),
|
||||
style: context.textTheme.bodyMedium!.copyWith(color: context.textTheme.bodyMedium!.color!.withAlpha(215)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
albumsAsync.when(
|
||||
loading: () => const Center(
|
||||
child: Padding(padding: EdgeInsets.all(16.0), child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
@@ -801,22 +791,18 @@ class _ExcludedAlbumsSection extends ConsumerWidget {
|
||||
itemCount: albums.length,
|
||||
itemBuilder: (context, index) {
|
||||
final album = albums[index];
|
||||
final isExcluded = excludedAlbumIds.contains(album.id);
|
||||
return _AlbumExclusionTile(
|
||||
album: album,
|
||||
isExcluded: isExcluded,
|
||||
onToggle: () => onAlbumToggled(album.id),
|
||||
);
|
||||
final isSelected = albumIds.contains(album.id);
|
||||
return _AlbumTile(album: album, isSelected: isSelected, onToggle: () => onAlbumToggled(album.id));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (excludedAlbumIds.isNotEmpty) ...[
|
||||
if (albumIds.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'excluded_albums_count'.t(context: context, args: {'count': excludedAlbumIds.length.toString()}),
|
||||
'keep_albums_count'.t(context: context, args: {'count': albumIds.length.toString()}),
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -828,12 +814,12 @@ class _ExcludedAlbumsSection extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _AlbumExclusionTile extends StatelessWidget {
|
||||
class _AlbumTile extends StatelessWidget {
|
||||
final LocalAlbum album;
|
||||
final bool isExcluded;
|
||||
final bool isSelected;
|
||||
final VoidCallback onToggle;
|
||||
|
||||
const _AlbumExclusionTile({required this.album, required this.isExcluded, required this.onToggle});
|
||||
const _AlbumTile({required this.album, required this.isSelected, required this.onToggle});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -841,13 +827,13 @@ class _AlbumExclusionTile extends StatelessWidget {
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0),
|
||||
leading: Icon(
|
||||
isExcluded ? Icons.check_circle : Icons.circle_outlined,
|
||||
color: isExcluded ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant,
|
||||
isSelected ? Icons.check_circle : Icons.circle_outlined,
|
||||
color: isSelected ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(
|
||||
album.name,
|
||||
style: context.textTheme.bodyMedium?.copyWith(color: isExcluded ? context.colorScheme.primary : null),
|
||||
style: context.textTheme.bodyMedium?.copyWith(color: isSelected ? context.colorScheme.primary : null),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
@@ -462,7 +462,7 @@ void main() {
|
||||
await insertRemoteAsset(id: 'remote-excluded', checksum: 'checksum-excluded', ownerId: userId);
|
||||
await insertLocalAlbumAsset(albumId: 'album-exclude', assetId: 'local-in-excluded');
|
||||
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, excludedAlbumIds: {'album-exclude'});
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {'album-exclude'});
|
||||
|
||||
expect(candidates.length, 1);
|
||||
expect(candidates[0].id, 'local-in-included');
|
||||
@@ -510,7 +510,7 @@ void main() {
|
||||
final candidates = await repository.getRemovalCandidates(
|
||||
userId,
|
||||
cutoffDate,
|
||||
excludedAlbumIds: {'album-1', 'album-2'},
|
||||
keepAlbumIds: {'album-1', 'album-2'},
|
||||
);
|
||||
|
||||
expect(candidates.length, 1);
|
||||
@@ -533,11 +533,7 @@ void main() {
|
||||
await insertLocalAlbumAsset(albumId: 'album-included', assetId: 'local-both');
|
||||
await insertLocalAlbumAsset(albumId: 'album-excluded', assetId: 'local-both');
|
||||
|
||||
final candidates = await repository.getRemovalCandidates(
|
||||
userId,
|
||||
cutoffDate,
|
||||
excludedAlbumIds: {'album-excluded'},
|
||||
);
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {'album-excluded'});
|
||||
|
||||
expect(candidates, isEmpty);
|
||||
});
|
||||
@@ -565,7 +561,7 @@ void main() {
|
||||
await insertRemoteAsset(id: 'remote-2', checksum: 'checksum-2', ownerId: userId);
|
||||
|
||||
// Empty excludedAlbumIds should include all eligible assets
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, excludedAlbumIds: {});
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {});
|
||||
|
||||
expect(candidates.length, 2);
|
||||
});
|
||||
@@ -594,11 +590,7 @@ void main() {
|
||||
await insertRemoteAsset(id: 'remote-in-excluded', checksum: 'checksum-in-excluded', ownerId: userId);
|
||||
await insertLocalAlbumAsset(albumId: 'album-excluded', assetId: 'local-in-excluded');
|
||||
|
||||
final candidates = await repository.getRemovalCandidates(
|
||||
userId,
|
||||
cutoffDate,
|
||||
excludedAlbumIds: {'album-excluded'},
|
||||
);
|
||||
final candidates = await repository.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {'album-excluded'});
|
||||
|
||||
expect(candidates.length, 1);
|
||||
expect(candidates[0].id, 'local-no-album');
|
||||
@@ -645,7 +637,7 @@ void main() {
|
||||
userId,
|
||||
cutoffDate,
|
||||
keepMediaType: AssetKeepType.photosOnly,
|
||||
excludedAlbumIds: {'album-excluded'},
|
||||
keepAlbumIds: {'album-excluded'},
|
||||
);
|
||||
|
||||
expect(candidates.length, 1);
|
||||
|
||||
Reference in New Issue
Block a user