diff --git a/i18n/en.json b/i18n/en.json index 765e03d985..84b74b172a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2358,6 +2358,7 @@ "view_qr_code": "View QR code", "view_similar_photos": "View similar photos", "view_stack": "View Stack", + "view_in_app": "View in the Immich app", "view_user": "View User", "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 0d4925077a..75dbebc93e 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -123,6 +123,9 @@ + diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index 386a542ba9..b7a49c5a11 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -20,6 +20,7 @@ import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/memory.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:url_launcher/url_launcher.dart'; final deepLinkServiceProvider = Provider( (ref) => DeepLinkService( @@ -102,10 +103,13 @@ class DeepLinkService { Future handleMyImmichApp(PlatformDeepLink link, WidgetRef ref, bool isColdStart) async { final path = link.uri.path; + final queryParams = link.uri.queryParameters; const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; final assetRegex = RegExp('/photos/($uuidRegex)'); final albumRegex = RegExp('/albums/($uuidRegex)'); + // Share links can use UUID keys or custom slugs + final shareRegex = RegExp(r'/share/([^/?]+)'); PageRouteInfo? deepLinkRoute; if (assetRegex.hasMatch(path)) { @@ -116,6 +120,19 @@ class DeepLinkService { deepLinkRoute = await _buildAlbumDeepLink(albumId); } else if (path == "/memory") { deepLinkRoute = await _buildMemoryDeepLink(null); + } else if (shareRegex.hasMatch(path)) { + // Handle shared links by opening them in the browser + // The mobile app doesn't have a native viewer for external shared links yet + final serverUrl = queryParams['server']; + final shareKey = shareRegex.firstMatch(path)?.group(1); + if (serverUrl != null && shareKey != null) { + final decodedServerUrl = Uri.decodeComponent(serverUrl); + final shareUrl = Uri.parse('$decodedServerUrl/share/$shareKey'); + await launchUrl(shareUrl, mode: LaunchMode.externalApplication); + } + // Return appropriate deep link based on app state + if (isColdStart) return DeepLink.defaultPath; + return DeepLink.none; } // Deep link resolution failed, safely handle it based on the app state diff --git a/web/src/lib/components/pages/SharedLinkPage.svelte b/web/src/lib/components/pages/SharedLinkPage.svelte index d30c3ce341..fca1cc6515 100644 --- a/web/src/lib/components/pages/SharedLinkPage.svelte +++ b/web/src/lib/components/pages/SharedLinkPage.svelte @@ -1,7 +1,9 @@ + +{#if showBanner} +
+
+
+ +
+
+

+ Immich +

+

+ {$t('view_in_app')} +

+
+
+ +
+ + +
+
+{/if}