diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 337a6d728d..8b253f83a3 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -21,7 +21,7 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { } extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { - LocalAsset toDto() => LocalAsset( + LocalAsset toDto({String? remoteId}) => LocalAsset( id: id, name: name, checksum: checksum, @@ -32,7 +32,7 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { isFavorite: isFavorite, height: height, width: width, - remoteId: null, + remoteId: remoteId, orientation: orientation, ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 4426974413..dcc885a2a9 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -49,7 +49,7 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin } extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { - RemoteAsset toDto() => RemoteAsset( + RemoteAsset toDto({String? localId}) => RemoteAsset( id: id, name: name, ownerId: ownerId, @@ -64,7 +64,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { thumbHash: thumbHash, visibility: visibility, livePhotoVideoId: livePhotoVideoId, - localId: null, + localId: localId, stackId: stackId, ); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 86f68c397e..06722ca17a 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -148,10 +148,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) ..limit(count, offset: offset); - return query.map((row) { - final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); - }).get(); + return query + .map((row) => row.readTable(_db.localAssetEntity).toDto(remoteId: row.read(_db.remoteAssetEntity.id))) + .get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( @@ -165,17 +164,15 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .count(where: (row) => row.albumId.equals(albumId)) .map(_generateBuckets) .watch() - .map((results) => results.isNotEmpty ? results.first : []) - .handleError((error) { - return []; - }); + .map((results) => results.isNotEmpty ? results.first : const []) + .handleError((error) => const []); } return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) .watch() .switchMap((albums) { if (albums.isEmpty) { - return Stream.value([]); + return Stream.value(const []); } final album = albums.first; @@ -207,10 +204,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); }) - .handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + // If there's an error (e.g., album was deleted), return empty buckets + .handleError((error) => const []); } Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { @@ -218,18 +213,23 @@ class DriftTimelineRepository extends DriftDatabaseRepository { // If album doesn't exist (was deleted), return empty list if (albumData == null) { - return []; + return const []; } final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join([ + final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ innerJoin( _db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), useColumns: false, ), - ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAssetEntity.id.equals(albumId)); if (isAscending) { query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); @@ -239,12 +239,14 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.limit(count, offset: offset); - return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) + .get(); } TimelineQuery fromAssets(List assets) => ( bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList(growable: false)), ); TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( @@ -486,6 +488,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } + @pragma('vm:prefer-inline') TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, @@ -523,6 +526,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } + @pragma('vm:prefer-inline') Future> _getRemoteAssets({ required Expression Function($RemoteAssetEntityTable row) filter, required int offset, @@ -543,11 +547,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) ..limit(count, offset: offset); - return query.map((row) { - final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final localId = row.read(_db.localAssetEntity.id); - return asset.copyWith(localId: localId); - }).get(); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) + .get(); } else { final query = _db.remoteAssetEntity.select() ..where(filter) @@ -560,12 +562,12 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } List _generateBuckets(int count) { - final buckets = List.generate( - (count / kTimelineNoneSegmentSize).floor(), - (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), + final buckets = List.filled( + (count / kTimelineNoneSegmentSize).ceil(), + const Bucket(assetCount: kTimelineNoneSegmentSize), ); if (count % kTimelineNoneSegmentSize != 0) { - buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); + buckets[buckets.length - 1] = Bucket(assetCount: count % kTimelineNoneSegmentSize); } return buckets; } @@ -590,10 +592,6 @@ extension on String { GroupAssetsBy.month => "y-M", GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; - try { - return DateFormat(format, 'en').parse(this); - } catch (e) { - throw FormatException("Invalid date format: $this", e); - } + return DateFormat(format, 'en').parse(this); } }