refactor(web): bulk (un)archive actions (#32189)

(cherry picked from commit d26c0eec10f1bb49117b71b667c88a8a715b2489)
This commit is contained in:
Mees Frensel
2026-10-27 23:12:21 +03:00
committed by Codex
parent eca952720a
commit f96816b55f
17 changed files with 145 additions and 163 deletions
@@ -17,14 +17,14 @@
import { showDeleteModal } from '#lib/stores/preferences.store.ts'; import { showDeleteModal } from '#lib/stores/preferences.store.ts';
import { handlePromiseError } from '#lib/utils.ts'; import { handlePromiseError } from '#lib/utils.ts';
import { deleteAssets } from '#lib/utils/actions.ts'; import { deleteAssets } from '#lib/utils/actions.ts';
import { archiveAssets, getNextAsset, getPreviousAsset, navigateToAsset } from '#lib/utils/asset-utils.ts'; import { getNextAsset, getPreviousAsset, navigateToAsset } from '#lib/utils/asset-utils.ts';
import { moveFocus } from '#lib/utils/focus-util.ts'; import { moveFocus } from '#lib/utils/focus-util.ts';
import { handleError } from '#lib/utils/handle-error.ts'; import { handleError } from '#lib/utils/handle-error.ts';
import { getJustifiedLayoutFromAssets } from '#lib/utils/layout-utils.ts'; import { getJustifiedLayoutFromAssets } from '#lib/utils/layout-utils.ts';
import { navigate } from '#lib/utils/navigation.ts'; import { navigate } from '#lib/utils/navigation.ts';
import { isTimelineAsset, toTimelineAsset } from '#lib/utils/timeline-util.ts'; import { isTimelineAsset, toTimelineAsset } from '#lib/utils/timeline-util.ts';
import { TUNABLES } from '#lib/utils/tunables.ts'; import { TUNABLES } from '#lib/utils/tunables.ts';
import { AssetVisibility, type AssetResponseDto } from '@immich/sdk'; import { type AssetResponseDto } from '@immich/sdk';
import { modalManager } from '@immich/ui'; import { modalManager } from '@immich/ui';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@@ -206,17 +206,6 @@
assetInteraction.clear(); assetInteraction.clear();
}; };
const toggleArchive = async () => {
const ids = await archiveAssets(
assetInteraction.assets,
assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive,
);
if (ids) {
assets = assets.filter((asset) => !ids.includes(asset.id));
assetInteraction.clear();
}
};
const focusNextAsset = () => moveFocus((element) => element.dataset.thumbnailFocusContainer !== undefined, 'next'); const focusNextAsset = () => moveFocus((element) => element.dataset.thumbnailFocusContainer !== undefined, 'next');
const focusPreviousAsset = () => const focusPreviousAsset = () =>
moveFocus((element) => element.dataset.thumbnailFocusContainer !== undefined, 'previous'); moveFocus((element) => element.dataset.thumbnailFocusContainer !== undefined, 'previous');
@@ -260,7 +249,6 @@
shortcuts.push( shortcuts.push(
{ shortcut: { key: 'Delete' }, onShortcut: onDelete }, { shortcut: { key: 'Delete' }, onShortcut: onDelete },
{ shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) }, { shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) },
{ shortcut: { key: 'a', shift: true }, onShortcut: toggleArchive },
); );
} }
} }
@@ -1,54 +0,0 @@
<script lang="ts">
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
import type { OnArchive } from '#lib/utils/actions.ts';
import { archiveAssets } from '#lib/utils/asset-utils.ts';
import { AssetVisibility } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
import { t } from 'svelte-i18n';
type Props = {
onArchive?: OnArchive;
menuItem?: boolean;
unarchive?: boolean;
};
let { onArchive, menuItem = false, unarchive = false }: Props = $props();
let text = $derived(unarchive ? $t('unarchive') : $t('to_archive'));
let icon = $derived(unarchive ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline);
let loading = $state(false);
const handleArchive = async () => {
const visibility = unarchive ? AssetVisibility.Timeline : AssetVisibility.Archive;
const assets = assetMultiSelectManager.getOwnedAssets().filter((asset) => asset.visibility !== visibility);
loading = true;
const ids = await archiveAssets(assets, visibility as AssetVisibility);
if (ids) {
onArchive?.(ids, visibility);
assetMultiSelectManager.clear();
}
loading = false;
};
</script>
{#if menuItem}
<MenuOption {text} {icon} onClick={handleArchive} />
{/if}
{#if !menuItem}
{#if loading}
<IconButton
shape="round"
color="secondary"
variant="ghost"
aria-label={$t('loading')}
icon={mdiTimerSand}
onclick={() => {}}
/>
{:else}
<IconButton shape="round" color="secondary" variant="ghost" aria-label={text} {icon} onclick={handleArchive} />
{/if}
{/if}
@@ -7,7 +7,6 @@
} from '#lib/components/timeline/actions/focus-actions.ts'; } from '#lib/components/timeline/actions/focus-actions.ts';
import type { AssetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts'; import type { AssetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
import { assetViewerManager } from '#lib/managers/asset-viewer-manager.svelte.ts'; import { assetViewerManager } from '#lib/managers/asset-viewer-manager.svelte.ts';
import { eventManager } from '#lib/managers/event-manager.svelte.ts';
import { featureFlagsManager } from '#lib/managers/feature-flags-manager.svelte.ts'; import { featureFlagsManager } from '#lib/managers/feature-flags-manager.svelte.ts';
import { TimelineManager } from '#lib/managers/timeline-manager/timeline-manager.svelte.ts'; import { TimelineManager } from '#lib/managers/timeline-manager/timeline-manager.svelte.ts';
import type { TimelineAsset } from '#lib/managers/timeline-manager/types.ts'; import type { TimelineAsset } from '#lib/managers/timeline-manager/types.ts';
@@ -21,8 +20,7 @@
import { searchStore } from '#lib/stores/search.svelte.ts'; import { searchStore } from '#lib/stores/search.svelte.ts';
import { handlePromiseError } from '#lib/utils.ts'; import { handlePromiseError } from '#lib/utils.ts';
import { deleteAssets } from '#lib/utils/actions.ts'; import { deleteAssets } from '#lib/utils/actions.ts';
import { archiveAssets, selectAllAssets } from '#lib/utils/asset-utils.ts'; import { selectAllAssets } from '#lib/utils/asset-utils.ts';
import { AssetVisibility } from '@immich/sdk';
import { isModalOpen, modalManager } from '@immich/ui'; import { isModalOpen, modalManager } from '@immich/ui';
type Props = { type Props = {
@@ -64,14 +62,6 @@
onEscape?.(); onEscape?.();
}; };
const toggleArchive = async () => {
const visibility = assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive;
const ids = await archiveAssets(assetInteraction.assets, visibility);
timelineManager.update(ids, (asset) => (asset.visibility = visibility));
eventManager.emit('AssetsArchive', ids);
assetInteraction.clear();
};
const onSelectStart = (e: Event) => { const onSelectStart = (e: Event) => {
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) { if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
e.preventDefault(); e.preventDefault();
@@ -135,7 +125,6 @@
{ shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) }, { shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) },
{ shortcut: { key: 'D', ctrl: true }, onShortcut: () => assetInteraction.clear() }, { shortcut: { key: 'D', ctrl: true }, onShortcut: () => assetInteraction.clear() },
{ shortcut: { key: 's' }, onShortcut: () => onStackAssets() }, { shortcut: { key: 's' }, onShortcut: () => onStackAssets() },
{ shortcut: { key: 'a', shift: true }, onShortcut: toggleArchive },
); );
} else { } else {
// conflicting shortcuts // conflicting shortcuts
@@ -25,7 +25,14 @@ import {
type TimelineDateTime, type TimelineDateTime,
type TimelineYearMonth, type TimelineYearMonth,
} from '#lib/utils/timeline-util.ts'; } from '#lib/utils/timeline-util.ts';
import { AssetOrder, AssetOrderBy, getAssetInfo, getTimeBuckets, type AssetResponseDto } from '@immich/sdk'; import {
AssetOrder,
AssetOrderBy,
AssetVisibility,
getAssetInfo,
getTimeBuckets,
type AssetResponseDto,
} from '@immich/sdk';
import { clamp, isEqual } from 'lodash-es'; import { clamp, isEqual } from 'lodash-es';
import { SvelteDate, SvelteSet } from 'svelte/reactivity'; import { SvelteDate, SvelteSet } from 'svelte/reactivity';
import { isMismatched, updateObject } from './internal/utils.svelte'; import { isMismatched, updateObject } from './internal/utils.svelte';
@@ -122,7 +129,14 @@ export class TimelineManager extends VirtualScrollManager {
this.upsertAssets([timelineAsset]); this.upsertAssets([timelineAsset]);
} }
}, },
AssetsUnarchive: (assets) => this.upsertAssets(assets), AssetsArchive: (assetIds) => this.update(assetIds, (asset) => (asset.visibility = AssetVisibility.Archive)),
AssetsUnarchive: (assets) => {
if (this.#options.visibility === AssetVisibility.Archive) {
this.removeAssets(assets.map((asset) => asset.id));
} else {
this.upsertAssets(assets);
}
},
AlbumRemoveAssets: ({ assetIds, albumIds }) => { AlbumRemoveAssets: ({ assetIds, albumIds }) => {
if (this.#options.albumId && albumIds.includes(this.#options.albumId)) { if (this.#options.albumId && albumIds.includes(this.#options.albumId)) {
this.removeAssets(assetIds); this.removeAssets(assetIds);
+85
View File
@@ -3,6 +3,7 @@ import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manage
import { assetViewerManager } from '#lib/managers/asset-viewer-manager.svelte.ts'; import { assetViewerManager } from '#lib/managers/asset-viewer-manager.svelte.ts';
import { authManager } from '#lib/managers/auth-manager.svelte.ts'; import { authManager } from '#lib/managers/auth-manager.svelte.ts';
import { eventManager } from '#lib/managers/event-manager.svelte.ts'; import { eventManager } from '#lib/managers/event-manager.svelte.ts';
import type { TimelineAsset } from '#lib/managers/timeline-manager/types.ts';
import AssetAddToAlbumModal from '#lib/modals/AssetAddToAlbumModal.svelte'; import AssetAddToAlbumModal from '#lib/modals/AssetAddToAlbumModal.svelte';
import AssetTagModal from '#lib/modals/AssetTagModal.svelte'; import AssetTagModal from '#lib/modals/AssetTagModal.svelte';
import ProfileImageCropperModal from '#lib/modals/ProfileImageCropperModal.svelte'; import ProfileImageCropperModal from '#lib/modals/ProfileImageCropperModal.svelte';
@@ -25,6 +26,7 @@ import {
getAssetInfo, getAssetInfo,
runAssetJobs, runAssetJobs,
updateAsset, updateAsset,
updateAssets,
type AlbumResponseDto, type AlbumResponseDto,
type AssetJobsDto, type AssetJobsDto,
type AssetResponseDto, type AssetResponseDto,
@@ -33,6 +35,8 @@ import { modalManager, toastManager, type ActionItem } from '@immich/ui';
import { import {
mdiAccountCircleOutline, mdiAccountCircleOutline,
mdiAlertOutline, mdiAlertOutline,
mdiArchiveArrowDownOutline,
mdiArchiveArrowUpOutline,
mdiCogRefreshOutline, mdiCogRefreshOutline,
mdiCompare, mdiCompare,
mdiContentCopy, mdiContentCopy,
@@ -78,6 +82,19 @@ export const getAssetBulkActions = ($t: MessageFormatter, album?: AlbumResponseD
onAction: () => modalManager.show(AssetAddToAlbumModal, { assetIds }), onAction: () => modalManager.show(AssetAddToAlbumModal, { assetIds }),
}; };
const Archive: ActionItem = {
title: $t('to_archive'),
icon: mdiArchiveArrowDownOutline,
shortcuts: [{ key: 'a', shift: true }],
$if: () => assetMultiSelectManager.isAllUserOwned && !assetMultiSelectManager.isAllArchived,
onAction: async () => {
const assets = ownedAssets.filter((asset) => asset.visibility !== AssetVisibility.Archive);
if (await handleSetVisibility(assets, AssetVisibility.Archive)) {
assetMultiSelectManager.clear();
}
},
};
const CreateSharedLink: ActionItem = { const CreateSharedLink: ActionItem = {
title: $t('share'), title: $t('share'),
icon: mdiShareVariantOutline, icon: mdiShareVariantOutline,
@@ -104,6 +121,18 @@ export const getAssetBulkActions = ($t: MessageFormatter, album?: AlbumResponseD
shortcuts: { key: 't' }, shortcuts: { key: 't' },
}; };
const Unarchive: ActionItem = {
title: $t('unarchive'),
icon: mdiArchiveArrowUpOutline,
shortcuts: [{ key: 'a', shift: true }],
$if: () => assetMultiSelectManager.isAllUserOwned && assetMultiSelectManager.isAllArchived,
onAction: async () => {
if (await handleSetVisibility(ownedAssets, AssetVisibility.Timeline)) {
assetMultiSelectManager.clear();
}
},
};
const RefreshFacesJob: ActionItem = { const RefreshFacesJob: ActionItem = {
title: $t('refresh_faces'), title: $t('refresh_faces'),
icon: mdiHeadSyncOutline, icon: mdiHeadSyncOutline,
@@ -131,9 +160,11 @@ export const getAssetBulkActions = ($t: MessageFormatter, album?: AlbumResponseD
return { return {
AddToAlbum, AddToAlbum,
Archive,
CreateSharedLink, CreateSharedLink,
RemoveFromAlbum, RemoveFromAlbum,
Tag, Tag,
Unarchive,
RefreshFacesJob, RefreshFacesJob,
RefreshMetadataJob, RefreshMetadataJob,
RegenerateThumbnailJob, RegenerateThumbnailJob,
@@ -500,6 +531,60 @@ export const handleTagAssets = async (assetIds: string[], tagIds: string[]) => {
} }
}; };
const handleSetVisibility = async (assets: TimelineAsset[], visibility: AssetVisibility) => {
const $t = await getFormatter();
const ids = assets.map((asset) => asset.id);
try {
await updateAssets({ assetBulkUpdateDto: { ids, visibility } });
if (visibility === AssetVisibility.Archive) {
toastManager.primary({
description: $t('archived_count', { values: { count: assets.length } }),
button: (close) => ({
label: $t('undo'),
onclick: () => {
close();
void undoArchiveAssets(assets);
},
}),
});
eventManager.emit('AssetsArchive', ids);
} else {
// Timeline
toastManager.primary($t('unarchived_count', { values: { count: assets.length } }));
eventManager.emit('AssetsUnarchive', assets);
}
} catch (error) {
handleError(
error,
$t('errors.unable_to_archive_unarchive', { values: { archived: visibility === AssetVisibility.Archive } }),
);
return false;
}
return true;
};
const undoArchiveAssets = async (assets: TimelineAsset[]) => {
const $t = await getFormatter();
try {
const ids = assets.map((a) => a.id);
await updateAssets({
assetBulkUpdateDto: { ids, visibility: AssetVisibility.Timeline },
});
for (const asset of assets) {
asset.visibility = AssetVisibility.Timeline;
}
toastManager.success($t('unarchived_count', { values: { count: assets.length } }));
eventManager.emit('AssetsUnarchive', assets);
eventManager.emit('AssetsUndoArchive', assets);
} catch (error) {
handleError(error, $t('errors.unable_to_archive_unarchive', { values: { archived: false } }));
}
};
const handleBulkRemoveAssetsFromAlbum = async (assetIds: string[], album: AlbumResponseDto) => { const handleBulkRemoveAssetsFromAlbum = async (assetIds: string[], album: AlbumResponseDto) => {
const $t = await getFormatter(); const $t = await getFormatter();
-26
View File
@@ -337,32 +337,6 @@ const undoArchiveAssets = async (assets: TimelineAsset[]) => {
} }
}; };
export const archiveAssets = async (assets: TimelineAsset[], visibility: AssetVisibility) => {
const ids = assets.map(({ id }) => id);
const $t = get(t);
try {
if (ids.length > 0) {
await updateAssets({
assetBulkUpdateDto: { ids, visibility },
});
}
if (visibility === AssetVisibility.Archive) {
showUndoArchiveToast($t('archived_count', { values: { count: ids.length } }), assets);
} else {
toastManager.primary($t('unarchived_count', { values: { count: ids.length } }));
}
} catch (error) {
handleError(
error,
$t('errors.unable_to_archive_unarchive', { values: { archived: visibility === AssetVisibility.Archive } }),
);
}
return ids;
};
export const delay = async (ms: number) => { export const delay = async (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
}; };
@@ -11,7 +11,6 @@
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte'; import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte'; import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte';
import UserAvatar from '#lib/components/shared-components/UserAvatar.svelte'; import UserAvatar from '#lib/components/shared-components/UserAvatar.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -415,11 +414,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
unarchive={assetMultiSelectManager.isAllArchived}
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} /> <SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
{/if} {/if}
{#if assetMultiSelectManager.assets.length === 1} {#if assetMultiSelectManager.assets.length === 1}
@@ -2,7 +2,6 @@
import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte'; import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte';
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte'; import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import DeleteAssets from '#lib/components/timeline/actions/DeleteAssetsAction.svelte'; import DeleteAssets from '#lib/components/timeline/actions/DeleteAssetsAction.svelte';
import DownloadAction from '#lib/components/timeline/actions/DownloadAction.svelte'; import DownloadAction from '#lib/components/timeline/actions/DownloadAction.svelte';
import FavoriteAction from '#lib/components/timeline/actions/FavoriteAction.svelte'; import FavoriteAction from '#lib/components/timeline/actions/FavoriteAction.svelte';
@@ -63,10 +62,7 @@
<AssetSelectControlBar> <AssetSelectControlBar>
{const Actions = $derived(getAssetBulkActions($t))} {const Actions = $derived(getAssetBulkActions($t))}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} /> <CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<ArchiveAction <ActionButton action={Actions.Unarchive} />
unarchive
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionButton action={Actions.CreateSharedLink} /> <ActionButton action={Actions.CreateSharedLink} />
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} /> <SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
<ActionButton action={Actions.AddToAlbum} /> <ActionButton action={Actions.AddToAlbum} />
@@ -3,7 +3,6 @@
import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte'; import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte';
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte'; import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -75,11 +74,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
unarchive={assetMultiSelectManager.isAllArchived}
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} /> <SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<DeleteAssets <DeleteAssets
@@ -9,7 +9,6 @@
import TreeItems from '#lib/components/shared-components/tree/TreeItems.svelte'; import TreeItems from '#lib/components/shared-components/tree/TreeItems.svelte';
import Sidebar from '#lib/components/sidebar/Sidebar.svelte'; import Sidebar from '#lib/components/sidebar/Sidebar.svelte';
import AssetSelectControlBar from '#lib/components/timeline/AssetSelectControlBar.svelte'; import AssetSelectControlBar from '#lib/components/timeline/AssetSelectControlBar.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -111,7 +110,7 @@
</Sidebar> </Sidebar>
{/snippet} {/snippet}
<OnEvents onAssetsDelete={refreshAll} /> <OnEvents onAssetsDelete={refreshAll} onAssetsArchive={triggerAssetUpdate} onAssetsUnarchive={triggerAssetUpdate} />
<TreeBreadcrumbs node={data.tree} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} /> <TreeBreadcrumbs node={data.tree} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} />
@@ -168,7 +167,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} onArchive={triggerAssetUpdate} /> <ActionMenuItem action={Actions.Archive} />
<ActionMenuItem action={Actions.Unarchive} />
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} /> <SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} /> <DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} />
@@ -2,7 +2,6 @@
import ActionMenuItem from '#lib/components/ActionMenuItem.svelte'; import ActionMenuItem from '#lib/components/ActionMenuItem.svelte';
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import type { SelectionBBox } from '#lib/components/shared-components/map/types.ts'; import type { SelectionBBox } from '#lib/components/shared-components/map/types.ts';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -146,11 +145,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
unarchive={assetMultiSelectManager.isAllArchived}
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets <DeleteAssets
menuItem menuItem
@@ -4,10 +4,10 @@
import { shortcuts } from '#lib/actions/shortcut.ts'; import { shortcuts } from '#lib/actions/shortcut.ts';
import ActionMenuItem from '#lib/components/ActionMenuItem.svelte'; import ActionMenuItem from '#lib/components/ActionMenuItem.svelte';
import BirthdayConfetti from '#lib/components/memories/BirthdayConfetti.svelte'; import BirthdayConfetti from '#lib/components/memories/BirthdayConfetti.svelte';
import OnEvents from '#lib/components/OnEvents.svelte';
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte'; import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
import GalleryViewer from '#lib/components/shared-components/gallery-viewer/GalleryViewer.svelte'; import GalleryViewer from '#lib/components/shared-components/gallery-viewer/GalleryViewer.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -274,6 +274,8 @@
]} ]}
/> />
<OnEvents onAssetsArchive={handleHideAssets} />
{#if assetMultiSelectManager.selectionActive} {#if assetMultiSelectManager.selectionActive}
<div class="dark sticky top-0 z-1"> <div class="dark sticky top-0 z-1">
<AssetSelectControlBar> <AssetSelectControlBar>
@@ -299,7 +301,7 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} onArchive={handleHideAssets} /> <ActionMenuItem action={Actions.Archive} />
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets menuItem onAssetDelete={handleHideAssets} /> <DeleteAssets menuItem onAssetDelete={handleHideAssets} />
</ButtonContextMenu> </ButtonContextMenu>
@@ -8,7 +8,6 @@
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte'; import MenuOption from '#lib/components/shared-components/context-menu/MenuOption.svelte';
import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte'; import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -500,14 +499,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
unarchive={assetMultiSelectManager.isAllArchived}
onArchive={(ids, visibility) =>
timelineManager.update(ids, (asset) => {
asset.visibility = visibility;
})}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} /> <SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<DeleteAssets <DeleteAssets
@@ -152,10 +152,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets <DeleteAssets
menuItem menuItem
@@ -3,7 +3,6 @@
import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte'; import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte';
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte'; import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -127,10 +126,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets <DeleteAssets
menuItem menuItem
@@ -7,7 +7,6 @@
import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte'; import ControlAppBar from '#lib/components/shared-components/ControlAppBar.svelte';
import GalleryViewer from '#lib/components/shared-components/gallery-viewer/GalleryViewer.svelte'; import GalleryViewer from '#lib/components/shared-components/gallery-viewer/GalleryViewer.svelte';
import SearchBar from '#lib/components/shared-components/search-bar/SearchBar.svelte'; import SearchBar from '#lib/components/shared-components/search-bar/SearchBar.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -19,7 +18,7 @@
import { QueryParameter } from '#lib/constants.ts'; import { QueryParameter } from '#lib/constants.ts';
import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts'; import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
import { searchManager } from '#lib/managers/search-manager.svelte.ts'; import { searchManager } from '#lib/managers/search-manager.svelte.ts';
import type { Viewport } from '#lib/managers/timeline-manager/types.ts'; import type { TimelineAsset, Viewport } from '#lib/managers/timeline-manager/types.ts';
import { Route } from '#lib/route.ts'; import { Route } from '#lib/route.ts';
import { getAssetBulkActions } from '#lib/services/asset.service.ts'; import { getAssetBulkActions } from '#lib/services/asset.service.ts';
import { lang, locale } from '#lib/stores/preferences.store.ts'; import { lang, locale } from '#lib/stores/preferences.store.ts';
@@ -239,6 +238,21 @@
} }
}; };
const onAssetsArchive = (assetIds: string[]) => {
if (!terms.visibility || terms.visibility === AssetVisibility.Timeline) {
const assetIdSet = new Set(assetIds);
searchResultAssets = searchResultAssets.filter((asset) => !assetIdSet.has(asset.id));
}
};
// TODO: add back in opposite case
const onAssetsUnarchive = (assets: TimelineAsset[]) => {
if (terms.visibility === AssetVisibility.Archive) {
const assetIdSet = new Set(assets.map((asset) => asset.id));
searchResultAssets = searchResultAssets.filter((asset) => !assetIdSet.has(asset.id));
}
};
function getObjectKeys<T extends object>(obj: T): (keyof T)[] { function getObjectKeys<T extends object>(obj: T): (keyof T)[] {
return Object.keys(obj) as (keyof T)[]; return Object.keys(obj) as (keyof T)[];
} }
@@ -254,7 +268,7 @@
</script> </script>
<svelte:window bind:scrollY /> <svelte:window bind:scrollY />
<OnEvents {onAlbumAddAssets} /> <OnEvents {onAlbumAddAssets} {onAssetsArchive} {onAssetsUnarchive} />
{#if searchTermKeys.length > 0} {#if searchTermKeys.length > 0}
<section id="search-chips" class="mx-auto mt-24 w-full max-w-7xl px-4 sm:px-8 lg:px-12"> <section id="search-chips" class="mx-auto mt-24 w-full max-w-7xl px-4 sm:px-8 lg:px-12">
@@ -377,7 +391,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction menuItem unarchive={assetMultiSelectManager.isAllArchived} /> <ActionMenuItem action={Actions.Archive} />
<ActionMenuItem action={Actions.Unarchive} />
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} /> <SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
@@ -10,7 +10,6 @@
import Sidebar from '#lib/components/sidebar/Sidebar.svelte'; import Sidebar from '#lib/components/sidebar/Sidebar.svelte';
import AssetSelectControlBar from '#lib/components/timeline/AssetSelectControlBar.svelte'; import AssetSelectControlBar from '#lib/components/timeline/AssetSelectControlBar.svelte';
import Timeline from '#lib/components/timeline/Timeline.svelte'; import Timeline from '#lib/components/timeline/Timeline.svelte';
import ArchiveAction from '#lib/components/timeline/actions/ArchiveAction.svelte';
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte'; import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte'; import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
@@ -134,10 +133,8 @@
<ChangeDate menuItem /> <ChangeDate menuItem />
<ChangeDescription menuItem /> <ChangeDescription menuItem />
<ChangeLocation menuItem /> <ChangeLocation menuItem />
<ArchiveAction <ActionMenuItem action={Actions.Archive} />
menuItem <ActionMenuItem action={Actions.Unarchive} />
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
/>
<ActionMenuItem action={Actions.Tag} /> <ActionMenuItem action={Actions.Tag} />
<DeleteAssets <DeleteAssets
menuItem menuItem