mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 21:24:28 -04:00
Finish up removing/reducing date parsing
This commit is contained in:
parent
5db3ed8412
commit
727c9a49d9
@ -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);
|
||||||
|
@ -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}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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'
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user