import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/models/stack.model.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' hide ExifInfo; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; class RemoteAssetRepository extends DriftDatabaseRepository { final Drift _db; const RemoteAssetRepository(this._db) : super(_db); /// For testing purposes Future> getSome(String userId) { final query = _db.remoteAssetEntity.select() ..where( (row) => _db.remoteAssetEntity.ownerId.equals(userId) & _db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), ) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) ..limit(10); return query.map((row) => row.toDto()).get(); } SingleOrNullSelectable _assetSelectable(String id) { final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }); } Stream watch(String id) { return _assetSelectable(id).watchSingleOrNull(); } Future get(String id) { return _assetSelectable(id).getSingleOrNull(); } Stream watchAsset(String id) { final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } Future> getStackChildren(RemoteAsset asset) { if (asset.stackId == null) { return Future.value([]); } final query = _db.remoteAssetEntity.select() ..where((row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not()) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); return query.map((row) => row.toDto()).get(); } Future getExif(String id) { return _db.managers.remoteExifEntity .filter((row) => row.assetId.id.equals(id)) .map((row) => row.toDto()) .getSingleOrNull(); } Future> getPlaces() { final asset = Subquery( _db.remoteAssetEntity.select()..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), "asset", ); final query = asset.selectOnly().join([ innerJoin( _db.remoteExifEntity, _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), useColumns: false, ), ]) ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId]) ..where( _db.remoteExifEntity.city.isNotNull() & asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), ) ..groupBy([_db.remoteExifEntity.city]) ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); return query.map((row) { final assetId = row.read(_db.remoteExifEntity.assetId); final city = row.read(_db.remoteExifEntity.city); return (city!, assetId!); }).get(); } Future updateFavorite(List ids, bool isFavorite) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteAssetEntity, RemoteAssetEntityCompanion(isFavorite: Value(isFavorite)), where: (e) => e.id.equals(id), ); } }); } Future updateVisibility(List ids, AssetVisibility visibility) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteAssetEntity, RemoteAssetEntityCompanion(visibility: Value(visibility)), where: (e) => e.id.equals(id), ); } }); } Future trash(List ids) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteAssetEntity, RemoteAssetEntityCompanion(deletedAt: Value(DateTime.now())), where: (e) => e.id.equals(id), ); } }); } Future restoreTrash(List ids) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteAssetEntity, const RemoteAssetEntityCompanion(deletedAt: Value(null)), where: (e) => e.id.equals(id), ); } }); } Future delete(List ids) { return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); } Future updateLocation(List ids, LatLng location) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteExifEntity, RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)), where: (e) => e.assetId.equals(id), ); } }); } Future updateDateTime(List ids, DateTime dateTime) { return _db.batch((batch) async { for (final id in ids) { batch.update( _db.remoteExifEntity, RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)), where: (e) => e.assetId.equals(id), ); batch.update( _db.remoteAssetEntity, RemoteAssetEntityCompanion(createdAt: Value(dateTime)), where: (e) => e.id.equals(id), ); } }); } Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity .filter((row) => row.primaryAssetId.isIn(stack.assetIds)) .map((row) => row.id) .get(); await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); await _db.batch((batch) { final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId)); batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion)); for (final assetId in stack.assetIds) { batch.update( _db.remoteAssetEntity, RemoteAssetEntityCompanion(stackId: Value(stack.id)), where: (e) => e.id.equals(assetId), ); } }); }); } Future unStack(List stackIds) { return _db.transaction(() async { await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); // TODO: delete this after adding foreign key on stackId await _db.batch((batch) { batch.update( _db.remoteAssetEntity, const RemoteAssetEntityCompanion(stackId: Value(null)), where: (e) => e.stackId.isIn(stackIds), ); }); }); } Future updateDescription(String assetId, String description) async { await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write( RemoteExifEntityCompanion(description: Value(description)), ); } Future getCount() { return _db.managers.remoteAssetEntity.count(); } }