mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat(web): manual stacking asset (#4650)
Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
72dcde9e0f
commit
8b5b6d0821
22 changed files with 437 additions and 82 deletions
|
|
@ -0,0 +1,57 @@
|
|||
<script lang="ts">
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { api } from '@api';
|
||||
import { OnStack, getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
|
||||
export let onStack: OnStack | undefined = undefined;
|
||||
|
||||
const { getAssets, clearSelect } = getAssetControlContext();
|
||||
|
||||
const handleStack = async () => {
|
||||
try {
|
||||
const assets = Array.from(getAssets());
|
||||
const parent = assets.at(0);
|
||||
|
||||
if (parent == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const children = assets.slice(1);
|
||||
const ids = children.map(({ id }) => id);
|
||||
|
||||
if (children.length > 0) {
|
||||
await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids, stackParentId: parent.id } });
|
||||
}
|
||||
|
||||
let childrenCount = parent.stackCount ?? 0;
|
||||
for (const asset of children) {
|
||||
asset.stackParentId = parent?.id;
|
||||
// Add grand-children's count to new parent
|
||||
childrenCount += asset.stackCount == null ? 1 : asset.stackCount + 1;
|
||||
// Reset children stack info
|
||||
asset.stackCount = null;
|
||||
asset.stack = [];
|
||||
}
|
||||
|
||||
parent.stackCount = childrenCount;
|
||||
onStack?.(ids);
|
||||
|
||||
notificationController.show({
|
||||
message: `Stacked ${ids.length + 1} assets`,
|
||||
type: NotificationType.Info,
|
||||
timeout: 1500,
|
||||
});
|
||||
|
||||
clearSelect();
|
||||
} catch (error) {
|
||||
handleError(error, `Unable to stack`);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<MenuOption text="Stack" on:click={handleStack} />
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
export let isSelectionMode = false;
|
||||
export let viewport: Viewport;
|
||||
export let singleSelect = false;
|
||||
export let withStacked = false;
|
||||
|
||||
export let assetStore: AssetStore;
|
||||
export let assetInteractionStore: AssetInteractionStore;
|
||||
|
|
@ -178,6 +179,7 @@
|
|||
style="width: {box.width}px; height: {box.height}px; top: {box.top}px; left: {box.left}px"
|
||||
>
|
||||
<Thumbnail
|
||||
showStackedIcon={withStacked}
|
||||
{asset}
|
||||
{groupIndex}
|
||||
on:click={() => assetClickHandler(asset, groupAssets, groupTitle)}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
export let assetStore: AssetStore;
|
||||
export let assetInteractionStore: AssetInteractionStore;
|
||||
export let removeAction: AssetAction | null = null;
|
||||
export let withStacked = false;
|
||||
|
||||
$: isTrashEnabled = $featureFlags.loaded && $featureFlags.trash;
|
||||
export let forceDelete = false;
|
||||
|
|
@ -365,6 +366,7 @@
|
|||
<div id={'bucket_' + bucket.bucketDate} style:height={bucket.bucketHeight + 'px'}>
|
||||
{#if intersecting}
|
||||
<AssetDateGroup
|
||||
{withStacked}
|
||||
{assetStore}
|
||||
{assetInteractionStore}
|
||||
{isSelectionMode}
|
||||
|
|
@ -389,6 +391,7 @@
|
|||
<Portal target="body">
|
||||
{#if $showAssetViewer}
|
||||
<AssetViewer
|
||||
{withStacked}
|
||||
{assetStore}
|
||||
asset={$viewingAsset}
|
||||
force={forceDelete || !isTrashEnabled}
|
||||
|
|
@ -399,6 +402,7 @@
|
|||
on:unarchived={({ detail: asset }) => handleAction(asset, AssetAction.UNARCHIVE)}
|
||||
on:favorite={({ detail: asset }) => handleAction(asset, AssetAction.FAVORITE)}
|
||||
on:unfavorite={({ detail: asset }) => handleAction(asset, AssetAction.UNFAVORITE)}
|
||||
on:unstack={() => handleClose()}
|
||||
/>
|
||||
{/if}
|
||||
</Portal>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
export type OnRestore = (ids: string[]) => void;
|
||||
export type OnArchive = (ids: string[], isArchived: boolean) => void;
|
||||
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
||||
export type OnStack = (ids: string[]) => void;
|
||||
|
||||
export interface AssetControlContext {
|
||||
// Wrap assets in a function, because context isn't reactive.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue