Finish up removing/reducing date parsing

This commit is contained in:
Min Idzelis 2025-05-21 01:25:21 +00:00
parent 5db3ed8412
commit 727c9a49d9
7 changed files with 153 additions and 129 deletions

View File

@ -98,7 +98,7 @@
let showSkeleton = $state(true); let showSkeleton = $state(true);
let isShowSelectDate = $state(false); let isShowSelectDate = $state(false);
let scrubBucketPercent = $state(0); let scrubBucketPercent = $state(0);
let scrubBucket: { bucketDate: string | undefined } | undefined = $state(); let scrubBucket: { year: number; month: number } | undefined = $state();
let scrubOverallPercent: number = $state(0); let scrubOverallPercent: number = $state(0);
let scrubberWidth = $state(0); let scrubberWidth = $state(0);
@ -256,7 +256,7 @@
// note: don't throttle, debounch, or otherwise make this function async - it causes flicker // note: don't throttle, debounch, or otherwise make this function async - it causes flicker
const onScrub: ScrubberListener = ( const onScrub: ScrubberListener = (
bucketDate: string | undefined, bucketDate: { year: number; month: number } | undefined,
scrollPercent: number, scrollPercent: number,
bucketScrollPercent: number, bucketScrollPercent: number,
) => { ) => {
@ -269,7 +269,9 @@
} }
element.scrollTop = offset; element.scrollTop = offset;
} else { } else {
const bucket = assetStore.buckets.find((b) => b.bucketDate === bucketDate); const bucket = assetStore.buckets.find(
(bucket) => bucket.year === bucketDate.year && bucket.month === bucketDate.month,
);
if (!bucket) { if (!bucket) {
return; return;
} }
@ -309,7 +311,7 @@
const bucketsLength = assetStore.buckets.length; const bucketsLength = assetStore.buckets.length;
for (let i = -1; i < bucketsLength + 1; i++) { for (let i = -1; i < bucketsLength + 1; i++) {
let bucket: { bucketDate: string | undefined } | undefined; let bucket: { year: number; month: number } | undefined;
let bucketHeight = 0; let bucketHeight = 0;
if (i === -1) { if (i === -1) {
// lead-in // lead-in
@ -585,7 +587,7 @@
break; break;
} }
if (started) { if (started) {
await assetStore.loadBucket(bucket.bucketDate); await assetStore.loadBucket({ year: bucket.year, month: bucket.month });
for (const asset of bucket.getAssets()) { for (const asset of bucket.getAssets()) {
if (deselect) { if (deselect) {
assetInteraction.removeAssetFromMultiselectGroup(asset.id); assetInteraction.removeAssetFromMultiselectGroup(asset.id);

View File

@ -3,10 +3,9 @@
import type { AssetStore, LiteBucket } from '$lib/stores/assets-store.svelte'; import type { AssetStore, LiteBucket } from '$lib/stores/assets-store.svelte';
import { mobileDevice } from '$lib/stores/mobile-device.svelte'; import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { getTabbable } from '$lib/utils/focus-util'; import { getTabbable } from '$lib/utils/focus-util';
import { fromLocalDateTime, type ScrubberListener } from '$lib/utils/timeline-util'; import { type ScrubberListener } from '$lib/utils/timeline-util';
import { mdiPlay } from '@mdi/js'; import { mdiPlay } from '@mdi/js';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
import { DateTime } from 'luxon';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { fade, fly } from 'svelte/transition'; import { fade, fly } from 'svelte/transition';
@ -17,7 +16,7 @@
assetStore: AssetStore; assetStore: AssetStore;
scrubOverallPercent?: number; scrubOverallPercent?: number;
scrubBucketPercent?: number; scrubBucketPercent?: number;
scrubBucket?: { bucketDate: string | undefined }; scrubBucket?: { year: number; month: number };
leadout?: boolean; leadout?: boolean;
scrubberWidth?: number; scrubberWidth?: number;
onScrub?: ScrubberListener; onScrub?: ScrubberListener;
@ -81,7 +80,7 @@
}); });
const toScrollFromBucketPercentage = ( const toScrollFromBucketPercentage = (
scrubBucket: { bucketDate: string | undefined } | undefined, scrubBucket: { year: number; month: number } | undefined,
scrubBucketPercent: number, scrubBucketPercent: number,
scrubOverallPercent: number, scrubOverallPercent: number,
) => { ) => {
@ -89,7 +88,7 @@
let offset = relativeTopOffset; let offset = relativeTopOffset;
let match = false; let match = false;
for (const segment of segments) { for (const segment of segments) {
if (segment.bucketDate === scrubBucket.bucketDate) { if (segment.month === scrubBucket.month && segment.year === scrubBucket.year) {
offset += scrubBucketPercent * segment.height; offset += scrubBucketPercent * segment.height;
match = true; match = true;
break; break;
@ -120,8 +119,8 @@
count: number; count: number;
height: number; height: number;
dateFormatted: string; dateFormatted: string;
bucketDate: string; year: number;
date: DateTime; month: number;
hasLabel: boolean; hasLabel: boolean;
hasDot: boolean; hasDot: boolean;
}; };
@ -141,9 +140,9 @@
top, top,
count: bucket.assetCount, count: bucket.assetCount,
height: toScrollY(scrollBarPercentage), height: toScrollY(scrollBarPercentage),
bucketDate: bucket.bucketDate,
date: fromLocalDateTime(bucket.bucketDate),
dateFormatted: bucket.bucketDateFormattted, dateFormatted: bucket.bucketDateFormattted,
year: bucket.year,
month: bucket.month,
hasLabel: false, hasLabel: false,
hasDot: false, hasDot: false,
}; };
@ -153,7 +152,7 @@
segment.hasLabel = true; segment.hasLabel = true;
previousLabeledSegment = segment; previousLabeledSegment = segment;
} else { } else {
if (previousLabeledSegment?.date?.year !== segment.date.year && height > MIN_YEAR_LABEL_DISTANCE) { if (previousLabeledSegment?.year !== segment.year && height > MIN_YEAR_LABEL_DISTANCE) {
height = 0; height = 0;
segment.hasLabel = true; segment.hasLabel = true;
previousLabeledSegment = segment; previousLabeledSegment = segment;
@ -182,7 +181,13 @@
} }
return activeSegment?.dataset.label; return activeSegment?.dataset.label;
}); });
const bucketDate = $derived(activeSegment?.dataset.timeSegmentBucketDate); const bucketDate = $derived.by(() => {
if (!activeSegment?.dataset.timeSegmentBucketDate) {
return undefined;
}
const [year, month] = activeSegment.dataset.timeSegmentBucketDate.split('-').map(Number);
return { year, month };
});
const scrollSegment = $derived.by(() => { const scrollSegment = $derived.by(() => {
const y = scrollY; const y = scrollY;
let cur = relativeTopOffset; let cur = relativeTopOffset;
@ -289,12 +294,12 @@
const scrollPercent = toTimelineY(hoverY); const scrollPercent = toTimelineY(hoverY);
if (wasDragging === false && isDragging) { if (wasDragging === false && isDragging) {
void startScrub?.(bucketDate, scrollPercent, bucketPercentY); void startScrub?.(bucketDate!, scrollPercent, bucketPercentY);
void onScrub?.(bucketDate, scrollPercent, bucketPercentY); void onScrub?.(bucketDate!, scrollPercent, bucketPercentY);
} }
if (wasDragging && !isDragging) { if (wasDragging && !isDragging) {
void stopScrub?.(bucketDate, scrollPercent, bucketPercentY); void stopScrub?.(bucketDate!, scrollPercent, bucketPercentY);
return; return;
} }
@ -302,7 +307,7 @@
return; return;
} }
void onScrub?.(bucketDate, scrollPercent, bucketPercentY); void onScrub?.(bucketDate!, scrollPercent, bucketPercentY);
}; };
const getTouch = (event: TouchEvent) => { const getTouch = (event: TouchEvent) => {
if (event.touches.length === 1) { if (event.touches.length === 1) {
@ -404,7 +409,7 @@
} }
if (next) { if (next) {
event.preventDefault(); event.preventDefault();
void onScrub?.(next.bucketDate, -1, 0); void onScrub?.({ year: next.year, month: next.month }, -1, 0);
return true; return true;
} }
} }
@ -414,7 +419,7 @@
const next = segments[idx + 1]; const next = segments[idx + 1];
if (next) { if (next) {
event.preventDefault(); event.preventDefault();
void onScrub?.(next.bucketDate, -1, 0); void onScrub?.({ year: next.year, month: next.month }, -1, 0);
return true; return true;
} }
} }
@ -517,7 +522,7 @@
class="relative" class="relative"
style:height={relativeTopOffset + 'px'} style:height={relativeTopOffset + 'px'}
data-id="lead-in" data-id="lead-in"
data-time-segment-bucket-date={segments.at(0)?.date} data-time-segment-bucket-date={segments.at(0)?.year + '-' + segments.at(0)?.month}
data-label={segments.at(0)?.dateFormatted} data-label={segments.at(0)?.dateFormatted}
> >
{#if relativeTopOffset > 6} {#if relativeTopOffset > 6}
@ -525,18 +530,18 @@
{/if} {/if}
</div> </div>
<!-- Time Segment --> <!-- Time Segment -->
{#each segments as segment (segment.date)} {#each segments as segment (segment.year + '-' + segment.month)}
<div <div
class="relative" class="relative"
data-id="time-segment" data-id="time-segment"
data-time-segment-bucket-date={segment.date} data-time-segment-bucket-date={segment.year + '-' + segment.month}
data-label={segment.dateFormatted} data-label={segment.dateFormatted}
style:height={segment.height + 'px'} style:height={segment.height + 'px'}
> >
{#if !usingMobileDevice} {#if !usingMobileDevice}
{#if segment.hasLabel} {#if segment.hasLabel}
<div class="absolute end-5 top-[-16px] text-[12px] dark:text-immich-dark-fg font-immich-mono"> <div class="absolute end-5 top-[-16px] text-[12px] dark:text-immich-dark-fg font-immich-mono">
{segment.date.year} {segment.year}
</div> </div>
{/if} {/if}
{#if segment.hasDot} {#if segment.hasDot}

View File

@ -14,13 +14,13 @@ describe('AssetStore', () => {
const bucketAssets: Record<string, TimelineAsset[]> = { const bucketAssets: Record<string, TimelineAsset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory '2024-03-01T00:00:00.000Z': timelineAssetFactory
.buildList(1) .buildList(1)
.map((asset) => ({ ...asset, localDateTime: '2024-03-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-03-01T00:00:00.000Z') })),
'2024-02-01T00:00:00.000Z': timelineAssetFactory '2024-02-01T00:00:00.000Z': timelineAssetFactory
.buildList(100) .buildList(100)
.map((asset) => ({ ...asset, localDateTime: '2024-02-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-02-01T00:00:00.000Z') })),
'2024-01-01T00:00:00.000Z': timelineAssetFactory '2024-01-01T00:00:00.000Z': timelineAssetFactory
.buildList(3) .buildList(3)
.map((asset) => ({ ...asset, localDateTime: '2024-01-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-01-01T00:00:00.000Z') })),
}; };
const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries( const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries(
@ -47,15 +47,16 @@ describe('AssetStore', () => {
it('calculates bucket height', () => { it('calculates bucket height', () => {
const plainBuckets = assetStore.buckets.map((bucket) => ({ const plainBuckets = assetStore.buckets.map((bucket) => ({
bucketDate: bucket.bucketDate, year: bucket.year,
month: bucket.month,
bucketHeight: bucket.bucketHeight, bucketHeight: bucket.bucketHeight,
})); }));
expect(plainBuckets).toEqual( expect(plainBuckets).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 185.5 }), expect.objectContaining({ year: 2024, month: 3, bucketHeight: 185.5 }),
expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 12_016 }), expect.objectContaining({ year: 2024, month: 2, bucketHeight: 12_016 }),
expect.objectContaining({ bucketDate: '2024-01-01T00:00:00.000Z', bucketHeight: 286 }), expect.objectContaining({ year: 2024, month: 1, bucketHeight: 286 }),
]), ]),
); );
}); });
@ -70,10 +71,10 @@ describe('AssetStore', () => {
const bucketAssets: Record<string, TimelineAsset[]> = { const bucketAssets: Record<string, TimelineAsset[]> = {
'2024-01-03T00:00:00.000Z': timelineAssetFactory '2024-01-03T00:00:00.000Z': timelineAssetFactory
.buildList(1) .buildList(1)
.map((asset) => ({ ...asset, localDateTime: '2024-03-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-03-01T00:00:00.000Z') })),
'2024-01-01T00:00:00.000Z': timelineAssetFactory '2024-01-01T00:00:00.000Z': timelineAssetFactory
.buildList(3) .buildList(3)
.map((asset) => ({ ...asset, localDateTime: '2024-01-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-01-01T00:00:00.000Z') })),
}; };
const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries( const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries(
Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]), Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]),
@ -96,46 +97,46 @@ describe('AssetStore', () => {
it('loads a bucket', async () => { it('loads a bucket', async () => {
expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(0); expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(0);
await assetStore.loadBucket('2024-01-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 1 });
expect(sdkMock.getTimeBucket).toBeCalledTimes(1); expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(3); expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(3);
}); });
it('ignores invalid buckets', async () => { it('ignores invalid buckets', async () => {
await assetStore.loadBucket('2023-01-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2023, month: 1 });
expect(sdkMock.getTimeBucket).toBeCalledTimes(0); expect(sdkMock.getTimeBucket).toBeCalledTimes(0);
}); });
it('cancels bucket loading', async () => { it('cancels bucket loading', async () => {
const bucket = assetStore.getBucketByDate(2024, 1)!; const bucket = assetStore.getBucketByDate(2024, 1)!;
void assetStore.loadBucket(bucket!.bucketDate); void assetStore.loadBucket({ year: 2024, month: 1 });
const abortSpy = vi.spyOn(bucket!.loader!.cancelToken!, 'abort'); const abortSpy = vi.spyOn(bucket!.loader!.cancelToken!, 'abort');
bucket?.cancel(); bucket?.cancel();
expect(abortSpy).toBeCalledTimes(1); expect(abortSpy).toBeCalledTimes(1);
await assetStore.loadBucket(bucket!.bucketDate); await assetStore.loadBucket({ year: 2024, month: 1 });
expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(3); expect(assetStore.getBucketByDate(2024, 1)?.getAssets().length).toEqual(3);
}); });
it('prevents loading buckets multiple times', async () => { it('prevents loading buckets multiple times', async () => {
await Promise.all([ await Promise.all([
assetStore.loadBucket('2024-01-01T00:00:00.000Z'), assetStore.loadBucket({ year: 2024, month: 1 }),
assetStore.loadBucket('2024-01-01T00:00:00.000Z'), assetStore.loadBucket({ year: 2024, month: 1 }),
]); ]);
expect(sdkMock.getTimeBucket).toBeCalledTimes(1); expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
await assetStore.loadBucket('2024-01-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 1 });
expect(sdkMock.getTimeBucket).toBeCalledTimes(1); expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
}); });
it('allows loading a canceled bucket', async () => { it('allows loading a canceled bucket', async () => {
const bucket = assetStore.getBucketByDate(2024, 1)!; const bucket = assetStore.getBucketByDate(2024, 1)!;
const loadPromise = assetStore.loadBucket(bucket!.bucketDate); const loadPromise = assetStore.loadBucket({ year: 2024, month: 1 });
bucket.cancel(); bucket.cancel();
await loadPromise; await loadPromise;
expect(bucket?.getAssets().length).toEqual(0); expect(bucket?.getAssets().length).toEqual(0);
await assetStore.loadBucket(bucket.bucketDate); await assetStore.loadBucket({ year: 2024, month: 1 });
expect(bucket!.getAssets().length).toEqual(3); expect(bucket!.getAssets().length).toEqual(3);
}); });
}); });
@ -157,20 +158,21 @@ describe('AssetStore', () => {
it('adds assets to new bucket', () => { it('adds assets to new bucket', () => {
const asset = timelineAssetFactory.build({ const asset = timelineAssetFactory.build({
localDateTime: '2024-01-20T12:00:00.000Z', localDateTime: new Date('2024-01-20T12:00:00.000Z'),
}); });
assetStore.addAssets([asset]); assetStore.addAssets([asset]);
expect(assetStore.buckets.length).toEqual(1); expect(assetStore.buckets.length).toEqual(1);
expect(assetStore.getAssets().length).toEqual(1); expect(assetStore.getAssets().length).toEqual(1);
expect(assetStore.buckets[0].getAssets().length).toEqual(1); expect(assetStore.buckets[0].getAssets().length).toEqual(1);
expect(assetStore.buckets[0].bucketDate).toEqual('2024-01-01T00:00:00.000Z'); expect(assetStore.buckets[0].year).toEqual(2024);
expect(assetStore.buckets[0].month).toEqual(1);
expect(assetStore.getAssets()[0].id).toEqual(asset.id); expect(assetStore.getAssets()[0].id).toEqual(asset.id);
}); });
it('adds assets to existing bucket', () => { it('adds assets to existing bucket', () => {
const [assetOne, assetTwo] = timelineAssetFactory.buildList(2, { const [assetOne, assetTwo] = timelineAssetFactory.buildList(2, {
localDateTime: '2024-01-20T12:00:00.000Z', localDateTime: new Date('2024-01-20T12:00:00.000Z'),
}); });
assetStore.addAssets([assetOne]); assetStore.addAssets([assetOne]);
assetStore.addAssets([assetTwo]); assetStore.addAssets([assetTwo]);
@ -178,18 +180,19 @@ describe('AssetStore', () => {
expect(assetStore.buckets.length).toEqual(1); expect(assetStore.buckets.length).toEqual(1);
expect(assetStore.getAssets().length).toEqual(2); expect(assetStore.getAssets().length).toEqual(2);
expect(assetStore.buckets[0].getAssets().length).toEqual(2); expect(assetStore.buckets[0].getAssets().length).toEqual(2);
expect(assetStore.buckets[0].bucketDate).toEqual('2024-01-01T00:00:00.000Z'); expect(assetStore.buckets[0].year).toEqual(2024);
expect(assetStore.buckets[0].month).toEqual(1);
}); });
it('orders assets in buckets by descending date', () => { it('orders assets in buckets by descending date', () => {
const assetOne = timelineAssetFactory.build({ const assetOne = timelineAssetFactory.build({
localDateTime: '2024-01-20T12:00:00.000Z', localDateTime: new Date('2024-01-20T12:00:00.000Z'),
}); });
const assetTwo = timelineAssetFactory.build({ const assetTwo = timelineAssetFactory.build({
localDateTime: '2024-01-15T12:00:00.000Z', localDateTime: new Date('2024-01-15T12:00:00.000Z'),
}); });
const assetThree = timelineAssetFactory.build({ const assetThree = timelineAssetFactory.build({
localDateTime: '2024-01-16T12:00:00.000Z', localDateTime: new Date('2024-01-16T12:00:00.000Z'),
}); });
assetStore.addAssets([assetOne, assetTwo, assetThree]); assetStore.addAssets([assetOne, assetTwo, assetThree]);
@ -202,15 +205,20 @@ describe('AssetStore', () => {
}); });
it('orders buckets by descending date', () => { it('orders buckets by descending date', () => {
const assetOne = timelineAssetFactory.build({ localDateTime: '2024-01-20T12:00:00.000Z' }); const assetOne = timelineAssetFactory.build({ localDateTime: new Date('2024-01-20T12:00:00.000Z') });
const assetTwo = timelineAssetFactory.build({ localDateTime: '2024-04-20T12:00:00.000Z' }); const assetTwo = timelineAssetFactory.build({ localDateTime: new Date('2024-04-20T12:00:00.000Z') });
const assetThree = timelineAssetFactory.build({ localDateTime: '2023-01-20T12:00:00.000Z' }); const assetThree = timelineAssetFactory.build({ localDateTime: new Date('2023-01-20T12:00:00.000Z') });
assetStore.addAssets([assetOne, assetTwo, assetThree]); assetStore.addAssets([assetOne, assetTwo, assetThree]);
expect(assetStore.buckets.length).toEqual(3); expect(assetStore.buckets.length).toEqual(3);
expect(assetStore.buckets[0].bucketDate).toEqual('2024-04-01T00:00:00.000Z'); expect(assetStore.buckets[0].year).toEqual(2024);
expect(assetStore.buckets[1].bucketDate).toEqual('2024-01-01T00:00:00.000Z'); expect(assetStore.buckets[0].month).toEqual(4);
expect(assetStore.buckets[2].bucketDate).toEqual('2023-01-01T00:00:00.000Z');
expect(assetStore.buckets[1].year).toEqual(2024);
expect(assetStore.buckets[1].month).toEqual(1);
expect(assetStore.buckets[2].year).toEqual(2023);
expect(assetStore.buckets[2].month).toEqual(1);
}); });
it('updates existing asset', () => { it('updates existing asset', () => {
@ -266,8 +274,8 @@ describe('AssetStore', () => {
}); });
it('asset moves buckets when asset date changes', () => { it('asset moves buckets when asset date changes', () => {
const asset = timelineAssetFactory.build({ localDateTime: '2024-01-20T12:00:00.000Z' }); const asset = timelineAssetFactory.build({ localDateTime: new Date('2024-01-20T12:00:00.000Z') });
const updatedAsset = { ...asset, localDateTime: '2024-03-20T12:00:00.000Z' }; const updatedAsset = { ...asset, localDateTime: new Date('2024-03-20T12:00:00.000Z') };
assetStore.addAssets([asset]); assetStore.addAssets([asset]);
expect(assetStore.buckets.length).toEqual(1); expect(assetStore.buckets.length).toEqual(1);
@ -294,7 +302,7 @@ describe('AssetStore', () => {
}); });
it('ignores invalid IDs', () => { it('ignores invalid IDs', () => {
assetStore.addAssets(timelineAssetFactory.buildList(2, { localDateTime: '2024-01-20T12:00:00.000Z' })); assetStore.addAssets(timelineAssetFactory.buildList(2, { localDateTime: new Date('2024-01-20T12:00:00.000Z') }));
assetStore.removeAssets(['', 'invalid', '4c7d9acc']); assetStore.removeAssets(['', 'invalid', '4c7d9acc']);
expect(assetStore.getAssets().length).toEqual(2); expect(assetStore.getAssets().length).toEqual(2);
@ -304,7 +312,7 @@ describe('AssetStore', () => {
it('removes asset from bucket', () => { it('removes asset from bucket', () => {
const [assetOne, assetTwo] = timelineAssetFactory.buildList(2, { const [assetOne, assetTwo] = timelineAssetFactory.buildList(2, {
localDateTime: '2024-01-20T12:00:00.000Z', localDateTime: new Date('2024-01-20T12:00:00.000Z'),
}); });
assetStore.addAssets([assetOne, assetTwo]); assetStore.addAssets([assetOne, assetTwo]);
assetStore.removeAssets([assetOne.id]); assetStore.removeAssets([assetOne.id]);
@ -315,7 +323,7 @@ describe('AssetStore', () => {
}); });
it('does not remove bucket when empty', () => { it('does not remove bucket when empty', () => {
const assets = timelineAssetFactory.buildList(2, { localDateTime: '2024-01-20T12:00:00.000Z' }); const assets = timelineAssetFactory.buildList(2, { localDateTime: new Date('2024-01-20T12:00:00.000Z') });
assetStore.addAssets(assets); assetStore.addAssets(assets);
assetStore.removeAssets(assets.map((asset) => asset.id)); assetStore.removeAssets(assets.map((asset) => asset.id));
@ -339,10 +347,10 @@ describe('AssetStore', () => {
it('populated store returns first asset', () => { it('populated store returns first asset', () => {
const assetOne = timelineAssetFactory.build({ const assetOne = timelineAssetFactory.build({
localDateTime: '2024-01-20T12:00:00.000Z', localDateTime: new Date('2024-01-20T12:00:00.000Z'),
}); });
const assetTwo = timelineAssetFactory.build({ const assetTwo = timelineAssetFactory.build({
localDateTime: '2024-01-15T12:00:00.000Z', localDateTime: new Date('2024-01-15T12:00:00.000Z'),
}); });
assetStore.addAssets([assetOne, assetTwo]); assetStore.addAssets([assetOne, assetTwo]);
expect(assetStore.getFirstAsset()).toEqual(assetOne); expect(assetStore.getFirstAsset()).toEqual(assetOne);
@ -354,13 +362,13 @@ describe('AssetStore', () => {
const bucketAssets: Record<string, TimelineAsset[]> = { const bucketAssets: Record<string, TimelineAsset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory '2024-03-01T00:00:00.000Z': timelineAssetFactory
.buildList(1) .buildList(1)
.map((asset) => ({ ...asset, localDateTime: '2024-03-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-03-01T00:00:00.000Z') })),
'2024-02-01T00:00:00.000Z': timelineAssetFactory '2024-02-01T00:00:00.000Z': timelineAssetFactory
.buildList(6) .buildList(6)
.map((asset) => ({ ...asset, localDateTime: '2024-02-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-02-01T00:00:00.000Z') })),
'2024-01-01T00:00:00.000Z': timelineAssetFactory '2024-01-01T00:00:00.000Z': timelineAssetFactory
.buildList(3) .buildList(3)
.map((asset) => ({ ...asset, localDateTime: '2024-01-01T00:00:00.000Z' })), .map((asset) => ({ ...asset, localDateTime: new Date('2024-01-01T00:00:00.000Z') })),
}; };
const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries( const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries(
Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]), Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]),
@ -383,7 +391,7 @@ describe('AssetStore', () => {
}); });
it('returns previous assetId', async () => { it('returns previous assetId', async () => {
await assetStore.loadBucket('2024-01-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 1 });
const bucket = assetStore.getBucketByDate(2024, 1); const bucket = assetStore.getBucketByDate(2024, 1);
const a = bucket!.getAssets()[0]; const a = bucket!.getAssets()[0];
@ -393,8 +401,8 @@ describe('AssetStore', () => {
}); });
it('returns previous assetId spanning multiple buckets', async () => { it('returns previous assetId spanning multiple buckets', async () => {
await assetStore.loadBucket('2024-02-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 2 });
await assetStore.loadBucket('2024-03-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 3 });
const bucket = assetStore.getBucketByDate(2024, 2); const bucket = assetStore.getBucketByDate(2024, 2);
const previousBucket = assetStore.getBucketByDate(2024, 3); const previousBucket = assetStore.getBucketByDate(2024, 3);
@ -405,7 +413,7 @@ describe('AssetStore', () => {
}); });
it('loads previous bucket', async () => { it('loads previous bucket', async () => {
await assetStore.loadBucket('2024-02-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 2 });
const loadBucketSpy = vi.spyOn(assetStore, 'loadBucket'); const loadBucketSpy = vi.spyOn(assetStore, 'loadBucket');
const bucket = assetStore.getBucketByDate(2024, 2); const bucket = assetStore.getBucketByDate(2024, 2);
@ -418,9 +426,9 @@ describe('AssetStore', () => {
}); });
it('skips removed assets', async () => { it('skips removed assets', async () => {
await assetStore.loadBucket('2024-01-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 1 });
await assetStore.loadBucket('2024-02-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 2 });
await assetStore.loadBucket('2024-03-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 3 });
const [assetOne, assetTwo, assetThree] = assetStore.getAssets(); const [assetOne, assetTwo, assetThree] = assetStore.getAssets();
assetStore.removeAssets([assetTwo.id]); assetStore.removeAssets([assetTwo.id]);
@ -428,7 +436,7 @@ describe('AssetStore', () => {
}); });
it('returns null when no more assets', async () => { it('returns null when no more assets', async () => {
await assetStore.loadBucket('2024-03-01T00:00:00.000Z'); await assetStore.loadBucket({ year: 2024, month: 3 });
expect(await assetStore.getLaterAsset(assetStore.getAssets()[0])).toBeUndefined(); expect(await assetStore.getLaterAsset(assetStore.getAssets()[0])).toBeUndefined();
}); });
}); });
@ -449,21 +457,24 @@ describe('AssetStore', () => {
}); });
it('returns the bucket index', () => { it('returns the bucket index', () => {
const assetOne = timelineAssetFactory.build({ localDateTime: '2024-01-20T12:00:00.000Z' }); const assetOne = timelineAssetFactory.build({ localDateTime: new Date('2024-01-20T12:00:00.000Z') });
const assetTwo = timelineAssetFactory.build({ localDateTime: '2024-02-15T12:00:00.000Z' }); const assetTwo = timelineAssetFactory.build({ localDateTime: new Date('2024-02-15T12:00:00.000Z') });
assetStore.addAssets([assetOne, assetTwo]); assetStore.addAssets([assetOne, assetTwo]);
expect(assetStore.getBucketIndexByAssetId(assetTwo.id)?.bucketDate).toEqual('2024-02-01T00:00:00.000Z'); expect(assetStore.getBucketIndexByAssetId(assetTwo.id)?.year).toEqual(2024);
expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.bucketDate).toEqual('2024-01-01T00:00:00.000Z'); expect(assetStore.getBucketIndexByAssetId(assetTwo.id)?.month).toEqual(2);
expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.year).toEqual(2024);
expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.month).toEqual(1);
}); });
it('ignores removed buckets', () => { it('ignores removed buckets', () => {
const assetOne = timelineAssetFactory.build({ localDateTime: '2024-01-20T12:00:00.000Z' }); const assetOne = timelineAssetFactory.build({ localDateTime: new Date('2024-01-20T12:00:00.000Z') });
const assetTwo = timelineAssetFactory.build({ localDateTime: '2024-02-15T12:00:00.000Z' }); const assetTwo = timelineAssetFactory.build({ localDateTime: new Date('2024-02-15T12:00:00.000Z') });
assetStore.addAssets([assetOne, assetTwo]); assetStore.addAssets([assetOne, assetTwo]);
assetStore.removeAssets([assetTwo.id]); assetStore.removeAssets([assetTwo.id]);
expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.bucketDate).toEqual('2024-01-01T00:00:00.000Z'); expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.year).toEqual(2024);
expect(assetStore.getBucketIndexByAssetId(assetOne.id)?.month).toEqual(1);
}); });
}); });
}); });

View File

@ -49,7 +49,8 @@ function updateObject(target: any, source: any): boolean {
if (!source.hasOwnProperty(key)) { if (!source.hasOwnProperty(key)) {
continue; continue;
} }
if (typeof target[key] === 'object') { const isDate = target[key] instanceof Date;
if (typeof target[key] === 'object' && !isDate) {
updated = updated || updateObject(target[key], source[key]); updated = updated || updateObject(target[key], source[key]);
} else { } else {
// Otherwise, directly copy the value // Otherwise, directly copy the value
@ -204,7 +205,7 @@ export class AssetDateGroup {
const newTime = asset.localDateTime; const newTime = asset.localDateTime;
if (oldTime.valueOf() !== newTime.valueOf()) { if (oldTime.valueOf() !== newTime.valueOf()) {
const year = newTime.getUTCFullYear(); const year = newTime.getUTCFullYear();
const month = newTime.getUTCMonth(); const month = newTime.getUTCMonth() + 1;
if (this.bucket.year !== year || this.bucket.month !== month) { if (this.bucket.year !== year || this.bucket.month !== month) {
remove = true; remove = true;
moveAssets.push({ asset, year, month }); moveAssets.push({ asset, year, month });
@ -337,24 +338,27 @@ export class AssetBucket {
isBucketHeightActual: boolean = $state(false); isBucketHeightActual: boolean = $state(false);
readonly bucketDateFormatted: string; readonly bucketDateFormatted: string;
readonly bucketDate: string;
readonly month: number; readonly month: number;
readonly year: number; readonly year: number;
constructor(store: AssetStore, date: Date, initialCount: number, order: AssetOrder = AssetOrder.Desc) { constructor(
store: AssetStore,
{ year, month }: { year: number; month: number },
initialCount: number,
order: AssetOrder = AssetOrder.Desc,
) {
this.store = store; this.store = store;
this.#initialCount = initialCount; this.#initialCount = initialCount;
this.#sortOrder = order; this.#sortOrder = order;
const bucketDateFormatted = date.toLocaleString(get(locale), { this.month = month;
this.year = year;
const date = new Date(Date.UTC(year, month - 1));
this.bucketDateFormatted = date.toLocaleString(get(locale), {
month: 'short', month: 'short',
year: 'numeric', year: 'numeric',
timeZone: 'UTC', timeZone: 'UTC',
}); });
this.bucketDate = date.toISOString();
this.bucketDateFormatted = bucketDateFormatted;
this.month = date.getUTCMonth();
this.year = date.getUTCFullYear();
this.loader = new CancellableTask( this.loader = new CancellableTask(
() => { () => {
@ -373,7 +377,7 @@ export class AssetBucket {
if (old !== newValue) { if (old !== newValue) {
this.#intersecting = newValue; this.#intersecting = newValue;
if (newValue) { if (newValue) {
void this.store.loadBucket(this.bucketDate); void this.store.loadBucket({ year: this.year, month: this.month });
} else { } else {
this.cancel(); this.cancel();
} }
@ -454,7 +458,6 @@ export class AssetBucket {
} }
addAssets(bucketAssets: TimeBucketAssetResponseDto) { addAssets(bucketAssets: TimeBucketAssetResponseDto) {
const time1 = performance.now();
const addContext = new AddContext(); const addContext = new AddContext();
const people: string[] = []; const people: string[] = [];
for (let i = 0; i < bucketAssets.id.length; i++) { for (let i = 0; i < bucketAssets.id.length; i++) {
@ -496,16 +499,13 @@ export class AssetBucket {
addContext.sort(this, this.#sortOrder); addContext.sort(this, this.#sortOrder);
const time2 = performance.now();
const time = time2 - time1;
console.log(`AssetBucket.addAssets took ${time}ms`);
return addContext.unprocessedAssets; return addContext.unprocessedAssets;
} }
addTimelineAsset(timelineAsset: TimelineAsset, addContext: AddContext) { addTimelineAsset(timelineAsset: TimelineAsset, addContext: AddContext) {
const { localDateTime } = timelineAsset; const { localDateTime } = timelineAsset;
const month = localDateTime.getUTCMonth(); const month = localDateTime.getUTCMonth() + 1;
const year = localDateTime.getUTCFullYear(); const year = localDateTime.getUTCFullYear();
if (this.month !== month || this.year !== year) { if (this.month !== month || this.year !== year) {
@ -541,7 +541,7 @@ export class AssetBucket {
/** The svelte key for this view model object */ /** The svelte key for this view model object */
get viewId() { get viewId() {
return this.bucketDate; return this.year + '-' + this.month;
} }
set bucketHeight(height: number) { set bucketHeight(height: number) {
@ -675,7 +675,8 @@ type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAssets | Update
export type LiteBucket = { export type LiteBucket = {
bucketHeight: number; bucketHeight: number;
assetCount: number; assetCount: number;
bucketDate: string; year: number;
month: number;
bucketDateFormattted: string; bucketDateFormattted: string;
}; };
@ -914,8 +915,9 @@ export class AssetStore {
} }
#findBucketForDate(date: Date) { #findBucketForDate(date: Date) {
const targetMonth = date.getUTCMonth() + 1;
for (const bucket of this.buckets) { for (const bucket of this.buckets) {
if (bucket.month === date.getUTCMonth() && bucket.year === date.getUTCFullYear()) { if (bucket.month === targetMonth && bucket.year === date.getUTCFullYear()) {
return bucket; return bucket;
} }
} }
@ -1018,7 +1020,13 @@ export class AssetStore {
}); });
this.buckets = timebuckets.map((bucket) => { this.buckets = timebuckets.map((bucket) => {
return new AssetBucket(this, new Date(bucket.timeBucket), bucket.count, this.#options.order); const date = new Date(bucket.timeBucket);
return new AssetBucket(
this,
{ year: date.getUTCFullYear(), month: date.getUTCMonth() + 1 },
bucket.count,
this.#options.order,
);
}); });
this.albumAssets.clear(); this.albumAssets.clear();
this.#updateViewportGeometry(false); this.#updateViewportGeometry(false);
@ -1103,7 +1111,8 @@ export class AssetStore {
#createScrubBuckets() { #createScrubBuckets() {
this.scrubberBuckets = this.buckets.map((bucket) => ({ this.scrubberBuckets = this.buckets.map((bucket) => ({
assetCount: bucket.bucketCount, assetCount: bucket.bucketCount,
bucketDate: bucket.bucketDate, year: bucket.year,
month: bucket.month,
bucketDateFormattted: bucket.bucketDateFormatted, bucketDateFormattted: bucket.bucketDateFormatted,
bucketHeight: bucket.bucketHeight, bucketHeight: bucket.bucketHeight,
})); }));
@ -1195,15 +1204,13 @@ export class AssetStore {
bucket.isBucketHeightActual = true; bucket.isBucketHeightActual = true;
} }
async loadBucket(bucketDate: string, options?: { cancelable: boolean }): Promise<void> { // Month is 1-indexed
async loadBucket({ year, month }: { year: number; month: number }, options?: { cancelable: boolean }): Promise<void> {
let cancelable = true; let cancelable = true;
if (options) { if (options) {
cancelable = options.cancelable; cancelable = options.cancelable;
} }
const date = new Date(bucketDate);
const year = date.getUTCFullYear();
const month = date.getUTCMonth();
const bucket = this.getBucketByDate(year, month); const bucket = this.getBucketByDate(year, month);
if (!bucket) { if (!bucket) {
return; return;
@ -1222,8 +1229,7 @@ export class AssetStore {
const bucketResponse = await getTimeBucket( const bucketResponse = await getTimeBucket(
{ {
...this.#options, ...this.#options,
timeBucket: bucketDate, timeBucket: new Date(Date.UTC(year, month - 1)).toISOString(),
key: authManager.key, key: authManager.key,
}, },
{ signal }, { signal },
@ -1233,7 +1239,7 @@ export class AssetStore {
const albumAssets = await getTimeBucket( const albumAssets = await getTimeBucket(
{ {
albumId: this.#options.timelineAlbumId, albumId: this.#options.timelineAlbumId,
timeBucket: bucketDate, timeBucket: new Date(Date.UTC(year, month - 1)).toISOString(),
key: authManager.key, key: authManager.key,
}, },
{ signal }, { signal },
@ -1245,7 +1251,7 @@ export class AssetStore {
const unprocessed = bucket.addAssets(bucketResponse); const unprocessed = bucket.addAssets(bucketResponse);
if (unprocessed.length > 0) { if (unprocessed.length > 0) {
console.error( console.error(
`Warning: getTimeBucket API returning assets not in requested month: ${bucket.bucketDate}, ${JSON.stringify(unprocessed.map((a) => ({ id: a.id, localDateTime: a.localDateTime })))}`, `Warning: getTimeBucket API returning assets not in requested month: ${bucket.month}, ${JSON.stringify(unprocessed.map((a) => ({ id: a.id, localDateTime: a.localDateTime })))}`,
); );
} }
this.#layoutBucket(bucket); this.#layoutBucket(bucket);
@ -1280,11 +1286,11 @@ export class AssetStore {
const bucketCount = this.buckets.length; const bucketCount = this.buckets.length;
for (const asset of assets) { for (const asset of assets) {
const year = asset.localDateTime.getUTCFullYear(); const year = asset.localDateTime.getUTCFullYear();
const month = asset.localDateTime.getUTCMonth(); const month = asset.localDateTime.getUTCMonth() + 1;
let bucket = this.getBucketByDate(year, month); let bucket = this.getBucketByDate(year, month);
if (!bucket) { if (!bucket) {
bucket = new AssetBucket(this, asset.localDateTime, 1, this.#options.order); bucket = new AssetBucket(this, { year, month }, 1, this.#options.order);
this.buckets.push(bucket); this.buckets.push(bucket);
} }
@ -1325,7 +1331,10 @@ export class AssetStore {
if (!asset || this.isExcluded(asset)) { if (!asset || this.isExcluded(asset)) {
return; return;
} }
bucket = await this.#loadBucketAtTime(asset.localDateTime, { cancelable: false }); const { localDateTime } = asset;
const year = localDateTime.getUTCFullYear();
const month = localDateTime.getUTCMonth() + 1;
bucket = await this.#loadBucketAtTime({ year, month }, { cancelable: false });
} }
if (bucket && bucket?.containsAssetId(id)) { if (bucket && bucket?.containsAssetId(id)) {
@ -1333,12 +1342,9 @@ export class AssetStore {
} }
} }
async #loadBucketAtTime(localDateTime: Date, options?: { cancelable: boolean }) { async #loadBucketAtTime({ year, month }: { year: number; month: number }, options?: { cancelable: boolean }) {
// Only support TimeBucketSize.Month // Only support TimeBucketSize.Month
const year = localDateTime.getUTCFullYear(); await this.loadBucket({ year, month }, options);
const month = localDateTime.getUTCMonth();
localDateTime = new Date(year, month);
await this.loadBucket(localDateTime.toISOString(), options);
return this.getBucketByDate(year, month); return this.getBucketByDate(year, month);
} }
@ -1349,7 +1355,7 @@ export class AssetStore {
async getRandomBucket() { async getRandomBucket() {
const random = Math.floor(Math.random() * this.buckets.length); const random = Math.floor(Math.random() * this.buckets.length);
const bucket = this.buckets[random]; const bucket = this.buckets[random];
await this.loadBucket(bucket.bucketDate, { cancelable: false }); await this.loadBucket({ year: bucket.year, month: bucket.month }, { cancelable: false });
return bucket; return bucket;
} }
@ -1456,7 +1462,7 @@ export class AssetStore {
if (!bucket) { if (!bucket) {
return; return;
} }
await this.loadBucket(bucket.bucketDate, { cancelable: false }); await this.loadBucket({ year: bucket.year, month: bucket.month }, { cancelable: false });
const asset = bucket.findClosest(date); const asset = bucket.findClosest(date);
if (asset) { if (asset) {
return asset; return asset;
@ -1465,7 +1471,7 @@ export class AssetStore {
const startIndex = this.buckets.indexOf(bucket); const startIndex = this.buckets.indexOf(bucket);
for (let currentIndex = startIndex + 1; currentIndex < this.buckets.length; currentIndex++) { for (let currentIndex = startIndex + 1; currentIndex < this.buckets.length; currentIndex++) {
bucket = this.buckets[currentIndex]; bucket = this.buckets[currentIndex];
await this.loadBucket(bucket.bucketDate); await this.loadBucket({ year: bucket.year, month: bucket.month }, { cancelable: false });
const next = bucket.dateGroups[0]?.intersetingAssets[0]?.asset; const next = bucket.dateGroups[0]?.intersetingAssets[0]?.asset;
if (next) { if (next) {
return next; return next;
@ -1525,7 +1531,7 @@ export class AssetStore {
for (let currentIndex = startIndex + increment; endCondition(currentIndex); currentIndex += increment) { for (let currentIndex = startIndex + increment; endCondition(currentIndex); currentIndex += increment) {
const targetBucket = this.buckets[currentIndex]; const targetBucket = this.buckets[currentIndex];
await this.loadBucket(targetBucket.bucketDate, { cancelable: false }); await this.loadBucket({ year: targetBucket.year, month: targetBucket.month }, { cancelable: false });
if (targetBucket.dateGroups.length > 0) { if (targetBucket.dateGroups.length > 0) {
return targetBucket.dateGroups[0]?.intersetingAssets[0]?.asset; return targetBucket.dateGroups[0]?.intersetingAssets[0]?.asset;
} }
@ -1539,7 +1545,7 @@ export class AssetStore {
const targetBucket = this.buckets[targetBucketIndex]; const targetBucket = this.buckets[targetBucketIndex];
if (targetBucket) { if (targetBucket) {
await this.loadBucket(targetBucket.bucketDate, { cancelable: false }); await this.loadBucket({ year: targetBucket.year, month: targetBucket.month }, { cancelable: false });
return targetBucket.dateGroups[0]?.intersetingAssets[0]?.asset; return targetBucket.dateGroups[0]?.intersetingAssets[0]?.asset;
} }
return; return;
@ -1558,7 +1564,7 @@ export class AssetStore {
for (let currentIndex = startIndex; endCondition(currentIndex); currentIndex += increment) { for (let currentIndex = startIndex; endCondition(currentIndex); currentIndex += increment) {
const otherBucket = this.buckets[currentIndex]; const otherBucket = this.buckets[currentIndex];
const otherBucketYear = DateTime.fromISO(otherBucket.bucketDate).toUTC().get('year'); const otherBucketYear = otherBucket.year;
const yearCondition = const yearCondition =
direction === 'forward' direction === 'forward'
@ -1566,7 +1572,7 @@ export class AssetStore {
: otherBucketYear <= targetYear; // Looking for older years : otherBucketYear <= targetYear; // Looking for older years
if (yearCondition) { if (yearCondition) {
await this.loadBucket(otherBucket.bucketDate, { cancelable: false }); await this.loadBucket({ year: otherBucket.year, month: otherBucket.month }, { cancelable: false });
return otherBucket.dateGroups[0]?.intersetingAssets[0]?.asset; return otherBucket.dateGroups[0]?.intersetingAssets[0]?.asset;
} }
} }
@ -1608,7 +1614,7 @@ export class AssetStore {
for (let currentIndex = startIndex + increment; endCondition(currentIndex); currentIndex += increment) { for (let currentIndex = startIndex + increment; endCondition(currentIndex); currentIndex += increment) {
const adjacentBucket = this.buckets[currentIndex]; const adjacentBucket = this.buckets[currentIndex];
await this.loadBucket(adjacentBucket.bucketDate); await this.loadBucket({ year: adjacentBucket.year, month: adjacentBucket.month }, { cancelable: false });
if (adjacentBucket.dateGroups.length > 0) { if (adjacentBucket.dateGroups.length > 0) {
return direction === 'forward' return direction === 'forward'

View File

@ -479,7 +479,7 @@ export const selectAllAssets = async (assetStore: AssetStore, assetInteraction:
try { try {
for (const bucket of assetStore.buckets) { for (const bucket of assetStore.buckets) {
await assetStore.loadBucket(bucket.bucketDate); await assetStore.loadBucket({ year: bucket.year, month: bucket.month });
if (!get(isSelectingAllAssets)) { if (!get(isSelectingAllAssets)) {
assetInteraction.clearMultiselect(); assetInteraction.clearMultiselect();

View File

@ -61,7 +61,7 @@ describe('getAltText', () => {
ownerId: 'test-owner', ownerId: 'test-owner',
ratio: 1, ratio: 1,
thumbhash: null, thumbhash: null,
localDateTime: '2024-01-01T12:00:00.000Z', localDateTime: new Date('2024-01-01T12:00:00.000Z'),
visibility: AssetVisibility.Timeline, visibility: AssetVisibility.Timeline,
isFavorite: false, isFavorite: false,
isTrashed: false, isTrashed: false,

View File

@ -9,7 +9,7 @@ import { DateTime, type LocaleOptions } from 'luxon';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
export type ScrubberListener = ( export type ScrubberListener = (
bucketDate: string | undefined, bucketDate: { year: number; month: number },
overallScrollPercent: number, overallScrollPercent: number,
bucketScrollPercent: number, bucketScrollPercent: number,
) => void | Promise<void>; ) => void | Promise<void>;