fix: gallery viewer sliding window offload assets (#17016)

* fix: gallery viewer sliding window offload assets

* fix: update bottom sliding window

* do not use negative

* Calculate offset before gallery

---------

Co-authored-by: Min Idzelis <min123@gmail.com>
This commit is contained in:
Alex 2025-03-20 22:30:01 -05:00 committed by GitHub
parent 21954939cf
commit dbc279f843
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 40 deletions

View File

@ -67,9 +67,12 @@
let paused = $state(false);
let current = $state<MemoryAsset | undefined>(undefined);
let isSaved = $derived(current?.memory.isSaved);
let viewerHeight = $state(0);
const { isViewing } = assetViewingStore;
const viewport: Viewport = $state({ width: 0, height: 0 });
// need to include padding in the viewport for gallery
const galleryViewport: Viewport = $derived({ height: viewport.height, width: viewport.width - 32 });
const assetInteraction = new AssetInteraction();
let progressBarController: Tween<number> | undefined = $state(undefined);
let videoPlayer: HTMLVideoElement | undefined = $state();
@ -331,7 +334,12 @@
</div>
{/if}
<section id="memory-viewer" class="w-full bg-immich-dark-gray" bind:this={memoryWrapper}>
<section
id="memory-viewer"
class="w-full bg-immich-dark-gray"
bind:this={memoryWrapper}
use:resizeObserver={({ height, width }) => ((viewport.height = height), (viewport.width = width))}
>
{#if current}
<ControlAppBar onClose={() => goto(AppRoute.PHOTOS)} forceDark multiRow>
{#snippet leading()}
@ -386,7 +394,7 @@
</div>
{/if}
<!-- Viewer -->
<section class="overflow-hidden pt-32 md:pt-20">
<section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}>
<div
class="ml-[-100%] box-border flex h-[calc(100vh_-_224px)] md:h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
>
@ -580,43 +588,44 @@
</div>
</div>
</section>
<!-- GALLERY VIEWER -->
<section class="bg-immich-dark-gray p-4">
<div
class="sticky mb-10 flex place-content-center place-items-center transition-all"
class:opacity-0={galleryInView}
class:opacity-100={!galleryInView}
>
<CircleIconButton
title={$t('show_gallery')}
icon={mdiChevronDown}
color="light"
onclick={() => memoryGallery?.scrollIntoView({ behavior: 'smooth' })}
/>
</div>
<div
id="gallery-memory"
use:intersectionObserver={{
onIntersect: handleGalleryScrollsIntoView,
onSeparate: handleGalleryScrollsOutOfView,
bottom: '-200px',
}}
use:resizeObserver={({ height, width }) => ((viewport.height = height), (viewport.width = width))}
bind:this={memoryGallery}
>
<GalleryViewer
onNext={handleNextAsset}
onPrevious={handlePreviousAsset}
assets={current.memory.assets}
{viewport}
{assetInteraction}
/>
</div>
</section>
{/if}
</section>
{#if current}
<!-- GALLERY VIEWER -->
<section class="bg-immich-dark-gray p-4">
<div
class="sticky mb-10 flex place-content-center place-items-center transition-all"
class:opacity-0={galleryInView}
class:opacity-100={!galleryInView}
>
<CircleIconButton
title={$t('show_gallery')}
icon={mdiChevronDown}
color="light"
onclick={() => memoryGallery?.scrollIntoView({ behavior: 'smooth' })}
/>
</div>
<div
id="gallery-memory"
use:intersectionObserver={{
onIntersect: handleGalleryScrollsIntoView,
onSeparate: handleGalleryScrollsOutOfView,
bottom: '-200px',
}}
bind:this={memoryGallery}
>
<GalleryViewer
onNext={handleNextAsset}
onPrevious={handlePreviousAsset}
assets={current.memory.assets}
viewport={galleryViewport}
{assetInteraction}
slidingWindowOffset={viewerHeight}
/>
</div>
</section>
{/if}
<style>
.main-view {

View File

@ -35,6 +35,8 @@
onPrevious?: (() => Promise<AssetResponseDto | undefined>) | undefined;
onNext?: (() => Promise<AssetResponseDto | undefined>) | undefined;
onRandom?: (() => Promise<AssetResponseDto | undefined>) | undefined;
pageHeaderOffset?: number;
slidingWindowOffset?: number;
}
let {
@ -49,6 +51,8 @@
onPrevious = undefined,
onNext = undefined,
onRandom = undefined,
slidingWindowOffset = 0,
pageHeaderOffset = 0,
}: Props = $props();
let { isViewing: isViewerOpen, asset: viewingAsset, setAsset } = assetViewingStore;
@ -86,7 +90,7 @@
height: geometry.getHeight(i),
};
// 54 is the content height of the asset-selection-app-bar
const layoutTopWithOffset = layout.top + 54;
const layoutTopWithOffset = layout.top + pageHeaderOffset;
const layoutBottom = layoutTopWithOffset + layout.height;
const display = layoutTopWithOffset < slidingWindow.bottom && layoutBottom > slidingWindow.top;
@ -109,7 +113,7 @@
const updateSlidingWindow = () => {
const v = $state.snapshot(viewport);
const top = document.scrollingElement?.scrollTop || 0;
const top = (document.scrollingElement?.scrollTop || 0) - slidingWindowOffset;
const bottom = top + v.height;
const w = {
top,

View File

@ -154,7 +154,13 @@
<!-- Assets -->
{#if data.pathAssets && data.pathAssets.length > 0}
<div bind:clientHeight={viewport.height} bind:clientWidth={viewport.width} class="mt-2">
<GalleryViewer assets={data.pathAssets} {assetInteraction} {viewport} showAssetName={true} />
<GalleryViewer
assets={data.pathAssets}
{assetInteraction}
{viewport}
showAssetName={true}
pageHeaderOffset={54}
/>
</div>
{/if}
</section>

View File

@ -368,6 +368,7 @@
onIntersected={loadNextPage}
showArchiveIcon={true}
{viewport}
pageHeaderOffset={54}
/>
{:else if !isLoading}
<div class="flex min-h-[calc(66vh_-_11rem)] w-full place-content-center items-center dark:text-white">