0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-18 01:24:26 -05:00
This commit is contained in:
Alex 2025-02-17 09:30:41 -06:00
parent 8c50e441b7
commit d0a512a6c9
No known key found for this signature in database
GPG key ID: 53CD082B3A5E1082
4 changed files with 63 additions and 47 deletions

View file

@ -61,6 +61,8 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
Future<List<Asset>> getStackAssets(String stackId); Future<List<Asset>> getStackAssets(String stackId);
Future<void> dropTable(); Future<void> dropTable();
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
} }
enum AssetSort { checksum, ownerIdChecksum } enum AssetSort { checksum, ownerIdChecksum }

View file

@ -166,28 +166,38 @@ class AssetNotifier extends StateNotifier<bool> {
final assetDetailProvider = final assetDetailProvider =
StreamProvider.autoDispose.family<Asset, Asset>((ref, asset) async* { StreamProvider.autoDispose.family<Asset, Asset>((ref, asset) async* {
final assetService = ref.watch(assetServiceProvider);
yield await assetService.loadExif(asset);
await for (final asset in assetService.watchAsset(asset.id)) {
if (asset != null) {
yield await ref.watch(assetServiceProvider).loadExif(asset); yield await ref.watch(assetServiceProvider).loadExif(asset);
final db = ref.watch(dbProvider);
await for (final a in db.assets.watchObject(asset.id)) {
if (a != null) {
yield await ref.watch(assetServiceProvider).loadExif(a);
} }
} }
}); });
final assetWatcher = final assetWatcher =
StreamProvider.autoDispose.family<Asset?, Asset>((ref, asset) { StreamProvider.autoDispose.family<Asset?, Asset>((ref, asset) {
final db = ref.watch(dbProvider); final assetService = ref.watch(assetServiceProvider);
return db.assets.watchObject(asset.id, fireImmediately: true); return assetService.watchAsset(asset.id, fireImmediately: true);
}); });
final assetsProvider = StreamProvider.family<RenderList, int?>( final assetsProvider = StreamProvider.family<RenderList, int?>(
(ref, userId) { (ref, userId) {
if (userId == null) return const Stream.empty(); if (userId == null) return const Stream.empty();
ref.watch(localeProvider); ref.watch(localeProvider);
final query = _commonFilterAndSort(
_assets(ref).where().ownerIdEqualToAnyChecksum(userId), final query = ref
); .watch(dbProvider)
.assets
.where()
.ownerIdEqualToAnyChecksum(userId)
.filter()
.isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc();
return renderListGenerator(query, ref); return renderListGenerator(query, ref);
}, },
dependencies: [localeProvider], dependencies: [localeProvider],
@ -197,11 +207,17 @@ final multiUserAssetsProvider = StreamProvider.family<RenderList, List<int>>(
(ref, userIds) { (ref, userIds) {
if (userIds.isEmpty) return const Stream.empty(); if (userIds.isEmpty) return const Stream.empty();
ref.watch(localeProvider); ref.watch(localeProvider);
final query = _commonFilterAndSort( final query = ref
_assets(ref) .watch(dbProvider)
.assets
.where() .where()
.anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u)), .anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u))
); .filter()
.isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc();
return renderListGenerator(query, ref); return renderListGenerator(query, ref);
}, },
dependencies: [localeProvider], dependencies: [localeProvider],
@ -223,17 +239,3 @@ QueryBuilder<Asset, Asset, QAfterSortBy>? getRemoteAssetQuery(WidgetRef ref) {
.stackPrimaryAssetIdIsNull() .stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc(); .sortByFileCreatedAtDesc();
} }
IsarCollection<Asset> _assets(StreamProviderRef<RenderList> ref) =>
ref.watch(dbProvider).assets;
QueryBuilder<Asset, Asset, QAfterSortBy> _commonFilterAndSort(
QueryBuilder<Asset, Asset, QAfterWhereClause> query,
) {
return query
.filter()
.isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc();
}

View file

