diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 840d260950..8204572547 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -18,6 +18,11 @@ typedef TimelineAssetSource = Future> Function( typedef TimelineBucketSource = Stream> Function(); +typedef TimelineQuery = ({ + TimelineAssetSource assetSource, + TimelineBucketSource bucketSource, +}); + class TimelineFactory { final DriftTimelineRepository _timelineRepository; final SettingsService _settingsService; @@ -31,78 +36,32 @@ class TimelineFactory { GroupAssetsBy get groupBy => GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; - TimelineService main(List timelineUsers) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getMainBucketAssets(timelineUsers, offset: offset, count: count), - bucketSource: () => _timelineRepository.watchMainBucket( - timelineUsers, - groupBy: groupBy, - ), - ); + TimelineService main(List timelineUsers) => + TimelineService(_timelineRepository.main(timelineUsers, groupBy)); - TimelineService localAlbum({required String albumId}) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getLocalAlbumBucketAssets(albumId, offset: offset, count: count), - bucketSource: () => _timelineRepository.watchLocalAlbumBucket( - albumId, - groupBy: groupBy, - ), - ); + TimelineService localAlbum({required String albumId}) => + TimelineService(_timelineRepository.localAlbum(albumId, groupBy)); - TimelineService remoteAlbum({required String albumId}) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), - bucketSource: () => _timelineRepository.watchRemoteAlbumBucket( - albumId, - groupBy: groupBy, - ), - ); + TimelineService remoteAlbum({required String albumId}) => + TimelineService(_timelineRepository.remoteAlbum(albumId, groupBy)); - TimelineService remoteAssets(String ownerId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getRemoteBucketAssets(ownerId, offset: offset, count: count), - bucketSource: () => _timelineRepository.watchRemoteBucket( - ownerId, - groupBy: GroupAssetsBy.month, - ), - ); + TimelineService remoteAssets(String userId) => + TimelineService(_timelineRepository.remote(userId, groupBy)); - TimelineService favorite(String userId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getFavoriteBucketAssets(userId, offset: offset, count: count), - bucketSource: () => - _timelineRepository.watchFavoriteBucket(userId, groupBy: groupBy), - ); + TimelineService favorite(String userId) => + TimelineService(_timelineRepository.favorite(userId, groupBy)); - TimelineService trash(String userId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getTrashBucketAssets(userId, offset: offset, count: count), - bucketSource: () => - _timelineRepository.watchTrashBucket(userId, groupBy: groupBy), - ); + TimelineService trash(String userId) => + TimelineService(_timelineRepository.trash(userId, groupBy)); - TimelineService archive(String userId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getArchiveBucketAssets(userId, offset: offset, count: count), - bucketSource: () => - _timelineRepository.watchArchiveBucket(userId, groupBy: groupBy), - ); + TimelineService archive(String userId) => + TimelineService(_timelineRepository.archived(userId, groupBy)); - TimelineService lockedFolder(String userId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getLockedFolderBucketAssets(userId, offset: offset, count: count), - bucketSource: () => _timelineRepository.watchLockedFolderBucket( - userId, - groupBy: groupBy, - ), - ); + TimelineService lockedFolder(String userId) => + TimelineService(_timelineRepository.locked(userId, groupBy)); - TimelineService video(String userId) => TimelineService( - assetSource: (offset, count) => _timelineRepository - .getVideoBucketAssets(userId, offset: offset, count: count), - bucketSource: () => - _timelineRepository.watchVideoBucket(userId, groupBy: groupBy), - ); + TimelineService video(String userId) => + TimelineService(_timelineRepository.video(userId, groupBy)); } class TimelineService { @@ -116,7 +75,13 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService({ + TimelineService(TimelineQuery query) + : this._( + assetSource: query.assetSource, + bucketSource: query.bucketSource, + ); + + TimelineService._({ required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource, }) : _assetSource = assetSource, diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 569df0aed8..76ad9bad8f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -5,8 +5,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.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/repositories/db.repository.dart'; import 'package:stream_transform/stream_transform.dart'; @@ -30,19 +32,19 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .map((users) => users..add(userId)); } - List _generateBuckets(int count) { - final numBuckets = (count / kTimelineNoneSegmentSize).floor(); - final buckets = List.generate( - numBuckets, - (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), - ); - if (count % kTimelineNoneSegmentSize != 0) { - buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); - } - return buckets; - } + TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchMainBucket( + userIds, + groupBy: groupBy, + ), + assetSource: (offset, count) => _getMainBucketAssets( + userIds, + offset: offset, + count: count, + ), + ); - Stream> watchMainBucket( + Stream> _watchMainBucket( List userIds, { GroupAssetsBy groupBy = GroupAssetsBy.day, }) { @@ -62,7 +64,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .throttle(const Duration(seconds: 3), trailing: true); } - Future> getMainBucketAssets( + Future> _getMainBucketAssets( List userIds, { required int offset, required int count, @@ -70,42 +72,53 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return _db.mergedAssetDrift .mergedAsset(userIds, limit: Limit(count, offset)) .map( - (row) { - return row.remoteId != null && row.ownerId != null - ? RemoteAsset( - id: row.remoteId!, - localId: row.localId, - name: row.name, - ownerId: row.ownerId!, - checksum: row.checksum, - type: row.type, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - thumbHash: row.thumbHash, - width: row.width, - height: row.height, - isFavorite: row.isFavorite, - durationInSeconds: row.durationInSeconds, - ) - : LocalAsset( - id: row.localId!, - remoteId: row.remoteId, - name: row.name, - checksum: row.checksum, - type: row.type, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - width: row.width, - height: row.height, - isFavorite: row.isFavorite, - durationInSeconds: row.durationInSeconds, - orientation: row.orientation, - ); - }, - ).get(); + (row) => row.remoteId != null && row.ownerId != null + ? RemoteAsset( + id: row.remoteId!, + localId: row.localId, + name: row.name, + ownerId: row.ownerId!, + checksum: row.checksum, + type: row.type, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + thumbHash: row.thumbHash, + width: row.width, + height: row.height, + isFavorite: row.isFavorite, + durationInSeconds: row.durationInSeconds, + ) + : LocalAsset( + id: row.localId!, + remoteId: row.remoteId, + name: row.name, + checksum: row.checksum, + type: row.type, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + width: row.width, + height: row.height, + isFavorite: row.isFavorite, + durationInSeconds: row.durationInSeconds, + orientation: row.orientation, + ), + ) + .get(); } - Stream> watchLocalAlbumBucket( + TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchLocalAlbumBucket( + albumId, + groupBy: groupBy, + ), + assetSource: (offset, count) => _getLocalAlbumBucketAssets( + albumId, + offset: offset, + count: count, + ), + ); + + Stream> _watchLocalAlbumBucket( String albumId, { GroupAssetsBy groupBy = GroupAssetsBy.day, }) { @@ -119,15 +132,14 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final assetCountExp = _db.localAssetEntity.id.count(); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.localAssetEntity.selectOnly() + final query = _db.localAssetEntity.selectOnly().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + ]) ..addColumns([assetCountExp, dateExp]) - ..join([ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - ]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..groupBy([dateExp]) ..orderBy([OrderingTerm.desc(dateExp)]); @@ -139,7 +151,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> getLocalAlbumBucketAssets( + Future> _getLocalAlbumBucketAssets( String albumId, { required int offset, required int count, @@ -156,12 +168,25 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) ..limit(count, offset: offset); + return query .map((row) => row.readTable(_db.localAssetEntity).toDto()) .get(); } - Stream> watchRemoteAlbumBucket( + TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchRemoteAlbumBucket( + albumId, + groupBy: groupBy, + ), + assetSource: (offset, count) => _getRemoteAlbumBucketAssets( + albumId, + offset: offset, + count: count, + ), + ); + + Stream> _watchRemoteAlbumBucket( String albumId, { GroupAssetsBy groupBy = GroupAssetsBy.day, }) { @@ -199,7 +224,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> getRemoteAlbumBucketAssets( + Future> _getRemoteAlbumBucketAssets( String albumId, { required int offset, required int count, @@ -225,325 +250,101 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .get(); } - Stream> watchRemoteBucket( - String ownerId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(ownerId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline) & - _db.remoteAssetEntity.ownerId.equals(ownerId), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getRemoteBucketAssets( - String ownerId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => + TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), - ) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + groupBy: groupBy, + ); - return query.map((row) => row.toDto()).get(); - } - - Stream> watchFavoriteBucket( - String userId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNull() & - row.isFavorite.equals(true) & - row.ownerId.equals(userId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.ownerId.equals(userId) & - _db.remoteAssetEntity.isFavorite.equals(true), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getFavoriteBucketAssets( - String userId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => + TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), - ) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + groupBy: groupBy, + ); - return query.map((row) => row.toDto()).get(); - } + TimelineQuery trash(String userId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + groupBy: groupBy, + ); - Stream> watchTrashBucket( - String userId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNotNull() & row.ownerId.equals(userId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.ownerId.equals(userId) & - _db.remoteAssetEntity.deletedAt.isNotNull(), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getTrashBucketAssets( - String userId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), - ) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); - - return query.map((row) => row.toDto()).get(); - } - - Stream> watchArchiveBucket( - String userId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.archive) & - row.ownerId.equals(userId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.ownerId.equals(userId) & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.archive), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getArchiveBucketAssets( - String userId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => + TimelineQuery archived(String userId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), - ) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + groupBy: groupBy, + ); - return query.map((row) => row.toDto()).get(); - } - - Stream> watchLockedFolderBucket( - String userId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.locked) & - row.ownerId.equals(userId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.ownerId.equals(userId) & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.locked), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getLockedFolderBucketAssets( - String userId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => + TimelineQuery locked(String userId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), - ) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + groupBy: groupBy, + ); - return query.map((row) => row.toDto()).get(); - } - - Stream> watchVideoBucket( - String userId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { - if (groupBy == GroupAssetsBy.none) { - return _db.remoteAssetEntity - .count( - where: (row) => - row.deletedAt.isNull() & - row.type.equalsValue(AssetType.video) & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(userId), - ) - .map(_generateBuckets) - .watchSingle(); - } - - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.ownerId.equals(userId) & - _db.remoteAssetEntity.type.equalsValue(AssetType.video) & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), - ) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); - - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - } - - Future> getVideoBucketAssets( - String userId, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select() - ..where( - (row) => + TimelineQuery video(String userId, GroupAssetsBy groupBy) => + _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.type.equalsValue(AssetType.video) & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(userId), - ) + groupBy: groupBy, + ); + + TimelineQuery _remoteQueryBuilder({ + required Expression Function($RemoteAssetEntityTable row) filter, + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + return ( + bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), + assetSource: (offset, count) => + _getRemoteAssets(filter: filter, offset: offset, count: count), + ); + } + + Stream> _watchRemoteBucket({ + required Expression Function($RemoteAssetEntityTable row) filter, + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + final query = _db.remoteAssetEntity.count(where: filter); + return query.map(_generateBuckets).watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..where(filter(_db.remoteAssetEntity)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getRemoteAssets({ + required Expression Function($RemoteAssetEntityTable row) filter, + required int offset, + required int count, + }) { + final query = _db.remoteAssetEntity.select() + ..where(filter) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) ..limit(count, offset: offset); @@ -551,6 +352,17 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } } +List _generateBuckets(int count) { + final buckets = List.generate( + (count / kTimelineNoneSegmentSize).floor(), + (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), + ); + if (count % kTimelineNoneSegmentSize != 0) { + buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); + } + return buckets; +} + extension on Expression { Expression dateFmt(GroupAssetsBy groupBy) { // DateTimes are stored in UTC, so we need to convert them to local time inside the query before formatting