feat(mobile): preserve mobile album info on upload (#11965)

* curating assets with albums to upload

* sorting for background backup

* background upload works

* transform fields string array to javascript array

* send json array

* generate sql

* refactor upload callback

* remove albums info from upload payload

* mechanism to create album on album selection

* album creation

* Sync to upload album

* Remove unused service

* unify name changes

* Add mechanism to sync uploaded assets to albums

* Put add to album operation after updating the UI state

* clean up

* background album sync

* add to album in background context

* remove add to album in callback

* refactor

* refactor

* refactor

* fix: make sure all selected albums are selected for building upload candidate

* clean up

* add manual sync button

* lint

* revert server changes

* pr feedback

* revert time filtering

* const

* sync album on manual upload

* linting

* pr feedback and proper time filtering

* wording
This commit is contained in:
Alex 2024-08-26 13:21:19 -05:00 committed by GitHub
parent f4371578f5
commit 6b6d2a6621
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 657 additions and 233 deletions

View file

@ -2,15 +2,20 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/etag.entity.dart';
import 'package:immich_mobile/entities/exif_info.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/services/album.service.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/services/backup.service.dart';
import 'package:immich_mobile/services/sync.service.dart';
import 'package:immich_mobile/services/user.service.dart';
import 'package:isar/isar.dart';
@ -23,6 +28,8 @@ final assetServiceProvider = Provider(
ref.watch(apiServiceProvider),
ref.watch(syncServiceProvider),
ref.watch(userServiceProvider),
ref.watch(backupServiceProvider),
ref.watch(albumServiceProvider),
ref.watch(dbProvider),
),
);
@ -31,6 +38,8 @@ class AssetService {
final ApiService _apiService;
final SyncService _syncService;
final UserService _userService;
final BackupService _backupService;
final AlbumService _albumService;
final log = Logger('AssetService');
final Isar _db;
@ -38,6 +47,8 @@ class AssetService {
this._apiService,
this._syncService,
this._userService,
this._backupService,
this._albumService,
this._db,
);
@ -284,4 +295,64 @@ class AssetService {
return Future.value(null);
}
}
Future<void> syncUploadedAssetToAlbums() async {
try {
final [selectedAlbums, excludedAlbums] = await Future.wait([
_backupService.selectedAlbumsQuery().findAll(),
_backupService.excludedAlbumsQuery().findAll(),
]);
final candidates = await _backupService.buildUploadCandidates(
selectedAlbums,
excludedAlbums,
useTimeFilter: false,
);
final duplicates = await _apiService.assetsApi.checkExistingAssets(
CheckExistingAssetsDto(
deviceAssetIds: candidates.map((c) => c.asset.id).toList(),
deviceId: Store.get(StoreKey.deviceId),
),
);
if (duplicates != null) {
candidates
.removeWhere((c) => !duplicates.existingIds.contains(c.asset.id));
}
await refreshRemoteAssets();
final remoteAssets = await _db.assets
.where()
.localIdIsNotNull()
.filter()
.remoteIdIsNotNull()
.findAll();
/// Map<AlbumName, [AssetId]>
Map<String, List<String>> assetToAlbums = {};
for (BackupCandidate candidate in candidates) {
final asset = remoteAssets.firstWhereOrNull(
(a) => a.localId == candidate.asset.id,
);
if (asset != null) {
for (final albumName in candidate.albumNames) {
assetToAlbums.putIfAbsent(albumName, () => []).add(asset.remoteId!);
}
}
}
// Upload assets to albums
for (final entry in assetToAlbums.entries) {
final albumName = entry.key;
final assetIds = entry.value;
await _albumService.syncUploadAlbums([albumName], assetIds);
}
} catch (error, stack) {
log.severe("Error while syncing uploaded asset to albums", error, stack);
}
}
}