mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
Revert "chore(mobile): remove exclude album mechanism for backup (#10552)"
This reverts commit 5f47cf604a
.
This commit is contained in:
parent
8f553ddb39
commit
887acb9d9f
12 changed files with 309 additions and 32 deletions
|
@ -18,4 +18,5 @@ class BackupAlbum {
|
||||||
enum BackupSelection {
|
enum BackupSelection {
|
||||||
none,
|
none,
|
||||||
select,
|
select,
|
||||||
|
exclude;
|
||||||
}
|
}
|
||||||
|
|
2
mobile/lib/entities/backup_album.entity.g.dart
generated
2
mobile/lib/entities/backup_album.entity.g.dart
generated
|
@ -107,10 +107,12 @@ P _backupAlbumDeserializeProp<P>(
|
||||||
const _BackupAlbumselectionEnumValueMap = {
|
const _BackupAlbumselectionEnumValueMap = {
|
||||||
'none': 0,
|
'none': 0,
|
||||||
'select': 1,
|
'select': 1,
|
||||||
|
'exclude': 2,
|
||||||
};
|
};
|
||||||
const _BackupAlbumselectionValueEnumMap = {
|
const _BackupAlbumselectionValueEnumMap = {
|
||||||
0: BackupSelection.none,
|
0: BackupSelection.none,
|
||||||
1: BackupSelection.select,
|
1: BackupSelection.select,
|
||||||
|
2: BackupSelection.exclude,
|
||||||
};
|
};
|
||||||
|
|
||||||
Id _backupAlbumGetId(BackupAlbum object) {
|
Id _backupAlbumGetId(BackupAlbum object) {
|
||||||
|
|
|
@ -38,9 +38,10 @@ class BackUpState {
|
||||||
/// All available albums on the device
|
/// All available albums on the device
|
||||||
final List<AvailableAlbum> availableAlbums;
|
final List<AvailableAlbum> availableAlbums;
|
||||||
final Set<AvailableAlbum> selectedBackupAlbums;
|
final Set<AvailableAlbum> selectedBackupAlbums;
|
||||||
|
final Set<AvailableAlbum> excludedBackupAlbums;
|
||||||
|
|
||||||
/// Assets that are not overlapping in selected backup albums and excluded backup albums
|
/// Assets that are not overlapping in selected backup albums and excluded backup albums
|
||||||
final Set<AssetEntity> backupCandidates;
|
final Set<AssetEntity> allUniqueAssets;
|
||||||
|
|
||||||
/// All assets from the selected albums that have been backup
|
/// All assets from the selected albums that have been backup
|
||||||
final Set<String> selectedAlbumsBackupAssetsIds;
|
final Set<String> selectedAlbumsBackupAssetsIds;
|
||||||
|
@ -67,7 +68,8 @@ class BackUpState {
|
||||||
required this.backupTriggerDelay,
|
required this.backupTriggerDelay,
|
||||||
required this.availableAlbums,
|
required this.availableAlbums,
|
||||||
required this.selectedBackupAlbums,
|
required this.selectedBackupAlbums,
|
||||||
required this.backupCandidates,
|
required this.excludedBackupAlbums,
|
||||||
|
required this.allUniqueAssets,
|
||||||
required this.selectedAlbumsBackupAssetsIds,
|
required this.selectedAlbumsBackupAssetsIds,
|
||||||
required this.currentUploadAsset,
|
required this.currentUploadAsset,
|
||||||
});
|
});
|
||||||
|
@ -91,7 +93,8 @@ class BackUpState {
|
||||||
int? backupTriggerDelay,
|
int? backupTriggerDelay,
|
||||||
List<AvailableAlbum>? availableAlbums,
|
List<AvailableAlbum>? availableAlbums,
|
||||||
Set<AvailableAlbum>? selectedBackupAlbums,
|
Set<AvailableAlbum>? selectedBackupAlbums,
|
||||||
Set<AssetEntity>? backupCandidates,
|
Set<AvailableAlbum>? excludedBackupAlbums,
|
||||||
|
Set<AssetEntity>? allUniqueAssets,
|
||||||
Set<String>? selectedAlbumsBackupAssetsIds,
|
Set<String>? selectedAlbumsBackupAssetsIds,
|
||||||
CurrentUploadAsset? currentUploadAsset,
|
CurrentUploadAsset? currentUploadAsset,
|
||||||
}) {
|
}) {
|
||||||
|
@ -118,7 +121,8 @@ class BackUpState {
|
||||||
backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay,
|
backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay,
|
||||||
availableAlbums: availableAlbums ?? this.availableAlbums,
|
availableAlbums: availableAlbums ?? this.availableAlbums,
|
||||||
selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
|
selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
|
||||||
backupCandidates: backupCandidates ?? this.backupCandidates,
|
excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
|
||||||
|
allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets,
|
||||||
selectedAlbumsBackupAssetsIds:
|
selectedAlbumsBackupAssetsIds:
|
||||||
selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds,
|
selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds,
|
||||||
currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset,
|
currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset,
|
||||||
|
@ -127,7 +131,7 @@ class BackUpState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, progressInFileSize: $progressInFileSize, progressInFileSpeed: $progressInFileSpeed, progressInFileSpeeds: $progressInFileSpeeds, progressInFileSpeedUpdateTime: $progressInFileSpeedUpdateTime, progressInFileSpeedUpdateSentBytes: $progressInFileSpeedUpdateSentBytes, iCloudDownloadProgress: $iCloudDownloadProgress, cancelToken: $cancelToken, serverInfo: $serverInfo, autoBackup: $autoBackup, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, backupCandidates: $backupCandidates, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)';
|
return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, progressInFileSize: $progressInFileSize, progressInFileSpeed: $progressInFileSpeed, progressInFileSpeeds: $progressInFileSpeeds, progressInFileSpeedUpdateTime: $progressInFileSpeedUpdateTime, progressInFileSpeedUpdateSentBytes: $progressInFileSpeedUpdateSentBytes, iCloudDownloadProgress: $iCloudDownloadProgress, cancelToken: $cancelToken, serverInfo: $serverInfo, autoBackup: $autoBackup, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -154,7 +158,8 @@ class BackUpState {
|
||||||
other.backupTriggerDelay == backupTriggerDelay &&
|
other.backupTriggerDelay == backupTriggerDelay &&
|
||||||
collectionEquals(other.availableAlbums, availableAlbums) &&
|
collectionEquals(other.availableAlbums, availableAlbums) &&
|
||||||
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
|
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
|
||||||
collectionEquals(other.backupCandidates, backupCandidates) &&
|
collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
|
||||||
|
collectionEquals(other.allUniqueAssets, allUniqueAssets) &&
|
||||||
collectionEquals(
|
collectionEquals(
|
||||||
other.selectedAlbumsBackupAssetsIds,
|
other.selectedAlbumsBackupAssetsIds,
|
||||||
selectedAlbumsBackupAssetsIds,
|
selectedAlbumsBackupAssetsIds,
|
||||||
|
@ -182,7 +187,8 @@ class BackUpState {
|
||||||
backupTriggerDelay.hashCode ^
|
backupTriggerDelay.hashCode ^
|
||||||
availableAlbums.hashCode ^
|
availableAlbums.hashCode ^
|
||||||
selectedBackupAlbums.hashCode ^
|
selectedBackupAlbums.hashCode ^
|
||||||
backupCandidates.hashCode ^
|
excludedBackupAlbums.hashCode ^
|
||||||
|
allUniqueAssets.hashCode ^
|
||||||
selectedAlbumsBackupAssetsIds.hashCode ^
|
selectedAlbumsBackupAssetsIds.hashCode ^
|
||||||
currentUploadAsset.hashCode;
|
currentUploadAsset.hashCode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/backup/album_info_card.dart';
|
import 'package:immich_mobile/widgets/backup/album_info_card.dart';
|
||||||
|
@ -16,6 +17,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
// final availableAlbums = ref.watch(backupProvider).availableAlbums;
|
// final availableAlbums = ref.watch(backupProvider).availableAlbums;
|
||||||
final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
|
final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
|
||||||
|
final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
|
||||||
final isDarkTheme = context.isDarkTheme;
|
final isDarkTheme = context.isDarkTheme;
|
||||||
final albums = ref.watch(backupProvider).availableAlbums;
|
final albums = ref.watch(backupProvider).availableAlbums;
|
||||||
|
|
||||||
|
@ -109,6 +111,83 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||||
}).toSet();
|
}).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildExcludedAlbumNameChip() {
|
||||||
|
return excludedBackupAlbums.map((album) {
|
||||||
|
void removeSelection() {
|
||||||
|
ref
|
||||||
|
.watch(backupProvider.notifier)
|
||||||
|
.removeExcludedAlbumForBackup(album);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: removeSelection,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Chip(
|
||||||
|
label: Text(
|
||||||
|
album.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.red[300],
|
||||||
|
deleteIconColor:
|
||||||
|
isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||||
|
deleteIcon: const Icon(
|
||||||
|
Icons.cancel_rounded,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
onDeleted: removeSelection,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildSearchBar() {
|
||||||
|
// return Padding(
|
||||||
|
// padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 8.0),
|
||||||
|
// child: TextFormField(
|
||||||
|
// onChanged: (searchValue) {
|
||||||
|
// // if (searchValue.isEmpty) {
|
||||||
|
// // albums = availableAlbums;
|
||||||
|
// // } else {
|
||||||
|
// // albums.value = availableAlbums
|
||||||
|
// // .where(
|
||||||
|
// // (album) => album.name
|
||||||
|
// // .toLowerCase()
|
||||||
|
// // .contains(searchValue.toLowerCase()),
|
||||||
|
// // )
|
||||||
|
// // .toList();
|
||||||
|
// // }
|
||||||
|
// },
|
||||||
|
// decoration: InputDecoration(
|
||||||
|
// contentPadding: const EdgeInsets.symmetric(
|
||||||
|
// horizontal: 8.0,
|
||||||
|
// vertical: 8.0,
|
||||||
|
// ),
|
||||||
|
// hintText: "Search",
|
||||||
|
// hintStyle: TextStyle(
|
||||||
|
// color: isDarkTheme ? Colors.white : Colors.grey,
|
||||||
|
// fontSize: 14.0,
|
||||||
|
// ),
|
||||||
|
// prefixIcon: const Icon(
|
||||||
|
// Icons.search,
|
||||||
|
// color: Colors.grey,
|
||||||
|
// ),
|
||||||
|
// border: OutlineInputBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(10),
|
||||||
|
// borderSide: BorderSide.none,
|
||||||
|
// ),
|
||||||
|
// filled: true,
|
||||||
|
// fillColor: isDarkTheme ? Colors.white30 : Colors.grey[200],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
@ -144,6 +223,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
children: [
|
children: [
|
||||||
...buildSelectedAlbumNameChip(),
|
...buildSelectedAlbumNameChip(),
|
||||||
|
...buildExcludedAlbumNameChip(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -29,7 +29,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
final didGetBackupInfo = useState(false);
|
final didGetBackupInfo = useState(false);
|
||||||
bool hasExclusiveAccess =
|
bool hasExclusiveAccess =
|
||||||
backupState.backupProgress != BackUpProgressEnum.inBackground;
|
backupState.backupProgress != BackUpProgressEnum.inBackground;
|
||||||
bool shouldBackup = backupState.backupCandidates.length -
|
bool shouldBackup = backupState.allUniqueAssets.length -
|
||||||
backupState.selectedAlbumsBackupAssetsIds.length ==
|
backupState.selectedAlbumsBackupAssetsIds.length ==
|
||||||
0 ||
|
0 ||
|
||||||
!hasExclusiveAccess
|
!hasExclusiveAccess
|
||||||
|
@ -100,6 +100,29 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildExcludedAlbumName() {
|
||||||
|
var text = "backup_controller_page_excluded".tr();
|
||||||
|
var albums = ref.watch(backupProvider).excludedBackupAlbums;
|
||||||
|
|
||||||
|
if (albums.isNotEmpty) {
|
||||||
|
for (var album in albums) {
|
||||||
|
text += "${album.name}, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
text.trim().substring(0, text.length - 2),
|
||||||
|
style: context.textTheme.labelLarge?.copyWith(
|
||||||
|
color: Colors.red[300],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildFolderSelectionTile() {
|
buildFolderSelectionTile() {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
@ -131,6 +154,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
).tr(),
|
).tr(),
|
||||||
buildSelectedAlbumName(),
|
buildSelectedAlbumName(),
|
||||||
|
buildExcludedAlbumName(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -268,7 +292,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
subtitle: "backup_controller_page_total_sub".tr(),
|
subtitle: "backup_controller_page_total_sub".tr(),
|
||||||
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
||||||
? "..."
|
? "..."
|
||||||
: "${backupState.backupCandidates.length}",
|
: "${backupState.allUniqueAssets.length}",
|
||||||
),
|
),
|
||||||
BackupInfoCard(
|
BackupInfoCard(
|
||||||
title: "backup_controller_page_backup".tr(),
|
title: "backup_controller_page_backup".tr(),
|
||||||
|
@ -282,7 +306,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
subtitle: "backup_controller_page_remainder_sub".tr(),
|
subtitle: "backup_controller_page_remainder_sub".tr(),
|
||||||
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
info: ref.watch(backupProvider).availableAlbums.isEmpty
|
||||||
? "..."
|
? "..."
|
||||||
: "${max(0, backupState.backupCandidates.length - backupState.selectedAlbumsBackupAssetsIds.length)}",
|
: "${max(0, backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length)}",
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const CurrentUploadingAssetInfoBox(),
|
const CurrentUploadingAssetInfoBox(),
|
||||||
|
|
|
@ -61,7 +61,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
),
|
),
|
||||||
availableAlbums: const [],
|
availableAlbums: const [],
|
||||||
selectedBackupAlbums: const {},
|
selectedBackupAlbums: const {},
|
||||||
backupCandidates: const {},
|
excludedBackupAlbums: const {},
|
||||||
|
allUniqueAssets: const {},
|
||||||
selectedAlbumsBackupAssetsIds: const {},
|
selectedAlbumsBackupAssetsIds: const {},
|
||||||
currentUploadAsset: CurrentUploadAsset(
|
currentUploadAsset: CurrentUploadAsset(
|
||||||
id: '...',
|
id: '...',
|
||||||
|
@ -93,10 +94,22 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
/// The total unique assets will be used for backing mechanism
|
/// The total unique assets will be used for backing mechanism
|
||||||
///
|
///
|
||||||
void addAlbumForBackup(AvailableAlbum album) {
|
void addAlbumForBackup(AvailableAlbum album) {
|
||||||
|
if (state.excludedBackupAlbums.contains(album)) {
|
||||||
|
removeExcludedAlbumForBackup(album);
|
||||||
|
}
|
||||||
|
|
||||||
state = state
|
state = state
|
||||||
.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album});
|
.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addExcludedAlbumForBackup(AvailableAlbum album) {
|
||||||
|
if (state.selectedBackupAlbums.contains(album)) {
|
||||||
|
removeAlbumForBackup(album);
|
||||||
|
}
|
||||||
|
state = state
|
||||||
|
.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album});
|
||||||
|
}
|
||||||
|
|
||||||
void removeAlbumForBackup(AvailableAlbum album) {
|
void removeAlbumForBackup(AvailableAlbum album) {
|
||||||
Set<AvailableAlbum> currentSelectedAlbums = state.selectedBackupAlbums;
|
Set<AvailableAlbum> currentSelectedAlbums = state.selectedBackupAlbums;
|
||||||
|
|
||||||
|
@ -105,6 +118,14 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
state = state.copyWith(selectedBackupAlbums: currentSelectedAlbums);
|
state = state.copyWith(selectedBackupAlbums: currentSelectedAlbums);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void removeExcludedAlbumForBackup(AvailableAlbum album) {
|
||||||
|
Set<AvailableAlbum> currentExcludedAlbums = state.excludedBackupAlbums;
|
||||||
|
|
||||||
|
currentExcludedAlbums.removeWhere((a) => a == album);
|
||||||
|
|
||||||
|
state = state.copyWith(excludedBackupAlbums: currentExcludedAlbums);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> backupAlbumSelectionDone() {
|
Future<void> backupAlbumSelectionDone() {
|
||||||
if (state.selectedBackupAlbums.isEmpty) {
|
if (state.selectedBackupAlbums.isEmpty) {
|
||||||
// disable any backup
|
// disable any backup
|
||||||
|
@ -219,6 +240,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
}
|
}
|
||||||
state = state.copyWith(availableAlbums: availableAlbums);
|
state = state.copyWith(availableAlbums: availableAlbums);
|
||||||
|
|
||||||
|
final List<BackupAlbum> excludedBackupAlbums =
|
||||||
|
await _backupService.excludedAlbumsQuery().findAll();
|
||||||
final List<BackupAlbum> selectedBackupAlbums =
|
final List<BackupAlbum> selectedBackupAlbums =
|
||||||
await _backupService.selectedAlbumsQuery().findAll();
|
await _backupService.selectedAlbumsQuery().findAll();
|
||||||
|
|
||||||
|
@ -236,8 +259,22 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Set<AvailableAlbum> excludedAlbums = {};
|
||||||
|
for (final BackupAlbum ba in excludedBackupAlbums) {
|
||||||
|
final albumAsset = albumMap[ba.id];
|
||||||
|
|
||||||
|
if (albumAsset != null) {
|
||||||
|
excludedAlbums.add(
|
||||||
|
AvailableAlbum(albumEntity: albumAsset, lastBackup: ba.lastBackup),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.severe('Excluded album not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
selectedBackupAlbums: selectedAlbums,
|
selectedBackupAlbums: selectedAlbums,
|
||||||
|
excludedBackupAlbums: excludedAlbums,
|
||||||
);
|
);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
|
@ -253,7 +290,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
///
|
///
|
||||||
Future<void> _updateBackupAssetCount() async {
|
Future<void> _updateBackupAssetCount() async {
|
||||||
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
||||||
final Set<AssetEntity> backupCandidates = {};
|
final Set<AssetEntity> assetsFromSelectedAlbums = {};
|
||||||
|
final Set<AssetEntity> assetsFromExcludedAlbums = {};
|
||||||
|
|
||||||
for (final album in state.selectedBackupAlbums) {
|
for (final album in state.selectedBackupAlbums) {
|
||||||
final assetCount = await album.albumEntity.assetCountAsync;
|
final assetCount = await album.albumEntity.assetCountAsync;
|
||||||
|
@ -266,9 +304,25 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: assetCount,
|
end: assetCount,
|
||||||
);
|
);
|
||||||
backupCandidates.addAll(assets);
|
assetsFromSelectedAlbums.addAll(assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final album in state.excludedBackupAlbums) {
|
||||||
|
final assetCount = await album.albumEntity.assetCountAsync;
|
||||||
|
|
||||||
|
if (assetCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final assets = await album.albumEntity.getAssetListRange(
|
||||||
|
start: 0,
|
||||||
|
end: assetCount,
|
||||||
|
);
|
||||||
|
assetsFromExcludedAlbums.addAll(assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<AssetEntity> allUniqueAssets =
|
||||||
|
assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
|
||||||
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
||||||
|
|
||||||
if (allAssetsInDatabase == null) {
|
if (allAssetsInDatabase == null) {
|
||||||
|
@ -277,28 +331,28 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
|
|
||||||
// Find asset that were backup from selected albums
|
// Find asset that were backup from selected albums
|
||||||
final Set<String> selectedAlbumsBackupAssets =
|
final Set<String> selectedAlbumsBackupAssets =
|
||||||
Set.from(backupCandidates.map((e) => e.id));
|
Set.from(allUniqueAssets.map((e) => e.id));
|
||||||
|
|
||||||
selectedAlbumsBackupAssets
|
selectedAlbumsBackupAssets
|
||||||
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
||||||
|
|
||||||
// Remove duplicated asset from all unique assets
|
// Remove duplicated asset from all unique assets
|
||||||
backupCandidates.removeWhere(
|
allUniqueAssets.removeWhere(
|
||||||
(asset) => duplicatedAssetIds.contains(asset.id),
|
(asset) => duplicatedAssetIds.contains(asset.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (backupCandidates.isEmpty) {
|
if (allUniqueAssets.isEmpty) {
|
||||||
log.info("No assets are selected for back up");
|
log.info("No assets are selected for back up");
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backupProgress: BackUpProgressEnum.idle,
|
backupProgress: BackUpProgressEnum.idle,
|
||||||
allAssetsInDatabase: allAssetsInDatabase,
|
allAssetsInDatabase: allAssetsInDatabase,
|
||||||
backupCandidates: {},
|
allUniqueAssets: {},
|
||||||
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
allAssetsInDatabase: allAssetsInDatabase,
|
allAssetsInDatabase: allAssetsInDatabase,
|
||||||
backupCandidates: backupCandidates,
|
allUniqueAssets: allUniqueAssets,
|
||||||
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -333,8 +387,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
final selected = state.selectedBackupAlbums.map(
|
final selected = state.selectedBackupAlbums.map(
|
||||||
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.select),
|
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.select),
|
||||||
);
|
);
|
||||||
|
final excluded = state.excludedBackupAlbums.map(
|
||||||
final backupAlbums = selected.toList();
|
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.exclude),
|
||||||
|
);
|
||||||
|
final backupAlbums = selected.followedBy(excluded).toList();
|
||||||
backupAlbums.sortBy((e) => e.id);
|
backupAlbums.sortBy((e) => e.id);
|
||||||
return _db.writeTxn(() async {
|
return _db.writeTxn(() async {
|
||||||
final dbAlbums = await _db.backupAlbums.where().sortById().findAll();
|
final dbAlbums = await _db.backupAlbums.where().sortById().findAll();
|
||||||
|
@ -371,13 +427,13 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
await PhotoManager.clearFileCache();
|
await PhotoManager.clearFileCache();
|
||||||
|
|
||||||
if (state.backupCandidates.isEmpty) {
|
if (state.allUniqueAssets.isEmpty) {
|
||||||
log.info("No Asset On Device - Abort Backup Process");
|
log.info("No Asset On Device - Abort Backup Process");
|
||||||
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<AssetEntity> assetsWillBeBackup = Set.from(state.backupCandidates);
|
Set<AssetEntity> assetsWillBeBackup = Set.from(state.allUniqueAssets);
|
||||||
// Remove item that has already been backed up
|
// Remove item that has already been backed up
|
||||||
for (final assetId in state.allAssetsInDatabase) {
|
for (final assetId in state.allAssetsInDatabase) {
|
||||||
assetsWillBeBackup.removeWhere((e) => e.id == assetId);
|
assetsWillBeBackup.removeWhere((e) => e.id == assetId);
|
||||||
|
@ -448,7 +504,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
) {
|
) {
|
||||||
if (isDuplicated) {
|
if (isDuplicated) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backupCandidates: state.backupCandidates
|
allUniqueAssets: state.allUniqueAssets
|
||||||
.where((asset) => asset.id != deviceAssetId)
|
.where((asset) => asset.id != deviceAssetId)
|
||||||
.toSet(),
|
.toSet(),
|
||||||
);
|
);
|
||||||
|
@ -462,17 +518,20 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.backupCandidates.length -
|
if (state.allUniqueAssets.length -
|
||||||
state.selectedAlbumsBackupAssetsIds.length ==
|
state.selectedAlbumsBackupAssetsIds.length ==
|
||||||
0) {
|
0) {
|
||||||
final latestAssetBackup =
|
final latestAssetBackup =
|
||||||
state.backupCandidates.map((e) => e.modifiedDateTime).reduce(
|
state.allUniqueAssets.map((e) => e.modifiedDateTime).reduce(
|
||||||
(v, e) => e.isAfter(v) ? e : v,
|
(v, e) => e.isAfter(v) ? e : v,
|
||||||
);
|
);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
selectedBackupAlbums: state.selectedBackupAlbums
|
selectedBackupAlbums: state.selectedBackupAlbums
|
||||||
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
||||||
.toSet(),
|
.toSet(),
|
||||||
|
excludedBackupAlbums: state.excludedBackupAlbums
|
||||||
|
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
||||||
|
.toSet(),
|
||||||
backupProgress: BackUpProgressEnum.done,
|
backupProgress: BackUpProgressEnum.done,
|
||||||
progressInPercentage: 0.0,
|
progressInPercentage: 0.0,
|
||||||
progressInFileSize: "0 B / 0 B",
|
progressInFileSize: "0 B / 0 B",
|
||||||
|
@ -571,8 +630,12 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
.filter()
|
.filter()
|
||||||
.selectionEqualTo(BackupSelection.select)
|
.selectionEqualTo(BackupSelection.select)
|
||||||
.findAll();
|
.findAll();
|
||||||
|
final List<BackupAlbum> excludedBackupAlbums = await _db.backupAlbums
|
||||||
|
.filter()
|
||||||
|
.selectionEqualTo(BackupSelection.exclude)
|
||||||
|
.findAll();
|
||||||
Set<AvailableAlbum> selectedAlbums = state.selectedBackupAlbums;
|
Set<AvailableAlbum> selectedAlbums = state.selectedBackupAlbums;
|
||||||
|
Set<AvailableAlbum> excludedAlbums = state.excludedBackupAlbums;
|
||||||
if (selectedAlbums.isNotEmpty) {
|
if (selectedAlbums.isNotEmpty) {
|
||||||
selectedAlbums = _updateAlbumsBackupTime(
|
selectedAlbums = _updateAlbumsBackupTime(
|
||||||
selectedAlbums,
|
selectedAlbums,
|
||||||
|
@ -580,10 +643,17 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (excludedAlbums.isNotEmpty) {
|
||||||
|
excludedAlbums = _updateAlbumsBackupTime(
|
||||||
|
excludedAlbums,
|
||||||
|
excludedBackupAlbums,
|
||||||
|
);
|
||||||
|
}
|
||||||
final BackUpProgressEnum previous = state.backupProgress;
|
final BackUpProgressEnum previous = state.backupProgress;
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backupProgress: BackUpProgressEnum.inBackground,
|
backupProgress: BackUpProgressEnum.inBackground,
|
||||||
selectedBackupAlbums: selectedAlbums,
|
selectedBackupAlbums: selectedAlbums,
|
||||||
|
excludedBackupAlbums: excludedAlbums,
|
||||||
);
|
);
|
||||||
// assumes the background service is currently running
|
// assumes the background service is currently running
|
||||||
// if true, waits until it has stopped to start the backup
|
// if true, waits until it has stopped to start the backup
|
||||||
|
|
|
@ -23,7 +23,7 @@ class BackupVerification extends _$BackupVerification {
|
||||||
state = true;
|
state = true;
|
||||||
final backupState = ref.read(backupProvider);
|
final backupState = ref.read(backupProvider);
|
||||||
|
|
||||||
if (backupState.backupCandidates.length >
|
if (backupState.allUniqueAssets.length >
|
||||||
backupState.selectedAlbumsBackupAssetsIds.length) {
|
backupState.selectedAlbumsBackupAssetsIds.length) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
|
|
|
@ -7,7 +7,7 @@ part of 'backup_verification.provider.dart';
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$backupVerificationHash() =>
|
String _$backupVerificationHash() =>
|
||||||
r'4f64459d68d20de4a61160ec8e9be347ec945fb6';
|
r'b691e0cc27856eef189258d3c102cc73ce4812a4';
|
||||||
|
|
||||||
/// See also [BackupVerification].
|
/// See also [BackupVerification].
|
||||||
@ProviderFor(BackupVerification)
|
@ProviderFor(BackupVerification)
|
||||||
|
|
|
@ -349,6 +349,7 @@ class BackgroundService {
|
||||||
AppSettingsService settingsService = AppSettingsService();
|
AppSettingsService settingsService = AppSettingsService();
|
||||||
|
|
||||||
final selectedAlbums = backupService.selectedAlbumsQuery().findAllSync();
|
final selectedAlbums = backupService.selectedAlbumsQuery().findAllSync();
|
||||||
|
final excludedAlbums = backupService.excludedAlbumsQuery().findAllSync();
|
||||||
if (selectedAlbums.isEmpty) {
|
if (selectedAlbums.isEmpty) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -360,10 +361,11 @@ class BackgroundService {
|
||||||
backupService,
|
backupService,
|
||||||
settingsService,
|
settingsService,
|
||||||
selectedAlbums,
|
selectedAlbums,
|
||||||
|
excludedAlbums,
|
||||||
);
|
);
|
||||||
if (backupOk) {
|
if (backupOk) {
|
||||||
await Store.delete(StoreKey.backupFailedSince);
|
await Store.delete(StoreKey.backupFailedSince);
|
||||||
final backupAlbums = [...selectedAlbums];
|
final backupAlbums = [...selectedAlbums, ...excludedAlbums];
|
||||||
backupAlbums.sortBy((e) => e.id);
|
backupAlbums.sortBy((e) => e.id);
|
||||||
db.writeTxnSync(() {
|
db.writeTxnSync(() {
|
||||||
final dbAlbums = db.backupAlbums.where().sortById().findAllSync();
|
final dbAlbums = db.backupAlbums.where().sortById().findAllSync();
|
||||||
|
@ -402,6 +404,7 @@ class BackgroundService {
|
||||||
BackupService backupService,
|
BackupService backupService,
|
||||||
AppSettingsService settingsService,
|
AppSettingsService settingsService,
|
||||||
List<BackupAlbum> selectedAlbums,
|
List<BackupAlbum> selectedAlbums,
|
||||||
|
List<BackupAlbum> excludedAlbums,
|
||||||
) async {
|
) async {
|
||||||
_errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService);
|
_errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService);
|
||||||
final bool notifyTotalProgress = settingsService
|
final bool notifyTotalProgress = settingsService
|
||||||
|
@ -415,6 +418,7 @@ class BackgroundService {
|
||||||
|
|
||||||
List<AssetEntity> toUpload = await backupService.buildUploadCandidates(
|
List<AssetEntity> toUpload = await backupService.buildUploadCandidates(
|
||||||
selectedAlbums,
|
selectedAlbums,
|
||||||
|
excludedAlbums,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -65,10 +65,14 @@ class BackupService {
|
||||||
QueryBuilder<BackupAlbum, BackupAlbum, QAfterFilterCondition>
|
QueryBuilder<BackupAlbum, BackupAlbum, QAfterFilterCondition>
|
||||||
selectedAlbumsQuery() =>
|
selectedAlbumsQuery() =>
|
||||||
_db.backupAlbums.filter().selectionEqualTo(BackupSelection.select);
|
_db.backupAlbums.filter().selectionEqualTo(BackupSelection.select);
|
||||||
|
QueryBuilder<BackupAlbum, BackupAlbum, QAfterFilterCondition>
|
||||||
|
excludedAlbumsQuery() =>
|
||||||
|
_db.backupAlbums.filter().selectionEqualTo(BackupSelection.exclude);
|
||||||
|
|
||||||
/// Returns all assets newer than the last successful backup per album
|
/// Returns all assets newer than the last successful backup per album
|
||||||
Future<List<AssetEntity>> buildUploadCandidates(
|
Future<List<AssetEntity>> buildUploadCandidates(
|
||||||
List<BackupAlbum> selectedBackupAlbums,
|
List<BackupAlbum> selectedBackupAlbums,
|
||||||
|
List<BackupAlbum> excludedBackupAlbums,
|
||||||
) async {
|
) async {
|
||||||
final filter = FilterOptionGroup(
|
final filter = FilterOptionGroup(
|
||||||
containsPathModified: true,
|
containsPathModified: true,
|
||||||
|
@ -85,13 +89,19 @@ class BackupService {
|
||||||
}
|
}
|
||||||
final int allIdx = selectedAlbums.indexWhere((e) => e != null && e.isAll);
|
final int allIdx = selectedAlbums.indexWhere((e) => e != null && e.isAll);
|
||||||
if (allIdx != -1) {
|
if (allIdx != -1) {
|
||||||
|
final List<AssetPathEntity?> excludedAlbums =
|
||||||
|
await _loadAlbumsWithTimeFilter(excludedBackupAlbums, filter, now);
|
||||||
final List<AssetEntity> toAdd = await _fetchAssetsAndUpdateLastBackup(
|
final List<AssetEntity> toAdd = await _fetchAssetsAndUpdateLastBackup(
|
||||||
selectedAlbums.slice(allIdx, allIdx + 1),
|
selectedAlbums.slice(allIdx, allIdx + 1),
|
||||||
selectedBackupAlbums.slice(allIdx, allIdx + 1),
|
selectedBackupAlbums.slice(allIdx, allIdx + 1),
|
||||||
now,
|
now,
|
||||||
);
|
);
|
||||||
|
final List<AssetEntity> toRemove = await _fetchAssetsAndUpdateLastBackup(
|
||||||
return toAdd.toSet().toList();
|
excludedAlbums,
|
||||||
|
excludedBackupAlbums,
|
||||||
|
now,
|
||||||
|
);
|
||||||
|
return toAdd.toSet().difference(toRemove.toSet()).toList();
|
||||||
} else {
|
} else {
|
||||||
return await _fetchAssetsAndUpdateLastBackup(
|
return await _fetchAssetsAndUpdateLastBackup(
|
||||||
selectedAlbums,
|
selectedAlbums,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
class AlbumInfoCard extends HookConsumerWidget {
|
class AlbumInfoCard extends HookConsumerWidget {
|
||||||
final AvailableAlbum album;
|
final AvailableAlbum album;
|
||||||
|
@ -17,6 +19,8 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final bool isSelected =
|
final bool isSelected =
|
||||||
ref.watch(backupProvider).selectedBackupAlbums.contains(album);
|
ref.watch(backupProvider).selectedBackupAlbums.contains(album);
|
||||||
|
final bool isExcluded =
|
||||||
|
ref.watch(backupProvider).excludedBackupAlbums.contains(album);
|
||||||
|
|
||||||
final isDarkTheme = context.isDarkTheme;
|
final isDarkTheme = context.isDarkTheme;
|
||||||
|
|
||||||
|
@ -24,7 +28,8 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||||
context.primaryColor.withAlpha(100),
|
context.primaryColor.withAlpha(100),
|
||||||
BlendMode.darken,
|
BlendMode.darken,
|
||||||
);
|
);
|
||||||
|
ColorFilter excludedFilter =
|
||||||
|
ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken);
|
||||||
ColorFilter unselectedFilter =
|
ColorFilter unselectedFilter =
|
||||||
const ColorFilter.mode(Colors.black, BlendMode.color);
|
const ColorFilter.mode(Colors.black, BlendMode.color);
|
||||||
|
|
||||||
|
@ -43,6 +48,20 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||||
).tr(),
|
).tr(),
|
||||||
backgroundColor: context.primaryColor,
|
backgroundColor: context.primaryColor,
|
||||||
);
|
);
|
||||||
|
} else if (isExcluded) {
|
||||||
|
return Chip(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||||
|
label: Text(
|
||||||
|
"album_info_card_backup_album_excluded",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: isDarkTheme ? Colors.black : Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
backgroundColor: Colors.red[300],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
|
@ -51,6 +70,8 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||||
buildImageFilter() {
|
buildImageFilter() {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return selectedFilter;
|
return selectedFilter;
|
||||||
|
} else if (isExcluded) {
|
||||||
|
return excludedFilter;
|
||||||
} else {
|
} else {
|
||||||
return unselectedFilter;
|
return unselectedFilter;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +87,28 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||||
ref.read(backupProvider.notifier).addAlbumForBackup(album);
|
ref.read(backupProvider.notifier).addAlbumForBackup(album);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onDoubleTap: () {
|
||||||
|
ref.read(hapticFeedbackProvider.notifier).selectionClick();
|
||||||
|
|
||||||
|
if (isExcluded) {
|
||||||
|
// Remove from exclude album list
|
||||||
|
ref.read(backupProvider.notifier).removeExcludedAlbumForBackup(album);
|
||||||
|
} else {
|
||||||
|
// Add to exclude album list
|
||||||
|
|
||||||
|
if (album.id == 'isAll' || album.name == 'Recents') {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: 'Cannot exclude album contains all assets',
|
||||||
|
toastType: ToastType.error,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(backupProvider.notifier).addExcludedAlbumForBackup(album);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Card(
|
child: Card(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
margin: const EdgeInsets.all(1),
|
margin: const EdgeInsets.all(1),
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
class AlbumInfoListTile extends HookConsumerWidget {
|
class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
final AvailableAlbum album;
|
final AvailableAlbum album;
|
||||||
|
@ -17,6 +19,8 @@ class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final bool isSelected =
|
final bool isSelected =
|
||||||
ref.watch(backupProvider).selectedBackupAlbums.contains(album);
|
ref.watch(backupProvider).selectedBackupAlbums.contains(album);
|
||||||
|
final bool isExcluded =
|
||||||
|
ref.watch(backupProvider).excludedBackupAlbums.contains(album);
|
||||||
var assetCount = useState(0);
|
var assetCount = useState(0);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
|
@ -32,6 +36,10 @@ class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
return context.isDarkTheme
|
return context.isDarkTheme
|
||||||
? context.primaryColor.withAlpha(100)
|
? context.primaryColor.withAlpha(100)
|
||||||
: context.primaryColor.withAlpha(25);
|
: context.primaryColor.withAlpha(25);
|
||||||
|
} else if (isExcluded) {
|
||||||
|
return context.isDarkTheme
|
||||||
|
? Colors.red[300]?.withAlpha(150)
|
||||||
|
: Colors.red[100]?.withAlpha(150);
|
||||||
} else {
|
} else {
|
||||||
return Colors.transparent;
|
return Colors.transparent;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +53,13 @@ class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isExcluded) {
|
||||||
|
return const Icon(
|
||||||
|
Icons.remove_circle_rounded,
|
||||||
|
color: Colors.red,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Icon(
|
return Icon(
|
||||||
Icons.circle,
|
Icons.circle,
|
||||||
color: context.isDarkTheme ? Colors.grey[400] : Colors.black45,
|
color: context.isDarkTheme ? Colors.grey[400] : Colors.black45,
|
||||||
|
@ -52,6 +67,28 @@ class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
onDoubleTap: () {
|
||||||
|
ref.watch(hapticFeedbackProvider.notifier).selectionClick();
|
||||||
|
|
||||||
|
if (isExcluded) {
|
||||||
|
// Remove from exclude album list
|
||||||
|
ref.read(backupProvider.notifier).removeExcludedAlbumForBackup(album);
|
||||||
|
} else {
|
||||||
|
// Add to exclude album list
|
||||||
|
|
||||||
|
if (album.id == 'isAll' || album.name == 'Recents') {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: 'Cannot exclude album contains all assets',
|
||||||
|
toastType: ToastType.error,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(backupProvider.notifier).addExcludedAlbumForBackup(album);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
tileColor: buildTileColor(),
|
tileColor: buildTileColor(),
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
|
|
Loading…
Add table
Reference in a new issue