Skip to content

Commit e34995e

Browse files
committed
feat: PreloadManager - improve perf and standardize preloading behevior
1 parent 8410469 commit e34995e

File tree

21 files changed

+615
-403
lines changed

21 files changed

+615
-403
lines changed

pnpm-lock.yaml

Lines changed: 1 addition & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
"@types/geojson": "^7946.0.16",
4141
"@zoom-image/core": "^0.41.0",
4242
"@zoom-image/svelte": "^0.3.0",
43-
"async-mutex": "^0.5.0",
4443
"dom-to-image": "^2.6.0",
4544
"fabric": "^6.5.4",
4645
"geo-coordinates-parser": "^1.7.4",

web/src/lib/components/asset-viewer/asset-viewer.svelte

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
import { AppRoute, AssetAction, ProjectionType } from '$lib/constants';
1111
import { activityManager } from '$lib/managers/activity-manager.svelte';
1212
import { authManager } from '$lib/managers/auth-manager.svelte';
13-
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
13+
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
1414
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
1515
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
1616
import { ocrManager } from '$lib/stores/ocr.svelte';
1717
import { alwaysLoadOriginalVideo, isShowDetail } from '$lib/stores/preferences.store';
1818
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
1919
import { user } from '$lib/stores/user.store';
2020
import { websocketEvents } from '$lib/stores/websocket';
21-
import { getAssetJobMessage, getSharedLink, handlePromiseError } from '$lib/utils';
21+
import { getAssetJobMessage, getAssetUrl, getSharedLink, handlePromiseError } from '$lib/utils';
2222
import { handleError } from '$lib/utils/handle-error';
23+
import { InvocationTracker } from '$lib/utils/invocationTracker';
2324
import { SlideshowHistory } from '$lib/utils/slideshow-history';
25+
import { preloadImageUrl } from '$lib/utils/sw-messaging';
2426
import { toTimelineAsset } from '$lib/utils/timeline-util';
2527
import {
2628
AssetJobName,
@@ -52,9 +54,14 @@
5254
5355
type HasAsset = boolean;
5456
57+
export type AssetCursor = {
58+
current: AssetResponseDto;
59+
nextAsset: AssetResponseDto | undefined | null;
60+
previousAsset: AssetResponseDto | undefined | null;
61+
};
62+
5563
interface Props {
56-
asset: AssetResponseDto;
57-
preloadAssets?: TimelineAsset[];
64+
cursor: AssetCursor;
5865
showNavigation?: boolean;
5966
withStacked?: boolean;
6067
isShared?: boolean;
@@ -71,8 +78,7 @@
7178
}
7279
7380
let {
74-
asset = $bindable(),
75-
preloadAssets = $bindable([]),
81+
cursor,
7682
showNavigation = true,
7783
withStacked = false,
7884
isShared = false,
@@ -99,10 +105,11 @@
99105
const stackThumbnailSize = 60;
100106
const stackSelectedThumbnailSize = 65;
101107
108+
let asset = $derived(cursor.current);
102109
let appearsInAlbums: AlbumResponseDto[] = $state([]);
103110
let shouldPlayMotionPhoto = $state(false);
104111
let sharedLink = getSharedLink();
105-
let enableDetailPanel = asset.hasMetadata;
112+
let enableDetailPanel = $derived(asset.hasMetadata);
106113
let slideshowStateUnsubscribe: () => void;
107114
let shuffleSlideshowUnsubscribe: () => void;
108115
let previewStackedAsset: AssetResponseDto | undefined = $state();
@@ -135,7 +142,7 @@
135142
136143
untrack(() => {
137144
if (stack && stack?.assets.length > 1) {
138-
preloadAssets.push(toTimelineAsset(stack.assets[1]));
145+
preloadImageUrl(getAssetUrl({ asset: stack.assets[1] }));
139146
}
140147
});
141148
};
@@ -234,7 +241,9 @@
234241
});
235242
};
236243
237-
const navigateAsset = async (order?: 'previous' | 'next', e?: Event) => {
244+
const tracker = new InvocationTracker();
245+
246+
const navigateAsset = (order?: 'previous' | 'next', e?: Event) => {
238247
if (!order) {
239248
if ($slideshowState === SlideshowState.PlaySlideshow) {
240249
order = $slideshowNavigation === SlideshowNavigation.AscendingOrder ? 'previous' : 'next';
@@ -244,38 +253,37 @@
244253
}
245254
246255
e?.stopPropagation();
256+
preloadManager.cancel(asset);
257+
if (tracker.isActive()) {
258+
return;
259+
}
247260
248-
let hasNext = false;
249-
250-
if ($slideshowState === SlideshowState.PlaySlideshow && $slideshowNavigation === SlideshowNavigation.Shuffle) {
251-
hasNext = order === 'previous' ? slideshowHistory.previous() : slideshowHistory.next();
252-
if (!hasNext) {
253-
const asset = await onRandom();
254-
if (asset) {
255-
slideshowHistory.queue(asset);
256-
hasNext = true;
261+
void tracker.invoke(async () => {
262+
let hasNext = false;
263+
264+
if ($slideshowState === SlideshowState.PlaySlideshow && $slideshowNavigation === SlideshowNavigation.Shuffle) {
265+
hasNext = order === 'previous' ? slideshowHistory.previous() : slideshowHistory.next();
266+
if (!hasNext) {
267+
const asset = await onRandom();
268+
if (asset) {
269+
slideshowHistory.queue(asset);
270+
hasNext = true;
271+
}
257272
}
273+
} else {
274+
hasNext = order === 'previous' ? await onPrevious() : await onNext();
258275
}
259-
} else {
260-
hasNext = order === 'previous' ? await onPrevious() : await onNext();
261-
}
262276
263-
if ($slideshowState === SlideshowState.PlaySlideshow) {
264-
if (hasNext) {
265-
$restartSlideshowProgress = true;
266-
} else {
267-
await handleStopSlideshow();
277+
if ($slideshowState === SlideshowState.PlaySlideshow) {
278+
if (hasNext) {
279+
$restartSlideshowProgress = true;
280+
} else {
281+
await handleStopSlideshow();
282+
}
268283
}
269-
}
284+
});
270285
};
271286
272-
// const showEditorHandler = () => {
273-
// if (isShowActivity) {
274-
// isShowActivity = false;
275-
// }
276-
// isShowEditor = !isShowEditor;
277-
// };
278-
279287
const handleRunJob = async (name: AssetJobName) => {
280288
try {
281289
await runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
@@ -378,12 +386,6 @@
378386
379387
let isFullScreen = $derived(fullscreenElement !== null);
380388
381-
$effect(() => {
382-
if (asset) {
383-
previewStackedAsset = undefined;
384-
handlePromiseError(refreshStack());
385-
}
386-
});
387389
$effect(() => {
388390
if (album && !album.isActivityEnabled && activityManager.commentCount === 0) {
389391
isShowActivity = false;
@@ -395,13 +397,24 @@
395397
}
396398
});
397399
398-
// primarily, this is reactive on `asset`
399-
$effect(() => {
400-
handlePromiseError(handleGetAllAlbums());
400+
const refresh = async () => {
401+
await refreshStack();
402+
await handleGetAllAlbums();
401403
ocrManager.clear();
402404
if (!sharedLink) {
403-
handlePromiseError(ocrManager.getAssetOcr(asset.id));
405+
if (previewStackedAsset) {
406+
await ocrManager.getAssetOcr(previewStackedAsset.id);
407+
}
408+
await ocrManager.getAssetOcr(asset.id);
404409
}
410+
};
411+
412+
$effect(() => {
413+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
414+
asset;
415+
untrack(() => handlePromiseError(refresh()));
416+
preloadManager.preload(cursor.nextAsset);
417+
preloadManager.preload(cursor.previousAsset);
405418
});
406419
</script>
407420

@@ -473,8 +486,7 @@
473486
<PhotoViewer
474487
bind:zoomToggle
475488
bind:copyImage
476-
asset={previewStackedAsset}
477-
{preloadAssets}
489+
cursor={{ ...cursor, current: previewStackedAsset }}
478490
onPreviousAsset={() => navigateAsset('previous')}
479491
onNextAsset={() => navigateAsset('next')}
480492
haveFadeTransition={false}
@@ -519,8 +531,7 @@
519531
<PhotoViewer
520532
bind:zoomToggle
521533
bind:copyImage
522-
{asset}
523-
{preloadAssets}
534+
{cursor}
524535
onPreviousAsset={() => navigateAsset('previous')}
525536
onNextAsset={() => navigateAsset('next')}
526537
{sharedLink}

0 commit comments

Comments
 (0)