mirror of
https://github.com/immich-app/immich.git
synced 2025-04-21 15:36:26 +02:00
fix(web): dynamically import wasm module (#16261)
* dynamically import wasm module * remove unused import
This commit is contained in:
parent
e4b6efc1f5
commit
4376fd72b7
7 changed files with 77 additions and 77 deletions
web/src/lib
components
photos-page
shared-components/gallery-viewer
stores
utils
|
@ -97,7 +97,7 @@
|
|||
{#each dateGroups as dateGroup, groupIndex (dateGroup.date)}
|
||||
{@const display =
|
||||
dateGroup.intersecting || !!dateGroup.assets.some((asset) => asset.id === $assetStore.pendingScrollAssetId)}
|
||||
{@const geometry = dateGroup.geometry}
|
||||
{@const geometry = dateGroup.geometry!}
|
||||
|
||||
<div
|
||||
id="date-group"
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
<div use:resizeObserver={({ height }) => $assetStore.updateBucketDateGroup(bucket, dateGroup, { height })}>
|
||||
<div
|
||||
class="flex z-[100] sticky top-[-1px] pt-7 pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm"
|
||||
style:width={dateGroup.geometry.containerWidth + 'px'}
|
||||
style:width={dateGroup.geometry!.containerWidth + 'px'}
|
||||
>
|
||||
<span class="w-full truncate first-letter:capitalize">
|
||||
{dateGroup.groupTitle}
|
||||
|
@ -81,8 +81,8 @@
|
|||
|
||||
<div
|
||||
class="relative overflow-clip"
|
||||
style:height={dateGroup.geometry.containerHeight + 'px'}
|
||||
style:width={dateGroup.geometry.containerWidth + 'px'}
|
||||
style:height={dateGroup.geometry!.containerHeight + 'px'}
|
||||
style:width={dateGroup.geometry!.containerWidth + 'px'}
|
||||
style:visibility={'hidden'}
|
||||
></div>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { Viewport } from '$lib/stores/assets.store';
|
||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { deleteAssets } from '$lib/utils/actions';
|
||||
import { archiveAssets, cancelMultiselect, getJustifiedLayoutFromAssets } from '$lib/utils/asset-utils';
|
||||
import { archiveAssets, cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { navigate } from '$lib/utils/navigation';
|
||||
|
@ -307,14 +307,15 @@
|
|||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssetsArray.map(({ id }) => id));
|
||||
|
||||
let geometry = $derived(
|
||||
getJustifiedLayoutFromAssets(assets, {
|
||||
let geometry = $derived.by(async () => {
|
||||
const { getJustifiedLayoutFromAssets } = await import('$lib/utils/layout-utils');
|
||||
return getJustifiedLayoutFromAssets(assets, {
|
||||
spacing: 2,
|
||||
rowWidth: Math.floor(viewport.width),
|
||||
heightTolerance: 0.15,
|
||||
rowHeight: 235,
|
||||
}),
|
||||
);
|
||||
rowWidth: Math.floor(viewport.width),
|
||||
});
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!lastAssetMouseEvent) {
|
||||
|
@ -350,47 +351,49 @@
|
|||
{/if}
|
||||
|
||||
{#if assets.length > 0}
|
||||
<div class="relative" style="height: {geometry.containerHeight}px;width: {geometry.containerWidth}px ">
|
||||
{#each assets as asset, i}
|
||||
{@const top = geometry.getTop(i)}
|
||||
{@const left = geometry.getLeft(i)}
|
||||
{@const width = geometry.getWidth(i)}
|
||||
{@const height = geometry.getHeight(i)}
|
||||
{#await geometry then geometry}
|
||||
<div class="relative" style="height: {geometry.containerHeight}px;width: {geometry.containerWidth}px ">
|
||||
{#each assets as asset, i}
|
||||
{@const top = geometry.getTop(i)}
|
||||
{@const left = geometry.getLeft(i)}
|
||||
{@const width = geometry.getWidth(i)}
|
||||
{@const height = geometry.getHeight(i)}
|
||||
|
||||
<div
|
||||
class="absolute"
|
||||
style="width: {width}px; height: {height}px; top: {top}px; left: {left}px"
|
||||
title={showAssetName ? asset.originalFileName : ''}
|
||||
>
|
||||
<Thumbnail
|
||||
readonly={disableAssetSelect}
|
||||
onClick={(asset) => {
|
||||
if (assetInteraction.selectionActive) {
|
||||
handleSelectAssets(asset);
|
||||
return;
|
||||
}
|
||||
void viewAssetHandler(asset);
|
||||
}}
|
||||
onSelect={(asset) => handleSelectAssets(asset)}
|
||||
onMouseEvent={() => assetMouseEventHandler(asset)}
|
||||
onIntersected={() => (i === Math.max(1, assets.length - 7) ? onIntersected?.() : void 0)}
|
||||
{showArchiveIcon}
|
||||
{asset}
|
||||
selected={assetInteraction.selectedAssets.has(asset)}
|
||||
selectionCandidate={assetInteraction.assetSelectionCandidates.has(asset)}
|
||||
thumbnailWidth={width}
|
||||
thumbnailHeight={height}
|
||||
/>
|
||||
{#if showAssetName}
|
||||
<div
|
||||
class="absolute text-center p-1 text-xs font-mono font-semibold w-full bottom-0 bg-gradient-to-t bg-slate-50/75 overflow-clip text-ellipsis whitespace-pre-wrap"
|
||||
>
|
||||
{asset.originalFileName}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div
|
||||
class="absolute"
|
||||
style="width: {width}px; height: {height}px; top: {top}px; left: {left}px"
|
||||
title={showAssetName ? asset.originalFileName : ''}
|
||||
>
|
||||
<Thumbnail
|
||||
readonly={disableAssetSelect}
|
||||
onClick={(asset) => {
|
||||
if (assetInteraction.selectionActive) {
|
||||
handleSelectAssets(asset);
|
||||
return;
|
||||
}
|
||||
void viewAssetHandler(asset);
|
||||
}}
|
||||
onSelect={(asset) => handleSelectAssets(asset)}
|
||||
onMouseEvent={() => assetMouseEventHandler(asset)}
|
||||
onIntersected={() => (i === Math.max(1, assets.length - 7) ? onIntersected?.() : void 0)}
|
||||
{showArchiveIcon}
|
||||
{asset}
|
||||
selected={assetInteraction.selectedAssets.has(asset)}
|
||||
selectionCandidate={assetInteraction.assetSelectionCandidates.has(asset)}
|
||||
thumbnailWidth={width}
|
||||
thumbnailHeight={height}
|
||||
/>
|
||||
{#if showAssetName}
|
||||
<div
|
||||
class="absolute text-center p-1 text-xs font-mono font-semibold w-full bottom-0 bg-gradient-to-t bg-slate-50/75 overflow-clip text-ellipsis whitespace-pre-wrap"
|
||||
>
|
||||
{asset.originalFileName}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
<!-- Overlay Asset Viewer -->
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { getKey } from '$lib/utils';
|
||||
import { AssetGridTaskManager } from '$lib/utils/asset-store-task-manager';
|
||||
import { getJustifiedLayoutFromAssets } from '$lib/utils/asset-utils';
|
||||
import { generateId } from '$lib/utils/generate-id';
|
||||
import type { AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
||||
import { fromLocalDateTime, splitBucketIntoDateGroups, type DateGroup } from '$lib/utils/timeline-util';
|
||||
|
@ -436,7 +435,7 @@ export class AssetStore {
|
|||
|
||||
private async initialLayout(changedWidth: boolean) {
|
||||
for (const bucket of this.buckets) {
|
||||
this.updateGeometry(bucket, changedWidth);
|
||||
await this.updateGeometry(bucket, changedWidth);
|
||||
}
|
||||
this.timelineHeight = this.buckets.reduce((accumulator, b) => accumulator + b.bucketHeight, 0);
|
||||
|
||||
|
@ -454,7 +453,7 @@ export class AssetStore {
|
|||
this.emit(false);
|
||||
}
|
||||
|
||||
private updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
|
||||
private async updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
|
||||
if (invalidateHeight) {
|
||||
bucket.isBucketHeightActual = false;
|
||||
bucket.measured = false;
|
||||
|
@ -477,6 +476,8 @@ export class AssetStore {
|
|||
rowHeight: 235,
|
||||
rowWidth: Math.floor(viewportWidth),
|
||||
};
|
||||
// TODO: move this import and make this method sync after https://github.com/sveltejs/kit/issues/7805 is fixed
|
||||
const { getJustifiedLayoutFromAssets } = await import('$lib/utils/layout-utils');
|
||||
for (const assetGroup of bucket.dateGroups) {
|
||||
if (!assetGroup.heightActual) {
|
||||
const unwrappedWidth = (3 / 2) * assetGroup.assets.length * THUMBNAIL_HEIGHT * (7 / 10);
|
||||
|
@ -552,7 +553,7 @@ export class AssetStore {
|
|||
bucket.assets = assets;
|
||||
bucket.dateGroups = splitBucketIntoDateGroups(bucket, get(locale));
|
||||
this.maxBucketAssets = Math.max(this.maxBucketAssets, assets.length);
|
||||
this.updateGeometry(bucket, true);
|
||||
await this.updateGeometry(bucket, true);
|
||||
this.timelineHeight = this.buckets.reduce((accumulator, b) => accumulator + b.bucketHeight, 0);
|
||||
bucket.loaded();
|
||||
this.notifyListeners({ type: 'loaded', bucket });
|
||||
|
@ -679,7 +680,7 @@ export class AssetStore {
|
|||
return bDate.diff(aDate).milliseconds;
|
||||
});
|
||||
bucket.dateGroups = splitBucketIntoDateGroups(bucket, get(locale));
|
||||
this.updateGeometry(bucket, true);
|
||||
void this.updateGeometry(bucket, true);
|
||||
}
|
||||
|
||||
this.emit(true);
|
||||
|
@ -821,7 +822,7 @@ export class AssetStore {
|
|||
}
|
||||
if (changed) {
|
||||
bucket.dateGroups = splitBucketIntoDateGroups(bucket, get(locale));
|
||||
this.updateGeometry(bucket, true);
|
||||
void this.updateGeometry(bucket, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { downloadRequest, getKey, withError } from '$lib/utils';
|
|||
import { createAlbum } from '$lib/utils/album-utils';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
import { JustifiedLayout, type LayoutOptions } from '@immich/justified-layout-wasm';
|
||||
import {
|
||||
addAssetsToAlbum as addAssets,
|
||||
createStack,
|
||||
|
@ -588,13 +587,3 @@ export const copyImageToClipboard = async (source: HTMLImageElement | string) =>
|
|||
const blob = source instanceof HTMLImageElement ? await imgToBlob(source) : await urlToBlob(source);
|
||||
await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
|
||||
};
|
||||
|
||||
export function getJustifiedLayoutFromAssets(assets: AssetResponseDto[], options: LayoutOptions) {
|
||||
const aspectRatios = new Float32Array(assets.length);
|
||||
// eslint-disable-next-line unicorn/no-for-loop
|
||||
for (let i = 0; i < assets.length; i++) {
|
||||
const { width, height } = getAssetRatio(assets[i]);
|
||||
aspectRatios[i] = width / height;
|
||||
}
|
||||
return new JustifiedLayout(aspectRatios, options);
|
||||
}
|
||||
|
|
14
web/src/lib/utils/layout-utils.ts
Normal file
14
web/src/lib/utils/layout-utils.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { getAssetRatio } from '$lib/utils/asset-utils';
|
||||
// note: it's important that this is not imported in more than one file due to https://github.com/sveltejs/kit/issues/7805
|
||||
import { JustifiedLayout, type LayoutOptions } from '@immich/justified-layout-wasm';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
|
||||
export function getJustifiedLayoutFromAssets(assets: AssetResponseDto[], options: LayoutOptions) {
|
||||
const aspectRatios = new Float32Array(assets.length);
|
||||
// eslint-disable-next-line unicorn/no-for-loop
|
||||
for (let i = 0; i < assets.length; i++) {
|
||||
const { width, height } = getAssetRatio(assets[i]);
|
||||
aspectRatios[i] = width / height;
|
||||
}
|
||||
return new JustifiedLayout(aspectRatios, options);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import type { AssetBucket } from '$lib/stores/assets.store';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { JustifiedLayout } from '@immich/justified-layout-wasm';
|
||||
import type { JustifiedLayout } from '@immich/justified-layout-wasm';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
import { groupBy, memoize, sortBy } from 'lodash-es';
|
||||
import { DateTime } from 'luxon';
|
||||
|
@ -13,7 +13,7 @@ export type DateGroup = {
|
|||
height: number;
|
||||
heightActual: boolean;
|
||||
intersecting: boolean;
|
||||
geometry: JustifiedLayout;
|
||||
geometry: JustifiedLayout | null;
|
||||
bucket: AssetBucket;
|
||||
};
|
||||
export type ScrubberListener = (
|
||||
|
@ -80,13 +80,6 @@ export function formatGroupTitle(_date: DateTime): string {
|
|||
return date.toLocaleString(groupDateFormat);
|
||||
}
|
||||
|
||||
const emptyGeometry = new JustifiedLayout(Float32Array.from([]), {
|
||||
rowHeight: 1,
|
||||
heightTolerance: 0,
|
||||
rowWidth: 1,
|
||||
spacing: 0,
|
||||
});
|
||||
|
||||
const formatDateGroupTitle = memoize(formatGroupTitle);
|
||||
|
||||
export function splitBucketIntoDateGroups(bucket: AssetBucket, locale: string | undefined): DateGroup[] {
|
||||
|
@ -104,7 +97,7 @@ export function splitBucketIntoDateGroups(bucket: AssetBucket, locale: string |
|
|||
height: 0,
|
||||
heightActual: false,
|
||||
intersecting: false,
|
||||
geometry: emptyGeometry,
|
||||
geometry: null,
|
||||
bucket,
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue