mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
refactor(mobile): backup provider (#16360)
* refactor(mobile): backup provider * refactor(mobile): backup provider
This commit is contained in:
parent
082471dfd9
commit
c70c9067b0
@ -1,7 +1,7 @@
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
||||
|
||||
abstract interface class IBackupRepository implements IDatabaseRepository {
|
||||
abstract interface class IBackupAlbumRepository implements IDatabaseRepository {
|
||||
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort});
|
||||
|
||||
Future<List<String>> getIdsBySelection(BackupSelection backup);
|
@ -10,7 +10,7 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
||||
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
||||
@ -23,21 +23,34 @@ import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
|
||||
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||
import 'package:immich_mobile/services/background.service.dart';
|
||||
import 'package:immich_mobile/services/backup.service.dart';
|
||||
import 'package:immich_mobile/services/backup_album.service.dart';
|
||||
import 'package:immich_mobile/services/server_info.service.dart';
|
||||
import 'package:immich_mobile/utils/backup_progress.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
|
||||
|
||||
final backupProvider =
|
||||
StateNotifierProvider<BackupNotifier, BackUpState>((ref) {
|
||||
return BackupNotifier(
|
||||
ref.watch(backupServiceProvider),
|
||||
ref.watch(serverInfoServiceProvider),
|
||||
ref.watch(authProvider),
|
||||
ref.watch(backgroundServiceProvider),
|
||||
ref.watch(galleryPermissionNotifier.notifier),
|
||||
ref.watch(albumMediaRepositoryProvider),
|
||||
ref.watch(fileMediaRepositoryProvider),
|
||||
ref.watch(backupAlbumServiceProvider),
|
||||
ref,
|
||||
);
|
||||
});
|
||||
|
||||
class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
BackupNotifier(
|
||||
this._backupService,
|
||||
@ -45,10 +58,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
this._authState,
|
||||
this._backgroundService,
|
||||
this._galleryPermissionNotifier,
|
||||
this._db,
|
||||
this._albumMediaRepository,
|
||||
this._fileMediaRepository,
|
||||
this._backupRepository,
|
||||
this._backupAlbumService,
|
||||
this.ref,
|
||||
) : super(
|
||||
BackUpState(
|
||||
@ -96,10 +108,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
final AuthState _authState;
|
||||
final BackgroundService _backgroundService;
|
||||
final GalleryPermissionNotifier _galleryPermissionNotifier;
|
||||
final Isar _db;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IFileMediaRepository _fileMediaRepository;
|
||||
final IBackupRepository _backupRepository;
|
||||
final BackupAlbumService _backupAlbumService;
|
||||
final Ref ref;
|
||||
|
||||
///
|
||||
@ -260,9 +271,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
state = state.copyWith(availableAlbums: availableAlbums);
|
||||
|
||||
final List<BackupAlbum> excludedBackupAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.exclude);
|
||||
await _backupAlbumService.getAllBySelection(BackupSelection.exclude);
|
||||
final List<BackupAlbum> selectedBackupAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.select);
|
||||
await _backupAlbumService.getAllBySelection(BackupSelection.select);
|
||||
|
||||
final Set<AvailableAlbum> selectedAlbums = {};
|
||||
for (final BackupAlbum ba in selectedBackupAlbums) {
|
||||
@ -439,7 +450,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
}
|
||||
|
||||
/// Save user selection of selected albums and excluded albums to database
|
||||
Future<void> _updatePersistentAlbumsSelection() {
|
||||
Future<void> _updatePersistentAlbumsSelection() async {
|
||||
final epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
|
||||
final selected = state.selectedBackupAlbums.map(
|
||||
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.select),
|
||||
@ -447,29 +458,30 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
final excluded = state.excludedBackupAlbums.map(
|
||||
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.exclude),
|
||||
);
|
||||
final backupAlbums = selected.followedBy(excluded).toList();
|
||||
backupAlbums.sortBy((e) => e.id);
|
||||
return _db.writeTxn(() async {
|
||||
final dbAlbums = await _db.backupAlbums.where().sortById().findAll();
|
||||
final List<int> toDelete = [];
|
||||
final List<BackupAlbum> toUpsert = [];
|
||||
// stores the most recent `lastBackup` per album but always keeps the `selection` the user just made
|
||||
diffSortedListsSync(
|
||||
dbAlbums,
|
||||
backupAlbums,
|
||||
compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id),
|
||||
both: (BackupAlbum a, BackupAlbum b) {
|
||||
b.lastBackup =
|
||||
a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup;
|
||||
toUpsert.add(b);
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (BackupAlbum a) => toDelete.add(a.isarId),
|
||||
onlySecond: (BackupAlbum b) => toUpsert.add(b),
|
||||
);
|
||||
await _db.backupAlbums.deleteAll(toDelete);
|
||||
await _db.backupAlbums.putAll(toUpsert);
|
||||
});
|
||||
final candidates = selected.followedBy(excluded).toList();
|
||||
candidates.sortBy((e) => e.id);
|
||||
|
||||
final savedBackupAlbums =
|
||||
await _backupAlbumService.getAll(sort: BackupAlbumSort.id);
|
||||
final List<int> toDelete = [];
|
||||
final List<BackupAlbum> toUpsert = [];
|
||||
|
||||
diffSortedListsSync(
|
||||
savedBackupAlbums,
|
||||
candidates,
|
||||
compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id),
|
||||
both: (BackupAlbum a, BackupAlbum b) {
|
||||
b.lastBackup =
|
||||
a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup;
|
||||
toUpsert.add(b);
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (BackupAlbum a) => toDelete.add(a.isarId),
|
||||
onlySecond: (BackupAlbum b) => toUpsert.add(b),
|
||||
);
|
||||
|
||||
await _backupAlbumService.deleteAll(toDelete);
|
||||
await _backupAlbumService.updateAll(toUpsert);
|
||||
}
|
||||
|
||||
/// Invoke backup process
|
||||
@ -686,14 +698,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
}
|
||||
|
||||
Future<void> resumeBackup() async {
|
||||
final List<BackupAlbum> selectedBackupAlbums = await _db.backupAlbums
|
||||
.filter()
|
||||
.selectionEqualTo(BackupSelection.select)
|
||||
.findAll();
|
||||
final List<BackupAlbum> excludedBackupAlbums = await _db.backupAlbums
|
||||
.filter()
|
||||
.selectionEqualTo(BackupSelection.exclude)
|
||||
.findAll();
|
||||
final List<BackupAlbum> selectedBackupAlbums =
|
||||
await _backupAlbumService.getAllBySelection(BackupSelection.select);
|
||||
final List<BackupAlbum> excludedBackupAlbums =
|
||||
await _backupAlbumService.getAllBySelection(BackupSelection.exclude);
|
||||
Set<AvailableAlbum> selectedAlbums = state.selectedBackupAlbums;
|
||||
Set<AvailableAlbum> excludedAlbums = state.excludedBackupAlbums;
|
||||
if (selectedAlbums.isNotEmpty) {
|
||||
@ -756,23 +764,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
}
|
||||
|
||||
BackUpProgressEnum get backupProgress => state.backupProgress;
|
||||
|
||||
void updateBackupProgress(BackUpProgressEnum backupProgress) {
|
||||
state = state.copyWith(backupProgress: backupProgress);
|
||||
}
|
||||
}
|
||||
|
||||
final backupProvider =
|
||||
StateNotifierProvider<BackupNotifier, BackUpState>((ref) {
|
||||
return BackupNotifier(
|
||||
ref.watch(backupServiceProvider),
|
||||
ref.watch(serverInfoServiceProvider),
|
||||
ref.watch(authProvider),
|
||||
ref.watch(backgroundServiceProvider),
|
||||
ref.watch(galleryPermissionNotifier.notifier),
|
||||
ref.watch(dbProvider),
|
||||
ref.watch(albumMediaRepositoryProvider),
|
||||
ref.watch(fileMediaRepositoryProvider),
|
||||
ref.watch(backupRepositoryProvider),
|
||||
ref,
|
||||
);
|
||||
});
|
||||
|
@ -9,7 +9,6 @@ import 'package:hooks_riverpod/hooks_riverpod.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/success_upload_asset.model.dart';
|
||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||
import 'package:immich_mobile/services/background.service.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||
@ -24,6 +23,7 @@ import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
||||
import 'package:immich_mobile/services/backup_album.service.dart';
|
||||
import 'package:immich_mobile/services/local_notification.service.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:immich_mobile/utils/backup_progress.dart';
|
||||
@ -37,7 +37,7 @@ final manualUploadProvider =
|
||||
ref.watch(localNotificationService),
|
||||
ref.watch(backupProvider.notifier),
|
||||
ref.watch(backupServiceProvider),
|
||||
ref.watch(backupRepositoryProvider),
|
||||
ref.watch(backupAlbumServiceProvider),
|
||||
ref,
|
||||
);
|
||||
});
|
||||
@ -47,14 +47,14 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
|
||||
final LocalNotificationService _localNotificationService;
|
||||
final BackupNotifier _backupProvider;
|
||||
final BackupService _backupService;
|
||||
final BackupRepository _backupRepository;
|
||||
final BackupAlbumService _backupAlbumService;
|
||||
final Ref ref;
|
||||
|
||||
ManualUploadNotifier(
|
||||
this._localNotificationService,
|
||||
this._backupProvider,
|
||||
this._backupService,
|
||||
this._backupRepository,
|
||||
this._backupAlbumService,
|
||||
this.ref,
|
||||
) : super(
|
||||
ManualUploadState(
|
||||
@ -210,9 +210,9 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
|
||||
}
|
||||
|
||||
final selectedBackupAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.select);
|
||||
final excludedBackupAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.exclude);
|
||||
await _backupAlbumService.getAllBySelection(BackupSelection.select);
|
||||
final excludedBackupAlbums = await _backupAlbumService
|
||||
.getAllBySelection(BackupSelection.exclude);
|
||||
|
||||
// Get candidates from selected albums and excluded albums
|
||||
Set<BackupCandidate> candidates =
|
||||
|
@ -1,15 +1,16 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
final backupRepositoryProvider =
|
||||
Provider((ref) => BackupRepository(ref.watch(dbProvider)));
|
||||
final backupAlbumRepositoryProvider =
|
||||
Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider)));
|
||||
|
||||
class BackupRepository extends DatabaseRepository implements IBackupRepository {
|
||||
BackupRepository(super.db);
|
||||
class BackupAlbumRepository extends DatabaseRepository
|
||||
implements IBackupAlbumRepository {
|
||||
BackupAlbumRepository(super.db);
|
||||
|
||||
@override
|
||||
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort}) {
|
||||
|
@ -16,7 +16,7 @@ import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
|
||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/repositories/album.repository.dart';
|
||||
@ -36,7 +36,7 @@ final albumServiceProvider = Provider(
|
||||
ref.watch(entityServiceProvider),
|
||||
ref.watch(albumRepositoryProvider),
|
||||
ref.watch(assetRepositoryProvider),
|
||||
ref.watch(backupRepositoryProvider),
|
||||
ref.watch(backupAlbumRepositoryProvider),
|
||||
ref.watch(albumMediaRepositoryProvider),
|
||||
ref.watch(albumApiRepositoryProvider),
|
||||
),
|
||||
@ -48,7 +48,7 @@ class AlbumService {
|
||||
final EntityService _entityService;
|
||||
final IAlbumRepository _albumRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IBackupRepository _backupAlbumRepository;
|
||||
final IBackupAlbumRepository _backupAlbumRepository;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IAlbumApiRepository _albumApiRepository;
|
||||
final Logger _log = Logger('AlbumService');
|
||||
|
@ -10,7 +10,7 @@ import 'package:immich_mobile/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||
@ -39,7 +39,7 @@ final assetServiceProvider = Provider(
|
||||
ref.watch(exifInfoRepositoryProvider),
|
||||
ref.watch(userRepositoryProvider),
|
||||
ref.watch(etagRepositoryProvider),
|
||||
ref.watch(backupRepositoryProvider),
|
||||
ref.watch(backupAlbumRepositoryProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(syncServiceProvider),
|
||||
ref.watch(userServiceProvider),
|
||||
@ -55,7 +55,7 @@ class AssetService {
|
||||
final IExifInfoRepository _exifInfoRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final IETagRepository _etagRepository;
|
||||
final IBackupRepository _backupRepository;
|
||||
final IBackupAlbumRepository _backupRepository;
|
||||
final ApiService _apiService;
|
||||
final SyncService _syncService;
|
||||
final UserService _userService;
|
||||
|
@ -14,7 +14,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/main.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
||||
@ -377,7 +377,7 @@ class BackgroundService {
|
||||
AppSettingsService settingsService = AppSettingsService();
|
||||
AlbumRepository albumRepository = AlbumRepository(db);
|
||||
AssetRepository assetRepository = AssetRepository(db);
|
||||
BackupRepository backupRepository = BackupRepository(db);
|
||||
BackupAlbumRepository backupRepository = BackupAlbumRepository(db);
|
||||
ExifInfoRepository exifInfoRepository = ExifInfoRepository(db);
|
||||
ETagRepository eTagRepository = ETagRepository(db);
|
||||
AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
|
||||
|
34
mobile/lib/services/backup_album.service.dart
Normal file
34
mobile/lib/services/backup_album.service.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
|
||||
final backupAlbumServiceProvider = Provider<BackupAlbumService>((ref) {
|
||||
return BackupAlbumService(ref.watch(backupAlbumRepositoryProvider));
|
||||
});
|
||||
|
||||
class BackupAlbumService {
|
||||
final IBackupAlbumRepository _backupAlbumRepository;
|
||||
|
||||
BackupAlbumService(this._backupAlbumRepository);
|
||||
|
||||
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort}) {
|
||||
return _backupAlbumRepository.getAll(sort: sort);
|
||||
}
|
||||
|
||||
Future<List<String>> getIdsBySelection(BackupSelection backup) {
|
||||
return _backupAlbumRepository.getIdsBySelection(backup);
|
||||
}
|
||||
|
||||
Future<List<BackupAlbum>> getAllBySelection(BackupSelection backup) {
|
||||
return _backupAlbumRepository.getAllBySelection(backup);
|
||||
}
|
||||
|
||||
Future<void> deleteAll(List<int> ids) {
|
||||
return _backupAlbumRepository.deleteAll(ids);
|
||||
}
|
||||
|
||||
Future<void> updateAll(List<BackupAlbum> backupAlbums) {
|
||||
return _backupAlbumRepository.updateAll(backupAlbums);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
@ -18,7 +18,7 @@ class MockAssetRepository extends Mock implements IAssetRepository {}
|
||||
|
||||
class MockUserRepository extends Mock implements IUserRepository {}
|
||||
|
||||
class MockBackupRepository extends Mock implements IBackupRepository {}
|
||||
class MockBackupRepository extends Mock implements IBackupAlbumRepository {}
|
||||
|
||||
class MockExifInfoRepository extends Mock implements IExifInfoRepository {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user