Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): preload assets in photo-viewer #7920

Merged
merged 3 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 10 additions & 4 deletions web/src/lib/components/asset-viewer/asset-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@

export let assetStore: AssetStore | null = null;
export let asset: AssetResponseDto;
export let preloadAssets: AssetResponseDto[] = [];
export let showNavigation = true;
export let sharedLink: SharedLinkResponseDto | undefined = undefined;
$: isTrashEnabled = $featureFlags.trash;
Expand Down Expand Up @@ -103,6 +104,8 @@
$stackAssetsStore = [...$stackAssetsStore, asset].sort(
(a, b) => new Date(b.fileCreatedAt).getTime() - new Date(a.fileCreatedAt).getTime(),
);
// if its a stack, add the next stack image in addition to the next asset
preloadAssets.push($stackAssetsStore[1]);
samholton marked this conversation as resolved.
Show resolved Hide resolved
}

if (!$stackAssetsStore.map((a) => a.id).includes(asset.id)) {
Expand Down Expand Up @@ -613,7 +616,7 @@
{#if previewStackedAsset}
{#key previewStackedAsset.id}
{#if previewStackedAsset.type === AssetTypeEnum.Image}
<PhotoViewer asset={previewStackedAsset} on:close={closeViewer} haveFadeTransition={false} />
<PhotoViewer asset={previewStackedAsset} {preloadAssets} on:close={closeViewer} haveFadeTransition={false} />
{:else}
<VideoViewer
assetId={previewStackedAsset.id}
Expand Down Expand Up @@ -645,7 +648,7 @@
.endsWith('.insp'))}
<PanoramaViewer {asset} />
{:else}
<PhotoViewer {asset} on:close={closeViewer} />
<PhotoViewer {asset} {preloadAssets} on:close={closeViewer} />
{/if}
{:else}
<VideoViewer
Expand Down Expand Up @@ -676,7 +679,7 @@
class="z-[1005] flex place-item-center place-content-center absolute bottom-0 w-full col-span-4 col-start-1 mb-1 overflow-x-auto horizontal-scrollbar"
>
<div class="relative w-full whitespace-nowrap transition-all">
{#each $stackAssetsStore as stackedAsset (stackedAsset.id)}
{#each $stackAssetsStore as stackedAsset, index (stackedAsset.id)}
<div
class="{stackedAsset.id == asset.id
? '-translate-y-[1px]'
Expand All @@ -687,7 +690,10 @@
? 'bg-transparent border-2 border-white'
: 'bg-gray-700/40'} inline-block hover:bg-transparent"
asset={stackedAsset}
on:click={() => (asset = stackedAsset)}
on:click={() => {
asset = stackedAsset;
preloadAssets = index + 1 >= $stackAssetsStore.length ? [] : [$stackAssetsStore[index + 1]];
}}
on:mouse-event={(e) => handleStackedAssetMouseEvent(e, stackedAsset)}
readonly
thumbnailSize={stackedAsset.id == asset.id ? 65 : 60}
Expand Down
27 changes: 22 additions & 5 deletions web/src/lib/components/asset-viewer/photo-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { getBoundingBox } from '$lib/utils/people-utils';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
import { type AssetResponseDto } from '@immich/sdk';
import { type AssetResponseDto, AssetTypeEnum } from '@immich/sdk';
import { useZoomImageWheel } from '@zoom-image/svelte';
import { onDestroy, onMount } from 'svelte';
import { fade } from 'svelte/transition';
Expand All @@ -16,6 +16,7 @@
import { getAltText } from '$lib/utils/thumbnail-util';

export let asset: AssetResponseDto;
export let preloadAssets: AssetResponseDto[] | null = null;
export let element: HTMLDivElement | undefined = undefined;
export let haveFadeTransition = true;

Expand All @@ -25,6 +26,7 @@
let hasZoomed = false;
let copyImageToClipboard: (source: string) => Promise<Blob>;
let canCopyImagesToClipboard: () => boolean;
let imageLoaded: boolean = false;

const loadOriginalByDefault = $alwaysLoadOriginalFile && isWebCompatibleImage(asset);

Expand All @@ -41,6 +43,9 @@
const module = await import('copy-image-clipboard');
copyImageToClipboard = module.copyImageToClipboard;
canCopyImagesToClipboard = module.canCopyImagesToClipboard;

imageLoaded = false;
await loadAssetData({ loadOriginal: loadOriginalByDefault });
});

onDestroy(() => {
Expand All @@ -60,8 +65,20 @@
});

assetData = URL.createObjectURL(data);
imageLoaded = true;

if (preloadAssets) {
samholton marked this conversation as resolved.
Show resolved Hide resolved
for (const preloadAsset of preloadAssets) {
if (preloadAsset.type === AssetTypeEnum.Image) {
await downloadRequest({
url: getAssetFileUrl(preloadAsset.id, !loadOriginal, false),
samholton marked this conversation as resolved.
Show resolved Hide resolved
signal: abortController.signal,
});
}
}
}
} catch {
// Do nothing
imageLoaded = false;
}
};