@ -218,6 +218,11 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
await db.exifInfos.clear(); await db.exifInfos.clear();
}); });
} }
@override
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false}) {
return db.assets.watchObject(id, fireImmediately: fireImmediately);
}
} }
Future<List<Asset>> _getMatchesImpl( Future<List<Asset>> _getMatchesImpl(

View file

@ -421,25 +421,26 @@ class AssetService {
/// Delete assets from local file system and unreference from the database /// Delete assets from local file system and unreference from the database
Future<void> deleteLocalAssets(Iterable<Asset> assets) async { Future<void> deleteLocalAssets(Iterable<Asset> assets) async {
// Delete files from local gallery // Delete files from local gallery
final candidates = assets.where((a) => a.isLocal); final candidates = assets.where((asset) => asset.isLocal);
final deletedIds = await _assetMediaRepository final deletedIds = await _assetMediaRepository
.deleteAll(candidates.map((a) => a.localId!).toList()); .deleteAll(candidates.map((asset) => asset.localId!).toList());
// Modify local database by removing the reference to the local assets // Modify local database by removing the reference to the local assets
if (deletedIds.isNotEmpty) { if (deletedIds.isNotEmpty) {
// Delete records from local database // Delete records from local database
final isarIds = assets final isarIds = assets
.where((e) => e.storage == AssetState.local) .where((asset) => asset.storage == AssetState.local)
.map((e) => e.id) .map((asset) => asset.id)
.toList(); .toList();
await _assetRepository.deleteByIds(isarIds); await _assetRepository.deleteByIds(isarIds);
// Modify Merged asset to be remote only // Modify Merged asset to be remote only
final updatedAssets = final updatedAssets = assets
assets.where((e) => e.storage == AssetState.merged).map((e) { .where((asset) => asset.storage == AssetState.merged)
e.localId = null; .map((asset) {
return e; asset.localId = null;
return asset;
}).toList(); }).toList();
await _assetRepository.updateAll(updatedAssets); await _assetRepository.updateAll(updatedAssets);
@ -465,13 +466,15 @@ class AssetService {
/// Update asset info bassed on the deletion type. /// Update asset info bassed on the deletion type.
final payload = shouldDeletePermanently final payload = shouldDeletePermanently
? assets.where((a) => a.storage == AssetState.merged).map((a) { ? assets
a.remoteId = null; .where((asset) => asset.storage == AssetState.merged)
return a; .map((asset) {
asset.remoteId = null;
return asset;
}) })
: assets.where((a) => a.isRemote).map((a) { : assets.where((asset) => asset.isRemote).map((asset) {
a.isTrashed = true; asset.isTrashed = true;
return a; return asset;
}); });
await _assetRepository.transaction(() async { await _assetRepository.transaction(() async {
@ -479,8 +482,8 @@ class AssetService {
if (shouldDeletePermanently) { if (shouldDeletePermanently) {
final remoteAssetIds = assets final remoteAssetIds = assets
.where((a) => a.storage == AssetState.remote) .where((asset) => asset.storage == AssetState.remote)
.map((a) => a.id) .map((asset) => asset.id)
.toList(); .toList();
await _assetRepository.deleteByIds(remoteAssetIds); await _assetRepository.deleteByIds(remoteAssetIds);
} }
@ -493,8 +496,8 @@ class AssetService {
Iterable<Asset> assets, { Iterable<Asset> assets, {
bool shouldDeletePermanently = false, bool shouldDeletePermanently = false,
}) async { }) async {
final hasLocal = assets.any((a) => a.isLocal); final hasLocal = assets.any((asset) => asset.isLocal);
final hasRemote = assets.any((a) => a.isRemote); final hasRemote = assets.any((asset) => asset.isRemote);
if (hasLocal) { if (hasLocal) {
await deleteLocalAssets(assets); await deleteLocalAssets(assets);
@ -507,4 +510,8 @@ class AssetService {
); );
} }
} }
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false}) {
return _assetRepository.watchAsset(id, fireImmediately: fireImmediately);
}
} }