mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
Review comments
This commit is contained in:
parent
a72f2d8e0c
commit
c23ca56adb
10 changed files with 80 additions and 112 deletions
|
|
@ -1,12 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import AssetSelectionChangeDateModal from '$lib/modals/AssetSelectionChangeDateModal.svelte';
|
import AssetSelectionChangeDateModal from '$lib/modals/AssetSelectionChangeDateModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
|
||||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
|
||||||
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util.js';
|
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
import { mdiCalendarEditOutline } from '@mdi/js';
|
import { mdiCalendarEditOutline } from '@mdi/js';
|
||||||
import { DateTime, Duration } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -16,37 +13,15 @@
|
||||||
let { menuItem = false }: Props = $props();
|
let { menuItem = false }: Props = $props();
|
||||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||||
|
|
||||||
const getCurrentInterval = () => {
|
const showChangeDate = async () => {
|
||||||
const ids = getSelectedAssets(getOwnedAssets(), $user);
|
const success = await modalManager.show(AssetSelectionChangeDateModal, {
|
||||||
const assets = getOwnedAssets().filter((asset) => ids.includes(asset.id));
|
|
||||||
const imageTimestamps = assets.map((asset) => {
|
|
||||||
let localDateTime = fromTimelinePlainDateTime(asset.localDateTime);
|
|
||||||
let fileCreatedAt = fromTimelinePlainDateTime(asset.fileCreatedAt);
|
|
||||||
let offsetMinutes = localDateTime.diff(fileCreatedAt, 'minutes').shiftTo('minutes').minutes;
|
|
||||||
const timeZone = `UTC${offsetMinutes >= 0 ? '+' : ''}${Duration.fromObject({ minutes: offsetMinutes }).toFormat('hh:mm')}`;
|
|
||||||
return fileCreatedAt.setZone('utc', { keepLocalTime: true }).setZone(timeZone);
|
|
||||||
});
|
|
||||||
let minTimestamp = imageTimestamps[0];
|
|
||||||
let maxTimestamp = imageTimestamps[0];
|
|
||||||
for (let current of imageTimestamps) {
|
|
||||||
if (current < minTimestamp) {
|
|
||||||
minTimestamp = current;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current > maxTimestamp) {
|
|
||||||
maxTimestamp = current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { start: minTimestamp, end: maxTimestamp };
|
|
||||||
};
|
|
||||||
|
|
||||||
const showChangeDate = async () =>
|
|
||||||
await modalManager.show(AssetSelectionChangeDateModal, {
|
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
assets: getOwnedAssets(),
|
assets: getOwnedAssets(),
|
||||||
currentInterval: getCurrentInterval(),
|
|
||||||
clearSelect,
|
|
||||||
});
|
});
|
||||||
|
if (success) {
|
||||||
|
clearSelect();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte';
|
import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
const handleUpdateDescription = async () => {
|
const handleUpdateDescription = async () => {
|
||||||
const description = await modalManager.show(AssetUpdateDescriptionConfirmModal);
|
const description = await modalManager.show(AssetUpdateDescriptionConfirmModal);
|
||||||
if (description) {
|
if (description) {
|
||||||
const ids = getSelectedAssets(getOwnedAssets(), $user);
|
const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, description } });
|
await updateAssets({ assetBulkUpdateDto: { ids, description } });
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import ChangeLocation from '$lib/components/shared-components/change-location.svelte';
|
import ChangeLocation from '$lib/components/shared-components/change-location.svelte';
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
import { mdiMapMarkerMultipleOutline } from '@mdi/js';
|
import { mdiMapMarkerMultipleOutline } from '@mdi/js';
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = getSelectedAssets(getOwnedAssets(), $user);
|
const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
|
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
|
||||||
|
|
||||||
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
import ShortcutsModal from '$lib/modals/ShortcutsModal.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 +21,6 @@
|
||||||
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
|
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
timelineManager: TimelineManager;
|
timelineManager: TimelineManager;
|
||||||
|
|
@ -40,6 +38,8 @@
|
||||||
scrollToAsset,
|
scrollToAsset,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
const { isViewing: showAssetViewer } = assetViewingStore;
|
||||||
|
|
||||||
const trashOrDelete = async (force: boolean = false) => {
|
const trashOrDelete = async (force: boolean = false) => {
|
||||||
isShowDeleteConfirmation = false;
|
isShowDeleteConfirmation = false;
|
||||||
await deleteAssets(
|
await deleteAssets(
|
||||||
|
|
@ -145,11 +145,14 @@
|
||||||
const setFocusTo = setFocusToInit.bind(undefined, scrollToAsset, timelineManager);
|
const setFocusTo = setFocusToInit.bind(undefined, scrollToAsset, timelineManager);
|
||||||
const setFocusAsset = setFocusAssetInit.bind(undefined, scrollToAsset);
|
const setFocusAsset = setFocusAssetInit.bind(undefined, scrollToAsset);
|
||||||
|
|
||||||
const handleOpenDateModal = async () =>
|
const handleOpenDateModal = async () => {
|
||||||
await modalManager.show(NavigateToDateModal, {
|
const asset = await modalManager.show(NavigateToDateModal, {
|
||||||
timelineManager,
|
timelineManager,
|
||||||
onFocusOnAsset: setFocusAsset,
|
|
||||||
});
|
});
|
||||||
|
if (asset) {
|
||||||
|
setFocusAsset(asset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let shortcutList = $derived(
|
let shortcutList = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
interface Props extends HTMLInputAttributes {
|
||||||
type: 'date' | 'datetime-local';
|
type: 'date' | 'datetime-local';
|
||||||
value?: string;
|
value?: string;
|
||||||
min?: string;
|
min?: string;
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@
|
||||||
onClose(true);
|
onClose(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_change_date'));
|
handleError(error, $t('errors.unable_to_change_date'));
|
||||||
|
onClose(false);
|
||||||
}
|
}
|
||||||
onClose(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,6 @@ describe('DateSelectionModal component', () => {
|
||||||
const initialDate = DateTime.fromISO('2024-01-01');
|
const initialDate = DateTime.fromISO('2024-01-01');
|
||||||
const initialTimeZone = 'Europe/Berlin';
|
const initialTimeZone = 'Europe/Berlin';
|
||||||
|
|
||||||
const currentInterval = {
|
|
||||||
start: DateTime.fromISO('2000-02-01T14:00:00+01:00'),
|
|
||||||
end: DateTime.fromISO('2001-02-01T14:00:00+01:00'),
|
|
||||||
};
|
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
|
|
||||||
const getRelativeInputToggle = () => screen.getByTestId('edit-by-offset-switch');
|
const getRelativeInputToggle = () => screen.getByTestId('edit-by-offset-switch');
|
||||||
|
|
@ -43,7 +39,7 @@ describe('DateSelectionModal component', () => {
|
||||||
initialDate,
|
initialDate,
|
||||||
initialTimeZone,
|
initialTimeZone,
|
||||||
assets: [],
|
assets: [],
|
||||||
clearSelect: vitest.fn(),
|
|
||||||
onClose,
|
onClose,
|
||||||
});
|
});
|
||||||
expect(getDateInput().value).toBe('2024-01-01T00:00');
|
expect(getDateInput().value).toBe('2024-01-01T00:00');
|
||||||
|
|
@ -52,7 +48,7 @@ describe('DateSelectionModal component', () => {
|
||||||
|
|
||||||
test('calls onConfirm with correct date on confirm', async () => {
|
test('calls onConfirm with correct date on confirm', async () => {
|
||||||
render(AssetSelectionChangeDateModal, {
|
render(AssetSelectionChangeDateModal, {
|
||||||
props: { initialDate, initialTimeZone, assets: [], clearSelect: vitest.fn(), onClose },
|
props: { initialDate, initialTimeZone, assets: [], onClose },
|
||||||
});
|
});
|
||||||
|
|
||||||
await fireEvent.click(getConfirmButton());
|
await fireEvent.click(getConfirmButton());
|
||||||
|
|
@ -67,7 +63,7 @@ describe('DateSelectionModal component', () => {
|
||||||
|
|
||||||
test('calls onCancel on cancel', async () => {
|
test('calls onCancel on cancel', async () => {
|
||||||
render(AssetSelectionChangeDateModal, {
|
render(AssetSelectionChangeDateModal, {
|
||||||
props: { initialDate, initialTimeZone, assets: [], clearSelect: vitest.fn(), onClose },
|
props: { initialDate, initialTimeZone, assets: [], onClose },
|
||||||
});
|
});
|
||||||
|
|
||||||
await fireEvent.click(getCancelButton());
|
await fireEvent.click(getCancelButton());
|
||||||
|
|
@ -83,7 +79,6 @@ describe('DateSelectionModal component', () => {
|
||||||
initialDate: dstDate,
|
initialDate: dstDate,
|
||||||
initialTimeZone,
|
initialTimeZone,
|
||||||
assets: [],
|
assets: [],
|
||||||
clearSelect: vitest.fn(),
|
|
||||||
onClose,
|
onClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -92,7 +87,7 @@ describe('DateSelectionModal component', () => {
|
||||||
|
|
||||||
test('calls onConfirm with correct date on confirm', async () => {
|
test('calls onConfirm with correct date on confirm', async () => {
|
||||||
render(AssetSelectionChangeDateModal, {
|
render(AssetSelectionChangeDateModal, {
|
||||||
props: { initialDate: dstDate, initialTimeZone, assets: [], clearSelect: vitest.fn(), onClose },
|
props: { initialDate: dstDate, initialTimeZone, assets: [], onClose },
|
||||||
});
|
});
|
||||||
|
|
||||||
await fireEvent.click(getConfirmButton());
|
await fireEvent.click(getConfirmButton());
|
||||||
|
|
@ -108,7 +103,7 @@ describe('DateSelectionModal component', () => {
|
||||||
|
|
||||||
test('calls onConfirm with correct offset in relative mode', async () => {
|
test('calls onConfirm with correct offset in relative mode', async () => {
|
||||||
render(AssetSelectionChangeDateModal, {
|
render(AssetSelectionChangeDateModal, {
|
||||||
props: { initialDate, initialTimeZone, currentInterval, assets: [], clearSelect: vitest.fn(), onClose },
|
props: { initialDate, initialTimeZone, assets: [], onClose },
|
||||||
});
|
});
|
||||||
|
|
||||||
await fireEvent.click(getRelativeInputToggle());
|
await fireEvent.click(getRelativeInputToggle());
|
||||||
|
|
@ -139,7 +134,7 @@ describe('DateSelectionModal component', () => {
|
||||||
test('calls onConfirm with correct timeZone in relative mode', async () => {
|
test('calls onConfirm with correct timeZone in relative mode', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(AssetSelectionChangeDateModal, {
|
render(AssetSelectionChangeDateModal, {
|
||||||
props: { initialDate, initialTimeZone, currentInterval, assets: [], clearSelect: vitest.fn(), onClose },
|
props: { initialDate, initialTimeZone, assets: [], onClose },
|
||||||
});
|
});
|
||||||
|
|
||||||
await user.click(getRelativeInputToggle());
|
await user.click(getRelativeInputToggle());
|
||||||
|
|
|
||||||
|
|
@ -3,38 +3,23 @@
|
||||||
import DateInput from '$lib/elements/DateInput.svelte';
|
import DateInput from '$lib/elements/DateInput.svelte';
|
||||||
import DurationInput from '$lib/elements/DurationInput.svelte';
|
import DurationInput from '$lib/elements/DurationInput.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import {
|
import { getPreferredTimeZone, getTimezones, toIsoDate, type ZoneOption } from '$lib/modals/timezone-utils';
|
||||||
calcNewDate,
|
|
||||||
getPreferredTimeZone,
|
|
||||||
getTimezones,
|
|
||||||
toIsoDate,
|
|
||||||
type ZoneOption,
|
|
||||||
} from '$lib/modals/timezone-utils';
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { getSelectedAssets } from '$lib/utils/asset-utils';
|
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { updateAssets } from '@immich/sdk';
|
import { updateAssets } from '@immich/sdk';
|
||||||
import { Button, Field, HStack, Modal, ModalBody, ModalFooter, Switch, VStack } from '@immich/ui';
|
import { Button, Field, HStack, Modal, ModalBody, ModalFooter, Switch, VStack } from '@immich/ui';
|
||||||
import { mdiCalendarEdit } from '@mdi/js';
|
import { mdiCalendarEdit } from '@mdi/js';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
initialDate?: DateTime;
|
initialDate?: DateTime;
|
||||||
initialTimeZone?: string;
|
initialTimeZone?: string;
|
||||||
currentInterval?: { start: DateTime; end: DateTime };
|
|
||||||
assets: TimelineAsset[];
|
assets: TimelineAsset[];
|
||||||
clearSelect: () => void;
|
|
||||||
onClose: (success: boolean) => void;
|
onClose: (success: boolean) => void;
|
||||||
}
|
}
|
||||||
|
let { initialDate = DateTime.now(), initialTimeZone, assets, onClose }: Props = $props();
|
||||||
let {
|
|
||||||
initialDate = DateTime.now(),
|
|
||||||
initialTimeZone,
|
|
||||||
currentInterval,
|
|
||||||
assets,
|
|
||||||
clearSelect,
|
|
||||||
onClose,
|
|
||||||
}: Props = $props();
|
|
||||||
|
|
||||||
let showRelative = $state(false);
|
let showRelative = $state(false);
|
||||||
let selectedDuration = $state(0);
|
let selectedDuration = $state(0);
|
||||||
|
|
@ -46,7 +31,7 @@
|
||||||
let selectedOption = $derived(getPreferredTimeZone(initialDate, initialTimeZone, timezones, lastSelectedTimezone));
|
let selectedOption = $derived(getPreferredTimeZone(initialDate, initialTimeZone, timezones, lastSelectedTimezone));
|
||||||
|
|
||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
const ids = getSelectedAssets(assets, $user);
|
const ids = getOwnedAssetsWithWarning(assets, $user);
|
||||||
try {
|
try {
|
||||||
if (showRelative && (selectedDuration || selectedOption)) {
|
if (showRelative && (selectedDuration || selectedOption)) {
|
||||||
await updateAssets({
|
await updateAssets({
|
||||||
|
|
@ -56,33 +41,33 @@
|
||||||
timeZone: selectedOption?.value,
|
timeZone: selectedOption?.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
clearSelect();
|
onClose(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isoDate = toIsoDate(selectedDate, selectedOption);
|
const isoDate = toIsoDate(selectedDate, selectedOption);
|
||||||
await updateAssets({ assetBulkUpdateDto: { ids, dateTimeOriginal: isoDate } });
|
await updateAssets({ assetBulkUpdateDto: { ids, dateTimeOriginal: isoDate } });
|
||||||
clearSelect();
|
onClose(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_change_date'));
|
handleError(error, $t('errors.unable_to_change_date'));
|
||||||
|
onClose(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let intervalFrom = $derived(
|
// let before = $derived(DateTime.fromObject(assets[0].localDateTime).toFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
|
||||||
currentInterval ? calcNewDate(currentInterval.start, selectedDuration, selectedOption?.value) : undefined,
|
|
||||||
);
|
// let after = $derived(
|
||||||
let intervalTo = $derived(
|
// currentInterval ? calcNewDate(currentInterval.end, selectedDuration, selectedOption?.value) : undefined,
|
||||||
currentInterval ? calcNewDate(currentInterval.end, selectedDuration, selectedOption?.value) : undefined,
|
// );
|
||||||
);
|
|
||||||
|
|
||||||
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
||||||
const date = $derived(DateTime.fromISO(selectedDate, { zone: selectedOption?.value, setZone: true }));
|
const date = $derived(DateTime.fromISO(selectedDate, { zone: selectedOption?.value, setZone: true }));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('edit_date_and_time')} icon={mdiCalendarEdit} onClose={() => onClose(false)} size="large">
|
<Modal title={$t('edit_date_and_time')} icon={mdiCalendarEdit} onClose={() => onClose(false)} size="small">
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<VStack fullWidth>
|
<VStack fullWidth>
|
||||||
<HStack fullWidth>
|
<HStack fullWidth>
|
||||||
<Field color="muted" label={$t('edit_date_and_time_by_offset')}>
|
<Field color="muted" label="Adjust selection by fixed duration (time zone adjust)">
|
||||||
<Switch data-testid="edit-by-offset-switch" bind:checked={showRelative} />
|
<Switch data-testid="edit-by-offset-switch" bind:checked={showRelative} />
|
||||||
</Field>
|
</Field>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
@ -91,7 +76,7 @@
|
||||||
<label class="immich-form-label" for="relativedatetime">{$t('offset')}</label>
|
<label class="immich-form-label" for="relativedatetime">{$t('offset')}</label>
|
||||||
</HStack>
|
</HStack>
|
||||||
<HStack fullWidth>
|
<HStack fullWidth>
|
||||||
<DurationInput class="immich-form-input text-gray-700" id="relativedatetime" bind:value={selectedDuration} />
|
<DurationInput class="immich-form-input text-gray-700" id="relativedatetime" bind:value={selectedDuration} />
|
||||||
</HStack>
|
</HStack>
|
||||||
{:else}
|
{:else}
|
||||||
<HStack fullWidth>
|
<HStack fullWidth>
|
||||||
|
|
@ -110,17 +95,32 @@
|
||||||
onSelect={(option) => (lastSelectedTimezone = option as ZoneOption)}
|
onSelect={(option) => (lastSelectedTimezone = option as ZoneOption)}
|
||||||
></Combobox>
|
></Combobox>
|
||||||
</div>
|
</div>
|
||||||
<VStack fullWidth class={!showRelative || !currentInterval ? 'invisible' : ''}>
|
<!-- <Card color="secondary" class={!showRelative || !currentInterval ? 'invisible' : ''}>
|
||||||
<HStack fullWidth>
|
<CardBody class="p-2">
|
||||||
<div class="immich-form-label" data-testid="interval-preview">{$t('new_date_range')}</div>
|
<div class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-3 items-center">
|
||||||
</HStack>
|
<div class="col-span-2 immich-form-label" data-testid="interval-preview">Preview</div>
|
||||||
<HStack fullWidth>
|
<Text size="small" class="-mt-2 immich-form-label col-span-2"
|
||||||
<label class="immich-form-label" for="from">From</label>
|
>Showing changes for first selected asset only</Text
|
||||||
<DateInput class="immich-form-input" id="from" type="datetime-local" bind:value={intervalFrom} />
|
>
|
||||||
<label class="immich-form-label" for="from">To</label>
|
<label class="immich-form-label" for="from">Before</label>
|
||||||
<DateInput class="immich-form-input" id="to" type="datetime-local" bind:value={intervalTo} />
|
<DateInput
|
||||||
</HStack>
|
class="dark:text-gray-300 text-gray-700 text-base"
|
||||||
</VStack>
|
id="from"
|
||||||
|
type="datetime-local"
|
||||||
|
readonly
|
||||||
|
bind:value={before}
|
||||||
|
/>
|
||||||
|
<label class="immich-form-label" for="to">After</label>
|
||||||
|
<DateInput
|
||||||
|
class="dark:text-gray-300 text-gray-700 text-base"
|
||||||
|
id="to"
|
||||||
|
type="datetime-local"
|
||||||
|
readonly
|
||||||
|
bind:value={after}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card> -->
|
||||||
</VStack>
|
</VStack>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,10 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
interface Props {
|
interface Props {
|
||||||
timelineManager: TimelineManager;
|
timelineManager: TimelineManager;
|
||||||
onFocusOnAsset: (asset: TimelineAsset) => void;
|
onClose: (asset?: TimelineAsset) => void;
|
||||||
onClose: (success: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let { timelineManager, onFocusOnAsset, onClose }: Props = $props();
|
let { timelineManager, onClose }: Props = $props();
|
||||||
|
|
||||||
const initialDate = DateTime.now();
|
const initialDate = DateTime.now();
|
||||||
let selectedDate = $state(initialDate.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
|
let selectedDate = $state(initialDate.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
|
||||||
|
|
@ -23,27 +22,21 @@
|
||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
if (!date.isValid || !selectedOption) {
|
if (!date.isValid || !selectedOption) {
|
||||||
onClose(false);
|
onClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the local date/time components from the selected string using neutral timezone
|
// Get the local date/time components from the selected string using neutral timezone
|
||||||
const dateTime = toDatetime(selectedDate, selectedOption);
|
const dateTime = toDatetime(selectedDate, selectedOption) as DateTime<true>;
|
||||||
const asset = await timelineManager.getClosestAssetToDate(dateTime);
|
const asset = await timelineManager.getClosestAssetToDate(dateTime.toObject());
|
||||||
if (asset) {
|
onClose(asset);
|
||||||
onFocusOnAsset(asset);
|
|
||||||
onClose(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
// when changing the time zone, assume the configured date/time is meant for that time zone (instead of updating it)
|
||||||
const date = $derived(DateTime.fromISO(selectedDate, { zone: selectedOption?.value, setZone: true }));
|
const date = $derived(DateTime.fromISO(selectedDate, { zone: selectedOption?.value, setZone: true }));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal title={$t('navigate_to_time')} icon={mdiNavigationVariantOutline} onClose={() => onClose(false)}>
|
<Modal title={$t('navigate_to_time')} icon={mdiNavigationVariantOutline} onClose={() => onClose()}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<VStack fullWidth>
|
<VStack fullWidth>
|
||||||
<HStack fullWidth>
|
<HStack fullWidth>
|
||||||
|
|
@ -61,7 +54,7 @@
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<HStack fullWidth>
|
<HStack fullWidth>
|
||||||
<Button shape="round" color="secondary" fullWidth onclick={() => onClose(false)}>{$t('cancel')}</Button>
|
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
|
||||||
<Button shape="round" type="submit" fullWidth onclick={handleClose}>{$t('confirm')}</Button>
|
<Button shape="round" type="submit" fullWidth onclick={handleClose}>{$t('confirm')}</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
|
||||||
|
|
@ -405,7 +405,7 @@ export const getAssetType = (type: AssetTypeEnum) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSelectedAssets = (assets: TimelineAsset[], user: UserResponseDto | null): string[] => {
|
export const getOwnedAssetsWithWarning = (assets: TimelineAsset[], user: UserResponseDto | null): string[] => {
|
||||||
const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
|
const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
|
||||||
|
|
||||||
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue