fix(web): fix shared link /s/photos.* navigation after password login (#27788)

* fix(web): fix shared link navigation after password login

* use regex after all

* chore: use special case for shared link with slug route

* dont use onMount

* fix lint

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Mees Frensel
2026-05-02 15:26:35 +02:00
committed by GitHub
parent b9e0e65bdb
commit 3d320d9751
2 changed files with 22 additions and 18 deletions
@@ -36,6 +36,10 @@
let isOwned = $derived(authManager.authenticated && authManager.user.id === sharedLink?.userId); let isOwned = $derived(authManager.authenticated && authManager.user.id === sharedLink?.userId);
let password = $state(''); let password = $state('');
if (passwordRequired) {
assetViewerManager.showAssetViewer(false);
}
const handlePasswordSubmit = async () => { const handlePasswordSubmit = async () => {
try { try {
sharedLink = await sharedLinkLogin({ key, slug, sharedLinkLoginDto: { password } }); sharedLink = await sharedLinkLogin({ key, slug, sharedLinkLoginDto: { password } });
@@ -101,10 +105,10 @@
</header> </header>
{/if} {/if}
{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} {#if !passwordRequired && sharedLink?.type === SharedLinkType.Album}
<AlbumViewer {sharedLink} /> <AlbumViewer {sharedLink} />
{/if} {/if}
{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} {#if !passwordRequired && sharedLink?.type === SharedLinkType.Individual}
<div class="immich-scrollbar"> <div class="immich-scrollbar">
<IndividualSharedViewer {sharedLink} {isOwned} /> <IndividualSharedViewer {sharedLink} {isOwned} />
</div> </div>
+16 -16
View File
@@ -1,6 +1,5 @@
import { get } from 'svelte/store';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/state';
import type { RouteId } from '$app/types'; import type { RouteId } from '$app/types';
import { assetCacheManager } from '$lib/managers/AssetCacheManager.svelte'; import { assetCacheManager } from '$lib/managers/AssetCacheManager.svelte';
import { Route } from '$lib/route'; import { Route } from '$lib/route';
@@ -13,8 +12,9 @@ export const isExternalUrl = (url: string): boolean => {
}; };
export const isPhotosRoute = (route?: string | null) => !!route?.startsWith('/(user)/photos/[[assetId=id]]'); export const isPhotosRoute = (route?: string | null) => !!route?.startsWith('/(user)/photos/[[assetId=id]]');
const isSharedLinkSlugRoute = (route?: string | null) => !!route?.startsWith('/(user)/s/[slug]');
export const isSharedLinkRoute = (route?: string | null) => export const isSharedLinkRoute = (route?: string | null) =>
!!route?.startsWith('/(user)/share/[key]') || !!route?.startsWith('/(user)/s/[slug]'); !!route?.startsWith('/(user)/share/[key]') || isSharedLinkSlugRoute(route);
export const isSearchRoute = (route?: string | null) => !!route?.startsWith('/(user)/search'); export const isSearchRoute = (route?: string | null) => !!route?.startsWith('/(user)/search');
export const isAlbumsRoute = (route?: string | null) => !!route?.startsWith('/(user)/albums/[albumId=id]'); export const isAlbumsRoute = (route?: string | null) => !!route?.startsWith('/(user)/albums/[albumId=id]');
export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]'); export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]');
@@ -29,31 +29,32 @@ export function getAssetInfoFromParam({ assetId, slug, key }: { assetId?: string
} }
function currentUrlWithoutAsset() { function currentUrlWithoutAsset() {
const $page = get(page);
// This contains special casing for the /photos/:assetId route, which hangs directly // This contains special casing for the /photos/:assetId route, which hangs directly
// off / instead of a subpath, unlike every other asset-containing route. // off / instead of a subpath, unlike every other asset-containing route.
return isPhotosRoute($page.route.id) if (isPhotosRoute(page.route.id)) {
? Route.photos() + $page.url.search return Route.photos() + page.url.search;
: $page.url.pathname.replace(/(\/photos.*)$/, '') + $page.url.search; } else if (isSharedLinkSlugRoute(page.route.id)) {
return Route.viewSharedLink({ slug: page.data.slug, key: page.data.key }) + page.url.search;
} else {
return page.url.pathname.replace(/(\/photos.*)$/, '') + page.url.search;
}
} }
export function currentUrlReplaceAssetId(assetId: string) { export function currentUrlReplaceAssetId(assetId: string) {
const $page = get(page); const params = new URLSearchParams(page.url.search);
const params = new URLSearchParams($page.url.search);
// always remove the assetGridScrollTargetParams // always remove the assetGridScrollTargetParams
params.delete('at'); params.delete('at');
const paramsString = params.toString(); const paramsString = params.toString();
const searchparams = paramsString == '' ? '' : '?' + params.toString(); const searchparams = paramsString == '' ? '' : '?' + params.toString();
// this contains special casing for the /photos/:assetId photos route, which hangs directly // this contains special casing for the /photos/:assetId photos route, which hangs directly
// off / instead of a subpath, unlike every other asset-containing route. // off / instead of a subpath, unlike every other asset-containing route.
return isPhotosRoute($page.route.id) return isPhotosRoute(page.route.id)
? `${Route.viewAsset({ id: assetId })}${searchparams}` ? `${Route.viewAsset({ id: assetId })}${searchparams}`
: `${$page.url.pathname.replace(/\/photos\/[^/]+$/, '')}/photos/${assetId}${searchparams}`; : `${page.url.pathname.replace(/\/photos\/[^/]+$/, '')}/photos/${assetId}${searchparams}`;
} }
function replaceScrollTarget(url: string, searchParams?: AssetGridRouteSearchParams | null) { function replaceScrollTarget(url: string, searchParams?: AssetGridRouteSearchParams | null) {
const $page = get(page); const parsed = new URL(url, page.url);
const parsed = new URL(url, $page.url);
const { at: assetId } = searchParams || { at: null }; const { at: assetId } = searchParams || { at: null };
@@ -61,7 +62,7 @@ function replaceScrollTarget(url: string, searchParams?: AssetGridRouteSearchPar
return parsed.pathname; return parsed.pathname;
} }
const params = new URLSearchParams($page.url.search); const params = new URLSearchParams(page.url.search);
if (assetId) { if (assetId) {
params.set('at', assetId); params.set('at', assetId);
} }
@@ -69,8 +70,7 @@ function replaceScrollTarget(url: string, searchParams?: AssetGridRouteSearchPar
} }
function currentUrl() { function currentUrl() {
const $page = get(page); const current = page.url;
const current = $page.url;
return current.pathname + current.search + current.hash; return current.pathname + current.search + current.hash;
} }