mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor: asset select manager (#27330)
This commit is contained in:
@@ -337,7 +337,7 @@
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="sticky top-0 z-1 dark">
|
||||
<AssetSelectControlBar forceDark>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CreateSharedLink />
|
||||
<IconButton
|
||||
shape="round"
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
</aside>
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
<Portal target="body">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { setAssetControlContext } from '$lib/utils/context';
|
||||
import { mdiClose } from '@mdi/js';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -13,8 +12,6 @@
|
||||
|
||||
let { children, forceDark }: Props = $props();
|
||||
|
||||
setAssetControlContext(assetMultiSelectManager.asControlContext());
|
||||
|
||||
const onClose = () => assetMultiSelectManager.clear();
|
||||
|
||||
const assets = $derived(assetMultiSelectManager.assets);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import type { OnArchive } from '$lib/utils/actions';
|
||||
import { archiveAssets } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { IconButton } from '@immich/ui';
|
||||
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
type Props = {
|
||||
onArchive?: OnArchive;
|
||||
menuItem?: boolean;
|
||||
unarchive?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
let { onArchive, menuItem = false, unarchive = false }: Props = $props();
|
||||
|
||||
@@ -21,16 +21,14 @@
|
||||
|
||||
let loading = $state(false);
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleArchive = async () => {
|
||||
const visibility = unarchive ? AssetVisibility.Timeline : AssetVisibility.Archive;
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.visibility !== visibility);
|
||||
const assets = assetMultiSelectManager.getOwnedAssets().filter((asset) => asset.visibility !== visibility);
|
||||
loading = true;
|
||||
const ids = await archiveAssets(assets, visibility as AssetVisibility);
|
||||
if (ids) {
|
||||
onArchive?.(ids, visibility);
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
}
|
||||
loading = false;
|
||||
};
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import AssetSelectionChangeDateModal from '$lib/modals/AssetSelectionChangeDateModal.svelte';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
|
||||
import { modalManager } from '@immich/ui';
|
||||
import { mdiCalendarEditOutline } from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { t } from 'svelte-i18n';
|
||||
interface Props {
|
||||
|
||||
type Props = {
|
||||
menuItem?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
let { menuItem = false }: Props = $props();
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleChangeDate = async () => {
|
||||
const assets = getOwnedAssets();
|
||||
const assets = assetMultiSelectManager.ownedAssets;
|
||||
const initialDate = assets.length === 1 ? fromTimelinePlainDateTime(assets[0].localDateTime) : DateTime.now();
|
||||
const success = await modalManager.show(AssetSelectionChangeDateModal, {
|
||||
initialDate,
|
||||
assets,
|
||||
});
|
||||
if (success) {
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import AssetUpdateDescriptionConfirmModal from '$lib/modals/AssetUpdateDescriptionConfirmModal.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { modalManager } from '@immich/ui';
|
||||
@@ -15,19 +15,18 @@
|
||||
}
|
||||
|
||||
let { menuItem = false }: Props = $props();
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleUpdateDescription = async () => {
|
||||
const description = await modalManager.show(AssetUpdateDescriptionConfirmModal);
|
||||
if (description) {
|
||||
const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user);
|
||||
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, $user);
|
||||
|
||||
try {
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, description } });
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_change_description'));
|
||||
}
|
||||
clearSelect();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getOwnedAssetsWithWarning } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { modalManager, toastManager } from '@immich/ui';
|
||||
@@ -15,7 +15,6 @@
|
||||
};
|
||||
|
||||
let { menuItem = false }: Props = $props();
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const onAction = async () => {
|
||||
const point = await modalManager.show(GeolocationPointPickerModal, {});
|
||||
@@ -23,12 +22,12 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = getOwnedAssetsWithWarning(getOwnedAssets(), $user);
|
||||
const ids = getOwnedAssetsWithWarning(assetMultiSelectManager.assets, $user);
|
||||
|
||||
try {
|
||||
await updateAssets({ assetBulkUpdateDto: { ids, latitude: point.lat, longitude: point.lng } });
|
||||
toastManager.primary();
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_update_location'));
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { IconButton, modalManager } from '@immich/ui';
|
||||
import { mdiShareVariantOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
const { getAssets } = getAssetControlContext();
|
||||
|
||||
const handleClick = async () => {
|
||||
await modalManager.show(SharedLinkCreateModal, { assetIds: [...getAssets()].map(({ id }) => id) });
|
||||
await modalManager.show(SharedLinkCreateModal, { assetIds: assetMultiSelectManager.assets.map(({ id }) => id) });
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||
import AssetDeleteConfirmModal from '$lib/modals/AssetDeleteConfirmModal.svelte';
|
||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { type OnDelete, type OnUndoDelete, deleteAssets } from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { IconButton, modalManager } from '@immich/ui';
|
||||
import { mdiDeleteForeverOutline, mdiDeleteOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -22,10 +22,8 @@
|
||||
let label = $derived(force ? $t('permanently_delete') : $t('delete'));
|
||||
let loading = $state(false);
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const onAction = async () => {
|
||||
const assets = getOwnedAssets();
|
||||
const assets = assetMultiSelectManager.ownedAssets;
|
||||
|
||||
if (force && $showDeleteModal) {
|
||||
const confirmed = await modalManager.show(AssetDeleteConfirmModal, { size: assets.length });
|
||||
@@ -36,7 +34,7 @@
|
||||
|
||||
loading = true;
|
||||
await deleteAssets(force, onAssetDelete, assets, onUndoDelete);
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
loading = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { handleDownloadAsset } from '$lib/services/asset.service';
|
||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { getAssetInfo } from '@immich/sdk';
|
||||
import { IconButton } from '@immich/ui';
|
||||
import { mdiDownload } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
|
||||
interface Props {
|
||||
filename?: string;
|
||||
@@ -18,18 +18,16 @@
|
||||
|
||||
let { filename = 'immich.zip', menuItem = false }: Props = $props();
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
const handleDownloadFiles = async () => {
|
||||
const assets = [...getAssets()];
|
||||
const assets = assetMultiSelectManager.assets;
|
||||
if (assets.length === 1) {
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
let asset = await getAssetInfo({ ...authManager.params, id: assets[0].id });
|
||||
await handleDownloadAsset(asset, { edited: true });
|
||||
return;
|
||||
}
|
||||
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
await downloadArchive(filename, { assetIds: assets.map((asset) => asset.id) });
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import type { OnFavorite } from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { updateAssets } from '@immich/sdk';
|
||||
import { IconButton, toastManager } from '@immich/ui';
|
||||
@@ -21,14 +21,12 @@
|
||||
|
||||
let loading = $state(false);
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleFavorite = async () => {
|
||||
const isFavorite = !removeFavorite;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
const assets = [...getOwnedAssets()].filter((asset) => asset.isFavorite !== isFavorite);
|
||||
const assets = assetMultiSelectManager.ownedAssets.filter((asset) => asset.isFavorite !== isFavorite);
|
||||
|
||||
const ids = assets.map(({ id }) => id);
|
||||
|
||||
@@ -48,7 +46,7 @@
|
||||
: $t('removed_from_favorites_count', { values: { count: ids.length } }),
|
||||
);
|
||||
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_add_remove_favorites', { values: { favorite: isFavorite } }));
|
||||
} finally {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import type { OnLink, OnUnlink } from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { getAssetInfo, updateAsset } from '@immich/sdk';
|
||||
import { IconButton } from '@immich/ui';
|
||||
import { mdiLinkOff, mdiMotionPlayOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
|
||||
interface Props {
|
||||
onLink: OnLink;
|
||||
@@ -25,12 +25,10 @@
|
||||
let text = $derived(unlink ? $t('unlink_motion_video') : $t('link_motion_video'));
|
||||
let icon = $derived(unlink ? mdiLinkOff : mdiMotionPlayOutline);
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const onClick = () => (unlink ? handleUnlink() : handleLink());
|
||||
|
||||
const handleLink = async () => {
|
||||
let [still, motion] = [...getOwnedAssets()];
|
||||
let [still, motion] = assetMultiSelectManager.ownedAssets;
|
||||
if ((still as TimelineAsset).isVideo) {
|
||||
[still, motion] = [motion, still];
|
||||
}
|
||||
@@ -39,7 +37,7 @@
|
||||
loading = true;
|
||||
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: motion.id } });
|
||||
onLink({ still: toTimelineAsset(stillResponse), motion: motion as TimelineAsset });
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_link_motion_video'));
|
||||
} finally {
|
||||
@@ -48,7 +46,7 @@
|
||||
};
|
||||
|
||||
const handleUnlink = async () => {
|
||||
const [still] = [...getOwnedAssets()];
|
||||
const [still] = assetMultiSelectManager.ownedAssets;
|
||||
if (!still) {
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +59,7 @@
|
||||
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: null } });
|
||||
const motionResponse = await getAssetInfo({ ...authManager.params, id: motionId });
|
||||
onUnlink({ still: toTimelineAsset(stillResponse), motion: toTimelineAsset(motionResponse) });
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_unlink_motion_video'));
|
||||
} finally {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getAlbumInfo, removeAssetFromAlbum, type AlbumResponseDto } from '@immich/sdk';
|
||||
import { IconButton, modalManager, toastManager } from '@immich/ui';
|
||||
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
|
||||
interface Props {
|
||||
album: AlbumResponseDto;
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
let { album = $bindable(), onRemove, assetIds, menuItem = false }: Props = $props();
|
||||
|
||||
const context = getAssetControlContext();
|
||||
|
||||
const removeFromAlbum = async () => {
|
||||
const ids = assetIds ?? context?.getAssets().map(({ id }) => id) ?? [];
|
||||
const ids = assetIds ?? assetMultiSelectManager.assets.map(({ id }) => id) ?? [];
|
||||
|
||||
const isConfirmed = await modalManager.showDialog({
|
||||
prompt: $t('remove_assets_album_confirmation', { values: { count: ids.length } }),
|
||||
@@ -42,7 +40,7 @@
|
||||
const count = results.filter(({ success }) => success).length;
|
||||
toastManager.primary($t('assets_removed_count', { values: { count } }));
|
||||
|
||||
context?.clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.error_removing_assets_from_album'));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { handleRemoveSharedLinkAssets } from '$lib/services/shared-link.service';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { type SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { IconButton } from '@immich/ui';
|
||||
import { mdiDeleteOutline } from '@mdi/js';
|
||||
@@ -13,13 +13,11 @@
|
||||
|
||||
let { sharedLink = $bindable() }: Props = $props();
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
const handleSelect = async () => {
|
||||
const assetIds = getAssets().map(({ id }) => id);
|
||||
const assetIds = assetMultiSelectManager.assets.map(({ id }) => id);
|
||||
const success = await handleRemoveSharedLinkAssets(sharedLink, assetIds);
|
||||
if (success) {
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import type { OnRestore } from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { restoreAssets } from '@immich/sdk';
|
||||
import { Button, toastManager } from '@immich/ui';
|
||||
@@ -13,19 +13,17 @@
|
||||
|
||||
let { onRestore }: Props = $props();
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
let loading = $state(false);
|
||||
|
||||
const handleRestore = async () => {
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
const ids = [...getAssets()].map((a) => a.id);
|
||||
const ids = assetMultiSelectManager.assets.map((a) => a.id);
|
||||
await restoreAssets({ bulkIdsDto: { ids } });
|
||||
onRestore?.(ids);
|
||||
toastManager.primary($t('assets_restored_count', { values: { count: ids.length } }));
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_restore_assets'));
|
||||
} finally {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import type { OnSetVisibility } from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { AssetVisibility, updateAssets } from '@immich/sdk';
|
||||
import { Button, modalManager } from '@immich/ui';
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
let { onVisibilitySet, menuItem = false, unlock = false }: Props = $props();
|
||||
let loading = $state(false);
|
||||
const { getAssets } = getAssetControlContext();
|
||||
|
||||
const setLockedVisibility = async () => {
|
||||
const isConfirmed = await modalManager.showDialog({
|
||||
@@ -33,7 +32,7 @@
|
||||
|
||||
try {
|
||||
loading = true;
|
||||
const assetIds = getAssets().map(({ id }) => id);
|
||||
const assetIds = assetMultiSelectManager.assets.map(({ id }) => id);
|
||||
|
||||
await updateAssets({
|
||||
assetBulkUpdateDto: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import type { OnStack, OnUnstack } from '$lib/utils/actions';
|
||||
import { deleteStack, stackAssets } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { mdiImageMultipleOutline, mdiImageOffOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -15,17 +15,14 @@
|
||||
|
||||
let { unstack = false, onStack, onUnstack }: Props = $props();
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleStack = async () => {
|
||||
const selectedAssets = [...getOwnedAssets()];
|
||||
const result = await stackAssets(selectedAssets);
|
||||
const result = await stackAssets(assetMultiSelectManager.ownedAssets);
|
||||
onStack?.(result);
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
};
|
||||
|
||||
const handleUnstack = async () => {
|
||||
const selectedAssets = [...getOwnedAssets()];
|
||||
const selectedAssets = assetMultiSelectManager.ownedAssets;
|
||||
if (selectedAssets.length !== 1) {
|
||||
return;
|
||||
}
|
||||
@@ -37,7 +34,7 @@
|
||||
if (unstackedAssets) {
|
||||
onUnstack?.(unstackedAssets.map((a) => toTimelineAsset(a)));
|
||||
}
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { IconButton, modalManager } from '@immich/ui';
|
||||
import { mdiTagMultipleOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
|
||||
interface Props {
|
||||
menuItem?: boolean;
|
||||
@@ -16,13 +16,11 @@
|
||||
const text = $t('tag');
|
||||
const icon = mdiTagMultipleOutline;
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
const handleTagAssets = async () => {
|
||||
const assets = [...getOwnedAssets()];
|
||||
const assets = assetMultiSelectManager.ownedAssets;
|
||||
const didUpdate = await modalManager.show(AssetTagModal, { assetIds: assets.map(({ id }) => id) });
|
||||
if (didUpdate) {
|
||||
clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import type { AssetControlContext } from '$lib/types';
|
||||
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||
import { fromStore } from 'svelte/store';
|
||||
@@ -22,7 +21,10 @@ export class AssetMultiSelectManager {
|
||||
candidates = $state<TimelineAsset[]>([]);
|
||||
|
||||
selectionActive = $derived(this.#selectedMap.size > 0);
|
||||
|
||||
assets = $derived(Array.from(this.#selectedMap.values()));
|
||||
ownedAssets = $derived(this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets);
|
||||
|
||||
isAllTrashed = $derived(this.assets.every((asset) => asset.isTrashed));
|
||||
isAllArchived = $derived(this.assets.every((asset) => asset.visibility === AssetVisibility.Archive));
|
||||
isAllFavorite = $derived(this.assets.every((asset) => asset.isFavorite));
|
||||
@@ -41,13 +43,8 @@ export class AssetMultiSelectManager {
|
||||
this.#unsubscribe?.();
|
||||
}
|
||||
|
||||
asControlContext(): AssetControlContext {
|
||||
return {
|
||||
getOwnedAssets: () =>
|
||||
this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets,
|
||||
getAssets: () => this.assets,
|
||||
clearSelect: () => this.clear(),
|
||||
};
|
||||
getOwnedAssets() {
|
||||
return this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets;
|
||||
}
|
||||
|
||||
hasSelectedAsset(assetId: string) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ProjectionType } from '$lib/constants';
|
||||
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
@@ -6,7 +7,6 @@ import AssetAddToAlbumModal from '$lib/modals/AssetAddToAlbumModal.svelte';
|
||||
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
|
||||
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
|
||||
import { user as authUser, preferences } from '$lib/stores/user.store';
|
||||
import type { AssetControlContext } from '$lib/types';
|
||||
import { getAssetMediaUrl, getSharedLink, sleep } from '$lib/utils';
|
||||
import { downloadUrl } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
@@ -48,14 +48,14 @@ import {
|
||||
import type { MessageFormatter } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const getAssetBulkActions = ($t: MessageFormatter, ctx: AssetControlContext) => {
|
||||
const ownedAssets = ctx.getOwnedAssets();
|
||||
export const getAssetBulkActions = ($t: MessageFormatter) => {
|
||||
const ownedAssets = assetMultiSelectManager.ownedAssets;
|
||||
const assetIds = ownedAssets.map((asset) => asset.id);
|
||||
const isAllVideos = ownedAssets.every((asset) => asset.isVideo);
|
||||
|
||||
const onAction = async (name: AssetJobName) => {
|
||||
await handleRunAssetJob({ name, assetIds });
|
||||
ctx.clearSelect();
|
||||
assetMultiSelectManager.clear();
|
||||
};
|
||||
|
||||
const AddToAlbum: ActionItem = {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { AssetControlContext } from '$lib/types';
|
||||
import { getContext, setContext } from 'svelte';
|
||||
|
||||
export function createContext<T>(key: string | symbol = Symbol()) {
|
||||
@@ -7,5 +6,3 @@ export function createContext<T>(key: string | symbol = Symbol()) {
|
||||
set: (context: T) => setContext<T>(key, context),
|
||||
};
|
||||
}
|
||||
|
||||
export const { get: getAssetControlContext, set: setAssetControlContext } = createContext<AssetControlContext>();
|
||||
|
||||
+1
-1
@@ -452,7 +452,7 @@
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<ArchiveAction
|
||||
unarchive
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<!-- Multiselection mode app bar -->
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<FavoriteAction removeFavorite onFavorite={(assetIds) => timelineManager.removeAssets(assetIds)} />
|
||||
<CreateSharedLink />
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<IconButton
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@
|
||||
<header>
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
<CreateSharedLink />
|
||||
|
||||
@@ -319,7 +319,7 @@
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full z-2">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
<CreateSharedLink />
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
{#if assetMultiSelectManager.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full">
|
||||
<AssetSelectControlBar>
|
||||
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t)}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
|
||||
|
||||
Reference in New Issue
Block a user