feat(web): wasm justified layout (#19150)

* wasm justified layout

* fix tests

* redundant layout generation

* raw position
This commit is contained in:
Mert
2025-06-17 10:20:14 -04:00
committed by GitHub
parent 8038ae1e7a
commit bc062da11b
13 changed files with 106 additions and 145 deletions

View File

@@ -1,7 +1,7 @@
import { AssetOrder } from '@immich/sdk';
import type { CommonLayoutOptions } from '$lib/utils/layout-utils';
import { getJustifiedLayoutFromAssets, getPosition } from '$lib/utils/layout-utils';
import { getJustifiedLayoutFromAssets } from '$lib/utils/layout-utils';
import { plainDateTimeCompare } from '$lib/utils/timeline-util';
import type { MonthGroup } from './month-group.svelte';
@@ -153,8 +153,7 @@ export class DayGroup {
this.width = geometry.containerWidth;
this.height = assets.length === 0 ? 0 : geometry.containerHeight;
for (let i = 0; i < this.viewerAssets.length; i++) {
const position = getPosition(geometry, i);
this.viewerAssets[i].position = position;
this.viewerAssets[i].position = geometry.getPosition(i);
}
}

View File

@@ -2,8 +2,10 @@ import { sdkMock } from '$lib/__mocks__/sdk.mock';
import { getMonthGroupByDate } from '$lib/managers/timeline-manager/internal/search-support.svelte';
import { AbortError } from '$lib/utils';
import { fromISODateTimeUTCToObject } from '$lib/utils/timeline-util';
import { initSync } from '@immich/justified-layout-wasm';
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory';
import { readFile } from 'node:fs/promises';
import { TimelineManager } from './timeline-manager.svelte';
import type { TimelineAsset } from './types';
@@ -23,6 +25,12 @@ function deriveLocalDateTimeFromFileCreatedAt(arg: TimelineAsset): TimelineAsset
}
describe('TimelineManager', () => {
beforeAll(async () => {
// needed for Node.js
const file = await readFile('node_modules/@immich/justified-layout-wasm/pkg/justified-layout-wasm_bg.wasm');
initSync({ module: file });
});
beforeEach(() => {
vi.resetAllMocks();
});
@@ -80,15 +88,15 @@ describe('TimelineManager', () => {
expect(plainMonths).toEqual(
expect.arrayContaining([
expect.objectContaining({ year: 2024, month: 3, height: 185.5 }),
expect.objectContaining({ year: 2024, month: 2, height: 12_016 }),
expect.objectContaining({ year: 2024, month: 3, height: 353.5 }),
expect.objectContaining({ year: 2024, month: 2, height: 7786.452_636_718_75 }),
expect.objectContaining({ year: 2024, month: 1, height: 286 }),
]),
);
});
it('calculates timeline height', () => {
expect(timelineManager.timelineHeight).toBe(12_487.5);
expect(timelineManager.timelineHeight).toBe(8425.952_636_718_75);
});
});

View File

@@ -377,13 +377,11 @@ export class TimelineManager {
}
createLayoutOptions() {
const viewportWidth = this.viewportWidth;
return {
spacing: 2,
heightTolerance: 0.15,
heightTolerance: 0.3,
rowHeight: this.#rowHeight,
rowWidth: Math.floor(viewportWidth),
rowWidth: Math.floor(this.viewportWidth),
};
}

View File

@@ -18,7 +18,7 @@ export class ViewerAsset {
return calculateViewerAssetIntersecting(store, positionTop, this.position.height);
});
position: CommonPosition | undefined = $state();
position: CommonPosition | undefined = $state.raw();
asset: TimelineAsset = <TimelineAsset>$state();
id: string = $derived(this.asset.id);