mirror of
https://github.com/immich-app/immich.git
synced 2025-09-29 15:31:13 -04:00
fix: back/forward navigation won't reset scroll in timeline
This commit is contained in:
parent
25dbb60574
commit
0a18d9c35e
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterNavigate, beforeNavigate } from '$app/navigation';
|
import { page } from '$app/state';
|
||||||
import { page } from '$app/stores';
|
|
||||||
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
||||||
import HotModuleReload from '$lib/elements/HotModuleReload.svelte';
|
import HotModuleReload from '$lib/elements/HotModuleReload.svelte';
|
||||||
import type { PhotostreamManager } from '$lib/managers/photostream-manager/PhotostreamManager.svelte';
|
import type { PhotostreamManager } from '$lib/managers/photostream-manager/PhotostreamManager.svelte';
|
||||||
@ -121,51 +120,41 @@
|
|||||||
return height;
|
return height;
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetIsVisible = (assetTop: number): boolean => {
|
export const scrollToAssetId = async (assetId: string) => {
|
||||||
if (!element) {
|
const monthGroup = await timelineManager.findSegmentForAssetId(assetId);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { clientHeight, scrollTop } = element;
|
|
||||||
return assetTop >= scrollTop && assetTop < scrollTop + clientHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const scrollToAssetId = (assetId: string) => {
|
|
||||||
const monthGroup = timelineManager.getSegmentForAssetId(assetId);
|
|
||||||
if (!monthGroup) {
|
if (!monthGroup) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const height = getAssetHeight(assetId, monthGroup);
|
const height = getAssetHeight(assetId, monthGroup);
|
||||||
|
|
||||||
// If the asset is already visible, then don't scroll.
|
|
||||||
if (assetIsVisible(height)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollTo(height);
|
scrollTo(height);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const completeNav = () => {
|
export const completeAfterNavigate = async ({ scrollToAssetQueryParam }: { scrollToAssetQueryParam: boolean }) => {
|
||||||
const scrollTarget = $gridScrollTarget?.at;
|
if (timelineManager.viewportHeight === 0 || timelineManager.viewportWidth === 0) {
|
||||||
let scrolled = false;
|
// this can happen if you do the following navigation order
|
||||||
if (scrollTarget) {
|
// /photos?at=<id>, /photos/<id>, http://example.com, browser back, browser back
|
||||||
scrolled = scrollToAssetId(scrollTarget);
|
const rect = element?.getBoundingClientRect();
|
||||||
|
if (rect) {
|
||||||
|
timelineManager.viewportHeight = rect.height;
|
||||||
|
timelineManager.viewportWidth = rect.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!scrolled) {
|
if (scrollToAssetQueryParam) {
|
||||||
// if the asset is not found, scroll to the top
|
const scrollTarget = $gridScrollTarget?.at;
|
||||||
scrollTo(0);
|
let scrolled = false;
|
||||||
|
if (scrollTarget) {
|
||||||
|
scrolled = await scrollToAssetId(scrollTarget);
|
||||||
|
}
|
||||||
|
if (!scrolled) {
|
||||||
|
// if the asset is not found, scroll to the top
|
||||||
|
scrollTo(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
showSkeleton = false;
|
showSkeleton = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeNavigate(() => (timelineManager.suspendTransitions = true));
|
|
||||||
|
|
||||||
afterNavigate((nav) => {
|
|
||||||
const { complete } = nav;
|
|
||||||
complete.then(completeNav, completeNav);
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateIsScrolling = () => (timelineManager.scrolling = true);
|
const updateIsScrolling = () => (timelineManager.scrolling = true);
|
||||||
// Yes, updateSlideWindow() is called by the onScroll event. However, if you also just scrolled
|
// Yes, updateSlideWindow() is called by the onScroll event. However, if you also just scrolled
|
||||||
// by explicitly invoking element.scrollX functions, there may be a delay with enough time to
|
// by explicitly invoking element.scrollX functions, there may be a delay with enough time to
|
||||||
@ -191,11 +180,11 @@
|
|||||||
// this handler will run the navigation/scroll-to-asset handler when hmr is performed,
|
// this handler will run the navigation/scroll-to-asset handler when hmr is performed,
|
||||||
// preventing skeleton from showing after hmr
|
// preventing skeleton from showing after hmr
|
||||||
const finishHmr = () => {
|
const finishHmr = () => {
|
||||||
const asset = $page.url.searchParams.get('at');
|
const asset = page.url.searchParams.get('at');
|
||||||
if (asset) {
|
if (asset) {
|
||||||
$gridScrollTarget = { at: asset };
|
$gridScrollTarget = { at: asset };
|
||||||
}
|
}
|
||||||
void completeNav();
|
void completeAfterNavigate({ scrollToAssetQueryParam: true });
|
||||||
};
|
};
|
||||||
const assetGridUpdate = payload.updates.some((update) => update.path.endsWith('Photostream.svelte'));
|
const assetGridUpdate = payload.updates.some((update) => update.path.endsWith('Photostream.svelte'));
|
||||||
if (assetGridUpdate) {
|
if (assetGridUpdate) {
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
enableRouting,
|
enableRouting,
|
||||||
timelineManager = $bindable(),
|
timelineManager = $bindable(),
|
||||||
|
|
||||||
showSkeleton = $bindable(true),
|
showSkeleton = true,
|
||||||
isShowDeleteConfirmation = $bindable(false),
|
isShowDeleteConfirmation = $bindable(false),
|
||||||
segment,
|
segment,
|
||||||
skeleton,
|
skeleton,
|
||||||
@ -160,7 +160,10 @@
|
|||||||
handleScrollTop?.(scrollToTop);
|
handleScrollTop?.(scrollToTop);
|
||||||
};
|
};
|
||||||
let baseTimelineViewer: Photostream | undefined = $state();
|
let baseTimelineViewer: Photostream | undefined = $state();
|
||||||
export const scrollToAsset = (asset: TimelineAsset) => baseTimelineViewer?.scrollToAssetId(asset.id) ?? false;
|
export const scrollToAsset = async (asset: TimelineAsset) =>
|
||||||
|
(await baseTimelineViewer?.scrollToAssetId(asset.id)) ?? false;
|
||||||
|
export const completeAfterNavigate = (args: { scrollToAssetQueryParam: boolean }) =>
|
||||||
|
baseTimelineViewer?.completeAfterNavigate(args);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Photostream
|
<Photostream
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { afterNavigate, beforeNavigate } from '$app/navigation';
|
||||||
|
import { page } from '$app/state';
|
||||||
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
||||||
import MonthSegment from '$lib/components/timeline/MonthSegment.svelte';
|
import MonthSegment from '$lib/components/timeline/MonthSegment.svelte';
|
||||||
import PhotostreamWithScrubber from '$lib/components/timeline/PhotostreamWithScrubber.svelte';
|
import PhotostreamWithScrubber from '$lib/components/timeline/PhotostreamWithScrubber.svelte';
|
||||||
@ -14,6 +16,7 @@
|
|||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
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';
|
||||||
|
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
|
||||||
import { getSegmentIdentifier, getTimes } from '$lib/utils/timeline-util';
|
import { getSegmentIdentifier, getTimes } 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';
|
||||||
@ -71,11 +74,51 @@
|
|||||||
customThumbnailLayout,
|
customThumbnailLayout,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let { isViewing: showAssetViewer, asset: viewingAsset } = assetViewingStore;
|
let { isViewing: showAssetViewer, asset: viewingAsset, gridScrollTarget } = assetViewingStore;
|
||||||
|
|
||||||
let viewer: PhotostreamWithScrubber | undefined = $state();
|
let viewer: PhotostreamWithScrubber | undefined = $state();
|
||||||
let showSkeleton: boolean = $state(true);
|
let showSkeleton: boolean = $state(true);
|
||||||
|
|
||||||
|
// tri-state boolean
|
||||||
|
let initialLoadWasAssetViewer: boolean | null = null;
|
||||||
|
let hasNavigatedToOrFromAssetViewer: boolean = false;
|
||||||
|
let timelineScrollPositionInitialized = false;
|
||||||
|
|
||||||
|
beforeNavigate(({ from, to }) => {
|
||||||
|
timelineManager.suspendTransitions = true;
|
||||||
|
hasNavigatedToOrFromAssetViewer = isAssetViewerRoute(to) || isAssetViewerRoute(from);
|
||||||
|
});
|
||||||
|
|
||||||
|
const completeAfterNavigate = () => {
|
||||||
|
const assetViewerPage = !!(page.route.id?.endsWith('/[[assetId=id]]') && page.params.assetId);
|
||||||
|
let isInitial = false;
|
||||||
|
// Set initial load state only once
|
||||||
|
if (initialLoadWasAssetViewer === null) {
|
||||||
|
initialLoadWasAssetViewer = assetViewerPage && !hasNavigatedToOrFromAssetViewer;
|
||||||
|
isInitial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scrollToAssetQueryParam = false;
|
||||||
|
if (
|
||||||
|
!timelineScrollPositionInitialized &&
|
||||||
|
((isInitial && !assetViewerPage) || // Direct timeline load
|
||||||
|
(!isInitial && hasNavigatedToOrFromAssetViewer)) // Navigated from asset viewer
|
||||||
|
) {
|
||||||
|
scrollToAssetQueryParam = true;
|
||||||
|
timelineScrollPositionInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewer?.completeAfterNavigate({ scrollToAssetQueryParam });
|
||||||
|
};
|
||||||
|
afterNavigate(({ complete }) => void complete.then(completeAfterNavigate, completeAfterNavigate));
|
||||||
|
|
||||||
|
const onViewerClose = async (asset: { id: string }) => {
|
||||||
|
assetViewingStore.showAssetViewer(false);
|
||||||
|
showSkeleton = true;
|
||||||
|
$gridScrollTarget = { at: asset.id };
|
||||||
|
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
|
||||||
|
};
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($showAssetViewer) {
|
if ($showAssetViewer) {
|
||||||
const { localDateTime } = getTimes($viewingAsset.fileCreatedAt, DateTime.local().offset / 60);
|
const { localDateTime } = getTimes($viewingAsset.fileCreatedAt, DateTime.local().offset / 60);
|
||||||
@ -85,7 +128,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TimelineKeyboardActions
|
<TimelineKeyboardActions
|
||||||
scrollToAsset={(asset) => viewer?.scrollToAsset(asset) ?? false}
|
scrollToAsset={async (asset) => (await viewer?.scrollToAsset(asset)) ?? Promise.resolve(false)}
|
||||||
{timelineManager}
|
{timelineManager}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
bind:isShowDeleteConfirmation
|
bind:isShowDeleteConfirmation
|
||||||
@ -165,6 +208,6 @@
|
|||||||
|
|
||||||
<Portal target="body">
|
<Portal target="body">
|
||||||
{#if $showAssetViewer}
|
{#if $showAssetViewer}
|
||||||
<TimelineAssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
|
<TimelineAssetViewer {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} {onViewerClose} />
|
||||||
{/if}
|
{/if}
|
||||||
</Portal>
|
</Portal>
|
||||||
|
@ -9,15 +9,16 @@
|
|||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
import { getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
import { getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
let { asset: viewingAsset, gridScrollTarget, mutex, preloadAssets } = assetViewingStore;
|
let { asset: viewingAsset, mutex, preloadAssets } = assetViewingStore;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
timelineManager: TimelineManager;
|
timelineManager: TimelineManager;
|
||||||
showSkeleton: boolean;
|
|
||||||
withStacked?: boolean;
|
withStacked?: boolean;
|
||||||
isShared?: boolean;
|
isShared?: boolean;
|
||||||
album?: AlbumResponseDto | null;
|
album?: AlbumResponseDto | null;
|
||||||
person?: PersonResponseDto | null;
|
person?: PersonResponseDto | null;
|
||||||
|
onViewerClose?: (asset: { id: string }) => Promise<void>;
|
||||||
|
|
||||||
removeAction?:
|
removeAction?:
|
||||||
| AssetAction.UNARCHIVE
|
| AssetAction.UNARCHIVE
|
||||||
@ -30,12 +31,12 @@
|
|||||||
|
|
||||||
let {
|
let {
|
||||||
timelineManager,
|
timelineManager,
|
||||||
showSkeleton = $bindable(false),
|
|
||||||
removeAction,
|
removeAction,
|
||||||
withStacked = false,
|
withStacked = false,
|
||||||
isShared = false,
|
isShared = false,
|
||||||
album = null,
|
album = null,
|
||||||
person = null,
|
person = null,
|
||||||
|
onViewerClose = () => Promise.resolve(void 0),
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const handlePrevious = async () => {
|
const handlePrevious = async () => {
|
||||||
@ -79,13 +80,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = async (asset: { id: string }) => {
|
|
||||||
assetViewingStore.showAssetViewer(false);
|
|
||||||
showSkeleton = true;
|
|
||||||
$gridScrollTarget = { at: asset.id };
|
|
||||||
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePreAction = async (action: Action) => {
|
const handlePreAction = async (action: Action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case removeAction:
|
case removeAction:
|
||||||
@ -97,7 +91,7 @@
|
|||||||
case AssetAction.SET_VISIBILITY_TIMELINE: {
|
case AssetAction.SET_VISIBILITY_TIMELINE: {
|
||||||
// find the next asset to show or close the viewer
|
// find the next asset to show or close the viewer
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
|
(await handleNext()) || (await handlePrevious()) || (await onViewerClose?.(action.asset));
|
||||||
|
|
||||||
// delete after find the next one
|
// delete after find the next one
|
||||||
timelineManager.removeAssets([action.asset.id]);
|
timelineManager.removeAssets([action.asset.id]);
|
||||||
@ -172,6 +166,6 @@
|
|||||||
onPrevious={handlePrevious}
|
onPrevious={handlePrevious}
|
||||||
onNext={handleNext}
|
onNext={handleNext}
|
||||||
onRandom={handleRandom}
|
onRandom={handleRandom}
|
||||||
onClose={handleClose}
|
onClose={onViewerClose}
|
||||||
/>
|
/>
|
||||||
{/await}
|
{/await}
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
type RelativeResult,
|
type RelativeResult,
|
||||||
} from '$lib/components/shared-components/change-date.svelte';
|
} from '$lib/components/shared-components/change-date.svelte';
|
||||||
import {
|
import {
|
||||||
setFocusToAsset as setFocusAssetInit,
|
setFocusToAsset as setFocusAssetUtil,
|
||||||
setFocusTo as setFocusToInit,
|
setFocusTo as setFocusToUtil,
|
||||||
|
type FocusDirection,
|
||||||
|
type FocusInterval,
|
||||||
} from '$lib/components/timeline/actions/focus-actions';
|
} from '$lib/components/timeline/actions/focus-actions';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
@ -32,7 +34,7 @@
|
|||||||
assetInteraction: AssetInteraction;
|
assetInteraction: AssetInteraction;
|
||||||
isShowDeleteConfirmation: boolean;
|
isShowDeleteConfirmation: boolean;
|
||||||
onEscape?: () => void;
|
onEscape?: () => void;
|
||||||
scrollToAsset: (asset: TimelineAsset) => boolean;
|
scrollToAsset: (asset: TimelineAsset) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@ -147,8 +149,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const setFocusTo = setFocusToInit.bind(undefined, scrollToAsset, timelineManager);
|
const setFocusTo = (direction: FocusDirection, interval: FocusInterval) =>
|
||||||
const setFocusAsset = setFocusAssetInit.bind(undefined, scrollToAsset);
|
setFocusToUtil(scrollToAsset, timelineManager, direction, interval);
|
||||||
|
|
||||||
|
const setFocusAsset = (asset: TimelineAsset) => setFocusAssetUtil(scrollToAsset, asset);
|
||||||
|
|
||||||
let shortcutList = $derived(
|
let shortcutList = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
@ -212,7 +216,7 @@
|
|||||||
(DateTime.fromISO(dateString.date) as DateTime<true>).toObject(),
|
(DateTime.fromISO(dateString.date) as DateTime<true>).toObject(),
|
||||||
);
|
);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
setFocusAsset(asset);
|
void setFocusAsset(asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -21,19 +21,26 @@ export const focusPreviousAsset = () =>
|
|||||||
|
|
||||||
const queryHTMLElement = (query: string) => document.querySelector(query) as HTMLElement;
|
const queryHTMLElement = (query: string) => document.querySelector(query) as HTMLElement;
|
||||||
|
|
||||||
export const setFocusToAsset = (scrollToAsset: (asset: TimelineAsset) => boolean, asset: TimelineAsset) => {
|
export const setFocusToAsset = async (
|
||||||
const scrolled = scrollToAsset(asset);
|
scrollToAsset: (asset: TimelineAsset) => Promise<boolean>,
|
||||||
|
asset: TimelineAsset,
|
||||||
|
) => {
|
||||||
|
const scrolled = await scrollToAsset(asset);
|
||||||
if (scrolled) {
|
if (scrolled) {
|
||||||
const element = queryHTMLElement(`[data-thumbnail-focus-container][data-asset="${asset.id}"]`);
|
const element = queryHTMLElement(`[data-thumbnail-focus-container][data-asset="${asset.id}"]`);
|
||||||
element?.focus();
|
element?.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FocusDirection = 'earlier' | 'later';
|
||||||
|
|
||||||
|
export type FocusInterval = 'day' | 'month' | 'year' | 'asset';
|
||||||
|
|
||||||
export const setFocusTo = async (
|
export const setFocusTo = async (
|
||||||
scrollToAsset: (asset: TimelineAsset) => boolean,
|
scrollToAsset: (asset: TimelineAsset) => Promise<boolean>,
|
||||||
store: TimelineManager,
|
store: TimelineManager,
|
||||||
direction: 'earlier' | 'later',
|
direction: FocusDirection,
|
||||||
interval: 'day' | 'month' | 'year' | 'asset',
|
interval: FocusInterval,
|
||||||
) => {
|
) => {
|
||||||
if (tracker.isActive()) {
|
if (tracker.isActive()) {
|
||||||
// there are unfinished running invocations, so return early
|
// there are unfinished running invocations, so return early
|
||||||
@ -65,7 +72,10 @@ export const setFocusTo = async (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrolled = scrollToAsset(asset);
|
const scrolled = await scrollToAsset(asset);
|
||||||
|
if (!invocation.isStillValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (scrolled) {
|
if (scrolled) {
|
||||||
await tick();
|
await tick();
|
||||||
if (!invocation.isStillValid()) {
|
if (!invocation.isStillValid()) {
|
||||||
|
@ -269,13 +269,14 @@ export abstract class PhotostreamManager {
|
|||||||
return this.months.find((segment) => identifier.matches(segment));
|
return this.months.find((segment) => identifier.matches(segment));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSegmentForAssetId(assetId: string) {
|
findSegmentForAssetId(assetId: string): Promise<PhotostreamSegment | undefined> {
|
||||||
for (const month of this.months) {
|
for (const month of this.months) {
|
||||||
const asset = month.assets.find((asset) => asset.id === assetId);
|
const asset = month.assets.find((asset) => asset.id === assetId);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
return month;
|
return Promise.resolve(month);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Promise.resolve(void 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshLayout() {
|
refreshLayout() {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { sdkMock } from '$lib/__mocks__/sdk.mock';
|
import { sdkMock } from '$lib/__mocks__/sdk.mock';
|
||||||
import { getMonthGroupByDate } from '$lib/managers/timeline-manager/internal/search-support.svelte';
|
import {
|
||||||
|
findMonthGroupForAsset,
|
||||||
|
getMonthGroupByDate,
|
||||||
|
} from '$lib/managers/timeline-manager/internal/search-support.svelte';
|
||||||
import { AbortError } from '$lib/utils';
|
import { AbortError } from '$lib/utils';
|
||||||
import { fromISODateTimeUTCToObject, getSegmentIdentifier } from '$lib/utils/timeline-util';
|
import { fromISODateTimeUTCToObject, getSegmentIdentifier } from '$lib/utils/timeline-util';
|
||||||
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
||||||
@ -556,10 +559,10 @@ describe('TimelineManager', () => {
|
|||||||
);
|
);
|
||||||
timelineManager.addAssets([assetOne, assetTwo]);
|
timelineManager.addAssets([assetOne, assetTwo]);
|
||||||
|
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetTwo.id)?.yearMonth.year).toEqual(2024);
|
expect(findMonthGroupForAsset(timelineManager, assetTwo.id)?.monthGroup.yearMonth.year).toEqual(2024);
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetTwo.id)?.yearMonth.month).toEqual(2);
|
expect(findMonthGroupForAsset(timelineManager, assetTwo.id)?.monthGroup.yearMonth.month).toEqual(2);
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.year).toEqual(2024);
|
expect(findMonthGroupForAsset(timelineManager, assetOne.id)?.monthGroup.yearMonth.year).toEqual(2024);
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.month).toEqual(1);
|
expect(findMonthGroupForAsset(timelineManager, assetOne.id)?.monthGroup.yearMonth.month).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores removed months', () => {
|
it('ignores removed months', () => {
|
||||||
@ -576,8 +579,8 @@ describe('TimelineManager', () => {
|
|||||||
timelineManager.addAssets([assetOne, assetTwo]);
|
timelineManager.addAssets([assetOne, assetTwo]);
|
||||||
|
|
||||||
timelineManager.removeAssets([assetTwo.id]);
|
timelineManager.removeAssets([assetTwo.id]);
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.year).toEqual(2024);
|
expect(findMonthGroupForAsset(timelineManager, assetOne.id)?.monthGroup.yearMonth.year).toEqual(2024);
|
||||||
expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.month).toEqual(1);
|
expect(findMonthGroupForAsset(timelineManager, assetOne.id)?.monthGroup.yearMonth.month).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -187,7 +187,7 @@ export class TimelineManager extends PhotostreamManager {
|
|||||||
addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc });
|
addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc });
|
||||||
}
|
}
|
||||||
|
|
||||||
async findMonthGroupForAsset(id: string) {
|
async findSegmentForAssetId(id: string) {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
await this.initTask.waitUntilCompletion();
|
await this.initTask.waitUntilCompletion();
|
||||||
}
|
}
|
||||||
@ -218,11 +218,6 @@ export class TimelineManager extends PhotostreamManager {
|
|||||||
return getMonthGroupByDate(this, yearMonth);
|
return getMonthGroupByDate(this, yearMonth);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMonthGroupByAssetId(assetId: string) {
|
|
||||||
const monthGroupInfo = findMonthGroupForAssetUtil(this, assetId);
|
|
||||||
return monthGroupInfo?.monthGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getRandomMonthGroup() {
|
async getRandomMonthGroup() {
|
||||||
const random = Math.floor(Math.random() * this.months.length);
|
const random = Math.floor(Math.random() * this.months.length);
|
||||||
const month = this.months[random];
|
const month = this.months[random];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user