diff --git a/web/src/lib/components/timeline/TimelineAssetViewer.svelte b/web/src/lib/components/timeline/TimelineAssetViewer.svelte index ccdd8bd5b4..a121bd1938 100644 --- a/web/src/lib/components/timeline/TimelineAssetViewer.svelte +++ b/web/src/lib/components/timeline/TimelineAssetViewer.svelte @@ -110,13 +110,9 @@ case AssetAction.ARCHIVE: case AssetAction.UNARCHIVE: case AssetAction.FAVORITE: - case AssetAction.UNFAVORITE: { - timelineManager.updateAssets([action.asset]); - break; - } - + case AssetAction.UNFAVORITE: case AssetAction.ADD: { - timelineManager.addAssets([action.asset]); + timelineManager.upsertAssets([action.asset]); break; } @@ -135,7 +131,7 @@ break; } case AssetAction.REMOVE_ASSET_FROM_STACK: { - timelineManager.addAssets([toTimelineAsset(action.asset)]); + timelineManager.upsertAssets([toTimelineAsset(action.asset)]); if (action.stack) { //Have to unstack then restack assets in timeline in order to update the stack count in the timeline. updateUnstackedAssetInTimeline( diff --git a/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte index d3f151a974..ad9921e47a 100644 --- a/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte +++ b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte @@ -46,7 +46,7 @@ !(isTrashEnabled && !force), (assetIds) => timelineManager.removeAssets(assetIds), assetInteraction.selectedAssets, - !isTrashEnabled || force ? undefined : (assets) => timelineManager.addAssets(assets), + !isTrashEnabled || force ? undefined : (assets) => timelineManager.upsertAssets(assets), ); assetInteraction.clearMultiselect(); }; diff --git a/web/src/lib/managers/timeline-manager/day-group.svelte.ts b/web/src/lib/managers/timeline-manager/day-group.svelte.ts index a3d3194dd2..5869983dc3 100644 --- a/web/src/lib/managers/timeline-manager/day-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/day-group.svelte.ts @@ -122,7 +122,11 @@ export class DayGroup { const asset = this.viewerAssets[index].asset!; const oldTime = { ...asset.localDateTime }; - let { remove } = operation(asset); + const opResult = operation(asset); + let remove = false; + if (opResult) { + remove = (opResult as { remove: boolean }).remove ?? false; + } const newTime = asset.localDateTime; if (oldTime.year !== newTime.year || oldTime.month !== newTime.month || oldTime.day !== newTime.day) { const { year, month, day } = newTime; diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts deleted file mode 100644 index 4bc99c0315..0000000000 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; -import { AssetOrder } from '@immich/sdk'; - -import { SvelteSet } from 'svelte/reactivity'; -import { GroupInsertionCache } from '../group-insertion-cache.svelte'; -import { MonthGroup } from '../month-group.svelte'; -import type { TimelineManager } from '../timeline-manager.svelte'; -import type { AssetOperation, TimelineAsset } from '../types'; -import { updateGeometry } from './layout-support.svelte'; -import { getMonthGroupByDate } from './search-support.svelte'; - -export function addAssetsToMonthGroups( - timelineManager: TimelineManager, - assets: TimelineAsset[], - options: { order: AssetOrder }, -) { - if (assets.length === 0) { - return; - } - - const addContext = new GroupInsertionCache(); - const updatedMonthGroups = new SvelteSet(); - const monthCount = timelineManager.months.length; - for (const asset of assets) { - let month = getMonthGroupByDate(timelineManager, asset.localDateTime); - - if (!month) { - month = new MonthGroup(timelineManager, asset.localDateTime, 1, options.order); - month.isLoaded = true; - timelineManager.months.push(month); - } - - month.addTimelineAsset(asset, addContext); - updatedMonthGroups.add(month); - } - - if (timelineManager.months.length !== monthCount) { - timelineManager.months.sort((a, b) => { - return a.yearMonth.year === b.yearMonth.year - ? b.yearMonth.month - a.yearMonth.month - : b.yearMonth.year - a.yearMonth.year; - }); - } - - for (const group of addContext.existingDayGroups) { - group.sortAssets(options.order); - } - - for (const monthGroup of addContext.bucketsWithNewDayGroups) { - monthGroup.sortDayGroups(); - } - - for (const month of addContext.updatedBuckets) { - month.sortDayGroups(); - updateGeometry(timelineManager, month, { invalidateHeight: true }); - } - timelineManager.updateIntersections(); -} - -export function runAssetOperation( - timelineManager: TimelineManager, - ids: Set, - operation: AssetOperation, - options: { order: AssetOrder }, -) { - if (ids.size === 0) { - return { processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false }; - } - - const changedMonthGroups = new SvelteSet(); - let idsToProcess = new SvelteSet(ids); - const idsProcessed = new SvelteSet(); - const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = []; - for (const month of timelineManager.months) { - if (idsToProcess.size > 0) { - const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation); - if (moveAssets.length > 0) { - combinedMoveAssets.push(moveAssets); - } - idsToProcess = setDifference(idsToProcess, processedIds); - for (const id of processedIds) { - idsProcessed.add(id); - } - if (changedGeometry) { - changedMonthGroups.add(month); - } - } - } - if (combinedMoveAssets.length > 0) { - addAssetsToMonthGroups( - timelineManager, - combinedMoveAssets.flat().map((a) => a.asset), - options, - ); - } - const changedGeometry = changedMonthGroups.size > 0; - for (const month of changedMonthGroups) { - updateGeometry(timelineManager, month, { invalidateHeight: true }); - } - if (changedGeometry) { - timelineManager.updateIntersections(); - } - return { unprocessedIds: idsToProcess, processedIds: idsProcessed, changedGeometry }; -} diff --git a/web/src/lib/managers/timeline-manager/internal/websocket-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/websocket-support.svelte.ts index 4ba237c50c..bff2f15cb9 100644 --- a/web/src/lib/managers/timeline-manager/internal/websocket-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/websocket-support.svelte.ts @@ -13,10 +13,10 @@ export class WebsocketSupport { #processPendingChanges = throttle(() => { const { add, update, remove } = this.#getPendingChangeBatches(); if (add.length > 0) { - this.#timelineManager.addAssets(add); + this.#timelineManager.upsertAssets(add); } if (update.length > 0) { - this.#timelineManager.updateAssets(update); + this.#timelineManager.upsertAssets(update); } if (remove.length > 0) { this.#timelineManager.removeAssets(remove); 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 bef512c226..841eb83219 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -50,12 +50,13 @@ export class MonthGroup { readonly yearMonth: TimelineYearMonth; constructor( - store: TimelineManager, + timelineManager: TimelineManager, yearMonth: TimelineYearMonth, initialCount: number, + loaded: boolean, order: AssetOrder = AssetOrder.Desc, ) { - this.timelineManager = store; + this.timelineManager = timelineManager; this.#initialCount = initialCount; this.#sortOrder = order; @@ -72,6 +73,9 @@ export class MonthGroup { }, this.#handleLoadError, ); + if (loaded) { + this.isLoaded = true; + } } set intersecting(newValue: boolean) { 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 a3fda3a85c..fc50888e77 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 @@ -175,7 +175,7 @@ describe('TimelineManager', () => { }); }); - describe('addAssets', () => { + describe('upsertAssets', () => { let timelineManager: TimelineManager; beforeEach(async () => { @@ -196,7 +196,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-20T12:00:00.000Z'), }), ); - timelineManager.addAssets([asset]); + timelineManager.upsertAssets([asset]); expect(timelineManager.months.length).toEqual(1); expect(timelineManager.assetCount).toEqual(1); @@ -212,8 +212,8 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-20T12:00:00.000Z'), }) .map((asset) => deriveLocalDateTimeFromFileCreatedAt(asset)); - timelineManager.addAssets([assetOne]); - timelineManager.addAssets([assetTwo]); + timelineManager.upsertAssets([assetOne]); + timelineManager.upsertAssets([assetTwo]); expect(timelineManager.months.length).toEqual(1); expect(timelineManager.assetCount).toEqual(2); @@ -238,7 +238,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-16T12:00:00.000Z'), }), ); - timelineManager.addAssets([assetOne, assetTwo, assetThree]); + timelineManager.upsertAssets([assetOne, assetTwo, assetThree]); const month = getMonthGroupByDate(timelineManager, { year: 2024, month: 1 }); expect(month).not.toBeNull(); @@ -264,7 +264,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2023-01-20T12:00:00.000Z'), }), ); - timelineManager.addAssets([assetOne, assetTwo, assetThree]); + timelineManager.upsertAssets([assetOne, assetTwo, assetThree]); expect(timelineManager.months.length).toEqual(3); expect(timelineManager.months[0].yearMonth.year).toEqual(2024); @@ -278,11 +278,11 @@ describe('TimelineManager', () => { }); it('updates existing asset', () => { - const updateAssetsSpy = vi.spyOn(timelineManager, 'updateAssets'); + const updateAssetsSpy = vi.spyOn(timelineManager, 'upsertAssets'); const asset = deriveLocalDateTimeFromFileCreatedAt(timelineAssetFactory.build()); - timelineManager.addAssets([asset]); + timelineManager.upsertAssets([asset]); - timelineManager.addAssets([asset]); + timelineManager.upsertAssets([asset]); expect(updateAssetsSpy).toBeCalledWith([asset]); expect(timelineManager.assetCount).toEqual(1); }); @@ -294,12 +294,12 @@ describe('TimelineManager', () => { const timelineManager = new TimelineManager(); await timelineManager.updateOptions({ isTrashed: true }); - timelineManager.addAssets([asset, trashedAsset]); + timelineManager.upsertAssets([asset, trashedAsset]); expect(await getAssets(timelineManager)).toEqual([trashedAsset]); }); }); - describe('updateAssets', () => { + describe('upsertAssets', () => { let timelineManager: TimelineManager; beforeEach(async () => { @@ -309,22 +309,15 @@ describe('TimelineManager', () => { await timelineManager.updateViewport({ width: 1588, height: 1000 }); }); - it('ignores non-existing assets', () => { - timelineManager.updateAssets([deriveLocalDateTimeFromFileCreatedAt(timelineAssetFactory.build())]); - - expect(timelineManager.months.length).toEqual(0); - expect(timelineManager.assetCount).toEqual(0); - }); - - it('updates an asset', () => { + it('upserts an asset', () => { const asset = deriveLocalDateTimeFromFileCreatedAt(timelineAssetFactory.build({ isFavorite: false })); const updatedAsset = { ...asset, isFavorite: true }; - timelineManager.addAssets([asset]); + timelineManager.upsertAssets([asset]); expect(timelineManager.assetCount).toEqual(1); expect(timelineManager.months[0].getFirstAsset().isFavorite).toEqual(false); - timelineManager.updateAssets([updatedAsset]); + timelineManager.upsertAssets([updatedAsset]); expect(timelineManager.assetCount).toEqual(1); expect(timelineManager.months[0].getFirstAsset().isFavorite).toEqual(true); }); @@ -340,12 +333,12 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-03-20T12:00:00.000Z'), }); - timelineManager.addAssets([asset]); + timelineManager.upsertAssets([asset]); expect(timelineManager.months.length).toEqual(1); expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })).not.toBeUndefined(); expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })?.getAssets().length).toEqual(1); - timelineManager.updateAssets([updatedAsset]); + timelineManager.upsertAssets([updatedAsset]); expect(timelineManager.months.length).toEqual(2); expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })).not.toBeUndefined(); expect(getMonthGroupByDate(timelineManager, { year: 2024, month: 1 })?.getAssets().length).toEqual(0); @@ -365,7 +358,7 @@ describe('TimelineManager', () => { }); it('ignores invalid IDs', () => { - timelineManager.addAssets( + timelineManager.upsertAssets( timelineAssetFactory .buildList(2, { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-20T12:00:00.000Z'), @@ -385,7 +378,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-20T12:00:00.000Z'), }) .map((asset) => deriveLocalDateTimeFromFileCreatedAt(asset)); - timelineManager.addAssets([assetOne, assetTwo]); + timelineManager.upsertAssets([assetOne, assetTwo]); timelineManager.removeAssets([assetOne.id]); expect(timelineManager.assetCount).toEqual(1); @@ -399,7 +392,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-20T12:00:00.000Z'), }) .map((asset) => deriveLocalDateTimeFromFileCreatedAt(asset)); - timelineManager.addAssets(assets); + timelineManager.upsertAssets(assets); timelineManager.removeAssets(assets.map((asset) => asset.id)); expect(timelineManager.assetCount).toEqual(0); @@ -431,7 +424,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-01-15T12:00:00.000Z'), }), ); - timelineManager.addAssets([assetOne, assetTwo]); + timelineManager.upsertAssets([assetOne, assetTwo]); expect(timelineManager.getFirstAsset()).toEqual(assetOne); }); }); @@ -556,7 +549,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-02-15T12:00:00.000Z'), }), ); - timelineManager.addAssets([assetOne, assetTwo]); + timelineManager.upsertAssets([assetOne, assetTwo]); expect(timelineManager.getMonthGroupByAssetId(assetTwo.id)?.yearMonth.year).toEqual(2024); expect(timelineManager.getMonthGroupByAssetId(assetTwo.id)?.yearMonth.month).toEqual(2); @@ -575,7 +568,7 @@ describe('TimelineManager', () => { fileCreatedAt: fromISODateTimeUTCToObject('2024-02-15T12:00:00.000Z'), }), ); - timelineManager.addAssets([assetOne, assetTwo]); + timelineManager.upsertAssets([assetOne, assetTwo]); timelineManager.removeAssets([assetTwo.id]); expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.year).toEqual(2024); 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 d2340224a2..66f1d4cc25 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -1,12 +1,9 @@ import { VirtualScrollManager } from '$lib/managers/VirtualScrollManager/VirtualScrollManager.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; +import { GroupInsertionCache } from '$lib/managers/timeline-manager/group-insertion-cache.svelte'; import { updateIntersectionMonthGroup } from '$lib/managers/timeline-manager/internal/intersection-support.svelte'; import { updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte'; import { loadFromTimeBuckets } from '$lib/managers/timeline-manager/internal/load-support.svelte'; -import { - addAssetsToMonthGroups, - runAssetOperation, -} from '$lib/managers/timeline-manager/internal/operations-support.svelte'; import { findClosestGroupForDate, findMonthGroupForAsset as findMonthGroupForAssetUtil, @@ -17,10 +14,15 @@ import { } from '$lib/managers/timeline-manager/internal/search-support.svelte'; import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websocket-support.svelte'; import { CancellableTask } from '$lib/utils/cancellable-task'; -import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; +import { + setDifference, + toTimelineAsset, + type TimelineDateTime, + type TimelineYearMonth, +} from '$lib/utils/timeline-util'; import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; import { clamp, isEqual } from 'lodash-es'; -import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; +import { SvelteDate, SvelteSet } from 'svelte/reactivity'; import { DayGroup } from './day-group.svelte'; import { isMismatched, updateObject } from './internal/utils.svelte'; import { MonthGroup } from './month-group.svelte'; @@ -28,6 +30,7 @@ import type { AssetDescriptor, AssetOperation, Direction, + MoveAsset, ScrubberMonth, TimelineAsset, TimelineManagerOptions, @@ -217,6 +220,7 @@ export class TimelineManager extends VirtualScrollManager { this, { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1 }, timeBucket.count, + false, this.#options.order, ); }); @@ -319,10 +323,10 @@ export class TimelineManager extends VirtualScrollManager { } } - addAssets(assets: TimelineAsset[]) { - const assetsToUpdate = assets.filter((asset) => !this.isExcluded(asset)); - const notUpdated = this.updateAssets(assetsToUpdate); - addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc }); + upsertAssets(assets: TimelineAsset[]) { + const notExcluded = assets.filter((asset) => !this.isExcluded(asset)); + const notUpdated = this.#updateAssets(notExcluded); + this.addAssetsToSegments([...notUpdated]); } async findMonthGroupForAsset(id: string) { @@ -400,19 +404,16 @@ export class TimelineManager extends VirtualScrollManager { } updateAssetOperation(ids: string[], operation: AssetOperation) { - runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + return this.#runAssetOperation(new Set(ids), operation); } - updateAssets(assets: TimelineAsset[]) { - const lookup = new SvelteMap(assets.map((asset) => [asset.id, asset])); - const { unprocessedIds } = runAssetOperation( - this, - new SvelteSet(lookup.keys()), - (asset) => { - updateObject(asset, lookup.get(asset.id)); - return { remove: false }; - }, - { order: this.#options.order ?? AssetOrder.Desc }, + #updateAssets(assets: TimelineAsset[]) { + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const lookup = new Map(assets.map((asset) => [asset.id, asset])); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const { unprocessedIds } = this.#runAssetOperation(new Set(lookup.keys()), (asset) => + updateObject(asset, lookup.get(asset.id)), ); const result: TimelineAsset[] = []; for (const id of unprocessedIds.values()) { @@ -422,17 +423,83 @@ export class TimelineManager extends VirtualScrollManager { } removeAssets(ids: string[]) { - const { unprocessedIds } = runAssetOperation( - this, - new SvelteSet(ids), - () => { - return { remove: true }; - }, - { order: this.#options.order ?? AssetOrder.Desc }, - ); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const { unprocessedIds } = this.#runAssetOperation(new Set(ids), () => ({ remove: true })); return [...unprocessedIds]; } + protected createUpsertContext(): GroupInsertionCache { + return new GroupInsertionCache(); + } + + protected upsertAssetIntoSegment(asset: TimelineAsset, context: GroupInsertionCache): void { + let month = getMonthGroupByDate(this, asset.localDateTime); + + if (!month) { + month = new MonthGroup(this, asset.localDateTime, 1, true, this.#options.order); + this.months.push(month); + } + + month.addTimelineAsset(asset, context); + } + + protected addAssetsToSegments(assets: TimelineAsset[]) { + if (assets.length === 0) { + return; + } + const context = this.createUpsertContext(); + const monthCount = this.months.length; + for (const asset of assets) { + this.upsertAssetIntoSegment(asset, context); + } + if (this.months.length !== monthCount) { + this.postCreateSegments(); + } + this.postUpsert(context); + this.updateIntersections(); + } + + #runAssetOperation(ids: Set, operation: AssetOperation) { + if (ids.size === 0) { + // eslint-disable-next-line svelte/prefer-svelte-reactivity + return { processedIds: new Set(), unprocessedIds: ids, changedGeometry: false }; + } + + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const changedMonthGroups = new Set(); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + let idsToProcess = new Set(ids); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const idsProcessed = new Set(); + const combinedMoveAssets: MoveAsset[][] = []; + for (const month of this.months) { + if (idsToProcess.size > 0) { + const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation); + if (moveAssets.length > 0) { + combinedMoveAssets.push(moveAssets); + } + idsToProcess = setDifference(idsToProcess, processedIds); + for (const id of processedIds) { + idsProcessed.add(id); + } + if (changedGeometry) { + changedMonthGroups.add(month); + } + } + } + if (combinedMoveAssets.length > 0) { + this.addAssetsToSegments(combinedMoveAssets.flat().map((a) => a.asset)); + } + const changedGeometry = changedMonthGroups.size > 0; + for (const month of changedMonthGroups) { + updateGeometry(this, month, { invalidateHeight: true }); + } + if (changedGeometry) { + this.updateIntersections(); + } + return { unprocessedIds: idsToProcess, processedIds: idsProcessed, changedGeometry }; + } + override refreshLayout() { for (const month of this.months) { updateGeometry(this, month, { invalidateHeight: true }); @@ -492,4 +559,27 @@ export class TimelineManager extends VirtualScrollManager { getAssetOrder() { return this.#options.order ?? AssetOrder.Desc; } + + protected postCreateSegments(): void { + this.months.sort((a, b) => { + return a.yearMonth.year === b.yearMonth.year + ? b.yearMonth.month - a.yearMonth.month + : b.yearMonth.year - a.yearMonth.year; + }); + } + + protected postUpsert(context: GroupInsertionCache): void { + for (const group of context.existingDayGroups) { + group.sortAssets(this.#options.order); + } + + for (const monthGroup of context.bucketsWithNewDayGroups) { + monthGroup.sortDayGroups(); + } + + for (const month of context.updatedBuckets) { + month.sortDayGroups(); + updateGeometry(this, month, { invalidateHeight: true }); + } + } } diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index 27c27dcb63..41f2653d2f 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -37,7 +37,7 @@ export type TimelineAsset = { longitude?: number | null; }; -export type AssetOperation = (asset: TimelineAsset) => { remove: boolean }; +export type AssetOperation = (asset: TimelineAsset) => { remove: boolean } | unknown; export type MoveAsset = { asset: TimelineAsset; date: TimelineDate }; diff --git a/web/src/lib/utils/actions.ts b/web/src/lib/utils/actions.ts index d84ca52a96..150bfc2bd7 100644 --- a/web/src/lib/utils/actions.ts +++ b/web/src/lib/utils/actions.ts @@ -109,5 +109,5 @@ export function updateUnstackedAssetInTimeline(timelineManager: TimelineManager, }, ); - timelineManager.addAssets(assets); + timelineManager.upsertAssets(assets); } diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 464543cdce..2391c66d19 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -266,7 +266,7 @@ }; const handleUndoRemoveAssets = async (assets: TimelineAsset[]) => { - timelineManager.addAssets(assets); + timelineManager.upsertAssets(assets); await refreshAlbum(); }; diff --git a/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte index 676a68e673..c962ea0685 100644 --- a/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -94,7 +94,7 @@ timelineManager.removeAssets(assetIds)} - onUndoDelete={(assets) => timelineManager.addAssets(assets)} + onUndoDelete={(assets) => timelineManager.upsertAssets(assets)} /> diff --git a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte index b29c54607a..5abcc6f5a3 100644 --- a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -339,7 +339,7 @@ }; const handleUndoDeleteAssets = async (assets: TimelineAsset[]) => { - timelineManager.addAssets(assets); + timelineManager.upsertAssets(assets); await updateAssetCount(); }; diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte index 748e0b7100..12e59c0261 100644 --- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte @@ -69,12 +69,12 @@ const handleLink: OnLink = ({ still, motion }) => { timelineManager.removeAssets([motion.id]); - timelineManager.updateAssets([still]); + timelineManager.upsertAssets([still]); }; const handleUnlink: OnUnlink = ({ still, motion }) => { - timelineManager.addAssets([motion]); - timelineManager.updateAssets([still]); + timelineManager.upsertAssets([motion]); + timelineManager.upsertAssets([still]); }; const handleSetVisibility = (assetIds: string[]) => { @@ -153,7 +153,7 @@ timelineManager.removeAssets(assetIds)} - onUndoDelete={(assets) => timelineManager.addAssets(assets)} + onUndoDelete={(assets) => timelineManager.upsertAssets(assets)} />
diff --git a/web/src/routes/(user)/utilities/geolocation/+page.svelte b/web/src/routes/(user)/utilities/geolocation/+page.svelte index 732e0625ab..3dcb9b68cd 100644 --- a/web/src/routes/(user)/utilities/geolocation/+page.svelte +++ b/web/src/routes/(user)/utilities/geolocation/+page.svelte @@ -63,7 +63,7 @@ }), ); - timelineManager.updateAssets(updatedAssets); + timelineManager.upsertAssets(updatedAssets); handleDeselectAll(); };