mirror of
https://github.com/immich-app/immich.git
synced 2026-02-20 18:20:21 -05:00
refactor: add to album (#26366)
This commit is contained in:
parent
8eec3c810e
commit
1d11106dd0
@ -1,6 +1,6 @@
|
||||
import type { AssetAction } from '$lib/constants';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import type { AlbumResponseDto, AssetResponseDto, PersonResponseDto, StackResponseDto } from '@immich/sdk';
|
||||
import type { AssetResponseDto, PersonResponseDto, StackResponseDto } from '@immich/sdk';
|
||||
|
||||
type ActionMap = {
|
||||
[AssetAction.ARCHIVE]: { asset: TimelineAsset };
|
||||
@ -8,7 +8,6 @@ type ActionMap = {
|
||||
[AssetAction.TRASH]: { asset: TimelineAsset };
|
||||
[AssetAction.DELETE]: { asset: TimelineAsset };
|
||||
[AssetAction.RESTORE]: { asset: TimelineAsset };
|
||||
[AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto };
|
||||
[AssetAction.STACK]: { stack: StackResponseDto };
|
||||
[AssetAction.UNSTACK]: { assets: TimelineAsset[] };
|
||||
[AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto };
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
||||
import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
import { modalManager } from '@immich/ui';
|
||||
import { mdiImageAlbum } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
asset: AssetResponseDto;
|
||||
onAction: OnAction;
|
||||
}
|
||||
|
||||
let { asset, onAction }: Props = $props();
|
||||
|
||||
const onClick = async () => {
|
||||
const albums = await modalManager.show(AlbumPickerModal, {});
|
||||
|
||||
if (!albums || albums.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (albums.length === 1) {
|
||||
const album = albums[0];
|
||||
await addAssetsToAlbum(album.id, [asset.id]);
|
||||
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album });
|
||||
} else {
|
||||
await addAssetsToAlbums(
|
||||
albums.map((a) => a.id),
|
||||
[asset.id],
|
||||
);
|
||||
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album: albums[0] });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:document use:shortcut={{ shortcut: { key: 'l' }, onShortcut: onClick }} />
|
||||
|
||||
<MenuOption icon={mdiImageAlbum} text={$t('add_to_album')} {onClick} />
|
||||
@ -3,7 +3,6 @@
|
||||
import ActionButton from '$lib/components/ActionButton.svelte';
|
||||
import ActionMenuItem from '$lib/components/ActionMenuItem.svelte';
|
||||
import type { OnAction, PreAction } from '$lib/components/asset-viewer/actions/action';
|
||||
import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte';
|
||||
import AddToStackAction from '$lib/components/asset-viewer/actions/add-to-stack-action.svelte';
|
||||
import ArchiveAction from '$lib/components/asset-viewer/actions/archive-action.svelte';
|
||||
import DeleteAction from '$lib/components/asset-viewer/actions/delete-action.svelte';
|
||||
@ -102,6 +101,7 @@
|
||||
Unfavorite,
|
||||
PlayMotionPhoto,
|
||||
StopMotionPhoto,
|
||||
AddToAlbum,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
Copy,
|
||||
@ -129,6 +129,7 @@
|
||||
Unfavorite,
|
||||
PlayMotionPhoto,
|
||||
StopMotionPhoto,
|
||||
AddToAlbum,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
Copy,
|
||||
@ -181,14 +182,12 @@
|
||||
<ActionMenuItem action={Download} />
|
||||
<ActionMenuItem action={DownloadOriginal} />
|
||||
|
||||
{#if !isLocked}
|
||||
{#if asset.isTrashed}
|
||||
<RestoreAction {asset} {onAction} />
|
||||
{:else}
|
||||
<AddToAlbumAction {asset} {onAction} />
|
||||
{/if}
|
||||
{#if !isLocked && asset.isTrashed}
|
||||
<RestoreAction {asset} {onAction} />
|
||||
{/if}
|
||||
|
||||
<ActionMenuItem action={AddToAlbum} />
|
||||
|
||||
{#if isOwner}
|
||||
<AddToStackAction {asset} {stack} {onAction} />
|
||||
{#if stack}
|
||||
|
||||
@ -167,9 +167,7 @@
|
||||
}),
|
||||
);
|
||||
|
||||
if (!sharedLink) {
|
||||
await handleGetAllAlbums();
|
||||
}
|
||||
await onAlbumAddAssets();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@ -182,7 +180,7 @@
|
||||
syncAssetViewerOpenClass(false);
|
||||
});
|
||||
|
||||
const handleGetAllAlbums = async () => {
|
||||
const onAlbumAddAssets = async () => {
|
||||
if (authManager.isSharedLink) {
|
||||
return;
|
||||
}
|
||||
@ -303,10 +301,6 @@
|
||||
};
|
||||
const handleAction = async (action: Action) => {
|
||||
switch (action.type) {
|
||||
case AssetAction.ADD_TO_ALBUM: {
|
||||
await handleGetAllAlbums();
|
||||
break;
|
||||
}
|
||||
case AssetAction.DELETE:
|
||||
case AssetAction.TRASH: {
|
||||
eventManager.emit('AssetsDelete', [asset.id]);
|
||||
@ -369,7 +363,7 @@
|
||||
|
||||
const refresh = async () => {
|
||||
await refreshStack();
|
||||
await handleGetAllAlbums();
|
||||
await onAlbumAddAssets();
|
||||
ocrManager.clear();
|
||||
if (!sharedLink) {
|
||||
if (previewStackedAsset) {
|
||||
@ -441,7 +435,7 @@
|
||||
</script>
|
||||
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={[Tag]} />
|
||||
<OnEvents {onAssetReplace} {onAssetUpdate} />
|
||||
<OnEvents {onAssetReplace} {onAssetUpdate} {onAlbumAddAssets} />
|
||||
|
||||
<svelte:document bind:fullscreenElement />
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -25,6 +24,7 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
|
||||
import { Route } from '$lib/route';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { memoryStore, type MemoryAsset } from '$lib/stores/memory.store.svelte';
|
||||
@ -34,7 +34,7 @@
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { AssetMediaSize, AssetTypeEnum, getAssetInfo } from '@immich/sdk';
|
||||
import { IconButton, toastManager } from '@immich/ui';
|
||||
import { ActionButton, IconButton, toastManager } from '@immich/ui';
|
||||
import {
|
||||
mdiCardsOutline,
|
||||
mdiChevronDown,
|
||||
@ -328,6 +328,7 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => cancelMultiselect(assetInteraction)}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CreateSharedLink />
|
||||
<IconButton
|
||||
shape="round"
|
||||
@ -338,7 +339,7 @@
|
||||
onclick={handleSelectAll}
|
||||
/>
|
||||
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
|
||||
<FavoriteAction removeFavorite={assetInteraction.isAllFavorite} />
|
||||
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
||||
import type { OnAddToAlbum } from '$lib/utils/actions';
|
||||
import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { IconButton, modalManager } from '@immich/ui';
|
||||
import { mdiImageAlbum, mdiPlus } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
onAddToAlbum?: OnAddToAlbum;
|
||||
menuItem?: boolean;
|
||||
}
|
||||
|
||||
let { onAddToAlbum = () => {}, menuItem = false }: Props = $props();
|
||||
|
||||
const { getAssets } = getAssetControlContext();
|
||||
|
||||
const onClick = async () => {
|
||||
const albums = await modalManager.show(AlbumPickerModal, {});
|
||||
if (!albums || albums.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetIds = [...getAssets()].map(({ id }) => id);
|
||||
if (albums.length === 1) {
|
||||
const album = albums[0];
|
||||
await addAssetsToAlbum(album.id, assetIds);
|
||||
onAddToAlbum(assetIds, album.id);
|
||||
} else {
|
||||
await addAssetsToAlbums(
|
||||
albums.map(({ id }) => id),
|
||||
assetIds,
|
||||
);
|
||||
onAddToAlbum(assetIds, albums[0].id);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if menuItem}
|
||||
<MenuOption {onClick} text={$t('add_to_album')} icon={mdiImageAlbum} shortcut={{ key: 'l' }} />
|
||||
{/if}
|
||||
|
||||
{#if !menuItem}
|
||||
<IconButton
|
||||
shape="round"
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
icon={mdiPlus}
|
||||
aria-label={$t('add_to_album')}
|
||||
onclick={onClick}
|
||||
/>
|
||||
{/if}
|
||||
@ -6,7 +6,6 @@ export enum AssetAction {
|
||||
TRASH = 'trash',
|
||||
DELETE = 'delete',
|
||||
RESTORE = 'restore',
|
||||
ADD_TO_ALBUM = 'add-to-album',
|
||||
STACK = 'stack',
|
||||
UNSTACK = 'unstack',
|
||||
SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset',
|
||||
|
||||
@ -39,7 +39,7 @@ export type Events = {
|
||||
AssetEditsApplied: [string];
|
||||
AssetsTag: [string[]];
|
||||
|
||||
AlbumAddAssets: [];
|
||||
AlbumAddAssets: [{ assetIds: string[]; albumIds: string[] }];
|
||||
AlbumUpdate: [AlbumResponseDto];
|
||||
AlbumDelete: [AlbumResponseDto];
|
||||
AlbumShare: [];
|
||||
|
||||
27
web/src/lib/modals/AssetAddToAlbumModal.svelte
Normal file
27
web/src/lib/modals/AssetAddToAlbumModal.svelte
Normal file
@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
||||
import { addAssetsToAlbums } from '$lib/services/album.service';
|
||||
import { type AlbumResponseDto } from '@immich/sdk';
|
||||
|
||||
type Props = {
|
||||
assetIds: string[];
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const { assetIds, onClose }: Props = $props();
|
||||
|
||||
const handleClose = async (albums?: AlbumResponseDto[]) => {
|
||||
const albumIds = (albums ?? []).map(({ id }) => id);
|
||||
if (albumIds.length === 0) {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await addAssetsToAlbums(albumIds, assetIds, { notify: true });
|
||||
if (success) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<AlbumPickerModal onClose={handleClose} />
|
||||
@ -1,6 +1,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import ToastAction from '$lib/components/ToastAction.svelte';
|
||||
import { AlbumPageViewMode } from '$lib/constants';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import AlbumAddUsersModal from '$lib/modals/AlbumAddUsersModal.svelte';
|
||||
@ -11,17 +12,22 @@ import { user } from '$lib/stores/user.store';
|
||||
import { createAlbumAndRedirect } from '$lib/utils/album-utils';
|
||||
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
import {
|
||||
addAssetsToAlbum,
|
||||
addAssetsToAlbum as addToAlbum,
|
||||
addAssetsToAlbums as addToAlbums,
|
||||
addUsersToAlbum,
|
||||
AlbumUserRole,
|
||||
BulkIdErrorReason,
|
||||
deleteAlbum,
|
||||
removeUserFromAlbum,
|
||||
updateAlbumInfo,
|
||||
updateAlbumUser,
|
||||
type AlbumResponseDto,
|
||||
type AlbumsAddAssetsResponseDto,
|
||||
type BulkIdResponseDto,
|
||||
type UpdateAlbumDto,
|
||||
type UserResponseDto,
|
||||
} from '@immich/sdk';
|
||||
@ -86,7 +92,12 @@ export const getAlbumAssetsActions = ($t: MessageFormatter, album: AlbumResponse
|
||||
color: 'primary',
|
||||
icon: mdiPlusBoxOutline,
|
||||
$if: () => assets.length > 0,
|
||||
onAction: () => addAssets(album, assets),
|
||||
onAction: () =>
|
||||
addAssetsToAlbums(
|
||||
[album.id],
|
||||
assets.map(({ id }) => id),
|
||||
{ notify: true },
|
||||
).then(() => undefined),
|
||||
};
|
||||
|
||||
const Upload: ActionItem = {
|
||||
@ -100,18 +111,73 @@ export const getAlbumAssetsActions = ($t: MessageFormatter, album: AlbumResponse
|
||||
return { AddAssets, Upload };
|
||||
};
|
||||
|
||||
const addAssets = async (album: AlbumResponseDto, assets: TimelineAsset[]) => {
|
||||
export const addAssetsToAlbums = async (albumIds: string[], assetIds: string[], { notify }: { notify: boolean }) => {
|
||||
const $t = await getFormatter();
|
||||
const assetIds = assets.map(({ id }) => id);
|
||||
|
||||
try {
|
||||
const results = await addAssetsToAlbum({ id: album.id, bulkIdsDto: { ids: assetIds } });
|
||||
if (albumIds.length === 1) {
|
||||
const albumId = albumIds[0];
|
||||
const results = await addToAlbum({ ...authManager.params, id: albumId, bulkIdsDto: { ids: assetIds } });
|
||||
if (notify) {
|
||||
notifyAddToAlbum($t, albumId, assetIds, results);
|
||||
}
|
||||
}
|
||||
|
||||
const count = results.filter(({ success }) => success).length;
|
||||
toastManager.success($t('assets_added_count', { values: { count } }));
|
||||
eventManager.emit('AlbumAddAssets');
|
||||
if (albumIds.length > 1) {
|
||||
const results = await addToAlbums({ ...authManager.params, albumsAddAssetsDto: { albumIds, assetIds } });
|
||||
if (notify) {
|
||||
notifyAddToAlbums($t, albumIds, assetIds, results);
|
||||
}
|
||||
}
|
||||
|
||||
eventManager.emit('AlbumAddAssets', { assetIds, albumIds });
|
||||
return true;
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.error_adding_assets_to_album'));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const notifyAddToAlbum = ($t: MessageFormatter, albumId: string, assetIds: string[], results: BulkIdResponseDto[]) => {
|
||||
const successCount = results.filter(({ success }) => success).length;
|
||||
const duplicateCount = results.filter(({ error }) => error === 'duplicate').length;
|
||||
let description = $t('assets_cannot_be_added_to_album_count', { values: { count: assetIds.length } });
|
||||
if (successCount > 0) {
|
||||
description = $t('assets_added_to_album_count', { values: { count: successCount } });
|
||||
} else if (duplicateCount > 0) {
|
||||
description = $t('assets_were_part_of_album_count', { values: { count: duplicateCount } });
|
||||
}
|
||||
|
||||
toastManager.custom(
|
||||
{
|
||||
component: ToastAction,
|
||||
props: {
|
||||
title: $t('info'),
|
||||
color: 'primary',
|
||||
description,
|
||||
button: { text: $t('view_album'), color: 'primary', onClick: () => goto(Route.viewAlbum({ id: albumId })) },
|
||||
},
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
};
|
||||
|
||||
const notifyAddToAlbums = (
|
||||
$t: MessageFormatter,
|
||||
albumIds: string[],
|
||||
assetIds: string[],
|
||||
results: AlbumsAddAssetsResponseDto,
|
||||
) => {
|
||||
if (results.error === BulkIdErrorReason.Duplicate) {
|
||||
toastManager.info($t('assets_were_part_of_albums_count', { values: { count: assetIds.length } }));
|
||||
} else if (results.error) {
|
||||
toastManager.warning($t('assets_cannot_be_added_to_albums', { values: { count: assetIds.length } }));
|
||||
} else {
|
||||
toastManager.success(
|
||||
$t('assets_added_to_albums_count', {
|
||||
values: { albumTotal: albumIds.length, assetTotal: assetIds.length },
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import { ProjectionType } from '$lib/constants';
|
||||
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';
|
||||
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';
|
||||
@ -42,6 +43,7 @@ import {
|
||||
mdiMagnifyPlusOutline,
|
||||
mdiMotionPauseOutline,
|
||||
mdiMotionPlayOutline,
|
||||
mdiPlus,
|
||||
mdiShareVariantOutline,
|
||||
mdiTagPlusOutline,
|
||||
mdiTune,
|
||||
@ -59,6 +61,13 @@ export const getAssetBulkActions = ($t: MessageFormatter, ctx: AssetControlConte
|
||||
ctx.clearSelect();
|
||||
};
|
||||
|
||||
const AddToAlbum: ActionItem = {
|
||||
title: $t('add_to_album'),
|
||||
icon: mdiPlus,
|
||||
shortcuts: [{ key: 'l' }],
|
||||
onAction: () => modalManager.show(AssetAddToAlbumModal, { assetIds }),
|
||||
};
|
||||
|
||||
const RefreshFacesJob: ActionItem = {
|
||||
title: $t('refresh_faces'),
|
||||
icon: mdiHeadSyncOutline,
|
||||
@ -84,7 +93,7 @@ export const getAssetBulkActions = ($t: MessageFormatter, ctx: AssetControlConte
|
||||
$if: () => isAllVideos,
|
||||
};
|
||||
|
||||
return { RefreshFacesJob, RefreshMetadataJob, RegenerateThumbnailJob, TranscodeVideoJob };
|
||||
return { AddToAlbum, RefreshFacesJob, RefreshMetadataJob, RegenerateThumbnailJob, TranscodeVideoJob };
|
||||
};
|
||||
|
||||
export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) => {
|
||||
@ -161,6 +170,14 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
|
||||
shortcuts: [{ key: 'f' }],
|
||||
};
|
||||
|
||||
const AddToAlbum: ActionItem = {
|
||||
title: $t('add_to_album'),
|
||||
icon: mdiPlus,
|
||||
shortcuts: [{ key: 'l' }],
|
||||
$if: () => asset.visibility !== AssetVisibility.Locked && !asset.isTrashed,
|
||||
onAction: () => modalManager.show(AssetAddToAlbumModal, { assetIds: [asset.id] }),
|
||||
};
|
||||
|
||||
const Offline: ActionItem = {
|
||||
title: $t('asset_offline'),
|
||||
icon: mdiAlertOutline,
|
||||
@ -260,6 +277,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
|
||||
Unfavorite,
|
||||
PlayMotionPhoto,
|
||||
StopMotionPhoto,
|
||||
AddToAlbum,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
Copy,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
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,6 +23,14 @@ export class AssetInteraction {
|
||||
private user = fromStore<UserAdminResponseDto | undefined>(user);
|
||||
private userId = $derived(this.user.current?.id);
|
||||
|
||||
asControlContext(): AssetControlContext {
|
||||
return {
|
||||
getOwnedAssets: () => this.selectedAssets.filter((asset) => asset.ownerId === this.userId),
|
||||
getAssets: () => this.selectedAssets,
|
||||
clearSelect: () => this.clearMultiselect(),
|
||||
};
|
||||
}
|
||||
|
||||
isAllTrashed = $derived(this.selectedAssets.every((asset) => asset.isTrashed));
|
||||
isAllArchived = $derived(this.selectedAssets.every((asset) => asset.visibility === AssetVisibility.Archive));
|
||||
isAllFavorite = $derived(this.selectedAssets.every((asset) => asset.isFavorite));
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import ToastAction from '$lib/components/ToastAction.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { Route } from '$lib/route';
|
||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { downloadRequest, withError } from '$lib/utils';
|
||||
@ -13,10 +11,7 @@ import { getFormatter } from '$lib/utils/i18n';
|
||||
import { navigate } from '$lib/utils/navigation';
|
||||
import { asQueryString } from '$lib/utils/shared-links';
|
||||
import {
|
||||
addAssetsToAlbum as addAssets,
|
||||
addAssetsToAlbums as addToAlbums,
|
||||
AssetVisibility,
|
||||
BulkIdErrorReason,
|
||||
bulkTagAssets,
|
||||
createStack,
|
||||
deleteAssets,
|
||||
@ -41,77 +36,6 @@ import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export const addAssetsToAlbum = async (albumId: string, assetIds: string[], showNotification = true) => {
|
||||
const result = await addAssets({
|
||||
...authManager.params,
|
||||
id: albumId,
|
||||
bulkIdsDto: {
|
||||
ids: assetIds,
|
||||
},
|
||||
});
|
||||
const count = result.filter(({ success }) => success).length;
|
||||
const duplicateErrorCount = result.filter(({ error }) => error === 'duplicate').length;
|
||||
const $t = get(t);
|
||||
|
||||
if (showNotification) {
|
||||
let description = $t('assets_cannot_be_added_to_album_count', { values: { count: assetIds.length } });
|
||||
if (count > 0) {
|
||||
description = $t('assets_added_to_album_count', { values: { count } });
|
||||
} else if (duplicateErrorCount > 0) {
|
||||
description = $t('assets_were_part_of_album_count', { values: { count: duplicateErrorCount } });
|
||||
}
|
||||
toastManager.custom(
|
||||
{
|
||||
component: ToastAction,
|
||||
props: {
|
||||
title: $t('info'),
|
||||
color: 'primary',
|
||||
description,
|
||||
button: {
|
||||
text: $t('view_album'),
|
||||
color: 'primary',
|
||||
onClick() {
|
||||
return goto(Route.viewAlbum({ id: albumId }));
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const addAssetsToAlbums = async (albumIds: string[], assetIds: string[], showNotification = true) => {
|
||||
const result = await addToAlbums({
|
||||
...authManager.params,
|
||||
albumsAddAssetsDto: {
|
||||
albumIds,
|
||||
assetIds,
|
||||
},
|
||||
});
|
||||
|
||||
if (!showNotification) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (showNotification) {
|
||||
const $t = get(t);
|
||||
|
||||
if (result.error === BulkIdErrorReason.Duplicate) {
|
||||
toastManager.info($t('assets_were_part_of_albums_count', { values: { count: assetIds.length } }));
|
||||
return result;
|
||||
}
|
||||
if (result.error) {
|
||||
toastManager.warning($t('assets_cannot_be_added_to_albums', { values: { count: assetIds.length } }));
|
||||
return result;
|
||||
}
|
||||
toastManager.success(
|
||||
$t('assets_added_to_albums_count', { values: { albumTotal: albumIds.length, assetTotal: assetIds.length } }),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export const tagAssets = async ({
|
||||
assetIds,
|
||||
tagIds,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
||||
import { addAssetsToAlbums } from '$lib/services/album.service';
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { UploadState } from '$lib/types';
|
||||
import { uploadRequest } from '$lib/utils';
|
||||
import { addAssetsToAlbum } from '$lib/utils/asset-utils';
|
||||
import { ExecutorQueue } from '$lib/utils/executor-queue';
|
||||
import { asQueryString } from '$lib/utils/shared-links';
|
||||
import {
|
||||
@ -213,7 +213,7 @@ async function fileUploader({
|
||||
|
||||
if (albumId) {
|
||||
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_adding_to_album') });
|
||||
await addAssetsToAlbum(albumId, [responseData.id], false);
|
||||
await addAssetsToAlbums([albumId], [responseData.id], { notify: false });
|
||||
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_added_to_album') });
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -45,6 +44,7 @@
|
||||
handleDownloadAlbum,
|
||||
} from '$lib/services/album.service';
|
||||
import { getGlobalActions } from '$lib/services/app.service';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
@ -438,9 +438,11 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte';
|
||||
import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte';
|
||||
@ -15,8 +14,10 @@
|
||||
|
||||
import SetVisibilityAction from '$lib/components/timeline/actions/SetVisibilityAction.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
@ -64,13 +65,15 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<ArchiveAction
|
||||
unarchive
|
||||
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||
/>
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={(ids, isFavorite) => timelineManager.update(ids, (asset) => (asset.isFavorite = isFavorite))}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -17,8 +16,10 @@
|
||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
@ -68,10 +69,12 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<FavoriteAction removeFavorite onFavorite={(assetIds) => timelineManager.removeAssets(assetIds)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
import TreeItemThumbnails from '$lib/components/shared-components/tree/tree-item-thumbnails.svelte';
|
||||
import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte';
|
||||
import Sidebar from '$lib/components/sidebar/sidebar.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -27,10 +26,9 @@
|
||||
import { foldersStore } from '$lib/stores/folders.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { joinPaths } from '$lib/utils/tree-utils';
|
||||
import { IconButton, Text } from '@immich/ui';
|
||||
import { ActionButton, CommandPaletteDefaultProvider, IconButton, Text } from '@immich/ui';
|
||||
import { mdiDotsVertical, mdiFolder, mdiFolderHome, mdiFolderOutline, mdiSelectAll } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
@ -119,8 +117,8 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => cancelMultiselect(assetInteraction)}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, getAssetControlContext())}
|
||||
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<IconButton
|
||||
shape="round"
|
||||
@ -130,7 +128,7 @@
|
||||
icon={mdiSelectAll}
|
||||
onclick={handleSelectAllAssets}
|
||||
/>
|
||||
<AddToAlbum onAddToAlbum={() => cancelMultiselect(assetInteraction)} />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={function handleFavoriteUpdate(ids, isFavorite) {
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte';
|
||||
import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
||||
import { mdiArrowLeft } from '@mdi/js';
|
||||
import type { PageData } from './$types';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
@ -44,8 +45,10 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<DownloadAction />
|
||||
</AssetSelectControlBar>
|
||||
{:else}
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -31,6 +30,7 @@
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { getPersonActions } from '$lib/services/person.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
@ -40,7 +40,15 @@
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isExternalUrl } from '$lib/utils/navigation';
|
||||
import { AssetVisibility, searchPerson, updatePerson, type PersonResponseDto } from '@immich/sdk';
|
||||
import { ContextMenuButton, LoadingSpinner, modalManager, toastManager, type ActionItem } from '@immich/ui';
|
||||
import {
|
||||
ActionButton,
|
||||
CommandPaletteDefaultProvider,
|
||||
ContextMenuButton,
|
||||
LoadingSpinner,
|
||||
modalManager,
|
||||
toastManager,
|
||||
type ActionItem,
|
||||
} from '@immich/ui';
|
||||
import { mdiAccountBoxOutline, mdiAccountMultipleCheckOutline, mdiArrowLeft, mdiDotsVertical } from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onMount } from 'svelte';
|
||||
@ -455,9 +463,11 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={(ids, isFavorite) => timelineManager.update(ids, (asset) => (asset.isFavorite = isFavorite))}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -36,12 +35,11 @@
|
||||
type OnLink,
|
||||
type OnUnlink,
|
||||
} from '$lib/utils/actions';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { ImageCarousel } from '@immich/ui';
|
||||
import { ActionButton, CommandPaletteDefaultProvider, ImageCarousel } from '@immich/ui';
|
||||
import { mdiDotsVertical } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
@ -130,11 +128,12 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, getAssetControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
|
||||
{#if isAllUserOwned}
|
||||
<FavoriteAction
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
import { afterNavigate, goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import ActionMenuItem from '$lib/components/ActionMenuItem.svelte';
|
||||
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -28,7 +28,6 @@
|
||||
import { preferences, user } from '$lib/stores/user.store';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { getAssetControlContext } from '$lib/utils/context';
|
||||
import { parseUtcDate } from '$lib/utils/date-time';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
|
||||
@ -43,7 +42,7 @@
|
||||
searchSmart,
|
||||
type SmartSearchDto,
|
||||
} from '@immich/sdk';
|
||||
import { Icon, IconButton, LoadingSpinner } from '@immich/ui';
|
||||
import { ActionButton, CommandPaletteDefaultProvider, Icon, IconButton, LoadingSpinner } from '@immich/ui';
|
||||
import { mdiArrowLeft, mdiDotsVertical, mdiImageOffOutline, mdiSelectAll } from '@mdi/js';
|
||||
import { tick, untrack } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
@ -232,7 +231,7 @@
|
||||
return tagNames.join(', ');
|
||||
}
|
||||
|
||||
const onAddToAlbum = (assetIds: string[]) => {
|
||||
const onAlbumAddAssets = ({ assetIds }: { assetIds: string[] }) => {
|
||||
cancelMultiselect(assetInteraction);
|
||||
|
||||
if (terms.isNotInAlbum.toString() == 'true') {
|
||||
@ -248,6 +247,8 @@
|
||||
|
||||
<svelte:window bind:scrollY />
|
||||
|
||||
<OnEvents {onAlbumAddAssets} />
|
||||
|
||||
{#if terms}
|
||||
<section
|
||||
id="search-chips"
|
||||
@ -328,7 +329,8 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => cancelMultiselect(assetInteraction)}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, getAssetControlContext())}
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
|
||||
<CreateSharedLink />
|
||||
<IconButton
|
||||
@ -339,7 +341,7 @@
|
||||
icon={mdiSelectAll}
|
||||
onclick={handleSelectAll}
|
||||
/>
|
||||
<AddToAlbum {onAddToAlbum} />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
{#if isAllUserOwned}
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
@ -354,7 +356,7 @@
|
||||
/>
|
||||
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<AddToAlbum menuItem {onAddToAlbum} />
|
||||
<ActionMenuItem action={Actions.AddToAlbum} />
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
<ChangeDescription menuItem />
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
import Sidebar from '$lib/components/sidebar/sidebar.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||
@ -25,12 +24,13 @@
|
||||
import SkipLink from '$lib/elements/SkipLink.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { getAssetBulkActions } from '$lib/services/asset.service';
|
||||
import { getTagActions } from '$lib/services/tag.service';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { preferences, user } from '$lib/stores/user.store';
|
||||
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
|
||||
import { getAllTags, type TagResponseDto } from '@immich/sdk';
|
||||
import { Text } from '@immich/ui';
|
||||
import { ActionButton, CommandPaletteDefaultProvider, Text } from '@immich/ui';
|
||||
import { mdiDotsVertical, mdiTag, mdiTagMultiple } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
@ -120,9 +120,11 @@
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
{@const Actions = getAssetBulkActions($t, assetInteraction.asControlContext())}
|
||||
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||
<AddToAlbum />
|
||||
<ActionButton action={Actions.AddToAlbum} />
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={(ids, isFavorite) => timelineManager.update(ids, (asset) => (asset.isFavorite = isFavorite))}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user