feat: in-app deeplink viewer

This commit is contained in:
Alex
2026-01-25 17:27:04 -06:00
parent be0eef405d
commit 44b4239a4f
5 changed files with 101 additions and 0 deletions
@@ -1,7 +1,9 @@
<script lang="ts">
import { browser } from '$app/environment';
import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
import IndividualSharedViewer from '$lib/components/share-page/individual-shared-viewer.svelte';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import OpenInAppBanner from '$lib/components/shared-components/open-in-app-banner.svelte';
import ThemeButton from '$lib/components/shared-components/theme-button.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { user } from '$lib/stores/user.store';
@@ -65,6 +67,14 @@
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
{#if key}
<meta
name="apple-itunes-app"
content="app-id=1613945652, app-argument=https://my.immich.app/share/{key}?server={encodeURIComponent(
globalThis.location?.origin ?? ''
)}"
/>
{/if}
</svelte:head>
{#if passwordRequired}
<main
@@ -106,3 +116,7 @@
<IndividualSharedViewer {sharedLink} {isOwned} />
</div>
{/if}
{#if key && browser}
<OpenInAppBanner shareKey={key} serverUrl={globalThis.location?.origin ?? ''} />
{/if}
@@ -0,0 +1,66 @@
<script lang="ts">
import { browser } from '$app/environment';
import { mdiClose } from '@mdi/js';
import { Button, Icon, Logo } from '@immich/ui';
import { t } from 'svelte-i18n';
type Props = {
shareKey: string;
serverUrl: string;
};
const { shareKey, serverUrl }: Props = $props();
const STORAGE_KEY = 'immich-open-in-app-dismissed';
const isMobile = $derived(
browser && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
);
let isDismissed = $state(browser && localStorage.getItem(STORAGE_KEY) === 'true');
let showBanner = $derived(isMobile && !isDismissed);
const deepLinkUrl = $derived(
`https://my.immich.app/share/${shareKey}?server=${encodeURIComponent(serverUrl)}`,
);
function dismiss() {
isDismissed = true;
if (browser) {
localStorage.setItem(STORAGE_KEY, 'true');
}
}
</script>
{#if showBanner}
<div
class="fixed bottom-0 left-0 right-0 z-50 flex items-center justify-between gap-3 bg-immich-bg dark:bg-immich-dark-gray p-3 shadow-lg border-t border-gray-200 dark:border-gray-700"
>
<div class="flex items-center gap-3 min-w-0">
<div class="shrink-0 w-10 h-10">
<Logo variant="icon" />
</div>
<div class="min-w-0">
<p class="font-semibold text-immich-primary dark:text-immich-dark-primary text-sm truncate">
Immich
</p>
<p class="text-xs text-gray-500 dark:text-gray-400 truncate">
{$t('view_in_app')}
</p>
</div>
</div>
<div class="flex items-center gap-2 shrink-0">
<Button href={deepLinkUrl} size="small" shape="round">
{$t('open')}
</Button>
<button
type="button"
onclick={dismiss}
class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
aria-label={$t('close')}
>
<Icon icon={mdiClose} size="20" />
</button>
</div>
</div>
{/if}