chore(web): revert wasm new justify layout (#16277)

* Revert "fix(web): justify layout import (#16267) "

This reverts commit ec58e1065f.

* Revert "fix(web): dynamically import wasm module (#16261)"

This reverts commit 4376fd72b7.

* Revert "feat(web): use wasm for justified layout calculation (#15524)"

This reverts commit 3925445de8.

* Revert "fix(web): viewport reactivity, off-screen thumbhashes being rendered (#15435)"

This reverts commit 52f21fb331.
This commit is contained in:
Alex
2025-02-25 09:39:56 -06:00
committed by GitHub
parent 16266c9f5a
commit bbcaee82f0
14 changed files with 201 additions and 330 deletions
@@ -38,7 +38,7 @@
$: dateGroups = bucket.dateGroups;
const {
DATEGROUP: { INTERSECTION_ROOT_TOP, INTERSECTION_ROOT_BOTTOM, SMALL_GROUP_THRESHOLD },
DATEGROUP: { INTERSECTION_ROOT_TOP, INTERSECTION_ROOT_BOTTOM },
} = TUNABLES;
/* TODO figure out a way to calculate this*/
const TITLE_HEIGHT = 51;
@@ -97,7 +97,6 @@
{#each dateGroups as dateGroup, groupIndex (dateGroup.date)}
{@const display =
dateGroup.intersecting || !!dateGroup.assets.some((asset) => asset.id === $assetStore.pendingScrollAssetId)}
{@const geometry = dateGroup.geometry!}
<div
id="date-group"
@@ -119,7 +118,7 @@
data-display={display}
data-date-group={dateGroup.date}
style:height={dateGroup.height + 'px'}
style:width={geometry.containerWidth + 'px'}
style:width={dateGroup.geometry.containerWidth + 'px'}
style:overflow={'clip'}
>
{#if !display}
@@ -150,7 +149,7 @@
<!-- Date group title -->
<div
class="flex z-[100] sticky top-[-1px] pt-[calc(1.75rem+1px)] 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={geometry.containerWidth + 'px'}
style:width={dateGroup.geometry.containerWidth + 'px'}
>
{#if !singleSelect && ((hoveredDateGroup == dateGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dateGroup.groupTitle))}
<div
@@ -175,17 +174,11 @@
<!-- Image grid -->
<div
class="relative overflow-clip"
style:height={geometry.containerHeight + 'px'}
style:width={geometry.containerWidth + 'px'}
style:height={dateGroup.geometry.containerHeight + 'px'}
style:width={dateGroup.geometry.containerWidth + 'px'}
>
{#each dateGroup.assets as asset, i (asset.id)}
{@const isSmallGroup = dateGroup.assets.length <= SMALL_GROUP_THRESHOLD}
<!-- getting these together here in this order is very cache-efficient -->
{@const top = geometry.getTop(i)}
{@const left = geometry.getLeft(i)}
{@const width = geometry.getWidth(i)}
{@const height = geometry.getHeight(i)}
{#each dateGroup.assets as asset, index (asset.id)}
{@const box = dateGroup.geometry.boxes[index]}
<!-- update ASSET_GRID_PADDING-->
<div
use:intersectionObserver={{
@@ -197,10 +190,10 @@
}}
data-asset-id={asset.id}
class="absolute"
style:top={top + 'px'}
style:left={left + 'px'}
style:width={width + 'px'}
style:height={height + 'px'}
style:width={box.width + 'px'}
style:height={box.height + 'px'}
style:top={box.top + 'px'}
style:left={box.left + 'px'}
>
<Thumbnail
{dateGroup}
@@ -222,9 +215,8 @@
selected={assetInteraction.selectedAssets.has(asset) || $assetStore.albumAssets.has(asset.id)}
selectionCandidate={assetInteraction.assetSelectionCandidates.has(asset)}
disabled={$assetStore.albumAssets.has(asset.id)}
thumbnailWidth={width}
thumbnailHeight={height}
eagerThumbhash={isSmallGroup}
thumbnailWidth={box.width}
thumbnailHeight={box.height}
/>
</div>
{/each}
@@ -20,7 +20,7 @@
} from '$lib/utils/timeline-util';
import { TUNABLES } from '$lib/utils/tunables';
import type { AlbumResponseDto, AssetResponseDto, PersonResponseDto } from '@immich/sdk';
import { debounce, throttle } from 'lodash-es';
import { throttle } from 'lodash-es';
import { onDestroy, onMount, type Snippet } from 'svelte';
import Portal from '../shared-components/portal/portal.svelte';
import Scrubber from '../shared-components/scrubber/scrubber.svelte';
@@ -81,9 +81,8 @@
let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget } = assetViewingStore;
// this does *not* need to be reactive and making it reactive causes expensive repeated updates
// svelte-ignore non_reactive_update
let safeViewport: ViewportXY = { width: 0, height: 0, x: 0, y: 0 };
const viewport: ViewportXY = $state({ width: 0, height: 0, x: 0, y: 0 });
const safeViewport: ViewportXY = $state({ width: 0, height: 0, x: 0, y: 0 });
const componentId = generateId();
let element: HTMLElement | undefined = $state();
@@ -104,7 +103,7 @@
let leadout = $state(false);
const {
ASSET_GRID: { NAVIGATE_ON_ASSET_IN_VIEW, LARGE_BUCKET_THRESHOLD, LARGE_BUCKET_DEBOUNCE_MS },
ASSET_GRID: { NAVIGATE_ON_ASSET_IN_VIEW },
BUCKET: {
INTERSECTION_ROOT_TOP: BUCKET_INTERSECTION_ROOT_TOP,
INTERSECTION_ROOT_BOTTOM: BUCKET_INTERSECTION_ROOT_BOTTOM,
@@ -115,6 +114,14 @@
},
} = TUNABLES;
const isViewportOrigin = () => {
return viewport.height === 0 && viewport.width === 0;
};
const isEqual = (a: ViewportXY, b: ViewportXY) => {
return a.height == b.height && a.width == b.width && a.x === b.x && a.y === b.y;
};
const completeNav = () => {
navigating = false;
if (internalScroll) {
@@ -228,14 +235,6 @@
};
onMount(() => {
if (element) {
const rect = element.getBoundingClientRect();
safeViewport.height = rect.height;
safeViewport.width = rect.width;
safeViewport.x = rect.x;
safeViewport.y = rect.y;
}
void $assetStore
.init({ bucketListener })
.then(() => ($assetStore.connect(), $assetStore.updateViewport(safeViewport)));
@@ -260,6 +259,8 @@
}
return offset;
}
const _updateViewport = () => void $assetStore.updateViewport(safeViewport);
const updateViewport = throttle(_updateViewport, 16);
const getMaxScrollPercent = () =>
($assetStore.timelineHeight + bottomSectionHeight + topSectionHeight - safeViewport.height) /
@@ -743,8 +744,23 @@
}
});
let largeBucketMode = false;
let updateViewport = debounce(() => $assetStore.updateViewport(safeViewport), 8);
$effect(() => {
if (element && isViewportOrigin()) {
const rect = element.getBoundingClientRect();
viewport.height = rect.height;
viewport.width = rect.width;
viewport.x = rect.x;
viewport.y = rect.y;
}
if (!isViewportOrigin() && !isEqual(viewport, safeViewport)) {
safeViewport.height = viewport.height;
safeViewport.width = viewport.width;
safeViewport.x = viewport.x;
safeViewport.y = viewport.y;
updateViewport();
}
});
let shortcutList = $derived(
(() => {
if ($isSearchEnabled || $showAssetViewer) {
@@ -827,21 +843,7 @@
id="asset-grid"
class="scrollbar-hidden h-full overflow-y-auto outline-none {isEmpty ? 'm-0' : 'ml-4 tall:ml-0 mr-[60px]'}"
tabindex="-1"
use:resizeObserver={({ width, height }) => {
if (!largeBucketMode && assetStore.maxBucketAssets >= LARGE_BUCKET_THRESHOLD) {
largeBucketMode = true;
// Each viewport update causes each asset to re-decode both the thumbhash and the thumbnail.
// This is because the thumbnail components are destroyed and re-mounted, possibly because of the intersection observer.
// For larger buckets, this can lead to freezing and a poor user experience.
// As a mitigation, we aggressively debounce the viewport update to reduce the number of these events.
updateViewport = debounce(() => $assetStore.updateViewport(safeViewport), LARGE_BUCKET_DEBOUNCE_MS, {
leading: false,
trailing: true,
});
}
safeViewport = { width, height, x: safeViewport.x, y: safeViewport.y };
void updateViewport();
}}
use:resizeObserver={({ height, width }) => ((viewport.width = width), (viewport.height = height))}
bind:this={element}
onscroll={() => ((assetStore.lastScrollTime = Date.now()), handleTimelineScroll())}
>
@@ -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>