mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
refactor: timeline manager renames (#19007)
* refactor: timeline manager renames * refactor(web): improve timeline manager naming consistency - Rename AddContext → GroupInsertionCache for clearer purpose - Rename TimelineDay → DayGroup for better clarity - Rename TimelineMonth → MonthGroup for better clarity - Replace all "bucket" references with "monthGroup" terminology - Update all component props, method names, and variable references - Maintain consistent naming patterns across TypeScript and Svelte files * refactor(web): rename buckets to months in timeline manager - Rename TimelineManager.buckets property to months - Update all store.buckets references to store.months - Use 'month' shorthand for monthGroup arguments (not method names) - Update component templates and test files for consistency - Maintain API-related 'bucket' terminology (bucketHeight, getTimeBucket) * refactor(web): rename assetStore to timelineManager and update types - Rename assetStore variables to timelineManager in all .svelte files - Update parameter names in actions.ts and asset-utils.ts functions - Rename AssetStoreLayoutOptions to TimelineManagerLayoutOptions - Rename AssetStoreOptions to TimelineManagerOptions - Move assets-store.spec.ts to timeline-manager.spec.ts * refactor(web): rename intersectingAssets to viewerAssets and fix property references - Rename intersectingAssets to viewerAssets in DayGroup and MonthGroup classes - Update arrow function parameters to use viewerAsset/viewAsset shorthand - Rename topIntersectingBucket to topIntersectingMonthGroup - Fix dateGroups references to dayGroups in asset-utils.ts and album page - Update template loops and variable names in Svelte components * refactor(web): rename #initializeTimeBuckets to #initializeMonthGroups and bucketDateFormatted to monthGroupTitle * refactor(web): rename monthGroupHeight to height * refactor(web): rename bucketCount to assetsCount, bucketsIterator to monthGroupIterator, and related properties * refactor(web): rename count to assetCount in TimelineManager * refactor(web): rename LiteBucket to ScrubberMonth and update scrubber variables - Rename LiteBucket type to ScrubberMonth - Rename bucketDateFormattted to title in ScrubberMonth type - Rename bucketPercentY to monthGroupPercentY in scrubber component - Rename scrubBucket to scrubberMonth and scrubBucketPercent to scrubberMonthPercent * fix remaining refs to bucket * reset submodule to correct commit * reset submodule to correct commit * refactor(web): extract TimelineManager internals into separate modules - Move search-related functions to internal/search-support.svelte.ts - Extract websocket event handling into WebsocketSupport class - Move utility functions (updateObject, isMismatched) to internal/utils.svelte.ts - Update imports in tests to use new module structure * refactor(web): extract intersection logic from TimelineManager - Create intersection-support.svelte.ts with updateIntersection and calculateIntersecting functions - Remove private intersection methods from TimelineManager - Export findMonthGroupForAsset from search-support for reuse - Update TimelineManager to use the extracted intersection functions * refactor(web): rename a few methods in intersecting * refactor(web): rename a few methods in intersecting * refactor(web): extract layout logic from TimelineManager - Create layout-support.svelte.ts with updateGeometry and layoutMonthGroup functions - Remove private layout methods from TimelineManager - Update TimelineManager to use the extracted layout functions - Remove unused UpdateGeometryOptions import * refactor(web): extract asset operations from TimelineManager - Create operations-support.svelte.ts with addAssetsToMonthGroups and runAssetOperation functions - Remove private asset operation methods from TimelineManager - Update TimelineManager to use extracted operation functions with proper AssetOrder handling - Rename getMonthGroupIndexByAssetId to getMonthGroupByAssetId for consistency - Move utility functions from utils.svelte.ts to internal/utils.svelte.ts - Fix method name references in asset-grid.svelte and tests * refactor(web): extract loading logic from TimelineManager - Create load-support.svelte.ts with loadFromTimeBuckets function - Extract time bucket loading, album asset handling, and error logging - Simplify TimelineManager's loadMonthGroup method to use extracted function * refresh timeline after archive keyboard shortcut * remove debugger * rename * Review comments - remove shadowed var * reduce indents - early return * review comment * refactor: simplify asset filtering in addAssets method Replace for loop with filter operation for better readability * fix: bad merge * refactor(web): simplify timeline layout algorithm - Replace rowSpaceRemaining array with direct cumulative width tracking - Invert logic from tracking remaining space to tracking used space - Fix spelling: cummulative to cumulative - Rename lastRowHeight to currentRowHeight for clarity - Remove confusing lastRow variable and simplify final height calculation - Add explanatory comments for clarity - Rename loop variable assetGroup to dayGroup for consistency * simplify assetsIterator usage * merge/lint --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
6499057b4c
commit
4b4ee5abf3
39 changed files with 2288 additions and 2139 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AssetStore } from '$lib/managers/timeline-manager/asset-store.svelte';
|
||||
import type { LiteBucket } from '$lib/managers/timeline-manager/types';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import type { ScrubberMonth } from '$lib/managers/timeline-manager/types';
|
||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||
import { getTabbable } from '$lib/utils/focus-util';
|
||||
import { type ScrubberListener } from '$lib/utils/timeline-util';
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
timelineTopOffset?: number;
|
||||
timelineBottomOffset?: number;
|
||||
height?: number;
|
||||
assetStore: AssetStore;
|
||||
timelineManager: TimelineManager;
|
||||
scrubOverallPercent?: number;
|
||||
scrubBucketPercent?: number;
|
||||
scrubBucket?: { year: number; month: number };
|
||||
scrubberMonthPercent?: number;
|
||||
scrubberMonth?: { year: number; month: number };
|
||||
leadout?: boolean;
|
||||
scrubberWidth?: number;
|
||||
onScrub?: ScrubberListener;
|
||||
|
|
@ -30,10 +30,10 @@
|
|||
timelineTopOffset = 0,
|
||||
timelineBottomOffset = 0,
|
||||
height = 0,
|
||||
assetStore,
|
||||
timelineManager,
|
||||
scrubOverallPercent = 0,
|
||||
scrubBucketPercent = 0,
|
||||
scrubBucket = undefined,
|
||||
scrubberMonthPercent = 0,
|
||||
scrubberMonth = undefined,
|
||||
leadout = false,
|
||||
onScrub = undefined,
|
||||
onScrubKeyDown = undefined,
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
return '100vw';
|
||||
}
|
||||
if (usingMobileDevice) {
|
||||
if (assetStore.scrolling) {
|
||||
if (timelineManager.scrolling) {
|
||||
return MOBILE_WIDTH + 'px';
|
||||
}
|
||||
return '0px';
|
||||
|
|
@ -80,24 +80,24 @@
|
|||
scrubberWidth = usingMobileDevice ? MOBILE_WIDTH : DESKTOP_WIDTH;
|
||||
});
|
||||
|
||||
const toScrollFromBucketPercentage = (
|
||||
scrubBucket: { year: number; month: number } | undefined,
|
||||
scrubBucketPercent: number,
|
||||
const toScrollFromMonthGroupPercentage = (
|
||||
scrubberMonth: { year: number; month: number } | undefined,
|
||||
scrubberMonthPercent: number,
|
||||
scrubOverallPercent: number,
|
||||
) => {
|
||||
if (scrubBucket) {
|
||||
if (scrubberMonth) {
|
||||
let offset = relativeTopOffset;
|
||||
let match = false;
|
||||
for (const segment of segments) {
|
||||
if (segment.month === scrubBucket.month && segment.year === scrubBucket.year) {
|
||||
offset += scrubBucketPercent * segment.height;
|
||||
if (segment.month === scrubberMonth.month && segment.year === scrubberMonth.year) {
|
||||
offset += scrubberMonthPercent * segment.height;
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
offset += segment.height;
|
||||
}
|
||||
if (!match) {
|
||||
offset += scrubBucketPercent * relativeBottomOffset;
|
||||
offset += scrubberMonthPercent * relativeBottomOffset;
|
||||
}
|
||||
return offset;
|
||||
} else if (leadout) {
|
||||
|
|
@ -111,8 +111,8 @@
|
|||
return scrubOverallPercent * (height - (PADDING_TOP + PADDING_BOTTOM));
|
||||
}
|
||||
};
|
||||
let scrollY = $derived(toScrollFromBucketPercentage(scrubBucket, scrubBucketPercent, scrubOverallPercent));
|
||||
let timelineFullHeight = $derived(assetStore.scrubberTimelineHeight + timelineTopOffset + timelineBottomOffset);
|
||||
let scrollY = $derived(toScrollFromMonthGroupPercentage(scrubberMonth, scrubberMonthPercent, scrubOverallPercent));
|
||||
let timelineFullHeight = $derived(timelineManager.scrubberTimelineHeight + timelineTopOffset + timelineBottomOffset);
|
||||
let relativeTopOffset = $derived(toScrollY(timelineTopOffset / timelineFullHeight));
|
||||
let relativeBottomOffset = $derived(toScrollY(timelineBottomOffset / timelineFullHeight));
|
||||
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
hasDot: boolean;
|
||||
};
|
||||
|
||||
const calculateSegments = (buckets: LiteBucket[]) => {
|
||||
const calculateSegments = (months: ScrubberMonth[]) => {
|
||||
let height = 0;
|
||||
let dotHeight = 0;
|
||||
|
||||
|
|
@ -134,16 +134,16 @@
|
|||
let previousLabeledSegment: Segment | undefined;
|
||||
|
||||
let top = 0;
|
||||
for (const [i, bucket] of buckets.entries()) {
|
||||
const scrollBarPercentage = bucket.bucketHeight / timelineFullHeight;
|
||||
for (const [i, scrubMonth] of months.entries()) {
|
||||
const scrollBarPercentage = scrubMonth.height / timelineFullHeight;
|
||||
|
||||
const segment = {
|
||||
top,
|
||||
count: bucket.assetCount,
|
||||
count: scrubMonth.assetCount,
|
||||
height: toScrollY(scrollBarPercentage),
|
||||
dateFormatted: bucket.bucketDateFormattted,
|
||||
year: bucket.year,
|
||||
month: bucket.month,
|
||||
dateFormatted: scrubMonth.title,
|
||||
year: scrubMonth.year,
|
||||
month: scrubMonth.month,
|
||||
hasLabel: false,
|
||||
hasDot: false,
|
||||
};
|
||||
|
|
@ -172,7 +172,7 @@
|
|||
return segments;
|
||||
};
|
||||
let activeSegment: HTMLElement | undefined = $state();
|
||||
const segments = $derived(calculateSegments(assetStore.scrubberBuckets));
|
||||
const segments = $derived(calculateSegments(timelineManager.scrubberMonths));
|
||||
const hoverLabel = $derived.by(() => {
|
||||
if (isHoverOnPaddingTop) {
|
||||
return segments.at(0)?.dateFormatted;
|
||||
|
|
@ -182,11 +182,11 @@
|
|||
}
|
||||
return activeSegment?.dataset.label;
|
||||
});
|
||||
const bucketDate = $derived.by(() => {
|
||||
if (!activeSegment?.dataset.timeSegmentBucketDate) {
|
||||
const segmentDate = $derived.by(() => {
|
||||
if (!activeSegment?.dataset.segmentYearMonth) {
|
||||
return undefined;
|
||||
}
|
||||
const [year, month] = activeSegment.dataset.timeSegmentBucketDate.split('-').map(Number);
|
||||
const [year, month] = activeSegment.dataset.segmentYearMonth.split('-').map(Number);
|
||||
return { year, month };
|
||||
});
|
||||
const scrollSegment = $derived.by(() => {
|
||||
|
|
@ -241,17 +241,17 @@
|
|||
const boundingClientRect = bestElement.boundingClientRect;
|
||||
const sy = boundingClientRect.y;
|
||||
const relativeY = y - sy;
|
||||
const bucketPercentY = relativeY / boundingClientRect.height;
|
||||
const monthGroupPercentY = relativeY / boundingClientRect.height;
|
||||
return {
|
||||
isOnPaddingTop: false,
|
||||
isOnPaddingBottom: false,
|
||||
segment,
|
||||
bucketPercentY,
|
||||
monthGroupPercentY,
|
||||
};
|
||||
}
|
||||
|
||||
// check if padding
|
||||
const bar = findElementBestY(elements, 0, 'immich-scrubbable-scrollbar');
|
||||
const bar = findElementBestY(elements, 0, 'scrubber');
|
||||
let isOnPaddingTop = false;
|
||||
let isOnPaddingBottom = false;
|
||||
|
||||
|
|
@ -269,7 +269,7 @@
|
|||
isOnPaddingTop,
|
||||
isOnPaddingBottom,
|
||||
segment: undefined,
|
||||
bucketPercentY: 0,
|
||||
monthGroupPercentY: 0,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -288,19 +288,19 @@
|
|||
const upper = rect?.height - (PADDING_TOP + PADDING_BOTTOM);
|
||||
hoverY = clamp(clientY - rect?.top - PADDING_TOP, lower, upper);
|
||||
const x = rect!.left + rect!.width / 2;
|
||||
const { segment, bucketPercentY, isOnPaddingTop, isOnPaddingBottom } = getActive(x, clientY);
|
||||
const { segment, monthGroupPercentY, isOnPaddingTop, isOnPaddingBottom } = getActive(x, clientY);
|
||||
activeSegment = segment;
|
||||
isHoverOnPaddingTop = isOnPaddingTop;
|
||||
isHoverOnPaddingBottom = isOnPaddingBottom;
|
||||
|
||||
const scrollPercent = toTimelineY(hoverY);
|
||||
if (wasDragging === false && isDragging) {
|
||||
void startScrub?.(bucketDate!, scrollPercent, bucketPercentY);
|
||||
void onScrub?.(bucketDate!, scrollPercent, bucketPercentY);
|
||||
void startScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
||||
void onScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
||||
}
|
||||
|
||||
if (wasDragging && !isDragging) {
|
||||
void stopScrub?.(bucketDate!, scrollPercent, bucketPercentY);
|
||||
void stopScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +308,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
void onScrub?.(bucketDate!, scrollPercent, bucketPercentY);
|
||||
void onScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
||||
};
|
||||
const getTouch = (event: TouchEvent) => {
|
||||
if (event.touches.length === 1) {
|
||||
|
|
@ -324,7 +324,7 @@
|
|||
}
|
||||
const elements = document.elementsFromPoint(touch.clientX, touch.clientY);
|
||||
const isHoverScrollbar =
|
||||
findElementBestY(elements, 0, 'immich-scrubbable-scrollbar', 'time-label', 'lead-in', 'lead-out') !== undefined;
|
||||
findElementBestY(elements, 0, 'scrubber', 'time-label', 'lead-in', 'lead-out') !== undefined;
|
||||
|
||||
isHover = isHoverScrollbar;
|
||||
|
||||
|
|
@ -451,7 +451,7 @@
|
|||
aria-valuenow={scrollY + PADDING_TOP}
|
||||
aria-valuemax={toScrollY(1)}
|
||||
aria-valuemin={toScrollY(0)}
|
||||
data-id="immich-scrubbable-scrollbar"
|
||||
data-id="scrubber"
|
||||
class="absolute end-0 z-1 select-none hover:cursor-row-resize"
|
||||
style:padding-top={PADDING_TOP + 'px'}
|
||||
style:padding-bottom={PADDING_BOTTOM + 'px'}
|
||||
|
|
@ -477,7 +477,7 @@
|
|||
{hoverLabel}
|
||||
</div>
|
||||
{/if}
|
||||
{#if usingMobileDevice && ((assetStore.scrolling && scrollHoverLabel) || isHover || isDragging)}
|
||||
{#if usingMobileDevice && ((timelineManager.scrolling && scrollHoverLabel) || isHover || isDragging)}
|
||||
<div
|
||||
id="time-label"
|
||||
class="rounded-s-full w-[32px] ps-2 text-white bg-immich-primary dark:bg-gray-600 hover:cursor-pointer select-none"
|
||||
|
|
@ -490,7 +490,7 @@
|
|||
>
|
||||
<Icon path={mdiPlay} size="20" class="-rotate-90 relative top-[9px] -end-[2px]" />
|
||||
<Icon path={mdiPlay} size="20" class="rotate-90 relative top-px -end-[2px]" />
|
||||
{#if (assetStore.scrolling && scrollHoverLabel) || isHover || isDragging}
|
||||
{#if (timelineManager.scrolling && scrollHoverLabel) || isHover || isDragging}
|
||||
<p
|
||||
transition:fade={{ duration: 200 }}
|
||||
style:bottom={50 / 2 - 30 / 2 + 'px'}
|
||||
|
|
@ -509,7 +509,7 @@
|
|||
class="absolute end-0 h-[2px] w-10 bg-immich-primary dark:bg-immich-dark-primary"
|
||||
style:top="{scrollY + PADDING_TOP - 2}px"
|
||||
>
|
||||
{#if assetStore.scrolling && scrollHoverLabel && !isHover}
|
||||
{#if timelineManager.scrolling && scrollHoverLabel && !isHover}
|
||||
<p
|
||||
transition:fade={{ duration: 200 }}
|
||||
class="truncate pointer-events-none absolute end-0 bottom-0 min-w-20 max-w-64 w-fit rounded-tl-md border-b-2 border-immich-primary bg-subtle/90 z-1 py-1 px-1 text-sm font-medium shadow-[0_0_8px_rgba(0,0,0,0.25)] dark:border-immich-dark-primary dark:text-immich-dark-fg"
|
||||
|
|
@ -523,7 +523,7 @@
|
|||
class="relative"
|
||||
style:height={relativeTopOffset + 'px'}
|
||||
data-id="lead-in"
|
||||
data-time-segment-bucket-date={segments.at(0)?.year + '-' + segments.at(0)?.month}
|
||||
data-segment-year-month={segments.at(0)?.year + '-' + segments.at(0)?.month}
|
||||
data-label={segments.at(0)?.dateFormatted}
|
||||
>
|
||||
{#if relativeTopOffset > 6}
|
||||
|
|
@ -535,7 +535,7 @@
|
|||
<div
|
||||
class="relative"
|
||||
data-id="time-segment"
|
||||
data-time-segment-bucket-date={segment.year + '-' + segment.month}
|
||||
data-segment-year-month={segment.year + '-' + segment.month}
|
||||
data-label={segment.dateFormatted}
|
||||
style:height={segment.height + 'px'}
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue