Create Timeline facade component to unify timeline usage

• Create timeline/timeline.svelte as main entry point for timeline functionality
• Combine BaseTimeline, TimelineKeyboardActions, and TimelineAssetViewer
• Update all route imports from base-timeline to use Timeline component
• Move scrubber.svelte to timeline/base-components/
• Fix timeline-keyboard-actions date handling from result.dateTime to result.date
• Clean up unused imports and props
This commit is contained in:
midzelis
2025-08-14 21:56:22 +00:00
parent dcc34bd1be
commit 364468afac
18 changed files with 141 additions and 97 deletions
@@ -2,11 +2,8 @@
import { afterNavigate, beforeNavigate } from '$app/navigation';
import { page } from '$app/stores';
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
import AssetGridActions from '$lib/components/timeline/actions/timeline-keyboard-actions.svelte';
import Skeleton from '$lib/components/timeline/base-components/skeleton.svelte';
import SelectableTimelineDay from '$lib/components/timeline/internal-components/selectable-timeline-day.svelte';
import TimelineAssetViewer from '$lib/components/timeline/internal-components/timeline-asset-viewer.svelte';
import { AssetAction } from '$lib/constants';
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';
@@ -15,10 +12,8 @@
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { navigate } from '$lib/utils/navigation';
import { type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
import { onMount, type Snippet } from 'svelte';
import type { UpdatePayload } from 'vite';
import Portal from '../../shared-components/portal/portal.svelte';
interface Props {
customThumbnailLayout?: Snippet<[TimelineAsset]>;
@@ -31,21 +26,12 @@
enableRouting: boolean;
timelineManager: TimelineManager;
assetInteraction: AssetInteraction;
removeAction?:
| AssetAction.UNARCHIVE
| AssetAction.ARCHIVE
| AssetAction.FAVORITE
| AssetAction.UNFAVORITE
| AssetAction.SET_VISIBILITY_TIMELINE;
withStacked?: boolean;
showArchiveIcon?: boolean;
isShared?: boolean;
album?: AlbumResponseDto | null;
person?: PersonResponseDto | null;
showSkeleton?: boolean;
isShowDeleteConfirmation?: boolean;
onAssetOpen?: (dayGroup: DayGroup, asset: TimelineAsset, defaultAssetOpen: () => void) => void;
onSelect?: (asset: TimelineAsset) => void;
onEscape?: () => void;
header?: Snippet<[handleScrollTop: (top: number) => void]>;
children?: Snippet;
empty?: Snippet;
@@ -60,30 +46,23 @@
enableRouting,
timelineManager = $bindable(),
assetInteraction,
removeAction,
withStacked = false,
showSkeleton = $bindable(true),
showArchiveIcon = false,
isShared = false,
album = null,
person = null,
isShowDeleteConfirmation = $bindable(false),
onAssetOpen,
onSelect,
onEscape,
children,
empty,
header,
handleTimelineScroll = () => {},
}: Props = $props();
let { isViewing: showAssetViewer, gridScrollTarget } = assetViewingStore;
let { gridScrollTarget } = assetViewingStore;
let element: HTMLElement | undefined = $state();
let timelineElement: HTMLElement | undefined = $state();
let showSkeleton = $state(true);
let scrubberWidth = $state(0);
const maxMd = $derived(mobileDevice.maxMd);
@@ -161,7 +140,7 @@
return true;
};
const scrollToAsset = (asset: TimelineAsset) => {
export const scrollToAsset = (asset: TimelineAsset) => {
const monthGroup = timelineManager.getMonthGroupByAssetId(asset.id);
if (!monthGroup) {
return false;
@@ -264,9 +243,6 @@
});
</script>
<AssetGridActions {scrollToAsset} {timelineManager} {assetInteraction} bind:isShowDeleteConfirmation {onEscape}
></AssetGridActions>
{@render header?.(scrollTop)}
<!-- Right margin MUST be equal to the width of scrubber -->
@@ -352,12 +328,6 @@
</section>
</section>
<Portal target="body">
{#if $showAssetViewer}
<TimelineAssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
{/if}
</Portal>
<style>
#asset-grid {
contain: strict;