From 0e4cf9ac573d142b77b2153d292d9ca370ae481e Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Tue, 29 Apr 2025 13:59:06 -0400 Subject: [PATCH] feat(web): responsive date group header height (#17944) * feat: responsive date group header height * update tests * feat(web): improve perf when changing mobile orientation (#17945) fix: improve perf when changing mobile orientation --- .../photos-page/asset-date-group.svelte | 2 +- .../components/photos-page/asset-grid.svelte | 11 +++- .../components/photos-page/skeleton.svelte | 2 +- web/src/lib/stores/assets-store.spec.ts | 6 +-- web/src/lib/stores/assets-store.svelte.ts | 54 ++++++++++++++----- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte index ecb25b06975..f20c04d3d47 100644 --- a/web/src/lib/components/photos-page/asset-date-group.svelte +++ b/web/src/lib/components/photos-page/asset-date-group.svelte @@ -131,7 +131,7 @@ >
{#if !singleSelect && ((hoveredDateGroup === dateGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dateGroup.groupTitle))} diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index da4387a4909..e46aaefe0e2 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -88,7 +88,16 @@ const usingMobileDevice = $derived(mobileDevice.pointerCoarse); $effect(() => { - assetStore.rowHeight = maxMd ? 100 : 235; + const layoutOptions = maxMd + ? { + rowHeight: 100, + headerHeight: 32, + } + : { + rowHeight: 235, + headerHeight: 48, + }; + assetStore.setLayoutOptions(layoutOptions); }); const scrollTo = (top: number) => { diff --git a/web/src/lib/components/photos-page/skeleton.svelte b/web/src/lib/components/photos-page/skeleton.svelte index 5c438874503..87ff91c511c 100644 --- a/web/src/lib/components/photos-page/skeleton.svelte +++ b/web/src/lib/components/photos-page/skeleton.svelte @@ -9,7 +9,7 @@
{title}
diff --git a/web/src/lib/stores/assets-store.spec.ts b/web/src/lib/stores/assets-store.spec.ts index 0685103a1b2..3d0292bde8a 100644 --- a/web/src/lib/stores/assets-store.spec.ts +++ b/web/src/lib/stores/assets-store.spec.ts @@ -48,15 +48,15 @@ describe('AssetStore', () => { expect(plainBuckets).toEqual( expect.arrayContaining([ - expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 304 }), - expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4515.333_333_333_333 }), + expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 303 }), + expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4514.333_333_333_333 }), expect.objectContaining({ bucketDate: '2024-01-01T00:00:00.000Z', bucketHeight: 286 }), ]), ); }); it('calculates timeline height', () => { - expect(assetStore.timelineHeight).toBe(5105.333_333_333_333); + expect(assetStore.timelineHeight).toBe(5103.333_333_333_333); }); }); diff --git a/web/src/lib/stores/assets-store.svelte.ts b/web/src/lib/stores/assets-store.svelte.ts index e048fabbc80..b4b4a4ade22 100644 --- a/web/src/lib/stores/assets-store.svelte.ts +++ b/web/src/lib/stores/assets-store.svelte.ts @@ -35,9 +35,7 @@ export type AssetStoreOptions = Omit & { timelineAlbumId?: string; deferInit?: boolean; }; -export type AssetStoreLayoutOptions = { - rowHeight: number; -}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any function updateObject(target: any, source: any): boolean { if (!target) { @@ -110,7 +108,6 @@ export class AssetDateGroup { readonly date: DateTime; readonly dayOfMonth: number; intersetingAssets: IntersectingAsset[] = $state([]); - dodo: IntersectingAsset[] = $state([]); height = $state(0); width = $state(0); @@ -121,6 +118,7 @@ export class AssetDateGroup { left: number = $state(0); row = $state(0); col = $state(0); + deferredLayout = false; constructor(bucket: AssetBucket, index: number, date: DateTime, dayOfMonth: number) { this.index = index; @@ -195,6 +193,10 @@ export class AssetDateGroup { } layout(options: CommonLayoutOptions) { + if (!this.bucket.intersecting) { + this.deferredLayout = true; + return; + } const assets = this.intersetingAssets.map((intersetingAsset) => intersetingAsset.asset!); const geometry = getJustifiedLayoutFromAssets(assets, options); this.width = geometry.containerWidth; @@ -547,6 +549,11 @@ export type LiteBucket = { bucketDateFormattted: string; }; +type AssetStoreLayoutOptions = { + rowHeight?: number; + headerHeight?: number; + gap?: number; +}; export class AssetStore { // --- public ---- isInitialized = $state(false); @@ -596,7 +603,7 @@ export class AssetStore { #unsubscribers: Unsubscriber[] = []; #rowHeight = $state(235); - #headerHeight = $state(49); + #headerHeight = $state(48); #gap = $state(12); #options: AssetStoreOptions = AssetStore.#INIT_OPTIONS; @@ -608,36 +615,46 @@ export class AssetStore { constructor() {} - set headerHeight(value) { + setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: AssetStoreLayoutOptions) { + let changed = false; + changed ||= this.#setHeaderHeight(headerHeight); + changed ||= this.#setGap(gap); + changed ||= this.#setRowHeight(rowHeight); + if (changed) { + this.refreshLayout(); + } + } + + #setHeaderHeight(value: number) { if (this.#headerHeight == value) { - return; + return false; } this.#headerHeight = value; - this.refreshLayout(); + return true; } get headerHeight() { return this.#headerHeight; } - set gap(value) { + #setGap(value: number) { if (this.#gap == value) { - return; + return false; } this.#gap = value; - this.refreshLayout(); + return true; } get gap() { return this.#gap; } - set rowHeight(value) { + #setRowHeight(value: number) { if (this.#rowHeight == value) { - return; + return false; } this.#rowHeight = value; - this.refreshLayout(); + return true; } get rowHeight() { @@ -815,6 +832,15 @@ export class AssetStore { } bucket.intersecting = actuallyIntersecting || preIntersecting; bucket.actuallyIntersecting = actuallyIntersecting; + if (preIntersecting || actuallyIntersecting) { + const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout); + if (hasDeferred) { + this.#updateGeometry(bucket, true); + for (const group of bucket.dateGroups) { + group.deferredLayout = false; + } + } + } } #processPendingChanges = throttle(() => {