mirror of
https://github.com/immich-app/immich.git
synced 2026-05-26 17:42:32 -04:00
refactor(web): switch consumers to use day-tier viewport boundaries
TimelineDay.isInOrNearViewport now derives from the firstInOrNearIndex
$state added in the previous commit (true iff first index != -1). This
replaces the old $derived.by that read every asset's isInOrNearViewport
via viewerAssets.some(), removing a per-asset subscription point that
filter() in AssetLayout had been creating for every render.
AssetLayout switches from filterIsInOrNearViewport(viewerAssets) to
viewerAssets.slice(firstInOrNearIndex, lastInOrNearIndex + 1). The slice
expression depends only on the two boundary $state values, not on any
asset's proximity $derived. Reactive churn during scroll collapses to:
boundary indices change → slice recomputes → {#each} reconciles.
Month.svelte passes the new boundary props through. filterIsInOrNearViewport
is still used at the month tier (to filter days) and stays in utils.
Change-Id: If4e30192146f3e987307b1efd7c6d41d6a6a6964
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { filterIsInOrNearViewport } from '$lib/managers/timeline-manager/utils.svelte';
|
||||
import type { ViewerAsset } from '$lib/managers/timeline-manager/viewer-asset.svelte';
|
||||
import type { VirtualScrollManager } from '$lib/managers/VirtualScrollManager/VirtualScrollManager.svelte';
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
@@ -13,6 +12,8 @@
|
||||
|
||||
type Props = {
|
||||
viewerAssets: ViewerAsset[];
|
||||
firstInOrNearIndex: number;
|
||||
lastInOrNearIndex: number;
|
||||
width: number;
|
||||
height: number;
|
||||
manager: VirtualScrollManager;
|
||||
@@ -27,15 +28,27 @@
|
||||
customThumbnailLayout?: Snippet<[asset: TimelineAsset]>;
|
||||
};
|
||||
|
||||
const { viewerAssets, width, height, manager, thumbnail, customThumbnailLayout }: Props = $props();
|
||||
const {
|
||||
viewerAssets,
|
||||
firstInOrNearIndex,
|
||||
lastInOrNearIndex,
|
||||
width,
|
||||
height,
|
||||
manager,
|
||||
thumbnail,
|
||||
customThumbnailLayout,
|
||||
}: Props = $props();
|
||||
|
||||
const transitionDuration = $derived(manager.suspendTransitions && !$isUploading ? 0 : 150);
|
||||
const scaleDuration = $derived(transitionDuration === 0 ? 0 : transitionDuration + 100);
|
||||
const visibleViewerAssets = $derived(
|
||||
firstInOrNearIndex === -1 ? [] : viewerAssets.slice(firstInOrNearIndex, lastInOrNearIndex + 1),
|
||||
);
|
||||
</script>
|
||||
|
||||
<!-- Image grid -->
|
||||
<div data-image-grid class="relative overflow-clip" style:height={height + 'px'} style:width={width + 'px'}>
|
||||
{#each filterIsInOrNearViewport(viewerAssets) as viewerAsset (viewerAsset.id)}
|
||||
{#each visibleViewerAssets as viewerAsset (viewerAsset.id)}
|
||||
{@const position = viewerAsset.position!}
|
||||
{@const asset = viewerAsset.asset!}
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
<AssetLayout
|
||||
{manager}
|
||||
viewerAssets={timelineDay.viewerAssets}
|
||||
firstInOrNearIndex={timelineDay.firstInOrNearIndex}
|
||||
lastInOrNearIndex={timelineDay.lastInOrNearIndex}
|
||||
height={timelineDay.height}
|
||||
width={timelineDay.width}
|
||||
{customThumbnailLayout}
|
||||
|
||||
@@ -22,12 +22,12 @@ export class TimelineDay {
|
||||
|
||||
height = $state(0);
|
||||
width = $state(0);
|
||||
isInOrNearViewport = $derived.by(() => this.viewerAssets.some((viewAsset) => viewAsset.isInOrNearViewport));
|
||||
|
||||
// Indices into viewerAssets bounding the in-or-near range. -1/-1 means no assets are in-or-near.
|
||||
// Updated imperatively by updateAssetBoundaries() from updateViewportProximities() and layout().
|
||||
firstInOrNearIndex = $state(-1);
|
||||
lastInOrNearIndex = $state(-1);
|
||||
isInOrNearViewport = $derived(this.firstInOrNearIndex !== -1);
|
||||
|
||||
#top: number = $state(0);
|
||||
#start: number = $state(0);
|
||||
|
||||
Reference in New Issue
Block a user