fix: include partner shared assets for people even without showInTimeline (#32675)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> (cherry picked from commit 21ed7454836073a646273c7475b347e043439a90)
This commit is contained in:
@@ -74,8 +74,8 @@ class TimelineFactory {
|
|||||||
|
|
||||||
TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy));
|
TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy));
|
||||||
|
|
||||||
TimelineService person(List<String> userIds, String personId) =>
|
TimelineService person(String userId, String personId) =>
|
||||||
TimelineService(_timelineRepository.person(userIds, personId, groupBy));
|
TimelineService(_timelineRepository.person(userId, personId, groupBy));
|
||||||
|
|
||||||
TimelineService fromAssets(List<BaseAsset> assets, TimelineOrigin type) =>
|
TimelineService fromAssets(List<BaseAsset> assets, TimelineOrigin type) =>
|
||||||
TimelineService(_timelineRepository.fromAssets(assets, type));
|
TimelineService(_timelineRepository.fromAssets(assets, type));
|
||||||
|
|||||||
@@ -366,10 +366,10 @@ class TimelineRepository extends DatabaseAccessor<Drift> with $TimelineRepositor
|
|||||||
origin: TimelineOrigin.place,
|
origin: TimelineOrigin.place,
|
||||||
);
|
);
|
||||||
|
|
||||||
TimelineQuery person(List<String> userIds, String personId, GroupAssetsBy groupBy) => (
|
TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => (
|
||||||
bucketSource: () => _watchPersonBucket(userIds, personId, groupBy: groupBy),
|
bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy),
|
||||||
assetSource: (offset, count) =>
|
assetSource: (offset, count) =>
|
||||||
_getPersonBucketAssets(userIds, personId, groupBy: groupBy, offset: offset, count: count),
|
_getPersonBucketAssets(userId, personId, groupBy: groupBy, offset: offset, count: count),
|
||||||
origin: TimelineOrigin.person,
|
origin: TimelineOrigin.person,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -430,7 +430,7 @@ class TimelineRepository extends DatabaseAccessor<Drift> with $TimelineRepositor
|
|||||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression<bool> _personAssetFilter(List<String> userIds, String personId) {
|
Expression<bool> _personAssetFilter(String userId, String personId) {
|
||||||
final faceAssetIds = _db.assetFaceEntity.selectOnly()
|
final faceAssetIds = _db.assetFaceEntity.selectOnly()
|
||||||
..addColumns([_db.assetFaceEntity.assetId])
|
..addColumns([_db.assetFaceEntity.assetId])
|
||||||
..where(
|
..where(
|
||||||
@@ -438,22 +438,23 @@ class TimelineRepository extends DatabaseAccessor<Drift> with $TimelineRepositor
|
|||||||
_db.assetFaceEntity.isVisible.equals(true) &
|
_db.assetFaceEntity.isVisible.equals(true) &
|
||||||
_db.assetFaceEntity.deletedAt.isNull(),
|
_db.assetFaceEntity.deletedAt.isNull(),
|
||||||
);
|
);
|
||||||
|
final partnerIds = _db.partnerEntity.selectOnly()
|
||||||
|
..addColumns([_db.partnerEntity.sharedById])
|
||||||
|
..where(_db.partnerEntity.sharedWithId.equals(userId));
|
||||||
final albumAssetIds = _db.remoteAlbumAssetEntity.selectOnly()..addColumns([_db.remoteAlbumAssetEntity.assetId]);
|
final albumAssetIds = _db.remoteAlbumAssetEntity.selectOnly()..addColumns([_db.remoteAlbumAssetEntity.assetId]);
|
||||||
return _db.remoteAssetEntity.id.isInQuery(faceAssetIds) &
|
return _db.remoteAssetEntity.id.isInQuery(faceAssetIds) &
|
||||||
(_db.remoteAssetEntity.ownerId.isIn(userIds) | _db.remoteAssetEntity.id.isInQuery(albumAssetIds)) &
|
(_db.remoteAssetEntity.ownerId.equals(userId) |
|
||||||
|
_db.remoteAssetEntity.ownerId.isInQuery(partnerIds) |
|
||||||
|
_db.remoteAssetEntity.id.isInQuery(albumAssetIds)) &
|
||||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline);
|
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Bucket>> _watchPersonBucket(
|
Stream<List<Bucket>> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||||
List<String> userIds,
|
|
||||||
String personId, {
|
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
|
||||||
}) {
|
|
||||||
if (groupBy == GroupAssetsBy.none) {
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
final query = _db.remoteAssetEntity.selectOnly()
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
..addColumns([_db.remoteAssetEntity.id.count()])
|
..addColumns([_db.remoteAssetEntity.id.count()])
|
||||||
..where(_personAssetFilter(userIds, personId));
|
..where(_personAssetFilter(userId, personId));
|
||||||
|
|
||||||
return query.map((row) {
|
return query.map((row) {
|
||||||
final count = row.read(_db.remoteAssetEntity.id.count())!;
|
final count = row.read(_db.remoteAssetEntity.id.count())!;
|
||||||
@@ -466,7 +467,7 @@ class TimelineRepository extends DatabaseAccessor<Drift> with $TimelineRepositor
|
|||||||
|
|
||||||
final query = _db.remoteAssetEntity.selectOnly()
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
..addColumns([assetCountExp, dateExp])
|
..addColumns([assetCountExp, dateExp])
|
||||||
..where(_personAssetFilter(userIds, personId))
|
..where(_personAssetFilter(userId, personId))
|
||||||
..groupBy([dateExp])
|
..groupBy([dateExp])
|
||||||
..orderBy([OrderingTerm.desc(dateExp)]);
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
@@ -478,14 +479,14 @@ class TimelineRepository extends DatabaseAccessor<Drift> with $TimelineRepositor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<BaseAsset>> _getPersonBucketAssets(
|
Future<List<BaseAsset>> _getPersonBucketAssets(
|
||||||
List<String> userIds,
|
String userId,
|
||||||
String personId, {
|
String personId, {
|
||||||
required int offset,
|
required int offset,
|
||||||
required int count,
|
required int count,
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
}) {
|
}) {
|
||||||
final query = _db.remoteAssetEntity.select()
|
final query = _db.remoteAssetEntity.select()
|
||||||
..where((_) => _personAssetFilter(userIds, personId))
|
..where((_) => _personAssetFilter(userId, personId))
|
||||||
..orderBy(_assetDateOrder(groupBy))
|
..orderBy(_assetDateOrder(groupBy))
|
||||||
..limit(count, offset: offset);
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ class _PersonPageState extends ConsumerState<PersonPage> {
|
|||||||
throw Exception('User must be logged in to view person timeline');
|
throw Exception('User must be logged in to view person timeline');
|
||||||
}
|
}
|
||||||
|
|
||||||
final users = ref.watch(timelineUsersProvider).valueOrNull ?? [user.id];
|
final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id);
|
||||||
final timelineService = ref.watch(timelineFactoryProvider).person(users, _person.id);
|
|
||||||
ref.onDispose(timelineService.dispose);
|
ref.onDispose(timelineService.dispose);
|
||||||
return timelineService;
|
return timelineService;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -103,6 +103,22 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('person assets', () {
|
group('person assets', () {
|
||||||
|
test('includes partner assets even when the partner is not shown in the timeline', () async {
|
||||||
|
final user = await ctx.newUser();
|
||||||
|
final partner = await ctx.newUser();
|
||||||
|
final stranger = await ctx.newUser();
|
||||||
|
await ctx.newPartner(sharedById: partner.id, sharedWithId: user.id, inTimeline: false);
|
||||||
|
|
||||||
|
final person = await ctx.newPerson(ownerId: user.id);
|
||||||
|
final partnerAsset = await ctx.newRemoteAsset(ownerId: partner.id);
|
||||||
|
final strangerAsset = await ctx.newRemoteAsset(ownerId: stranger.id);
|
||||||
|
await ctx.newFace(assetId: partnerAsset.id, personId: person.id);
|
||||||
|
await ctx.newFace(assetId: strangerAsset.id, personId: person.id);
|
||||||
|
|
||||||
|
final assets = await sut.person(user.id, person.id, .day).assetSource(0, 10);
|
||||||
|
expect(assets.map((a) => (a as RemoteAsset).id), [partnerAsset.id]);
|
||||||
|
});
|
||||||
|
|
||||||
test('does not duplicate an asset that has multiple face records for the same person', () async {
|
test('does not duplicate an asset that has multiple face records for the same person', () async {
|
||||||
// Regression check for #26723: an INNER JOIN between remote_asset_entity and asset_face_entity
|
// Regression check for #26723: an INNER JOIN between remote_asset_entity and asset_face_entity
|
||||||
// fanned out one asset into N rows when N face records pointed at the same (asset, person) pair
|
// fanned out one asset into N rows when N face records pointed at the same (asset, person) pair
|
||||||
@@ -113,7 +129,7 @@ void main() {
|
|||||||
await ctx.newFace(assetId: asset.id, personId: person.id);
|
await ctx.newFace(assetId: asset.id, personId: person.id);
|
||||||
await ctx.newFace(assetId: asset.id, personId: person.id);
|
await ctx.newFace(assetId: asset.id, personId: person.id);
|
||||||
|
|
||||||
final query = sut.person([user.id], person.id, .day);
|
final query = sut.person(user.id, person.id, .day);
|
||||||
|
|
||||||
final buckets = await query.bucketSource().first;
|
final buckets = await query.bucketSource().first;
|
||||||
expect(buckets, hasLength(1));
|
expect(buckets, hasLength(1));
|
||||||
@@ -140,7 +156,7 @@ void main() {
|
|||||||
await ctx.newFace(assetId: shiftedLater.id, personId: person.id);
|
await ctx.newFace(assetId: shiftedLater.id, personId: person.id);
|
||||||
await ctx.newFace(assetId: shiftedEarlier.id, personId: person.id);
|
await ctx.newFace(assetId: shiftedEarlier.id, personId: person.id);
|
||||||
|
|
||||||
final query = sut.person([user.id], person.id, .day);
|
final query = sut.person(user.id, person.id, .day);
|
||||||
|
|
||||||
final buckets = await query.bucketSource().first;
|
final buckets = await query.bucketSource().first;
|
||||||
expect(buckets, hasLength(2));
|
expect(buckets, hasLength(2));
|
||||||
|
|||||||
Reference in New Issue
Block a user