mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
feat: cache asset info for prev/next navigation (#24482)
Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
4f803832ad
commit
0a9f1a3cbf
3 changed files with 75 additions and 8 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
import type { Action } from '$lib/components/asset-viewer/actions/action';
|
import type { Action } from '$lib/components/asset-viewer/actions/action';
|
||||||
import type { AssetCursor } from '$lib/components/asset-viewer/asset-viewer.svelte';
|
import type { AssetCursor } from '$lib/components/asset-viewer/asset-viewer.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
|
import { assetCacheManager } from '$lib/managers/AssetCacheManager.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
import { navigate } from '$lib/utils/navigation';
|
import { navigate } from '$lib/utils/navigation';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { type AlbumResponseDto, type AssetResponseDto, type PersonResponseDto, getAssetInfo } from '@immich/sdk';
|
import { type AlbumResponseDto, type AssetResponseDto, type PersonResponseDto, getAssetInfo } from '@immich/sdk';
|
||||||
import { onMount, untrack } from 'svelte';
|
import { onDestroy, onMount, untrack } from 'svelte';
|
||||||
|
|
||||||
let { asset: viewingAsset, gridScrollTarget } = assetViewingStore;
|
let { asset: viewingAsset, gridScrollTarget } = assetViewingStore;
|
||||||
|
|
||||||
|
|
@ -196,6 +196,9 @@
|
||||||
await navigate({ targetRoute: 'current', assetId: restoredAsset.id });
|
await navigate({ targetRoute: 'current', assetId: restoredAsset.id });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
onDestroy(() => {
|
||||||
|
assetCacheManager.invalidate();
|
||||||
|
});
|
||||||
const onAssetUpdate = ({ asset }: { event: 'upload' | 'update'; asset: AssetResponseDto }) => {
|
const onAssetUpdate = ({ asset }: { event: 'upload' | 'update'; asset: AssetResponseDto }) => {
|
||||||
if (asset.id === assetCursor.current.id) {
|
if (asset.id === assetCursor.current.id) {
|
||||||
void loadCloseAssets(asset);
|
void loadCloseAssets(asset);
|
||||||
|
|
@ -222,7 +225,10 @@
|
||||||
{album}
|
{album}
|
||||||
{person}
|
{person}
|
||||||
preAction={handlePreAction}
|
preAction={handlePreAction}
|
||||||
onAction={handleAction}
|
onAction={(action) => {
|
||||||
|
handleAction(action);
|
||||||
|
assetCacheManager.invalidate();
|
||||||
|
}}
|
||||||
onUndoDelete={handleUndoDelete}
|
onUndoDelete={handleUndoDelete}
|
||||||
onPrevious={() => handleNavigateToAsset(assetCursor.previousAsset)}
|
onPrevious={() => handleNavigateToAsset(assetCursor.previousAsset)}
|
||||||
onNext={() => handleNavigateToAsset(assetCursor.nextAsset)}
|
onNext={() => handleNavigateToAsset(assetCursor.nextAsset)}
|
||||||
|
|
|
||||||
60
web/src/lib/managers/AssetCacheManager.svelte.ts
Normal file
60
web/src/lib/managers/AssetCacheManager.svelte.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { getAssetInfo, getAssetOcr, type AssetOcrResponseDto, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
|
const defaultSerializer = <K>(params: K) => JSON.stringify(params);
|
||||||
|
|
||||||
|
class AsyncCache<V> {
|
||||||
|
#cache = new Map<string, V>();
|
||||||
|
|
||||||
|
async getOrFetch<K>(
|
||||||
|
params: K,
|
||||||
|
fetcher: (params: K) => Promise<V>,
|
||||||
|
keySerializer: (params: K) => string = defaultSerializer,
|
||||||
|
updateCache: boolean,
|
||||||
|
): Promise<V> {
|
||||||
|
const cacheKey = keySerializer(params);
|
||||||
|
|
||||||
|
const cached = this.#cache.get(cacheKey);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = await fetcher(params);
|
||||||
|
if (value && updateCache) {
|
||||||
|
this.#cache.set(cacheKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AssetCacheManager {
|
||||||
|
#assetCache = new AsyncCache<AssetResponseDto>();
|
||||||
|
#ocrCache = new AsyncCache<AssetOcrResponseDto[]>();
|
||||||
|
|
||||||
|
async getAsset(assetIdentifier: { key?: string; slug?: string; id: string }, updateCache = true) {
|
||||||
|
return this.#assetCache.getOrFetch(assetIdentifier, getAssetInfo, defaultSerializer, updateCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAssetOcr(id: string) {
|
||||||
|
return this.#ocrCache.getOrFetch({ id }, getAssetOcr, (params) => params.id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAssetCache() {
|
||||||
|
this.#assetCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearOcrCache() {
|
||||||
|
this.#ocrCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate() {
|
||||||
|
this.clearAssetCache();
|
||||||
|
this.clearOcrCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const assetCacheManager = new AssetCacheManager();
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import type { RouteId } from '$app/types';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { getAssetInfo } from '@immich/sdk';
|
import { assetCacheManager } from '$lib/managers/AssetCacheManager.svelte';
|
||||||
import type { NavigationTarget } from '@sveltejs/kit';
|
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export type AssetGridRouteSearchParams = {
|
export type AssetGridRouteSearchParams = {
|
||||||
|
|
@ -20,11 +20,12 @@ export const isAlbumsRoute = (route?: string | null) => !!route?.startsWith('/(u
|
||||||
export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]');
|
export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]');
|
||||||
export const isLockedFolderRoute = (route?: string | null) => !!route?.startsWith('/(user)/locked');
|
export const isLockedFolderRoute = (route?: string | null) => !!route?.startsWith('/(user)/locked');
|
||||||
|
|
||||||
export const isAssetViewerRoute = (target?: NavigationTarget | null) =>
|
export const isAssetViewerRoute = (
|
||||||
!!(target?.route.id?.endsWith('/[[assetId=id]]') && 'assetId' in (target?.params || {}));
|
target?: { route?: { id?: RouteId | null }; params?: Record<string, string> | null } | null,
|
||||||
|
) => !!(target?.route?.id?.endsWith('/[[assetId=id]]') && 'assetId' in (target?.params || {}));
|
||||||
|
|
||||||
export function getAssetInfoFromParam({ assetId, slug, key }: { assetId?: string; key?: string; slug?: string }) {
|
export function getAssetInfoFromParam({ assetId, slug, key }: { assetId?: string; key?: string; slug?: string }) {
|
||||||
return assetId ? getAssetInfo({ id: assetId, slug, key }) : undefined;
|
return assetId ? assetCacheManager.getAsset({ id: assetId, slug, key }, false) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentUrlWithoutAsset() {
|
function currentUrlWithoutAsset() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue