fix(mobile): make EXIF provider reactive (#31552)

* fix(mobile): make EXIF provider reactive

* Fix tests

* Remove unnecessary invalidations
This commit is contained in:
Adam Gastineau
2026-09-16 17:36:37 +00:00
committed by GitHub
parent 32b2cdba61
commit bd59a373de
9 changed files with 17 additions and 23 deletions
@@ -63,13 +63,13 @@ class AssetService {
return [asset, ...stack];
}
Future<ExifInfo?> getExif(BaseAsset asset) async {
Stream<ExifInfo?> watchExif(BaseAsset asset) {
if (!asset.hasRemote) {
return null;
return Stream.value(null);
}
final id = asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id;
return _remoteRepository.getExif(id);
return _remoteRepository.watchExif(id);
}
Future<List<(String, String)>> getPlaces(String userId) {
@@ -64,12 +64,10 @@ class RemoteAssetRepository extends DatabaseAccessor<Drift> with $RemoteAssetRep
return query.map((row) => row.toDto()).get();
}
Future<ExifInfo?> getExif(String id) {
return _db.managers.remoteExifEntity
.filter((row) => row.assetId.id.equals(id))
.map((row) => row.toDto())
.getSingleOrNull();
}
Stream<ExifInfo?> watchExif(String id) => _db.managers.remoteExifEntity
.filter((row) => row.assetId.id.equals(id))
.map((row) => row.toDto())
.watchSingleOrNull();
Future<List<(String, String)>> getPlaces(String userId) {
final asset = Subquery(
@@ -50,7 +50,7 @@ class EditAssetAction extends AssetActionBuilder {
try {
// TODO(shenlong): Move all EXIF and Apply Edits logic onto the Route
final repository = ref.read(driftProvider).remoteAssetRepository;
final (edits, exif) = await (repository.getAssetEdits(asset.id), repository.getExif(asset.id)).wait;
final (edits, exif) = await (repository.getAssetEdits(asset.id), repository.watchExif(asset.id).first).wait;
if (exif == null || !context.mounted) {
return;
}
@@ -6,7 +6,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/toast.provider.dart';
import 'package:immich_mobile/utils/error_handler.dart';
@@ -56,7 +55,7 @@ class EditDateTimeAction extends AssetActionBuilder {
Duration? offset;
if (origin != null) {
final exif = await remoteAssetRepository.getExif(origin.id);
final exif = await remoteAssetRepository.watchExif(origin.id).first;
// Prefer the EXIF timezone, so the picker opens on what the asset actually shows.
DateTime dateTime = origin.createdAt.toLocal();
@@ -96,6 +95,5 @@ Future<void> saveDateTime(BuildContext context, WidgetRef ref, List<String> asse
final toastService = ref.read(toastServiceProvider);
await ref.read(assetServiceProvider).update(assetIds, dateTime: .some(dateTime));
ref.invalidate(assetExifProvider);
toastService.success(message);
}
@@ -6,7 +6,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/toast.provider.dart';
import 'package:immich_mobile/utils/error_handler.dart';
@@ -53,7 +52,7 @@ class EditLocationAction extends AssetActionBuilder {
try {
LatLng? initialLatLng;
if (origin != null) {
final exif = await remoteAssetRepository.getExif(origin.id);
final exif = await remoteAssetRepository.watchExif(origin.id).first;
if (exif?.latitude != null && exif?.longitude != null) {
initialLatLng = LatLng(exif!.latitude!, exif.longitude!);
}
@@ -81,6 +80,5 @@ Future<void> saveLocation(BuildContext context, WidgetRef ref, List<String> asse
final toastService = ref.read(toastServiceProvider);
await ref.read(assetServiceProvider).update(assetIds, location: .some(location));
ref.invalidate(assetExifProvider);
toastService.success(message);
}
@@ -159,7 +159,7 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection
properties.insertAll(4, additionalProps);
final exif = await ref.read(assetServiceProvider).getExif(asset);
final exif = await ref.read(assetServiceProvider).watchExif(asset).first;
if (exif != null) {
_addExifProperties(exif);
} else {
@@ -3,6 +3,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
final assetExifProvider = FutureProvider.autoDispose.family<ExifInfo?, BaseAsset>((ref, asset) {
return ref.watch(assetServiceProvider).getExif(asset);
final assetExifProvider = StreamProvider.autoDispose.family<ExifInfo?, BaseAsset>((ref, asset) {
return ref.watch(assetServiceProvider).watchExif(asset);
});
+3 -3
View File
@@ -70,7 +70,7 @@ class RepositoryMocks {
}
void _stubRemoteAssetRepository() {
when(remoteAsset.getExif).thenAnswer((_) async => null);
when(remoteAsset.watchExif).thenAnswer((_) => Stream.value(null));
when(remoteAsset.getAssetEdits).thenAnswer((_) async => const []);
when(remoteAsset.update).thenAnswer((_) async {});
}
@@ -269,8 +269,8 @@ extension type const LocalAssetRepositoryStub(MockLocalAssetRepository repo) imp
extension type const RemoteAssetRepositoryStub(MockRemoteAssetRepository repo)
implements Stub<MockRemoteAssetRepository> {
Future<ExifInfo?> Function() get getExif =>
() => repo.getExif(any());
Stream<ExifInfo?> Function() get watchExif =>
() => repo.watchExif(any());
Future<List<AssetEdit>> Function() get getAssetEdits =>
() => repo.getAssetEdits(any());
@@ -111,7 +111,7 @@ void main() {
await tester.pump();
verify(() => context.repository.remoteAsset.repo.getAssetEdits(asset.id)).called(1);
verify(() => context.repository.remoteAsset.repo.getExif(asset.id)).called(1);
verify(() => context.repository.remoteAsset.repo.watchExif(asset.id)).called(1);
});
});