diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index 76aaf8bc6..6db90679d 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -108,7 +108,7 @@ class DeepLinkService { Future _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, ); } diff --git a/mobile/test/services/deep_link_service_test.dart b/mobile/test/services/deep_link_service_test.dart index 09ea2a19d..e3cac477f 100644 --- a/mobile/test/services/deep_link_service_test.dart +++ b/mobile/test/services/deep_link_service_test.dart @@ -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 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.from(invocation.positionalArguments[0] as List); 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()); + expect((route!.args! as AssetViewerRouteArgs).timelineService.origin, TimelineOrigin.trash); + }); }