fix: navigate to time action

This commit is contained in:
midzelis 2025-08-14 20:06:22 +00:00
parent ba0cfb76ed
commit d5cc017bba
7 changed files with 70 additions and 17 deletions

View File

@ -1360,6 +1360,8 @@
"my_albums": "My albums",
"name": "Name",
"name_or_nickname": "Name or nickname",
"navigate": "Navigate",
"navigate_to_time": "Navigate to Time",
"network_requirement_photos_upload": "Use cellular data to backup photos",
"network_requirement_videos_upload": "Use cellular data to backup videos",
"network_requirements": "Network Requirements",

View File

@ -8,6 +8,9 @@ import ChangeDate from './change-date.svelte';
describe('ChangeDate component', () => {
const initialDate = DateTime.fromISO('2024-01-01');
const initialTimeZone = 'Europe/Berlin';
const targetDate = DateTime.fromISO('2024-01-01').setZone('UTC+1', {
keepLocalTime: true,
});
const currentInterval = {
start: DateTime.fromISO('2000-02-01T14:00:00+01:00'),
end: DateTime.fromISO('2001-02-01T14:00:00+01:00'),
@ -50,7 +53,11 @@ describe('ChangeDate component', () => {
await fireEvent.click(getConfirmButton());
expect(onConfirm).toHaveBeenCalledWith({ mode: 'absolute', date: '2024-01-01T00:00:00.000+01:00' });
expect(onConfirm).toHaveBeenCalledWith({
mode: 'absolute',
date: '2024-01-01T00:00:00.000+01:00',
dateTime: targetDate,
});
});
test('calls onCancel on cancel', async () => {
@ -65,7 +72,9 @@ describe('ChangeDate component', () => {
describe('when date is in daylight saving time', () => {
const dstDate = DateTime.fromISO('2024-07-01');
const targetDate = DateTime.fromISO('2024-07-01').setZone('UTC+2', {
keepLocalTime: true,
});
test('should render correct timezone with offset', () => {
render(ChangeDate, { initialDate: dstDate, initialTimeZone, onCancel, onConfirm });
@ -79,7 +88,11 @@ describe('ChangeDate component', () => {
await fireEvent.click(getConfirmButton());
expect(onConfirm).toHaveBeenCalledWith({ mode: 'absolute', date: '2024-07-01T00:00:00.000+02:00' });
expect(onConfirm).toHaveBeenCalledWith({
mode: 'absolute',
date: '2024-07-01T00:00:00.000+02:00',
dateTime: targetDate,
});
});
});

View File

@ -4,7 +4,7 @@
import { locale } from '$lib/stores/preferences.store';
import { getDateTimeOffsetLocaleString } from '$lib/utils/timeline-util.js';
import { ConfirmModal, Field, Switch } from '@immich/ui';
import { mdiCalendarEditOutline } from '@mdi/js';
import { mdiCalendarEdit } from '@mdi/js';
import { DateTime, Duration } from 'luxon';
import { t } from 'svelte-i18n';
import { get } from 'svelte/store';
@ -17,6 +17,8 @@
timezoneInput?: boolean;
withDuration?: boolean;
currentInterval?: { start: DateTime; end: DateTime };
icon?: string;
confirmText?: string;
onCancel: () => void;
onConfirm: (result: AbsoluteResult | RelativeResult) => void;
}
@ -28,6 +30,8 @@
timezoneInput = true,
withDuration = true,
currentInterval = undefined,
icon = mdiCalendarEdit,
confirmText,
onCancel,
onConfirm,
}: Props = $props();
@ -35,6 +39,7 @@
export type AbsoluteResult = {
mode: 'absolute';
date: string;
dateTime: DateTime<true>;
};
export type RelativeResult = {
@ -191,9 +196,15 @@
const fixedOffsetZone = `UTC${offsetMinutes >= 0 ? '+' : ''}${Duration.fromObject({ minutes: offsetMinutes }).toFormat('hh:mm')}`;
// Create a DateTime object in this fixed-offset zone, preserving the local time.
const finalDateTime = DateTime.fromObject(dtComponents.toObject(), { zone: fixedOffsetZone });
const fixedOffsetDateTime = DateTime.fromObject(dtComponents.toObject(), {
zone: fixedOffsetZone,
}) as DateTime<true>;
onConfirm({ mode: 'absolute', date: finalDateTime.toISO({ includeOffset: true })! });
onConfirm({
mode: 'absolute',
date: fixedOffsetDateTime.toISO({ includeOffset: true })!,
dateTime: fixedOffsetDateTime,
});
}
if (showRelative && (selectedDuration || selectedRelativeOption)) {
@ -237,7 +248,8 @@
<ConfirmModal
confirmColor="primary"
{title}
icon={mdiCalendarEditOutline}
{icon}
{confirmText}
prompt="Please select a new date:"
disabled={!date.isValid}
onClose={(confirmed) => (confirmed ? handleConfirm() : onCancel())}

View File

@ -833,15 +833,14 @@
title="Navigate to Time"
initialDate={DateTime.now()}
timezoneInput={false}
onConfirm={async (dateString: AbsoluteResult | RelativeResult) => {
onConfirm={async (result: AbsoluteResult | RelativeResult) => {
isShowSelectDate = false;
if (dateString.mode == 'absolute') {
const asset = await timelineManager.getClosestAssetToDate(
(DateTime.fromISO(dateString.date) as DateTime<true>).toObject(),
);
if (asset) {
setFocusAsset(asset);
}
if (result.mode !== 'absolute') {
return;
}
const asset = await timelineManager.getClosestAssetToDate(result.dateTime.toObject());
if (asset) {
setFocusAsset(asset);
}
}}
onCancel={() => (isShowSelectDate = false)}

View File

@ -143,3 +143,24 @@ export function findMonthGroupForDate(timelineManager: TimelineManager, targetYe
}
}
}
export function findClosestGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelineYearMonth) {
let closestMonth: MonthGroup | undefined;
let minDifference = Number.MAX_SAFE_INTEGER;
for (const month of timelineManager.months) {
const { year, month: monthNum } = month.yearMonth;
// Calculate the absolute difference in months
const yearDiff = Math.abs(year - targetYearMonth.year);
const monthDiff = Math.abs(monthNum - targetYearMonth.month);
const totalDiff = yearDiff * 12 + monthDiff;
if (totalDiff < minDifference) {
minDifference = totalDiff;
closestMonth = month;
}
}
return closestMonth;
}

View File

@ -16,6 +16,7 @@ import {
runAssetOperation,
} from '$lib/managers/timeline-manager/internal/operations-support.svelte';
import {
findClosestGroupForDate,
findMonthGroupForAsset as findMonthGroupForAssetUtil,
findMonthGroupForDate,
getAssetWithOffset,
@ -523,9 +524,13 @@ export class TimelineManager {
}
async getClosestAssetToDate(dateTime: TimelineDateTime) {
const monthGroup = findMonthGroupForDate(this, dateTime);
let monthGroup = findMonthGroupForDate(this, dateTime);
if (!monthGroup) {
return;
// if exact match not found, find closest
monthGroup = findClosestGroupForDate(this, dateTime);
if (!monthGroup) {
return;
}
}
await this.loadMonthGroup(dateTime, { cancelable: false });
const asset = monthGroup.findClosest(dateTime);

View File

@ -27,6 +27,7 @@
{ key: ['D', 'd'], action: $t('previous_or_next_day') },
{ key: ['M', 'm'], action: $t('previous_or_next_month') },
{ key: ['Y', 'y'], action: $t('previous_or_next_year') },
{ key: ['g'], action: $t('navigate_to_time') },
{ key: ['x'], action: $t('select') },
{ key: ['Esc'], action: $t('back_close_deselect') },
{ key: ['Ctrl', 'k'], action: $t('search_your_photos') },