From c581bce715df660c5849db6536de0bfb5893842f Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 28 Oct 2026 19:55:32 +0530 Subject: [PATCH] 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) --- .../lib/domain/services/timeline.service.dart | 4 +-- .../repositories/timeline.repository.dart | 29 ++++++++++--------- .../lib/presentation/pages/person.page.dart | 3 +- .../timeline_repository_test.dart | 20 +++++++++++-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 4c77e8047..5dc8ec3a8 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -74,8 +74,8 @@ class TimelineFactory { TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy)); - TimelineService person(List userIds, String personId) => - TimelineService(_timelineRepository.person(userIds, personId, groupBy)); + TimelineService person(String userId, String personId) => + TimelineService(_timelineRepository.person(userId, personId, groupBy)); TimelineService fromAssets(List assets, TimelineOrigin type) => TimelineService(_timelineRepository.fromAssets(assets, type)); diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 9527e0b83..8a41f7d10 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -366,10 +366,10 @@ class TimelineRepository extends DatabaseAccessor with $TimelineRepositor origin: TimelineOrigin.place, ); - TimelineQuery person(List userIds, String personId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchPersonBucket(userIds, personId, groupBy: groupBy), + TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), assetSource: (offset, count) => - _getPersonBucketAssets(userIds, personId, groupBy: groupBy, offset: offset, count: count), + _getPersonBucketAssets(userId, personId, groupBy: groupBy, offset: offset, count: count), origin: TimelineOrigin.person, ); @@ -430,7 +430,7 @@ class TimelineRepository extends DatabaseAccessor with $TimelineRepositor return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } - Expression _personAssetFilter(List userIds, String personId) { + Expression _personAssetFilter(String userId, String personId) { final faceAssetIds = _db.assetFaceEntity.selectOnly() ..addColumns([_db.assetFaceEntity.assetId]) ..where( @@ -438,22 +438,23 @@ class TimelineRepository extends DatabaseAccessor with $TimelineRepositor _db.assetFaceEntity.isVisible.equals(true) & _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]); 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.visibility.equalsValue(AssetVisibility.timeline); } - Stream> _watchPersonBucket( - List userIds, - String personId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { final query = _db.remoteAssetEntity.selectOnly() ..addColumns([_db.remoteAssetEntity.id.count()]) - ..where(_personAssetFilter(userIds, personId)); + ..where(_personAssetFilter(userId, personId)); return query.map((row) { final count = row.read(_db.remoteAssetEntity.id.count())!; @@ -466,7 +467,7 @@ class TimelineRepository extends DatabaseAccessor with $TimelineRepositor final query = _db.remoteAssetEntity.selectOnly() ..addColumns([assetCountExp, dateExp]) - ..where(_personAssetFilter(userIds, personId)) + ..where(_personAssetFilter(userId, personId)) ..groupBy([dateExp]) ..orderBy([OrderingTerm.desc(dateExp)]); @@ -478,14 +479,14 @@ class TimelineRepository extends DatabaseAccessor with $TimelineRepositor } Future> _getPersonBucketAssets( - List userIds, + String userId, String personId, { required int offset, required int count, GroupAssetsBy groupBy = GroupAssetsBy.day, }) { final query = _db.remoteAssetEntity.select() - ..where((_) => _personAssetFilter(userIds, personId)) + ..where((_) => _personAssetFilter(userId, personId)) ..orderBy(_assetDateOrder(groupBy)) ..limit(count, offset: offset); diff --git a/mobile/lib/presentation/pages/person.page.dart b/mobile/lib/presentation/pages/person.page.dart index bb6e3f2da..15bbd6ad2 100644 --- a/mobile/lib/presentation/pages/person.page.dart +++ b/mobile/lib/presentation/pages/person.page.dart @@ -82,8 +82,7 @@ class _PersonPageState extends ConsumerState { 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(users, _person.id); + final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id); ref.onDispose(timelineService.dispose); return timelineService; }), diff --git a/mobile/test/medium/repositories/timeline_repository_test.dart b/mobile/test/medium/repositories/timeline_repository_test.dart index 15df15dd1..ddf7bb983 100644 --- a/mobile/test/medium/repositories/timeline_repository_test.dart +++ b/mobile/test/medium/repositories/timeline_repository_test.dart @@ -103,6 +103,22 @@ void main() { }); 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 { // 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 @@ -113,7 +129,7 @@ void main() { 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; expect(buckets, hasLength(1)); @@ -140,7 +156,7 @@ void main() { await ctx.newFace(assetId: shiftedLater.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; expect(buckets, hasLength(2));