mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
refactor: TimelineManager is owned by Timeline.svelte (#22839)
feat: TimelineManager is owned by Timeline.svelte
This commit is contained in:
parent
f1e03d0022
commit
b3055d2e94
14 changed files with 71 additions and 88 deletions
|
|
@ -17,7 +17,6 @@
|
||||||
import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk';
|
import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiDownload, mdiFileImagePlusOutline } from '@mdi/js';
|
import { mdiDownload, mdiFileImagePlusOutline } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||||
import ImmichLogoSmallLink from '../shared-components/immich-logo-small-link.svelte';
|
import ImmichLogoSmallLink from '../shared-components/immich-logo-small-link.svelte';
|
||||||
|
|
@ -35,9 +34,8 @@
|
||||||
|
|
||||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
const options = $derived({ albumId: album.id, order: album.order });
|
||||||
$effect(() => void timelineManager.updateOptions({ albumId: album.id, order: album.order }));
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -61,7 +59,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||||
<Timeline enableRouting={true} {album} {timelineManager} {assetInteraction}>
|
<Timeline enableRouting={true} {album} bind:timelineManager {options} {assetInteraction}>
|
||||||
<section class="pt-8 md:pt-24 px-2 md:px-0">
|
<section class="pt-8 md:pt-24 px-2 md:px-0">
|
||||||
<!-- ALBUM TITLE -->
|
<!-- ALBUM TITLE -->
|
||||||
<h1 class="text-2xl md:text-4xl lg:text-6xl text-primary outline-none transition-all">
|
<h1 class="text-2xl md:text-4xl lg:text-6xl text-primary outline-none transition-all">
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||||
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
|
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import type { TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
||||||
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
||||||
import { type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
import { type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { onMount, type Snippet } from 'svelte';
|
import { onDestroy, onMount, type Snippet } from 'svelte';
|
||||||
import type { UpdatePayload } from 'vite';
|
import type { UpdatePayload } from 'vite';
|
||||||
import TimelineDateGroup from './TimelineDateGroup.svelte';
|
import TimelineDateGroup from './TimelineDateGroup.svelte';
|
||||||
|
|
||||||
|
|
@ -33,7 +33,8 @@
|
||||||
`AssetViewingStore.gridScrollTarget` and load and scroll to the asset specified, and
|
`AssetViewingStore.gridScrollTarget` and load and scroll to the asset specified, and
|
||||||
additionally, update the page location/url with the asset as the timeline is scrolled */
|
additionally, update the page location/url with the asset as the timeline is scrolled */
|
||||||
enableRouting: boolean;
|
enableRouting: boolean;
|
||||||
timelineManager: TimelineManager;
|
timelineManager?: TimelineManager;
|
||||||
|
options?: TimelineManagerOptions;
|
||||||
assetInteraction: AssetInteraction;
|
assetInteraction: AssetInteraction;
|
||||||
removeAction?:
|
removeAction?:
|
||||||
| AssetAction.UNARCHIVE
|
| AssetAction.UNARCHIVE
|
||||||
|
|
@ -71,6 +72,7 @@
|
||||||
singleSelect = false,
|
singleSelect = false,
|
||||||
enableRouting,
|
enableRouting,
|
||||||
timelineManager = $bindable(),
|
timelineManager = $bindable(),
|
||||||
|
options,
|
||||||
assetInteraction,
|
assetInteraction,
|
||||||
removeAction = null,
|
removeAction = null,
|
||||||
withStacked = false,
|
withStacked = false,
|
||||||
|
|
@ -87,6 +89,10 @@
|
||||||
onThumbnailClick,
|
onThumbnailClick,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
timelineManager = new TimelineManager();
|
||||||
|
onDestroy(() => timelineManager.destroy());
|
||||||
|
$effect(() => options && void timelineManager.updateOptions(options));
|
||||||
|
|
||||||
let { isViewing: showAssetViewer, asset: viewingAsset, gridScrollTarget } = assetViewingStore;
|
let { isViewing: showAssetViewer, asset: viewingAsset, gridScrollTarget } = assetViewingStore;
|
||||||
|
|
||||||
let scrollableElement: HTMLElement | undefined = $state();
|
let scrollableElement: HTMLElement | undefined = $state();
|
||||||
|
|
@ -207,13 +213,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBeforeUpdate = (payload: UpdatePayload) => {
|
|
||||||
const timelineUpdate = payload.updates.some((update) => update.path.endsWith('Timeline.svelte'));
|
|
||||||
if (timelineUpdate) {
|
|
||||||
timelineManager.destroy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateIsScrolling = () => (timelineManager.scrolling = true);
|
const updateIsScrolling = () => (timelineManager.scrolling = true);
|
||||||
// note: don't throttle, debounch, or otherwise do this function async - it causes flicker
|
// note: don't throttle, debounch, or otherwise do this function async - it causes flicker
|
||||||
|
|
||||||
|
|
@ -498,7 +497,7 @@
|
||||||
|
|
||||||
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
|
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
|
||||||
|
|
||||||
<HotModuleReload onAfterUpdate={handleAfterUpdate} onBeforeUpdate={handleBeforeUpdate} />
|
<HotModuleReload onAfterUpdate={handleAfterUpdate} onBeforeUpdate={() => timelineManager.destroy()} />
|
||||||
|
|
||||||
<TimelineKeyboardActions
|
<TimelineKeyboardActions
|
||||||
scrollToAsset={(asset) => scrollToAsset(asset) ?? false}
|
scrollToAsset={(asset) => scrollToAsset(asset) ?? false}
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,6 @@ export class TimelineManager {
|
||||||
#updatingIntersections = false;
|
#updatingIntersections = false;
|
||||||
#scrollableElement: HTMLElement | undefined = $state();
|
#scrollableElement: HTMLElement | undefined = $state();
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: TimelineManagerLayoutOptions) {
|
setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: TimelineManagerLayoutOptions) {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
changed ||= this.#setHeaderHeight(headerHeight);
|
changed ||= this.#setHeaderHeight(headerHeight);
|
||||||
|
|
|
||||||
|
|
@ -328,18 +328,19 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
|
const options = $derived.by(() => {
|
||||||
$effect(() => {
|
|
||||||
if (viewMode === AlbumPageViewMode.VIEW) {
|
if (viewMode === AlbumPageViewMode.VIEW) {
|
||||||
void timelineManager.updateOptions({ albumId, order: albumOrder });
|
return { albumId, order: albumOrder };
|
||||||
} else if (viewMode === AlbumPageViewMode.SELECT_ASSETS) {
|
}
|
||||||
void timelineManager.updateOptions({
|
if (viewMode === AlbumPageViewMode.SELECT_ASSETS) {
|
||||||
|
return {
|
||||||
visibility: AssetVisibility.Timeline,
|
visibility: AssetVisibility.Timeline,
|
||||||
withPartners: true,
|
withPartners: true,
|
||||||
timelineAlbumId: albumId,
|
timelineAlbumId: albumId,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
const isShared = $derived(viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : album.albumUsers.length > 0);
|
const isShared = $derived(viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : album.albumUsers.length > 0);
|
||||||
|
|
@ -352,10 +353,7 @@
|
||||||
handlePromiseError(activityManager.init(album.id));
|
handlePromiseError(activityManager.init(album.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => activityManager.reset());
|
||||||
activityManager.reset();
|
|
||||||
timelineManager.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
let isOwned = $derived($user.id == album.ownerId);
|
let isOwned = $derived($user.id == album.ownerId);
|
||||||
|
|
||||||
|
|
@ -446,7 +444,8 @@
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
||||||
{album}
|
{album}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
assetInteraction={currentAssetIntersection}
|
assetInteraction={currentAssetIntersection}
|
||||||
{isShared}
|
{isShared}
|
||||||
{isSelectionMode}
|
{isSelectionMode}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -27,9 +26,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({ visibility: AssetVisibility.Archive });
|
const options = { visibility: AssetVisibility.Archive };
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -49,7 +47,8 @@
|
||||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
removeAction={AssetAction.UNARCHIVE}
|
removeAction={AssetAction.UNARCHIVE}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { preferences } from '$lib/stores/user.store';
|
||||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -31,9 +30,8 @@
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({ isFavorite: true, withStacked: true });
|
const options = { isFavorite: true, withStacked: true };
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -54,7 +52,8 @@
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
withStacked={true}
|
withStacked={true}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
removeAction={AssetAction.UNFAVORITE}
|
removeAction={AssetAction.UNFAVORITE}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
import { AssetVisibility, lockAuthSession } from '@immich/sdk';
|
import { AssetVisibility, lockAuthSession } from '@immich/sdk';
|
||||||
import { Button } from '@immich/ui';
|
import { Button } from '@immich/ui';
|
||||||
import { mdiDotsVertical, mdiLockOutline } from '@mdi/js';
|
import { mdiDotsVertical, mdiLockOutline } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -27,9 +26,8 @@
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({ visibility: AssetVisibility.Locked });
|
const options = { visibility: AssetVisibility.Locked };
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -60,7 +58,8 @@
|
||||||
|
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
removeAction={AssetAction.SET_VISIBILITY_TIMELINE}
|
removeAction={AssetAction.SET_VISIBILITY_TIMELINE}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,9 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { mdiArrowLeft, mdiPlus } from '@mdi/js';
|
import { mdiArrowLeft, mdiPlus } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -22,16 +20,12 @@
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
const options = $derived({
|
||||||
$effect(
|
userId: data.partner.id,
|
||||||
() =>
|
visibility: AssetVisibility.Timeline,
|
||||||
void timelineManager.updateOptions({
|
withStacked: true,
|
||||||
userId: data.partner.id,
|
});
|
||||||
visibility: AssetVisibility.Timeline,
|
|
||||||
withStacked: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
const handleEscape = () => {
|
const handleEscape = () => {
|
||||||
|
|
@ -43,7 +37,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape} />
|
<Timeline enableRouting={true} {options} {assetInteraction} onEscape={handleEscape} />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{#if assetInteraction.selectionActive}
|
{#if assetInteraction.selectionActive}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -76,10 +76,8 @@
|
||||||
let numberOfAssets = $state(data.statistics.assets);
|
let numberOfAssets = $state(data.statistics.assets);
|
||||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
$effect(() => void timelineManager.updateOptions({ visibility: AssetVisibility.Timeline, personId: data.person.id }));
|
const options = $derived({ visibility: AssetVisibility.Timeline, personId: data.person.id });
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
let viewMode: PersonPageViewMode = $state(PersonPageViewMode.VIEW_ASSETS);
|
let viewMode: PersonPageViewMode = $state(PersonPageViewMode.VIEW_ASSETS);
|
||||||
|
|
@ -388,7 +386,8 @@
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
{person}
|
{person}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
isSelectionMode={viewMode === PersonPageViewMode.SELECT_PERSON}
|
isSelectionMode={viewMode === PersonPageViewMode.SELECT_PERSON}
|
||||||
singleSelect={viewMode === PersonPageViewMode.SELECT_PERSON}
|
singleSelect={viewMode === PersonPageViewMode.SELECT_PERSON}
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,12 @@
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
|
|
||||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({ visibility: AssetVisibility.Timeline, withStacked: true, withPartners: true });
|
const options = { visibility: AssetVisibility.Timeline, withStacked: true, withPartners: true };
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -91,7 +90,8 @@
|
||||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} showUploadButton scrollbar={false}>
|
<UserPageLayout hideNavbar={assetInteraction.selectionActive} showUploadButton scrollbar={false}>
|
||||||
<Timeline
|
<Timeline
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
removeAction={AssetAction.ARCHIVE}
|
removeAction={AssetAction.ARCHIVE}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
import TagAction from '$lib/components/timeline/actions/TagAction.svelte';
|
import TagAction from '$lib/components/timeline/actions/TagAction.svelte';
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
|
||||||
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
|
@ -79,8 +78,6 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let timelineManager = new TimelineManager();
|
|
||||||
|
|
||||||
const onEscape = () => {
|
const onEscape = () => {
|
||||||
if ($showAssetViewer) {
|
if ($showAssetViewer) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -129,7 +126,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetVisibility = (assetIds: string[]) => {
|
const handleSetVisibility = (assetIds: string[]) => {
|
||||||
timelineManager.removeAssets(assetIds);
|
|
||||||
assetInteraction.clearMultiselect();
|
assetInteraction.clearMultiselect();
|
||||||
onAssetDelete(assetIds);
|
onAssetDelete(assetIds);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk';
|
import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk';
|
||||||
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
||||||
import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js';
|
import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -28,14 +27,13 @@
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
|
||||||
$effect(() => void timelineManager.updateOptions({ deferInit: !tag, tagId: tag?.id }));
|
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
let tags = $derived<TagResponseDto[]>(data.tags);
|
let tags = $derived<TagResponseDto[]>(data.tags);
|
||||||
const tree = $derived(TreeNode.fromTags(tags));
|
const tree = $derived(TreeNode.fromTags(tags));
|
||||||
const tag = $derived(tree.traverse(data.path));
|
const tag = $derived(tree.traverse(data.path));
|
||||||
|
|
||||||
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
|
const options = $derived({ deferInit: !tag, tagId: tag?.id });
|
||||||
|
|
||||||
const handleNavigation = (tag: string) => navigateToView(joinPaths(data.path, tag));
|
const handleNavigation = (tag: string) => navigateToView(joinPaths(data.path, tag));
|
||||||
|
|
||||||
const getLink = (path: string) => {
|
const getLink = (path: string) => {
|
||||||
|
|
@ -117,7 +115,13 @@
|
||||||
|
|
||||||
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
|
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
|
||||||
{#if tag.hasAssets}
|
{#if tag.hasAssets}
|
||||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} removeAction={AssetAction.UNARCHIVE}>
|
<Timeline
|
||||||
|
enableRouting={true}
|
||||||
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
|
{assetInteraction}
|
||||||
|
removeAction={AssetAction.UNARCHIVE}
|
||||||
|
>
|
||||||
{#snippet empty()}
|
{#snippet empty()}
|
||||||
<TreeItemThumbnails items={tag.children} icon={mdiTag} onClick={handleNavigation} />
|
<TreeItemThumbnails items={tag.children} icon={mdiTag} onClick={handleNavigation} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
import { emptyTrash, restoreTrash } from '@immich/sdk';
|
import { emptyTrash, restoreTrash } from '@immich/sdk';
|
||||||
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
||||||
import { mdiDeleteForeverOutline, mdiHistory } from '@mdi/js';
|
import { mdiDeleteForeverOutline, mdiHistory } from '@mdi/js';
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
@ -35,9 +34,8 @@
|
||||||
handlePromiseError(goto(AppRoute.PHOTOS));
|
handlePromiseError(goto(AppRoute.PHOTOS));
|
||||||
}
|
}
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({ isTrashed: true });
|
const options = { isTrashed: true };
|
||||||
onDestroy(() => timelineManager.destroy());
|
|
||||||
|
|
||||||
const assetInteraction = new AssetInteraction();
|
const assetInteraction = new AssetInteraction();
|
||||||
|
|
||||||
|
|
@ -116,7 +114,7 @@
|
||||||
</HStack>
|
</HStack>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<Timeline enableRouting={true} {timelineManager} {assetInteraction} onEscape={handleEscape}>
|
<Timeline enableRouting={true} {options} {assetInteraction} onEscape={handleEscape}>
|
||||||
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
<p class="font-medium text-gray-500/60 dark:text-gray-300/60 p-4">
|
||||||
{$t('trashed_items_will_be_permanently_deleted_after', { values: { days: $serverConfig.trashDays } })}
|
{$t('trashed_items_will_be_permanently_deleted_after', { values: { days: $serverConfig.trashDays } })}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,13 @@
|
||||||
let location = $state<{ latitude: number; longitude: number }>({ latitude: 0, longitude: 0 });
|
let location = $state<{ latitude: number; longitude: number }>({ latitude: 0, longitude: 0 });
|
||||||
let locationUpdated = $state(false);
|
let locationUpdated = $state(false);
|
||||||
|
|
||||||
const timelineManager = new TimelineManager();
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
||||||
void timelineManager.updateOptions({
|
const options = {
|
||||||
visibility: AssetVisibility.Timeline,
|
visibility: AssetVisibility.Timeline,
|
||||||
withStacked: true,
|
withStacked: true,
|
||||||
withPartners: true,
|
withPartners: true,
|
||||||
withCoordinates: true,
|
withCoordinates: true,
|
||||||
});
|
};
|
||||||
|
|
||||||
const handleUpdate = async () => {
|
const handleUpdate = async () => {
|
||||||
const confirmed = await modalManager.show(GeolocationUpdateConfirmModal, {
|
const confirmed = await modalManager.show(GeolocationUpdateConfirmModal, {
|
||||||
|
|
@ -188,7 +188,8 @@
|
||||||
<Timeline
|
<Timeline
|
||||||
isSelectionMode={true}
|
isSelectionMode={true}
|
||||||
enableRouting={true}
|
enableRouting={true}
|
||||||
{timelineManager}
|
bind:timelineManager
|
||||||
|
{options}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
removeAction={AssetAction.ARCHIVE}
|
removeAction={AssetAction.ARCHIVE}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue