mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 16:41:59 +00:00
feat(mobile): backup to album
This commit is contained in:
parent
1cd51cc2de
commit
a3e8701f0a
3 changed files with 62 additions and 12 deletions
36
mobile/lib/models/backup/backup_candidate.model.dart
Normal file
36
mobile/lib/models/backup/backup_candidate.model.dart
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
|
|
||||||
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
|
class BackupCandidate {
|
||||||
|
final String albumName;
|
||||||
|
final AssetEntity asset;
|
||||||
|
|
||||||
|
BackupCandidate({
|
||||||
|
required this.albumName,
|
||||||
|
required this.asset,
|
||||||
|
});
|
||||||
|
|
||||||
|
BackupCandidate copyWith({
|
||||||
|
String? albumName,
|
||||||
|
AssetEntity? asset,
|
||||||
|
}) {
|
||||||
|
return BackupCandidate(
|
||||||
|
albumName: albumName ?? this.albumName,
|
||||||
|
asset: asset ?? this.asset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'BackupCandidate(albumName: $albumName, asset: $asset)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant BackupCandidate other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.albumName == albumName && other.asset == asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => albumName.hashCode ^ asset.hashCode;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import 'package:cancellation_token_http/http.dart';
|
import 'package:cancellation_token_http/http.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:immich_mobile/models/backup/backup_candidate.model.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/models/backup/current_upload_asset.model.dart';
|
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
||||||
|
@ -41,7 +41,7 @@ class BackUpState {
|
||||||
final Set<AvailableAlbum> excludedBackupAlbums;
|
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> allUniqueAssets;
|
final Set<BackupCandidate> 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;
|
||||||
|
@ -94,7 +94,7 @@ class BackUpState {
|
||||||
List<AvailableAlbum>? availableAlbums,
|
List<AvailableAlbum>? availableAlbums,
|
||||||
Set<AvailableAlbum>? selectedBackupAlbums,
|
Set<AvailableAlbum>? selectedBackupAlbums,
|
||||||
Set<AvailableAlbum>? excludedBackupAlbums,
|
Set<AvailableAlbum>? excludedBackupAlbums,
|
||||||
Set<AssetEntity>? allUniqueAssets,
|
Set<BackupCandidate>? allUniqueAssets,
|
||||||
Set<String>? selectedAlbumsBackupAssetsIds,
|
Set<String>? selectedAlbumsBackupAssetsIds,
|
||||||
CurrentUploadAsset? currentUploadAsset,
|
CurrentUploadAsset? currentUploadAsset,
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.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/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
|
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||||
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
||||||
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
|
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
|
||||||
|
@ -289,9 +290,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
/// Those assets are unique and are used as the total assets
|
/// Those assets are unique and are used as the total assets
|
||||||
///
|
///
|
||||||
Future<void> _updateBackupAssetCount() async {
|
Future<void> _updateBackupAssetCount() async {
|
||||||
|
debugPrint("UPDATE BACKUP ASSET COUNTTT");
|
||||||
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
||||||
final Set<AssetEntity> assetsFromSelectedAlbums = {};
|
final Set<BackupCandidate> assetsFromSelectedAlbums = {};
|
||||||
final Set<AssetEntity> assetsFromExcludedAlbums = {};
|
final Set<BackupCandidate> 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;
|
||||||
|
@ -304,7 +306,12 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: assetCount,
|
end: assetCount,
|
||||||
);
|
);
|
||||||
assetsFromSelectedAlbums.addAll(assets);
|
|
||||||
|
final candidate = assets.map(
|
||||||
|
(e) => BackupCandidate(albumName: album.albumEntity.name, asset: e),
|
||||||
|
);
|
||||||
|
|
||||||
|
assetsFromSelectedAlbums.addAll(candidate.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final album in state.excludedBackupAlbums) {
|
for (final album in state.excludedBackupAlbums) {
|
||||||
|
@ -318,10 +325,15 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: assetCount,
|
end: assetCount,
|
||||||
);
|
);
|
||||||
assetsFromExcludedAlbums.addAll(assets);
|
|
||||||
|
final candidate = assets.map(
|
||||||
|
(e) => BackupCandidate(albumName: album.albumEntity.name, asset: e),
|
||||||
|
);
|
||||||
|
|
||||||
|
assetsFromExcludedAlbums.addAll(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<AssetEntity> allUniqueAssets =
|
final Set<BackupCandidate> allUniqueAssets =
|
||||||
assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
|
assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
|
||||||
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
||||||
|
|
||||||
|
@ -331,14 +343,14 @@ 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(allUniqueAssets.map((e) => e.id));
|
Set.from(allUniqueAssets.map((e) => e.asset.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
|
||||||
allUniqueAssets.removeWhere(
|
allUniqueAssets.removeWhere(
|
||||||
(asset) => duplicatedAssetIds.contains(asset.id),
|
(e) => duplicatedAssetIds.contains(e.asset.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (allUniqueAssets.isEmpty) {
|
if (allUniqueAssets.isEmpty) {
|
||||||
|
@ -359,6 +371,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
|
|
||||||
// Save to persistent storage
|
// Save to persistent storage
|
||||||
await _updatePersistentAlbumsSelection();
|
await _updatePersistentAlbumsSelection();
|
||||||
|
|
||||||
|
debugPrint("backup asset $allUniqueAssets", wrapWidth: 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all necessary information for calculating the available albums,
|
/// Get all necessary information for calculating the available albums,
|
||||||
|
@ -505,7 +519,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
if (isDuplicated) {
|
if (isDuplicated) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
allUniqueAssets: state.allUniqueAssets
|
allUniqueAssets: state.allUniqueAssets
|
||||||
.where((asset) => asset.id != deviceAssetId)
|
.where((e) => e.asset.id != deviceAssetId)
|
||||||
.toSet(),
|
.toSet(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -522,7 +536,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
state.selectedAlbumsBackupAssetsIds.length ==
|
state.selectedAlbumsBackupAssetsIds.length ==
|
||||||
0) {
|
0) {
|
||||||
final latestAssetBackup =
|
final latestAssetBackup =
|
||||||
state.allUniqueAssets.map((e) => e.modifiedDateTime).reduce(
|
state.allUniqueAssets.map((e) => e.asset.modifiedDateTime).reduce(
|
||||||
(v, e) => e.isAfter(v) ? e : v,
|
(v, e) => e.isAfter(v) ? e : v,
|
||||||
);
|
);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
|
|
Loading…
Reference in a new issue