mirror of
https://github.com/immich-app/immich.git
synced 2025-07-03 21:40:00 +02:00
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed * Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation * Reduce jank on scroll, delay DOM updates until after scroll * css opt, log measure time * Trickle out queue while scrolling, flush when stopped * yay * Cleanup cleanup... * everybody... * everywhere... * Clean up cleanup! * Everybody do their share * CLEANUP! * package-lock ? * dynamic measure, todo * Fix web test * type lint * fix e2e * e2e test * Better scrollbar * Tuning, and more tunables * Tunable tweaks, more tunables * Scrollbar dots and viewport events * lint * Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes * New tunables, and don't update url by default * Bug fixes * Bug fix, with debug * Fix flickr, fix graybox bug, reduced debug * Refactor/cleanup * Fix * naming * Final cleanup * review comment * Forgot to update this after naming change * scrubber works, with debug * cleanup * Rename scrollbar to scrubber * rename to * left over rename and change to previous album bar * bugfix addassets, comments * missing destroy(), cleanup --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
07538299cf
commit
837b1e4929
50 changed files with 2947 additions and 843 deletions
web/src/lib/utils
|
@ -5,6 +5,9 @@ import { getAssetInfo } from '@immich/sdk';
|
|||
import type { NavigationTarget } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export type AssetGridRouteSearchParams = {
|
||||
at: string | null | undefined;
|
||||
};
|
||||
export const isExternalUrl = (url: string): boolean => {
|
||||
return new URL(url, window.location.href).origin !== window.location.origin;
|
||||
};
|
||||
|
@ -33,17 +36,38 @@ function currentUrlWithoutAsset() {
|
|||
|
||||
export function currentUrlReplaceAssetId(assetId: string) {
|
||||
const $page = get(page);
|
||||
const params = new URLSearchParams($page.url.search);
|
||||
// always remove the assetGridScrollTargetParams
|
||||
params.delete('at');
|
||||
const searchparams = params.size > 0 ? '?' + params.toString() : '';
|
||||
// this contains special casing for the /photos/:assetId photos route, which hangs directly
|
||||
// off / instead of a subpath, unlike every other asset-containing route.
|
||||
return isPhotosRoute($page.route.id)
|
||||
? `${AppRoute.PHOTOS}/${assetId}${$page.url.search}`
|
||||
: `${$page.url.pathname.replace(/(\/photos.*)$/, '')}/photos/${assetId}${$page.url.search}`;
|
||||
? `${AppRoute.PHOTOS}/${assetId}${searchparams}`
|
||||
: `${$page.url.pathname.replace(/(\/photos.*)$/, '')}/photos/${assetId}${searchparams}`;
|
||||
}
|
||||
|
||||
function replaceScrollTarget(url: string, searchParams?: AssetGridRouteSearchParams | null) {
|
||||
const $page = get(page);
|
||||
const parsed = new URL(url, $page.url);
|
||||
|
||||
const { at: assetId } = searchParams || { at: null };
|
||||
|
||||
if (!assetId) {
|
||||
return parsed.pathname;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams($page.url.search);
|
||||
if (assetId) {
|
||||
params.set('at', assetId);
|
||||
}
|
||||
return parsed.pathname + '?' + params.toString();
|
||||
}
|
||||
|
||||
function currentUrl() {
|
||||
const $page = get(page);
|
||||
const current = $page.url;
|
||||
return current.pathname + current.search;
|
||||
return current.pathname + current.search + current.hash;
|
||||
}
|
||||
|
||||
interface Route {
|
||||
|
@ -55,24 +79,58 @@ interface Route {
|
|||
|
||||
interface AssetRoute extends Route {
|
||||
targetRoute: 'current';
|
||||
assetId: string | null;
|
||||
assetId: string | null | undefined;
|
||||
}
|
||||
interface AssetGridRoute extends Route {
|
||||
targetRoute: 'current';
|
||||
assetId: string | null | undefined;
|
||||
assetGridRouteSearchParams: AssetGridRouteSearchParams | null | undefined;
|
||||
}
|
||||
|
||||
type ImmichRoute = AssetRoute | AssetGridRoute;
|
||||
|
||||
type NavOptions = {
|
||||
/* navigate even if url is the same */
|
||||
forceNavigate?: boolean | undefined;
|
||||
replaceState?: boolean | undefined;
|
||||
noScroll?: boolean | undefined;
|
||||
keepFocus?: boolean | undefined;
|
||||
invalidateAll?: boolean | undefined;
|
||||
state?: App.PageState | undefined;
|
||||
};
|
||||
|
||||
function isAssetRoute(route: Route): route is AssetRoute {
|
||||
return route.targetRoute === 'current' && 'assetId' in route;
|
||||
}
|
||||
|
||||
async function navigateAssetRoute(route: AssetRoute) {
|
||||
function isAssetGridRoute(route: Route): route is AssetGridRoute {
|
||||
return route.targetRoute === 'current' && 'assetId' in route && 'assetGridRouteSearchParams' in route;
|
||||
}
|
||||
|
||||
async function navigateAssetRoute(route: AssetRoute, options?: NavOptions) {
|
||||
const { assetId } = route;
|
||||
const next = assetId ? currentUrlReplaceAssetId(assetId) : currentUrlWithoutAsset();
|
||||
if (next !== currentUrl()) {
|
||||
await goto(next, { replaceState: false });
|
||||
const current = currentUrl();
|
||||
if (next !== current || options?.forceNavigate) {
|
||||
await goto(next, options);
|
||||
}
|
||||
}
|
||||
|
||||
export function navigate<T extends Route>(change: T): Promise<void> {
|
||||
if (isAssetRoute(change)) {
|
||||
return navigateAssetRoute(change);
|
||||
async function navigateAssetGridRoute(route: AssetGridRoute, options?: NavOptions) {
|
||||
const { assetId, assetGridRouteSearchParams: assetGridScrollTarget } = route;
|
||||
const assetUrl = assetId ? currentUrlReplaceAssetId(assetId) : currentUrlWithoutAsset();
|
||||
const next = replaceScrollTarget(assetUrl, assetGridScrollTarget);
|
||||
const current = currentUrl();
|
||||
if (next !== current || options?.forceNavigate) {
|
||||
await goto(next, options);
|
||||
}
|
||||
}
|
||||
|
||||
export function navigate(change: ImmichRoute, options?: NavOptions): Promise<void> {
|
||||
if (isAssetGridRoute(change)) {
|
||||
return navigateAssetGridRoute(change, options);
|
||||
} else if (isAssetRoute(change)) {
|
||||
return navigateAssetRoute(change, options);
|
||||
}
|
||||
// future navigation requests here
|
||||
throw `Invalid navigation: ${JSON.stringify(change)}`;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue