fix(mobile): handle links to locked and trashed photos (#33034)

* fix(mobile): handle links to locked and trashed photos

* use a real timeline service in the deep link test

(cherry picked from commit 87ef895041e160aef4f61f2d7af1ad3f8e38827d)
This commit is contained in:
Santo Shakil authored and Codex committed 2026-11-07 21:31:45 +03:00
1 parent 8b7c9d7328
commit 6cce8efed8
2 files changed
+27 -5

No files matched your search

+2 -2
View File
@@ -108,7 +108,7 @@ class DeepLinkService {
Future<PageRouteInfo?> _buildAssetDeepLink(String assetId, WidgetRef ref, {String? albumId}) async {
final asset = await _betaAssetService.getRemoteAsset(assetId);
if (asset == null) {
if (asset == null || asset.isLocked) {
return null;
}
@@ -117,7 +117,7 @@ class DeepLinkService {
AssetViewer.setAsset(ref, asset);
return AssetViewerRoute(
initialIndex: 0,
timelineService: _betaTimelineFactory.fromAssets([asset], TimelineOrigin.deepLink),
timelineService: _betaTimelineFactory.fromAssets([asset], asset.isTrashed ? .trash : .deepLink),
currentAlbum: album,
);
}
@@ -23,7 +23,6 @@ class MockRemoteAlbumService extends Mock implements RemoteAlbumService {}
class MockMemoryService extends Mock implements MemoryService {}
class MockPlatformDeepLink extends Mock implements PlatformDeepLink {}
class MockWidgetRef extends Mock implements WidgetRef {}
@@ -88,6 +87,8 @@ void main() {
late List<TimelineService> createdTimelineServices;
late DeepLinkService sut;
setUpAll(() => registerFallbackValue(TimelineOrigin.main));
setUp(() {
timelineFactory = MockTimelineFactory();
assetService = MockAssetService();
@@ -96,12 +97,12 @@ void main() {
ref = MockWidgetRef();
createdTimelineServices = [];
when(() => timelineFactory.fromAssets(any(), TimelineOrigin.deepLink)).thenAnswer((invocation) {
when(() => timelineFactory.fromAssets(any(), any())).thenAnswer((invocation) {
final assets = List<BaseAsset>.from(invocation.positionalArguments[0] as List<BaseAsset>);
final timelineService = TimelineService((
assetSource: (index, count) async => assets.skip(index).take(count).toList(),
bucketSource: () => Stream.value([Bucket(assetCount: assets.length)]),
origin: TimelineOrigin.deepLink,
origin: invocation.positionalArguments[1] as TimelineOrigin,
));
createdTimelineServices.add(timelineService);
return timelineService;
@@ -179,4 +180,25 @@ void main() {
verifyNever(() => memoryService.getMemoryLane(any()));
verify(() => memoryService.get(_memoryId)).called(1);
});
test('locked photo link opens nothing', () async {
when(
() => assetService.getRemoteAsset(_assetId),
).thenAnswer((_) async => _asset.copyWith(visibility: AssetVisibility.locked));
final route = await sut.handleMyImmichApp(link('/photos/$_assetId'), ref);
expect(route, isNull);
});
test('trashed photo link opens the viewer on a trash timeline', () async {
when(
() => assetService.getRemoteAsset(_assetId),
).thenAnswer((_) async => _asset.copyWith(deletedAt: DateTime(2026, 6, 13)));
final route = await sut.handleMyImmichApp(link('/photos/$_assetId'), ref);
expect(route, isA<AssetViewerRoute>());
expect((route!.args! as AssetViewerRouteArgs).timelineService.origin, TimelineOrigin.trash);
});
}