拆分编辑器素材库模型
抽出素材库分组和本地状态变更规则 补充素材库模型单测 更新 TRACKING.md 记录第三十七阶段验证
This commit is contained in:
187
src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts
Normal file
187
src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type {
|
||||
EditorAsset,
|
||||
EditorAssetFolder,
|
||||
} from './ImageCanvasEditorTypes';
|
||||
import {
|
||||
areAllSelectableAssetsSelected,
|
||||
createLocalAssetFolder,
|
||||
deleteAssetFolderLocally,
|
||||
getSelectableAssets,
|
||||
groupAssetsByFolder,
|
||||
moveAssetToFolderLocally,
|
||||
removeAssetById,
|
||||
removeSelectedAssets,
|
||||
renameAssetById,
|
||||
renameAssetFolderById,
|
||||
replaceLocalAssetFolder,
|
||||
resolveAllAssetSelection,
|
||||
resolveDefaultAssetFolder,
|
||||
toggleAssetFolderCollapsed,
|
||||
toggleAssetSelection,
|
||||
} from './ImageCanvasAssetLibraryModel';
|
||||
|
||||
function createFolder(
|
||||
overrides: Partial<EditorAssetFolder> = {},
|
||||
): EditorAssetFolder {
|
||||
return {
|
||||
id: 'project',
|
||||
label: '项目素材',
|
||||
collapsed: false,
|
||||
systemDefault: true,
|
||||
persisted: true,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createAsset(overrides: Partial<EditorAsset> = {}): EditorAsset {
|
||||
return {
|
||||
id: 'asset-a',
|
||||
label: '素材A',
|
||||
src: 'data:image/png;base64,YQ==',
|
||||
width: 320,
|
||||
height: 240,
|
||||
folderId: 'project',
|
||||
sourceKind: 'uploaded',
|
||||
sourceType: 'uploaded',
|
||||
persisted: true,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('ImageCanvasAssetLibraryModel', () => {
|
||||
it('groups assets by folder and resolves selectable uploaded assets', () => {
|
||||
const folders = [
|
||||
createFolder(),
|
||||
createFolder({ id: 'folder-role', label: '角色素材' }),
|
||||
];
|
||||
const assets = [
|
||||
createAsset({ id: 'asset-a', folderId: 'project' }),
|
||||
createAsset({
|
||||
id: 'asset-b',
|
||||
label: '素材B',
|
||||
folderId: 'folder-role',
|
||||
}),
|
||||
createAsset({
|
||||
id: 'built-in',
|
||||
label: '内置素材',
|
||||
sourceKind: 'built-in',
|
||||
}),
|
||||
];
|
||||
|
||||
expect(groupAssetsByFolder(folders, assets)).toMatchObject([
|
||||
{ id: 'project', assets: [{ id: 'asset-a' }, { id: 'built-in' }] },
|
||||
{ id: 'folder-role', assets: [{ id: 'asset-b' }] },
|
||||
]);
|
||||
expect(getSelectableAssets(assets).map((asset) => asset.id)).toEqual([
|
||||
'asset-a',
|
||||
'asset-b',
|
||||
]);
|
||||
});
|
||||
|
||||
it('renames assets, toggles folders and creates local folders', () => {
|
||||
expect(renameAssetById([createAsset()], 'asset-a', '新名字')[0]).toMatchObject(
|
||||
{ label: '新名字' },
|
||||
);
|
||||
expect(
|
||||
renameAssetFolderById([createFolder()], 'project', '默认素材')[0],
|
||||
).toMatchObject({ label: '默认素材' });
|
||||
expect(toggleAssetFolderCollapsed([createFolder()], 'project')[0]).toMatchObject(
|
||||
{ collapsed: true },
|
||||
);
|
||||
expect(
|
||||
createLocalAssetFolder({
|
||||
folderId: 'folder-local',
|
||||
label: '角色素材',
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'folder-local',
|
||||
label: '角色素材',
|
||||
collapsed: false,
|
||||
systemDefault: false,
|
||||
persisted: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces local folder ids in folders and contained assets', () => {
|
||||
const result = replaceLocalAssetFolder({
|
||||
folders: [createFolder(), createFolder({ id: 'folder-local' })],
|
||||
assets: [createAsset({ folderId: 'folder-local' })],
|
||||
localFolderId: 'folder-local',
|
||||
persistedFolder: {
|
||||
folderId: 'folder-role',
|
||||
label: '角色素材',
|
||||
collapsed: false,
|
||||
systemDefault: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.folders[1]).toEqual({
|
||||
id: 'folder-role',
|
||||
label: '角色素材',
|
||||
collapsed: false,
|
||||
systemDefault: false,
|
||||
persisted: true,
|
||||
});
|
||||
expect(result.assets[0]).toMatchObject({ folderId: 'folder-role' });
|
||||
});
|
||||
|
||||
it('deletes assets and moves deleted folder contents to the default folder', () => {
|
||||
const folders = [
|
||||
createFolder(),
|
||||
createFolder({ id: 'folder-role', systemDefault: false }),
|
||||
];
|
||||
const assets = [
|
||||
createAsset({ id: 'asset-a', folderId: 'folder-role' }),
|
||||
createAsset({ id: 'asset-b', folderId: 'project' }),
|
||||
];
|
||||
|
||||
expect(removeAssetById(assets, 'asset-a').map((asset) => asset.id)).toEqual([
|
||||
'asset-b',
|
||||
]);
|
||||
expect(resolveDefaultAssetFolder(folders)?.id).toBe('project');
|
||||
expect(
|
||||
deleteAssetFolderLocally({
|
||||
folders,
|
||||
assets,
|
||||
folderId: 'folder-role',
|
||||
defaultFolderId: 'project',
|
||||
}),
|
||||
).toMatchObject({
|
||||
folders: [{ id: 'project' }],
|
||||
assets: [
|
||||
{ id: 'asset-a', folderId: 'project' },
|
||||
{ id: 'asset-b', folderId: 'project' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('toggles selection, selects all uploaded assets and removes selected assets', () => {
|
||||
const assets = [
|
||||
createAsset({ id: 'asset-a' }),
|
||||
createAsset({ id: 'asset-b' }),
|
||||
];
|
||||
const selected = toggleAssetSelection(new Set<string>(), 'asset-a');
|
||||
|
||||
expect([...selected]).toEqual(['asset-a']);
|
||||
expect([...toggleAssetSelection(selected, 'asset-a')]).toEqual([]);
|
||||
expect(areAllSelectableAssetsSelected(assets, new Set(['asset-a']))).toBe(
|
||||
false,
|
||||
);
|
||||
const allSelected = resolveAllAssetSelection({
|
||||
allSelectableAssetsSelected: false,
|
||||
selectableAssets: assets,
|
||||
});
|
||||
expect([...allSelected]).toEqual(['asset-a', 'asset-b']);
|
||||
const removal = removeSelectedAssets(assets, new Set(['asset-b']));
|
||||
expect(removal.assets.map((asset) => asset.id)).toEqual(['asset-a']);
|
||||
expect(removal.deletedAssets.map((asset) => asset.id)).toEqual(['asset-b']);
|
||||
});
|
||||
|
||||
it('moves assets between folders locally', () => {
|
||||
expect(
|
||||
moveAssetToFolderLocally([createAsset()], 'asset-a', 'folder-role')[0],
|
||||
).toMatchObject({ folderId: 'folder-role' });
|
||||
});
|
||||
});
|
||||
216
src/components/image-editor/ImageCanvasAssetLibraryModel.ts
Normal file
216
src/components/image-editor/ImageCanvasAssetLibraryModel.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import type {
|
||||
EditorAsset,
|
||||
EditorAssetFolder,
|
||||
} from './ImageCanvasEditorTypes';
|
||||
|
||||
export type GroupedAssetFolder = EditorAssetFolder & {
|
||||
assets: EditorAsset[];
|
||||
};
|
||||
|
||||
export function groupAssetsByFolder(
|
||||
assetFolders: EditorAssetFolder[],
|
||||
assets: EditorAsset[],
|
||||
): GroupedAssetFolder[] {
|
||||
return assetFolders.map((folder) => ({
|
||||
...folder,
|
||||
assets: assets.filter((asset) => asset.folderId === folder.id),
|
||||
}));
|
||||
}
|
||||
|
||||
export function getSelectableAssets(assets: EditorAsset[]) {
|
||||
return assets.filter((asset) => asset.sourceKind === 'uploaded');
|
||||
}
|
||||
|
||||
export function areAllSelectableAssetsSelected(
|
||||
selectableAssets: EditorAsset[],
|
||||
selectedAssetIds: Set<string>,
|
||||
) {
|
||||
return (
|
||||
selectableAssets.length > 0 &&
|
||||
selectableAssets.every((asset) => selectedAssetIds.has(asset.id))
|
||||
);
|
||||
}
|
||||
|
||||
export function renameAssetById(
|
||||
assets: EditorAsset[],
|
||||
assetId: string,
|
||||
label: string,
|
||||
) {
|
||||
return assets.map((asset) =>
|
||||
asset.id === assetId
|
||||
? {
|
||||
...asset,
|
||||
label,
|
||||
}
|
||||
: asset,
|
||||
);
|
||||
}
|
||||
|
||||
export function toggleAssetFolderCollapsed(
|
||||
assetFolders: EditorAssetFolder[],
|
||||
folderId: string,
|
||||
) {
|
||||
return assetFolders.map((folder) =>
|
||||
folder.id === folderId
|
||||
? {
|
||||
...folder,
|
||||
collapsed: !folder.collapsed,
|
||||
}
|
||||
: folder,
|
||||
);
|
||||
}
|
||||
|
||||
export function createLocalAssetFolder({
|
||||
folderId,
|
||||
label,
|
||||
}: {
|
||||
folderId: string;
|
||||
label: string;
|
||||
}): EditorAssetFolder {
|
||||
return {
|
||||
id: folderId,
|
||||
label,
|
||||
collapsed: false,
|
||||
systemDefault: false,
|
||||
persisted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function replaceLocalAssetFolder({
|
||||
folders,
|
||||
assets,
|
||||
localFolderId,
|
||||
persistedFolder,
|
||||
}: {
|
||||
folders: EditorAssetFolder[];
|
||||
assets: EditorAsset[];
|
||||
localFolderId: string;
|
||||
persistedFolder: {
|
||||
folderId: string;
|
||||
label: string;
|
||||
collapsed: boolean;
|
||||
systemDefault: boolean;
|
||||
};
|
||||
}) {
|
||||
return {
|
||||
folders: folders.map((folder) =>
|
||||
folder.id === localFolderId
|
||||
? {
|
||||
id: persistedFolder.folderId,
|
||||
label: persistedFolder.label,
|
||||
collapsed: persistedFolder.collapsed,
|
||||
systemDefault: persistedFolder.systemDefault,
|
||||
persisted: true,
|
||||
}
|
||||
: folder,
|
||||
),
|
||||
assets: assets.map((asset) =>
|
||||
asset.folderId === localFolderId
|
||||
? {
|
||||
...asset,
|
||||
folderId: persistedFolder.folderId,
|
||||
}
|
||||
: asset,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function removeAssetById(assets: EditorAsset[], assetId: string) {
|
||||
return assets.filter((asset) => asset.id !== assetId);
|
||||
}
|
||||
|
||||
export function renameAssetFolderById(
|
||||
assetFolders: EditorAssetFolder[],
|
||||
folderId: string,
|
||||
label: string,
|
||||
) {
|
||||
return assetFolders.map((folder) =>
|
||||
folder.id === folderId
|
||||
? {
|
||||
...folder,
|
||||
label,
|
||||
}
|
||||
: folder,
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveDefaultAssetFolder(assetFolders: EditorAssetFolder[]) {
|
||||
return (
|
||||
assetFolders.find((folder) => folder.systemDefault) ?? assetFolders[0] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteAssetFolderLocally({
|
||||
folders,
|
||||
assets,
|
||||
folderId,
|
||||
defaultFolderId,
|
||||
}: {
|
||||
folders: EditorAssetFolder[];
|
||||
assets: EditorAsset[];
|
||||
folderId: string;
|
||||
defaultFolderId: string;
|
||||
}) {
|
||||
return {
|
||||
folders: folders.filter((folder) => folder.id !== folderId),
|
||||
assets: assets.map((asset) =>
|
||||
asset.folderId === folderId
|
||||
? {
|
||||
...asset,
|
||||
folderId: defaultFolderId,
|
||||
}
|
||||
: asset,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleAssetSelection(
|
||||
selectedAssetIds: Set<string>,
|
||||
assetId: string,
|
||||
) {
|
||||
const nextIds = new Set(selectedAssetIds);
|
||||
if (nextIds.has(assetId)) {
|
||||
nextIds.delete(assetId);
|
||||
} else {
|
||||
nextIds.add(assetId);
|
||||
}
|
||||
return nextIds;
|
||||
}
|
||||
|
||||
export function resolveAllAssetSelection({
|
||||
allSelectableAssetsSelected,
|
||||
selectableAssets,
|
||||
}: {
|
||||
allSelectableAssetsSelected: boolean;
|
||||
selectableAssets: EditorAsset[];
|
||||
}) {
|
||||
return allSelectableAssetsSelected
|
||||
? new Set<string>()
|
||||
: new Set(selectableAssets.map((asset) => asset.id));
|
||||
}
|
||||
|
||||
export function removeSelectedAssets(
|
||||
assets: EditorAsset[],
|
||||
selectedAssetIds: Set<string>,
|
||||
) {
|
||||
const deletedAssets = assets.filter((asset) => selectedAssetIds.has(asset.id));
|
||||
return {
|
||||
assets: assets.filter((asset) => !selectedAssetIds.has(asset.id)),
|
||||
deletedAssets,
|
||||
};
|
||||
}
|
||||
|
||||
export function moveAssetToFolderLocally(
|
||||
assets: EditorAsset[],
|
||||
assetId: string,
|
||||
folderId: string,
|
||||
) {
|
||||
return assets.map((asset) =>
|
||||
asset.id === assetId
|
||||
? {
|
||||
...asset,
|
||||
folderId,
|
||||
}
|
||||
: asset,
|
||||
);
|
||||
}
|
||||
@@ -23,6 +23,23 @@ import {
|
||||
escapeCssIdentifier,
|
||||
normalizeAssetLibrary,
|
||||
} from './ImageCanvasEditorModel';
|
||||
import {
|
||||
areAllSelectableAssetsSelected,
|
||||
createLocalAssetFolder,
|
||||
deleteAssetFolderLocally,
|
||||
getSelectableAssets,
|
||||
groupAssetsByFolder,
|
||||
moveAssetToFolderLocally,
|
||||
removeAssetById,
|
||||
removeSelectedAssets,
|
||||
renameAssetById,
|
||||
renameAssetFolderById,
|
||||
replaceLocalAssetFolder,
|
||||
resolveAllAssetSelection,
|
||||
resolveDefaultAssetFolder,
|
||||
toggleAssetFolderCollapsed,
|
||||
toggleAssetSelection,
|
||||
} from './ImageCanvasAssetLibraryModel';
|
||||
import type {
|
||||
AssetMarqueeState,
|
||||
AssetPointerDragState,
|
||||
@@ -80,20 +97,17 @@ export function useImageCanvasAssetLibrary({
|
||||
>(null);
|
||||
|
||||
const groupedAssets = useMemo(
|
||||
() =>
|
||||
assetFolders.map((folder) => ({
|
||||
...folder,
|
||||
assets: assets.filter((asset) => asset.folderId === folder.id),
|
||||
})),
|
||||
() => groupAssetsByFolder(assetFolders, assets),
|
||||
[assetFolders, assets],
|
||||
);
|
||||
const selectableAssets = useMemo(
|
||||
() => assets.filter((asset) => asset.sourceKind === 'uploaded'),
|
||||
() => getSelectableAssets(assets),
|
||||
[assets],
|
||||
);
|
||||
const allSelectableAssetsSelected =
|
||||
selectableAssets.length > 0 &&
|
||||
selectableAssets.every((asset) => selectedAssetIds.has(asset.id));
|
||||
const allSelectableAssetsSelected = areAllSelectableAssetsSelected(
|
||||
selectableAssets,
|
||||
selectedAssetIds,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canAccessProtectedData) {
|
||||
@@ -196,14 +210,7 @@ export function useImageCanvasAssetLibrary({
|
||||
return;
|
||||
}
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.map((currentAsset) =>
|
||||
currentAsset.id === asset.id
|
||||
? {
|
||||
...currentAsset,
|
||||
label: nextLabel,
|
||||
}
|
||||
: currentAsset,
|
||||
),
|
||||
renameAssetById(currentAssets, asset.id, nextLabel),
|
||||
);
|
||||
if (asset.persisted) {
|
||||
updateEditorAsset(asset.id, { label: nextLabel }).catch(() => {});
|
||||
@@ -218,14 +225,7 @@ export function useImageCanvasAssetLibrary({
|
||||
const nextFolder = assetFolders.find((folder) => folder.id === folderId);
|
||||
const nextCollapsed = !(nextFolder?.collapsed ?? false);
|
||||
setAssetFolders((currentFolders) =>
|
||||
currentFolders.map((folder) =>
|
||||
folder.id === folderId
|
||||
? {
|
||||
...folder,
|
||||
collapsed: !folder.collapsed,
|
||||
}
|
||||
: folder,
|
||||
),
|
||||
toggleAssetFolderCollapsed(currentFolders, folderId),
|
||||
);
|
||||
if (nextFolder?.persisted) {
|
||||
updateEditorAssetFolder(folderId, { collapsed: nextCollapsed }).catch(
|
||||
@@ -246,13 +246,7 @@ export function useImageCanvasAssetLibrary({
|
||||
const folderId = `folder-${Date.now()}`;
|
||||
setAssetFolders((currentFolders) => [
|
||||
...currentFolders,
|
||||
{
|
||||
id: folderId,
|
||||
label,
|
||||
collapsed: false,
|
||||
systemDefault: false,
|
||||
persisted: false,
|
||||
},
|
||||
createLocalAssetFolder({ folderId, label }),
|
||||
]);
|
||||
setActiveUploadFolderId(folderId);
|
||||
setCreatingFolder(false);
|
||||
@@ -263,27 +257,20 @@ export function useImageCanvasAssetLibrary({
|
||||
assetFolders.length + 100,
|
||||
);
|
||||
setAssetFolders((currentFolders) =>
|
||||
currentFolders.map((currentFolder) =>
|
||||
currentFolder.id === folderId
|
||||
? {
|
||||
id: folder.folderId,
|
||||
label: folder.label,
|
||||
collapsed: folder.collapsed,
|
||||
systemDefault: folder.systemDefault,
|
||||
persisted: true,
|
||||
}
|
||||
: currentFolder,
|
||||
),
|
||||
replaceLocalAssetFolder({
|
||||
folders: currentFolders,
|
||||
assets: [],
|
||||
localFolderId: folderId,
|
||||
persistedFolder: folder,
|
||||
}).folders,
|
||||
);
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.map((asset) =>
|
||||
asset.folderId === folderId
|
||||
? {
|
||||
...asset,
|
||||
folderId: folder.folderId,
|
||||
}
|
||||
: asset,
|
||||
),
|
||||
replaceLocalAssetFolder({
|
||||
folders: [],
|
||||
assets: currentAssets,
|
||||
localFolderId: folderId,
|
||||
persistedFolder: folder,
|
||||
}).assets,
|
||||
);
|
||||
setActiveUploadFolderId(folder.folderId);
|
||||
} catch {
|
||||
@@ -296,9 +283,7 @@ export function useImageCanvasAssetLibrary({
|
||||
if (asset.sourceKind !== 'uploaded') {
|
||||
return;
|
||||
}
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.filter((currentAsset) => currentAsset.id !== asset.id),
|
||||
);
|
||||
setAssets((currentAssets) => removeAssetById(currentAssets, asset.id));
|
||||
onDeleteAssets?.([asset]);
|
||||
setRenamingAsset((currentRename) =>
|
||||
currentRename?.assetId === asset.id ? null : currentRename,
|
||||
@@ -325,14 +310,7 @@ export function useImageCanvasAssetLibrary({
|
||||
return;
|
||||
}
|
||||
setAssetFolders((currentFolders) =>
|
||||
currentFolders.map((currentFolder) =>
|
||||
currentFolder.id === folder.id
|
||||
? {
|
||||
...currentFolder,
|
||||
label: nextLabel,
|
||||
}
|
||||
: currentFolder,
|
||||
),
|
||||
renameAssetFolderById(currentFolders, folder.id, nextLabel),
|
||||
);
|
||||
if (folder.persisted) {
|
||||
updateEditorAssetFolder(folder.id, { label: nextLabel }).catch(
|
||||
@@ -349,26 +327,25 @@ export function useImageCanvasAssetLibrary({
|
||||
if (folder.systemDefault) {
|
||||
return;
|
||||
}
|
||||
const defaultFolder =
|
||||
assetFolders.find((currentFolder) => currentFolder.systemDefault) ??
|
||||
assetFolders[0];
|
||||
const defaultFolder = resolveDefaultAssetFolder(assetFolders);
|
||||
if (!defaultFolder) {
|
||||
return;
|
||||
}
|
||||
setAssetFolders((currentFolders) =>
|
||||
currentFolders.filter(
|
||||
(currentFolder) => currentFolder.id !== folder.id,
|
||||
),
|
||||
deleteAssetFolderLocally({
|
||||
folders: currentFolders,
|
||||
assets: [],
|
||||
folderId: folder.id,
|
||||
defaultFolderId: defaultFolder.id,
|
||||
}).folders,
|
||||
);
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.map((asset) =>
|
||||
asset.folderId === folder.id
|
||||
? {
|
||||
...asset,
|
||||
folderId: defaultFolder.id,
|
||||
}
|
||||
: asset,
|
||||
),
|
||||
deleteAssetFolderLocally({
|
||||
folders: [],
|
||||
assets: currentAssets,
|
||||
folderId: folder.id,
|
||||
defaultFolderId: defaultFolder.id,
|
||||
}).assets,
|
||||
);
|
||||
if (folder.persisted) {
|
||||
deleteEditorAssetFolder(folder.id)
|
||||
@@ -384,32 +361,26 @@ export function useImageCanvasAssetLibrary({
|
||||
);
|
||||
|
||||
const toggleAssetSelected = useCallback((assetId: string) => {
|
||||
setSelectedAssetIds((currentIds) => {
|
||||
const nextIds = new Set(currentIds);
|
||||
if (nextIds.has(assetId)) {
|
||||
nextIds.delete(assetId);
|
||||
} else {
|
||||
nextIds.add(assetId);
|
||||
}
|
||||
return nextIds;
|
||||
});
|
||||
setSelectedAssetIds((currentIds) =>
|
||||
toggleAssetSelection(currentIds, assetId),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const toggleAllAssetsSelected = useCallback(() => {
|
||||
setSelectedAssetIds(
|
||||
allSelectableAssetsSelected
|
||||
? new Set()
|
||||
: new Set(selectableAssets.map((asset) => asset.id)),
|
||||
resolveAllAssetSelection({
|
||||
allSelectableAssetsSelected,
|
||||
selectableAssets,
|
||||
}),
|
||||
);
|
||||
}, [allSelectableAssetsSelected, selectableAssets]);
|
||||
|
||||
const deleteSelectedAssets = useCallback(() => {
|
||||
const ids = [...selectedAssetIds];
|
||||
const deletedAssets = assets.filter((asset) =>
|
||||
selectedAssetIds.has(asset.id),
|
||||
);
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.filter((asset) => !selectedAssetIds.has(asset.id)),
|
||||
const deletedAssets = removeSelectedAssets(assets, selectedAssetIds)
|
||||
.deletedAssets;
|
||||
setAssets(
|
||||
(currentAssets) => removeSelectedAssets(currentAssets, selectedAssetIds).assets,
|
||||
);
|
||||
onDeleteAssets?.(deletedAssets);
|
||||
setSelectedAssetIds(new Set());
|
||||
@@ -425,14 +396,7 @@ export function useImageCanvasAssetLibrary({
|
||||
return;
|
||||
}
|
||||
setAssets((currentAssets) =>
|
||||
currentAssets.map((currentAsset) =>
|
||||
currentAsset.id === assetId
|
||||
? {
|
||||
...currentAsset,
|
||||
folderId,
|
||||
}
|
||||
: currentAsset,
|
||||
),
|
||||
moveAssetToFolderLocally(currentAssets, assetId, folderId),
|
||||
);
|
||||
if (asset.persisted) {
|
||||
updateEditorAsset(asset.id, { folderId }).catch(() => {});
|
||||
|
||||
Reference in New Issue
Block a user