refactor: timeline repo queries (#19871)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong 2025-07-11 20:27:46 +05:30 committed by GitHub
parent a17bba3328
commit a625921e8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 198 additions and 421 deletions

View File

@ -18,6 +18,11 @@ typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
typedef TimelineBucketSource = Stream<List<Bucket>> Function(); typedef TimelineBucketSource = Stream<List<Bucket>> Function();
typedef TimelineQuery = ({
TimelineAssetSource assetSource,
TimelineBucketSource bucketSource,
});
class TimelineFactory { class TimelineFactory {
final DriftTimelineRepository _timelineRepository; final DriftTimelineRepository _timelineRepository;
final SettingsService _settingsService; final SettingsService _settingsService;
@ -31,78 +36,32 @@ class TimelineFactory {
GroupAssetsBy get groupBy => GroupAssetsBy get groupBy =>
GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)];
TimelineService main(List<String> timelineUsers) => TimelineService( TimelineService main(List<String> timelineUsers) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.main(timelineUsers, groupBy));
.getMainBucketAssets(timelineUsers, offset: offset, count: count),
bucketSource: () => _timelineRepository.watchMainBucket(
timelineUsers,
groupBy: groupBy,
),
);
TimelineService localAlbum({required String albumId}) => TimelineService( TimelineService localAlbum({required String albumId}) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.localAlbum(albumId, groupBy));
.getLocalAlbumBucketAssets(albumId, offset: offset, count: count),
bucketSource: () => _timelineRepository.watchLocalAlbumBucket(
albumId,
groupBy: groupBy,
),
);
TimelineService remoteAlbum({required String albumId}) => TimelineService( TimelineService remoteAlbum({required String albumId}) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.remoteAlbum(albumId, groupBy));
.getRemoteAlbumBucketAssets(albumId, offset: offset, count: count),
bucketSource: () => _timelineRepository.watchRemoteAlbumBucket(
albumId,
groupBy: groupBy,
),
);
TimelineService remoteAssets(String ownerId) => TimelineService( TimelineService remoteAssets(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.remote(userId, groupBy));
.getRemoteBucketAssets(ownerId, offset: offset, count: count),
bucketSource: () => _timelineRepository.watchRemoteBucket(
ownerId,
groupBy: GroupAssetsBy.month,
),
);
TimelineService favorite(String userId) => TimelineService( TimelineService favorite(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.favorite(userId, groupBy));
.getFavoriteBucketAssets(userId, offset: offset, count: count),
bucketSource: () =>
_timelineRepository.watchFavoriteBucket(userId, groupBy: groupBy),
);
TimelineService trash(String userId) => TimelineService( TimelineService trash(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.trash(userId, groupBy));
.getTrashBucketAssets(userId, offset: offset, count: count),
bucketSource: () =>
_timelineRepository.watchTrashBucket(userId, groupBy: groupBy),
);
TimelineService archive(String userId) => TimelineService( TimelineService archive(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.archived(userId, groupBy));
.getArchiveBucketAssets(userId, offset: offset, count: count),
bucketSource: () =>
_timelineRepository.watchArchiveBucket(userId, groupBy: groupBy),
);
TimelineService lockedFolder(String userId) => TimelineService( TimelineService lockedFolder(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.locked(userId, groupBy));
.getLockedFolderBucketAssets(userId, offset: offset, count: count),
bucketSource: () => _timelineRepository.watchLockedFolderBucket(
userId,
groupBy: groupBy,
),
);
TimelineService video(String userId) => TimelineService( TimelineService video(String userId) =>
assetSource: (offset, count) => _timelineRepository TimelineService(_timelineRepository.video(userId, groupBy));
.getVideoBucketAssets(userId, offset: offset, count: count),
bucketSource: () =>
_timelineRepository.watchVideoBucket(userId, groupBy: groupBy),
);
} }
class TimelineService { class TimelineService {
@ -116,7 +75,13 @@ class TimelineService {
int _totalAssets = 0; int _totalAssets = 0;
int get totalAssets => _totalAssets; int get totalAssets => _totalAssets;
TimelineService({ TimelineService(TimelineQuery query)
: this._(
assetSource: query.assetSource,
bucketSource: query.bucketSource,
);
TimelineService._({
required TimelineAssetSource assetSource, required TimelineAssetSource assetSource,
required TimelineBucketSource bucketSource, required TimelineBucketSource bucketSource,
}) : _assetSource = assetSource, }) : _assetSource = assetSource,

View File

@ -5,8 +5,10 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:immich_mobile/constants/constants.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/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.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/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_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:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:stream_transform/stream_transform.dart'; import 'package:stream_transform/stream_transform.dart';
@ -30,19 +32,19 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.map((users) => users..add(userId)); .map((users) => users..add(userId));
} }
List<Bucket> _generateBuckets(int count) { TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => (
final numBuckets = (count / kTimelineNoneSegmentSize).floor(); bucketSource: () => _watchMainBucket(
final buckets = List.generate( userIds,
numBuckets, groupBy: groupBy,
(_) => const Bucket(assetCount: kTimelineNoneSegmentSize), ),
); assetSource: (offset, count) => _getMainBucketAssets(
if (count % kTimelineNoneSegmentSize != 0) { userIds,
buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); offset: offset,
} count: count,
return buckets; ),
} );
Stream<List<Bucket>> watchMainBucket( Stream<List<Bucket>> _watchMainBucket(
List<String> userIds, { List<String> userIds, {
GroupAssetsBy groupBy = GroupAssetsBy.day, GroupAssetsBy groupBy = GroupAssetsBy.day,
}) { }) {
@ -62,7 +64,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.throttle(const Duration(seconds: 3), trailing: true); .throttle(const Duration(seconds: 3), trailing: true);
} }
Future<List<BaseAsset>> getMainBucketAssets( Future<List<BaseAsset>> _getMainBucketAssets(
List<String> userIds, { List<String> userIds, {
required int offset, required int offset,
required int count, required int count,
@ -70,42 +72,53 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
return _db.mergedAssetDrift return _db.mergedAssetDrift
.mergedAsset(userIds, limit: Limit(count, offset)) .mergedAsset(userIds, limit: Limit(count, offset))
.map( .map(
(row) { (row) => row.remoteId != null && row.ownerId != null
return row.remoteId != null && row.ownerId != null ? RemoteAsset(
? RemoteAsset( id: row.remoteId!,
id: row.remoteId!, localId: row.localId,
localId: row.localId, name: row.name,
name: row.name, ownerId: row.ownerId!,
ownerId: row.ownerId!, checksum: row.checksum,
checksum: row.checksum, type: row.type,
type: row.type, createdAt: row.createdAt,
createdAt: row.createdAt, updatedAt: row.updatedAt,
updatedAt: row.updatedAt, thumbHash: row.thumbHash,
thumbHash: row.thumbHash, width: row.width,
width: row.width, height: row.height,
height: row.height, isFavorite: row.isFavorite,
isFavorite: row.isFavorite, durationInSeconds: row.durationInSeconds,
durationInSeconds: row.durationInSeconds, )
) : LocalAsset(
: LocalAsset( id: row.localId!,
id: row.localId!, remoteId: row.remoteId,
remoteId: row.remoteId, name: row.name,
name: row.name, checksum: row.checksum,
checksum: row.checksum, type: row.type,
type: row.type, createdAt: row.createdAt,
createdAt: row.createdAt, updatedAt: row.updatedAt,
updatedAt: row.updatedAt, width: row.width,
width: row.width, height: row.height,
height: row.height, isFavorite: row.isFavorite,
isFavorite: row.isFavorite, durationInSeconds: row.durationInSeconds,
durationInSeconds: row.durationInSeconds, orientation: row.orientation,
orientation: row.orientation, ),
); )
}, .get();
).get();
} }
Stream<List<Bucket>> watchLocalAlbumBucket( TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchLocalAlbumBucket(
albumId,
groupBy: groupBy,
),
assetSource: (offset, count) => _getLocalAlbumBucketAssets(
albumId,
offset: offset,
count: count,
),
);
Stream<List<Bucket>> _watchLocalAlbumBucket(
String albumId, { String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day, GroupAssetsBy groupBy = GroupAssetsBy.day,
}) { }) {
@ -119,15 +132,14 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
final assetCountExp = _db.localAssetEntity.id.count(); final assetCountExp = _db.localAssetEntity.id.count();
final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); 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]) ..addColumns([assetCountExp, dateExp])
..join([
innerJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
useColumns: false,
),
])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..groupBy([dateExp]) ..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]); ..orderBy([OrderingTerm.desc(dateExp)]);
@ -139,7 +151,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}).watch(); }).watch();
} }
Future<List<BaseAsset>> getLocalAlbumBucketAssets( Future<List<BaseAsset>> _getLocalAlbumBucketAssets(
String albumId, { String albumId, {
required int offset, required int offset,
required int count, required int count,
@ -156,12 +168,25 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
..limit(count, offset: offset); ..limit(count, offset: offset);
return query return query
.map((row) => row.readTable(_db.localAssetEntity).toDto()) .map((row) => row.readTable(_db.localAssetEntity).toDto())
.get(); .get();
} }
Stream<List<Bucket>> watchRemoteAlbumBucket( TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchRemoteAlbumBucket(
albumId,
groupBy: groupBy,
),
assetSource: (offset, count) => _getRemoteAlbumBucketAssets(
albumId,
offset: offset,
count: count,
),
);
Stream<List<Bucket>> _watchRemoteAlbumBucket(
String albumId, { String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day, GroupAssetsBy groupBy = GroupAssetsBy.day,
}) { }) {
@ -199,7 +224,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}).watch(); }).watch();
} }
Future<List<BaseAsset>> getRemoteAlbumBucketAssets( Future<List<BaseAsset>> _getRemoteAlbumBucketAssets(
String albumId, { String albumId, {
required int offset, required int offset,
required int count, required int count,
@ -225,325 +250,101 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.get(); .get();
} }
Stream<List<Bucket>> watchRemoteBucket( TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) =>
String ownerId, { _remoteQueryBuilder(
GroupAssetsBy groupBy = GroupAssetsBy.day, filter: (row) =>
}) {
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<List<BaseAsset>> getRemoteBucketAssets(
String ownerId, {
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.deletedAt.isNull() & row.deletedAt.isNull() &
row.visibility.equalsValue(AssetVisibility.timeline) & row.visibility.equalsValue(AssetVisibility.timeline) &
row.ownerId.equals(ownerId), row.ownerId.equals(ownerId),
) groupBy: groupBy,
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) );
..limit(count, offset: offset);
return query.map((row) => row.toDto()).get(); TimelineQuery favorite(String userId, GroupAssetsBy groupBy) =>
} _remoteQueryBuilder(
filter: (row) =>
Stream<List<Bucket>> 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<List<BaseAsset>> getFavoriteBucketAssets(
String userId, {
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.deletedAt.isNull() & row.deletedAt.isNull() &
row.isFavorite.equals(true) & row.isFavorite.equals(true) &
row.ownerId.equals(userId), row.ownerId.equals(userId),
) groupBy: groupBy,
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) );
..limit(count, offset: offset);
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<List<Bucket>> watchTrashBucket( TimelineQuery archived(String userId, GroupAssetsBy groupBy) =>
String userId, { _remoteQueryBuilder(
GroupAssetsBy groupBy = GroupAssetsBy.day, filter: (row) =>
}) {
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<List<BaseAsset>> 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<List<Bucket>> 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<List<BaseAsset>> getArchiveBucketAssets(
String userId, {
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.deletedAt.isNull() & row.deletedAt.isNull() &
row.ownerId.equals(userId) & row.ownerId.equals(userId) &
row.visibility.equalsValue(AssetVisibility.archive), row.visibility.equalsValue(AssetVisibility.archive),
) groupBy: groupBy,
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) );
..limit(count, offset: offset);
return query.map((row) => row.toDto()).get(); TimelineQuery locked(String userId, GroupAssetsBy groupBy) =>
} _remoteQueryBuilder(
filter: (row) =>
Stream<List<Bucket>> 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<List<BaseAsset>> getLockedFolderBucketAssets(
String userId, {
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.deletedAt.isNull() & row.deletedAt.isNull() &
row.visibility.equalsValue(AssetVisibility.locked) & row.visibility.equalsValue(AssetVisibility.locked) &
row.ownerId.equals(userId), row.ownerId.equals(userId),
) groupBy: groupBy,
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) );
..limit(count, offset: offset);
return query.map((row) => row.toDto()).get(); TimelineQuery video(String userId, GroupAssetsBy groupBy) =>
} _remoteQueryBuilder(
filter: (row) =>
Stream<List<Bucket>> 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<List<BaseAsset>> getVideoBucketAssets(
String userId, {
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.deletedAt.isNull() & row.deletedAt.isNull() &
row.type.equalsValue(AssetType.video) & row.type.equalsValue(AssetType.video) &
row.visibility.equalsValue(AssetVisibility.timeline) & row.visibility.equalsValue(AssetVisibility.timeline) &
row.ownerId.equals(userId), row.ownerId.equals(userId),
) groupBy: groupBy,
);
TimelineQuery _remoteQueryBuilder({
required Expression<bool> 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<List<Bucket>> _watchRemoteBucket({
required Expression<bool> 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<List<BaseAsset>> _getRemoteAssets({
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
required int offset,
required int count,
}) {
final query = _db.remoteAssetEntity.select()
..where(filter)
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(count, offset: offset); ..limit(count, offset: offset);
@ -551,6 +352,17 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
} }
} }
List<Bucket> _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<DateTime> { extension on Expression<DateTime> {
Expression<String> dateFmt(GroupAssetsBy groupBy) { Expression<String> dateFmt(GroupAssetsBy groupBy) {
// DateTimes are stored in UTC, so we need to convert them to local time inside the query before formatting // DateTimes are stored in UTC, so we need to convert them to local time inside the query before formatting