diff --git a/web/src/lib/components/timeline/Timeline.svelte b/web/src/lib/components/timeline/Timeline.svelte
index b883b5c489..c04fad1896 100644
--- a/web/src/lib/components/timeline/Timeline.svelte
+++ b/web/src/lib/components/timeline/Timeline.svelte
@@ -11,7 +11,7 @@
import Skeleton from '$lib/elements/Skeleton.svelte';
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
- import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
+ import { getSegmentIdentifier, TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
@@ -19,12 +19,7 @@
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { navigate } from '$lib/utils/navigation';
- import {
- getSegmentIdentifier,
- getTimes,
- type ScrubberListener,
- type TimelineYearMonth,
- } from '$lib/utils/timeline-util';
+ import { getTimes, type ScrubberListener, type TimelineYearMonth } from '$lib/utils/timeline-util';
import { type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
import { DateTime } from 'luxon';
import { onMount, type Snippet } from 'svelte';
@@ -272,11 +267,6 @@
}
});
- const getMaxScrollPercent = () => {
- const totalHeight = timelineManager.timelineHeight + bottomSectionHeight + timelineManager.topSectionHeight;
- return (totalHeight - timelineManager.viewportHeight) / totalHeight;
- };
-
const getMaxScroll = () => {
if (!element || !timelineElement) {
return 0;
@@ -288,7 +278,7 @@
const scrollToMonthGroupAndOffset = (monthGroup: MonthGroup, monthGroupScrollPercent: number) => {
const topOffset = monthGroup.top;
- const maxScrollPercent = getMaxScrollPercent();
+ const maxScrollPercent = timelineManager.maxScrollPercent;
const delta = monthGroup.height * monthGroupScrollPercent;
const scrollToTop = (topOffset + delta) * maxScrollPercent;
@@ -343,7 +333,7 @@
return;
}
- let maxScrollPercent = getMaxScrollPercent();
+ let maxScrollPercent = timelineManager.maxScrollPercent;
let found = false;
const monthsLength = timelineManager.months.length;
@@ -640,7 +630,7 @@
{@const display = monthGroup.intersecting}
{@const absoluteHeight = monthGroup.top}
- {#if !monthGroup.isLoaded}
+ {#if !monthGroup.loaded}
this.months.reduce((accumulator, b) => accumulator + b.height, 0) + this.topSectionHeight,
);
@@ -71,6 +69,17 @@ export abstract class PhotostreamManager {
}
}
+ abstract get months(): PhotostreamSegment[];
+
+ get maxScrollPercent() {
+ const totalHeight = this.timelineHeight + this.bottomSectionHeight + this.topSectionHeight;
+ return (totalHeight - this.viewportHeight) / totalHeight;
+ }
+
+ get maxScroll() {
+ return this.topSectionHeight + this.bottomSectionHeight + (this.timelineHeight - this.viewportHeight);
+ }
+
#setHeaderHeight(value: number) {
if (this.#headerHeight == value) {
return false;
@@ -151,6 +160,10 @@ export abstract class PhotostreamManager {
return this.#viewportHeight;
}
+ get hasEmptyViewport() {
+ return this.viewportWidth === 0 || this.viewportHeight === 0;
+ }
+
updateSlidingWindow(scrollTop: number) {
if (this.#scrollTop !== scrollTop) {
this.#scrollTop = scrollTop;
@@ -198,34 +211,12 @@ export abstract class PhotostreamManager {
await this.initTask.execute(() => Promise.resolve(undefined), true);
}
- public destroy() {
+ destroy() {
this.isInitialized = false;
}
- async updateViewport(viewport: Viewport) {
- if (viewport.height === 0 && viewport.width === 0) {
- return;
- }
-
- if (this.viewportHeight === viewport.height && this.viewportWidth === viewport.width) {
- return;
- }
-
- if (!this.initTask.executed) {
- await (this.initTask.loading ? this.initTask.waitUntilCompletion() : this.init());
- }
-
- const changedWidth = viewport.width !== this.viewportWidth;
- this.viewportHeight = viewport.height;
- this.viewportWidth = viewport.width;
- this.updateViewportGeometry(changedWidth);
- }
-
protected updateViewportGeometry(changedWidth: boolean) {
- if (!this.isInitialized) {
- return;
- }
- if (this.viewportWidth === 0 || this.viewportHeight === 0) {
+ if (!this.isInitialized || this.hasEmptyViewport) {
return;
}
for (const month of this.months) {
@@ -246,29 +237,18 @@ export abstract class PhotostreamManager {
}
async loadSegment(identifier: SegmentIdentifier, options?: { cancelable: boolean }): Promise {
- let cancelable = true;
- if (options) {
- cancelable = options.cancelable;
- }
- const segment = this.getSegmentByIdentifier(identifier);
- if (!segment) {
- return;
- }
-
- if (segment.loader?.executed) {
+ const { cancelable = true } = options ?? {};
+ const segment = this.months.find((segment) => identifier.matches(segment));
+ if (!segment || segment.loader?.executed) {
return;
}
const result = await segment.load(cancelable);
- if (result === 'LOADED') {
+ if (result === TaskStatus.LOADED) {
updateIntersectionMonthGroup(this, segment);
}
}
- getSegmentByIdentifier(identifier: SegmentIdentifier) {
- return this.months.find((segment) => identifier.matches(segment));
- }
-
getSegmentForAssetId(assetId: string) {
for (const month of this.months) {
const asset = month.assets.find((asset) => asset.id === assetId);
@@ -285,15 +265,6 @@ export abstract class PhotostreamManager {
this.updateIntersections();
}
- getMaxScrollPercent() {
- const totalHeight = this.timelineHeight + this.bottomSectionHeight + this.topSectionHeight;
- return (totalHeight - this.viewportHeight) / totalHeight;
- }
-
- getMaxScroll() {
- return this.topSectionHeight + this.bottomSectionHeight + (this.timelineHeight - this.viewportHeight);
- }
-
retrieveRange(start: AssetDescriptor, end: AssetDescriptor): Promise {
const range: TimelineAsset[] = [];
let collecting = false;
diff --git a/web/src/lib/managers/photostream-manager/PhotostreamSegment.svelte.ts b/web/src/lib/managers/photostream-manager/PhotostreamSegment.svelte.ts
index f78db5db75..1c5e65cdbe 100644
--- a/web/src/lib/managers/photostream-manager/PhotostreamSegment.svelte.ts
+++ b/web/src/lib/managers/photostream-manager/PhotostreamSegment.svelte.ts
@@ -1,13 +1,13 @@
-import { CancellableTask } from '$lib/utils/cancellable-task';
+import type { PhotostreamManager } from '$lib/managers/photostream-manager/PhotostreamManager.svelte';
+import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
+import type { ViewerAsset } from '$lib/managers/timeline-manager/viewer-asset.svelte';
+import { CancellableTask, TaskStatus } from '$lib/utils/cancellable-task';
import { handleError } from '$lib/utils/handle-error';
import { t } from 'svelte-i18n';
import { get } from 'svelte/store';
-import type { PhotostreamManager } from '$lib/managers/photostream-manager/PhotostreamManager.svelte';
-import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
-import type { ViewerAsset } from '$lib/managers/timeline-manager/viewer-asset.svelte';
-
export type SegmentIdentifier = {
+ id(): string;
matches(segment: PhotostreamSegment): boolean;
};
export abstract class PhotostreamSegment {
@@ -22,10 +22,10 @@ export abstract class PhotostreamSegment {
initialCount = $state(0);
percent = $state(0);
- assetsCount = $derived.by(() => (this.isLoaded ? this.viewerAssets.length : this.initialCount));
+ assetsCount = $derived.by(() => (this.loaded ? this.viewerAssets.length : this.initialCount));
loader = new CancellableTask(
- () => this.markLoaded(),
- () => this.markCanceled,
+ () => (this.loaded = true),
+ () => (this.loaded = false),
() => this.handleLoadError,
);
isHeightActual = $state(false);
@@ -34,18 +34,18 @@ export abstract class PhotostreamSegment {
abstract get identifier(): SegmentIdentifier;
- abstract get id(): string;
+ abstract get viewerAssets(): ViewerAsset[];
- get isLoaded() {
+ abstract findAssetAbsolutePosition(assetId: string): number;
+
+ protected abstract fetch(signal: AbortSignal): Promise;
+
+ get loaded() {
return this.#isLoaded;
}
- protected markLoaded() {
- this.#isLoaded = true;
- }
-
- protected markCanceled() {
- this.#isLoaded = false;
+ protected set loaded(newValue: boolean) {
+ this.#isLoaded = newValue;
}
set intersecting(newValue: boolean) {
@@ -65,22 +65,11 @@ export abstract class PhotostreamSegment {
return this.#intersecting;
}
- async load(cancelable: boolean): Promise<'DONE' | 'WAITED' | 'CANCELED' | 'LOADED' | 'ERRORED'> {
- return await this.loader?.execute(async (signal: AbortSignal) => {
- await this.fetch(signal);
- }, cancelable);
- }
-
- protected abstract fetch(signal: AbortSignal): Promise;
-
get assets(): TimelineAsset[] {
return this.#assets;
}
- abstract get viewerAssets(): ViewerAsset[];
-
set height(height: number) {
- console.log('height', height);
if (this.#height === height) {
return;
}
@@ -131,6 +120,12 @@ export abstract class PhotostreamSegment {
return this.#top + this.timelineManager.topSectionHeight;
}
+ async load(cancelable: boolean): Promise {
+ return await this.loader?.execute(async (signal: AbortSignal) => {
+ await this.fetch(signal);
+ }, cancelable);
+ }
+
protected handleLoadError(error: unknown) {
const _$t = get(t);
handleError(error, _$t('errors.failed_to_load_assets'));
@@ -146,6 +141,4 @@ export abstract class PhotostreamSegment {
this.intersecting = intersecting;
this.actuallyIntersecting = actuallyIntersecting;
}
-
- abstract findAssetAbsolutePosition(assetId: string): number;
}
diff --git a/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts
index 6b11b8800d..e288b7e6b1 100644
--- a/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts
+++ b/web/src/lib/managers/timeline-manager/internal/layout-support.svelte.ts
@@ -13,7 +13,7 @@ export function updateGeometry(
if (invalidateHeight) {
month.isHeightActual = false;
}
- if (!month.isLoaded) {
+ if (!month.loaded) {
const viewportWidth = timelineManager.viewportWidth;
if (!month.isHeightActual) {
const unwrappedWidth = (3 / 2) * month.assetsCount * timelineManager.rowHeight * (7 / 10);
diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts
index ed6aec9df3..1da1628ade 100644
--- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts
+++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts
@@ -1,5 +1,9 @@
-import { AssetOrder, type TimeBucketAssetResponseDto } from '@immich/sdk';
-
+import {
+ PhotostreamSegment,
+ type SegmentIdentifier,
+} from '$lib/managers/photostream-manager/PhotostreamSegment.svelte';
+import { layoutMonthGroup, updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte';
+import { loadFromTimeBuckets } from '$lib/managers/timeline-manager/internal/load-support.svelte';
import {
formatGroupTitle,
formatGroupTitleFull,
@@ -7,24 +11,16 @@ import {
fromTimelinePlainDate,
fromTimelinePlainDateTime,
fromTimelinePlainYearMonth,
- getSegmentIdentifier,
getTimes,
setDifference,
type TimelineDateTime,
type TimelineYearMonth,
} from '$lib/utils/timeline-util';
-
-import { layoutMonthGroup, updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte';
-import { loadFromTimeBuckets } from '$lib/managers/timeline-manager/internal/load-support.svelte';
-
-import {
- PhotostreamSegment,
- type SegmentIdentifier,
-} from '$lib/managers/photostream-manager/PhotostreamSegment.svelte';
+import { AssetOrder, type TimeBucketAssetResponseDto } from '@immich/sdk';
import { SvelteSet } from 'svelte/reactivity';
import { DayGroup } from './day-group.svelte';
import { GroupInsertionCache } from './group-insertion-cache.svelte';
-import type { TimelineManager } from './timeline-manager.svelte';
+import { getSegmentIdentifier, type TimelineManager } from './timeline-manager.svelte';
import type { AssetDescriptor, AssetOperation, Direction, MoveAsset, TimelineAsset } from './types';
import { ViewerAsset } from './viewer-asset.svelte';
@@ -52,9 +48,7 @@ export class MonthGroup extends PhotostreamSegment {
this.#timelineManager = timelineManager;
this.#sortOrder = order;
this.monthGroupTitle = formatMonthGroupTitle(fromTimelinePlainYearMonth(yearMonth));
- if (loaded) {
- this.markLoaded();
- }
+ this.loaded = loaded;
}
get identifier() {
diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts
index 2b9ab9dfca..ba37a753a3 100644
--- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts
+++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.spec.ts
@@ -1,7 +1,7 @@
import { sdkMock } from '$lib/__mocks__/sdk.mock';
import { getMonthGroupByDate } from '$lib/managers/timeline-manager/internal/search-support.svelte';
import { AbortError } from '$lib/utils';
-import { fromISODateTimeUTCToObject, getSegmentIdentifier } from '$lib/utils/timeline-util';
+import { fromISODateTimeUTCToObject } from '$lib/utils/timeline-util';
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory';
import { TimelineManager } from './timeline-manager.svelte';
@@ -22,6 +22,14 @@ function deriveLocalDateTimeFromFileCreatedAt(arg: TimelineAsset): TimelineAsset
};
}
+const initViewport = async (timelineManager: TimelineManager, viewportHeight = 1000, viewportWidth = 1588) => {
+ timelineManager.viewportHeight = viewportHeight;
+ timelineManager.viewportWidth = viewportWidth;
+ await timelineManager.init();
+ timelineManager.updateSlidingWindow(0);
+ timelineManager.updateIntersections();
+};
+
describe('TimelineManager', () => {
beforeEach(() => {
vi.resetAllMocks();
@@ -63,12 +71,12 @@ describe('TimelineManager', () => {
]);
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
- await timelineManager.updateViewport({ width: 1588, height: 1000 });
+ await initViewport(timelineManager);
});
it('should load months in viewport', () => {
expect(sdkMock.getTimeBuckets).toBeCalledTimes(1);
- expect(sdkMock.getTimeBucket).toHaveBeenCalledTimes(2);
+ expect(sdkMock.getTimeBucket).toHaveBeenCalledTimes(3);
});
it('calculates month height', () => {
@@ -82,13 +90,13 @@ describe('TimelineManager', () => {
expect.arrayContaining([
expect.objectContaining({ year: 2024, month: 3, height: 165.5 }),
expect.objectContaining({ year: 2024, month: 2, height: 11_996 }),
- expect.objectContaining({ year: 2024, month: 1, height: 286 }),
+ expect.objectContaining({ year: 2024, month: 1, height: 404.5 }),
]),
);
});
it('calculates timeline height', () => {
- expect(timelineManager.timelineHeight).toBe(12_447.5);
+ expect(timelineManager.timelineHeight).toBe(12566);
});
});
@@ -124,19 +132,19 @@ describe('TimelineManager', () => {
}
return bucketAssetsResponse[timeBucket];
});
- await timelineManager.updateViewport({ width: 1588, height: 0 });
+ await initViewport(timelineManager);
});
it('loads a month', async () => {
expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })?.assets.length).toEqual(0);
await timelineManager.loadSegment(getSegmentIdentifier({ year: 2024, month: 1 }));
- expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
+ expect(sdkMock.getTimeBucket).toBeCalledTimes(2);
expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })?.assets.length).toEqual(3);
});
it('ignores invalid months', async () => {
await timelineManager.loadSegment(getSegmentIdentifier({ year: 2023, month: 1 }));
- expect(sdkMock.getTimeBucket).toBeCalledTimes(0);
+ expect(sdkMock.getTimeBucket).toBeCalledTimes(2);
});
it('cancels month loading', async () => {
@@ -154,10 +162,10 @@ describe('TimelineManager', () => {
timelineManager.loadSegment(getSegmentIdentifier({ year: 2024, month: 1 })),
timelineManager.loadSegment(getSegmentIdentifier({ year: 2024, month: 1 })),
]);
- expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
+ expect(sdkMock.getTimeBucket).toBeCalledTimes(2);
await timelineManager.loadSegment(getSegmentIdentifier({ year: 2024, month: 1 }));
- expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
+ expect(sdkMock.getTimeBucket).toBeCalledTimes(2);
});
it('allows loading a canceled month', async () => {
@@ -180,7 +188,8 @@ describe('TimelineManager', () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([]);
- await timelineManager.updateViewport({ width: 1588, height: 1000 });
+ timelineManager.viewportHeight = 1000;
+ timelineManager.viewportHeight = 1588;
});
it('is empty initially', () => {
@@ -304,7 +313,8 @@ describe('TimelineManager', () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([]);
- await timelineManager.updateViewport({ width: 1588, height: 1000 });
+ timelineManager.viewportHeight = 1000;
+ timelineManager.viewportHeight = 1588;
});
it('ignores non-existing assets', () => {
@@ -359,7 +369,8 @@ describe('TimelineManager', () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([]);
- await timelineManager.updateViewport({ width: 1588, height: 1000 });
+ timelineManager.viewportHeight = 1000;
+ timelineManager.viewportHeight = 1588;
});
it('ignores invalid IDs', () => {
@@ -411,7 +422,8 @@ describe('TimelineManager', () => {
beforeEach(async () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([]);
- await timelineManager.updateViewport({ width: 0, height: 0 });
+
+ await initViewport(timelineManager, 0, 0);
});
it('empty store returns null', () => {
@@ -468,7 +480,8 @@ describe('TimelineManager', () => {
{ count: 3, timeBucket: '2024-01-01T00:00:00.000Z' },
]);
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
- await timelineManager.updateViewport({ width: 1588, height: 1000 });
+
+ initViewport(timelineManager);
});
it('returns null for invalid assetId', async () => {
@@ -535,7 +548,7 @@ describe('TimelineManager', () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([]);
- await timelineManager.updateViewport({ width: 0, height: 0 });
+ await initViewport(timelineManager, 0, 0);
});
it('returns null for invalid months', () => {
@@ -618,7 +631,7 @@ describe('TimelineManager', () => {
]);
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
- await timelineManager.updateViewport({ width: 1588, height: 0 });
+ await initViewport(timelineManager);
});
it('gets all assets once', async () => {
diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts
index 79c000b9c1..7da0bcf5a5 100644
--- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts
+++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts
@@ -3,12 +3,7 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { CancellableTask } from '$lib/utils/cancellable-task';
-import {
- getSegmentIdentifier,
- toTimelineAsset,
- type TimelineDateTime,
- type TimelineYearMonth,
-} from '$lib/utils/timeline-util';
+import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util';
import { isEqual } from 'lodash-es';
import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity';
@@ -39,6 +34,17 @@ import type {
TimelineManagerOptions,
} from './types';
+export const getSegmentIdentifier = (yearMonth: TimelineYearMonth | TimelineDateTime) => ({
+ id: () => {
+ return yearMonth.year + '-' + yearMonth.month;
+ },
+ matches: (segment: MonthGroup) => {
+ return (
+ segment.yearMonth && segment.yearMonth.year === yearMonth.year && segment.yearMonth.month === yearMonth.month
+ );
+ },
+});
+
export class TimelineManager extends PhotostreamManager {
albumAssets: Set = new SvelteSet();
scrubberMonths: ScrubberMonth[] = $state([]);
diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts
index e9ae7931f2..f4954b86d4 100644
--- a/web/src/lib/utils/asset-utils.ts
+++ b/web/src/lib/utils/asset-utils.ts
@@ -513,7 +513,7 @@ export const selectAllAssets = async (timelineManager: TimelineManager, assetInt
try {
for (const monthGroup of timelineManager.months) {
- await timelineManager.loadSegment(monthGroup.identifier);
+ await monthGroup.load(false);
if (!get(isSelectingAllAssets)) {
assetInteraction.clearMultiselect();
diff --git a/web/src/lib/utils/cancellable-task.ts b/web/src/lib/utils/cancellable-task.ts
index cf6335977a..f05603bb4c 100644
--- a/web/src/lib/utils/cancellable-task.ts
+++ b/web/src/lib/utils/cancellable-task.ts
@@ -1,3 +1,10 @@
+export enum TaskStatus {
+ DONE,
+ WAITED,
+ CANCELED,
+ LOADED,
+ ERRORED,
+}
export class CancellableTask {
cancelToken: AbortController | null = null;
cancellable: boolean = true;
@@ -32,18 +39,18 @@ export class CancellableTask {
async waitUntilCompletion() {
if (this.executed) {
- return 'DONE';
+ return TaskStatus.DONE;
}
// if there is a cancel token, task is currently executing, so wait on the promise. If it
// isn't, then the task is in new state, it hasn't been loaded, nor has it been executed.
// in either case, we wait on the promise.
await this.complete;
- return 'WAITED';
+ return TaskStatus.WAITED;
}
async execute Promise>(f: F, cancellable: boolean) {
if (this.executed) {
- return 'DONE';
+ return TaskStatus.DONE;
}
// if promise is pending, wait on previous request instead.
@@ -54,7 +61,7 @@ export class CancellableTask {
this.cancellable = cancellable;
}
await this.complete;
- return 'WAITED';
+ return TaskStatus.WAITED;
}
this.cancellable = cancellable;
const cancelToken = (this.cancelToken = new AbortController());
@@ -62,18 +69,18 @@ export class CancellableTask {
try {
await f(cancelToken.signal);
if (cancelToken.signal.aborted) {
- return 'CANCELED';
+ return TaskStatus.CANCELED;
}
this.#transitionToExecuted();
- return 'LOADED';
+ return TaskStatus.LOADED;
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((error as any).name === 'AbortError') {
// abort error is not treated as an error, but as a cancellation.
- return 'CANCELED';
+ return TaskStatus.CANCELED;
}
this.#transitionToErrored(error);
- return 'ERRORED';
+ return TaskStatus.ERRORED;
} finally {
this.cancelToken = null;
}
diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts
index 0a25040510..79331cf534 100644
--- a/web/src/lib/utils/timeline-util.ts
+++ b/web/src/lib/utils/timeline-util.ts
@@ -1,4 +1,3 @@
-import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import { locale } from '$lib/stores/preferences.store';
import { getAssetRatio } from '$lib/utils/asset-utils';
@@ -243,11 +242,3 @@ export function setDifference(setA: Set, setB: Set): SvelteSet {
}
return result;
}
-
-export const getSegmentIdentifier = (yearMonth: TimelineYearMonth | TimelineDateTime) => ({
- matches(segment: MonthGroup) {
- return (
- segment.yearMonth && segment.yearMonth.year === yearMonth.year && segment.yearMonth.month === yearMonth.month
- );
- },
-});