Expand Down Expand Up @@ -128,9 +145,9 @@
transition:fade={{ duration: haveFadeTransition ? 150 : 0 }}
class="flex h-full select-none place-content-center place-items-center"
>
{#await loadAssetData({ loadOriginal: loadOriginalByDefault })}
{#if !imageLoaded}
<LoadingSpinner />
{:then}
{:else}
<div bind:this={imgElement} class="h-full w-full">
<img
bind:this={$photoViewer}
Expand All @@ -147,5 +164,5 @@
/>
{/each}
</div>
{/await}
{/if}
</div>
15 changes: 12 additions & 3 deletions web/src/lib/components/photos-page/asset-grid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
const { assetSelectionCandidates, assetSelectionStart, selectedGroup, selectedAssets, isMultiSelectState } =
assetInteractionStore;
const viewport: Viewport = { width: 0, height: 0 };
let { isViewing: showAssetViewer, asset: viewingAsset } = assetViewingStore;
let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets } = assetViewingStore;
let element: HTMLElement;
let showShortcuts = false;
let showSkeleton = true;
Expand Down Expand Up @@ -141,17 +141,25 @@

const handlePrevious = async () => {
const previousAsset = await assetStore.getPreviousAssetId($viewingAsset.id);

if (previousAsset) {
await assetViewingStore.setAssetId(previousAsset);
const preloadId = await assetStore.getPreviousAssetId(previousAsset);
preloadId
? await assetViewingStore.setAssetId(previousAsset, [preloadId])
: await assetViewingStore.setAssetId(previousAsset);
}

return !!previousAsset;
};

const handleNext = async () => {
const nextAsset = await assetStore.getNextAssetId($viewingAsset.id);

if (nextAsset) {
await assetViewingStore.setAssetId(nextAsset);
const preloadId = await assetStore.getNextAssetId(nextAsset);
preloadId
? await assetViewingStore.setAssetId(nextAsset, [preloadId])
: await assetViewingStore.setAssetId(nextAsset);
}

return !!nextAsset;
Expand Down Expand Up @@ -455,6 +463,7 @@
{withStacked}
{assetStore}
asset={$viewingAsset}
preloadAssets={$preloadAssets}
{isShared}
{album}
on:previous={handlePrevious}
Expand Down
18 changes: 17 additions & 1 deletion web/src/lib/stores/asset-viewing.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@ import { writable } from 'svelte/store';

function createAssetViewingStore() {
const viewingAssetStoreState = writable<AssetResponseDto>();
const preloadAssets = writable<AssetResponseDto[]>([]);
const viewState = writable<boolean>(false);

const setAssetId = async (id: string) => {
const setAssetId = async (id: string, preloadIds?: string[]) => {
const data = await getAssetInfo({ id, key: getKey() });

if (preloadIds) {
const preloadList = [];
for (const preloadId of preloadIds) {
if (preloadId) {
const preloadAsset = await getAssetInfo({ id: preloadId, key: getKey() });
preloadList.push(preloadAsset);
}
}
preloadAssets.set(preloadList);
}

viewingAssetStoreState.set(data);
viewState.set(true);
};
Expand All @@ -20,6 +33,9 @@ function createAssetViewingStore() {
asset: {
subscribe: viewingAssetStoreState.subscribe,
},
preloadAssets: {
subscribe: preloadAssets.subscribe,
},
isViewing: {
subscribe: viewState.subscribe,
set: viewState.set,
Expand Down