foo
This commit is contained in:
+151
-26
@@ -3132,7 +3132,66 @@ const AppLayout = () => {
|
||||
);
|
||||
if (!uniqueIds.length) return;
|
||||
|
||||
const uniqueIdSet = new Set(uniqueIds);
|
||||
const target = targetFolderId === 'root' ? null : targetFolderId;
|
||||
const targetLabel =
|
||||
target === null
|
||||
? DEFAULT_FOLDER_NAME
|
||||
: folderLabelMap.get(targetFolderId) || 'target folder';
|
||||
|
||||
const movedDocs = uniqueIds
|
||||
.map((id) => {
|
||||
const doc = documentLookup.get(id);
|
||||
if (!doc) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id,
|
||||
sourceFolderId: doc.folder_id ?? null,
|
||||
document: doc,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const updatedDocsMap = new Map();
|
||||
const resolveTargetName = () => {
|
||||
if (!targetLabel) {
|
||||
return null;
|
||||
}
|
||||
const segments = String(targetLabel).split('/');
|
||||
return segments[segments.length - 1] || targetLabel;
|
||||
};
|
||||
const targetName = resolveTargetName();
|
||||
|
||||
movedDocs.forEach(({ id, document }) => {
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
const updated = {
|
||||
...document,
|
||||
folder_id: target,
|
||||
};
|
||||
if (targetLabel) {
|
||||
updated.folder_path = targetLabel;
|
||||
if (targetName) {
|
||||
updated.folder_name = targetName;
|
||||
}
|
||||
} else if (target === null) {
|
||||
updated.folder_path = DEFAULT_FOLDER_NAME;
|
||||
updated.folder_name = DEFAULT_FOLDER_NAME;
|
||||
}
|
||||
updatedDocsMap.set(id, updated);
|
||||
});
|
||||
|
||||
const pruneRowCollection = (collection) =>
|
||||
collection.filter((key) => {
|
||||
if (!isDocumentRowKey(key)) {
|
||||
return true;
|
||||
}
|
||||
const id = getRowId(key);
|
||||
return id ? !uniqueIdSet.has(id) : true;
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
if (uniqueIds.length === 1) {
|
||||
@@ -3146,34 +3205,100 @@ const AppLayout = () => {
|
||||
|
||||
const count = uniqueIds.length;
|
||||
const suffix = count === 1 ? '' : 's';
|
||||
const targetLabel =
|
||||
target === null
|
||||
? DEFAULT_FOLDER_NAME
|
||||
: folderLabelMap.get(targetFolderId) || 'target folder';
|
||||
setStatusMessage(
|
||||
`Moved ${count} document${suffix} to ${targetLabel}.`,
|
||||
'success',
|
||||
);
|
||||
setStatusMessage(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');
|
||||
|
||||
await refreshCurrentFolder();
|
||||
if (targetFolderId && targetFolderId !== selectedFolder) {
|
||||
await ensureFolderData(targetFolderId, { force: true, prefetchDepth: 1 });
|
||||
if (updatedDocsMap.size) {
|
||||
mapDocumentCaches((doc) => {
|
||||
if (!doc || !uniqueIdSet.has(doc.id)) {
|
||||
return doc;
|
||||
}
|
||||
const updated = updatedDocsMap.get(doc.id);
|
||||
if (updated) {
|
||||
return updated;
|
||||
}
|
||||
return { ...doc, folder_id: target };
|
||||
});
|
||||
} else {
|
||||
mapDocumentCaches((doc) => {
|
||||
if (!doc || !uniqueIdSet.has(doc.id)) {
|
||||
return doc;
|
||||
}
|
||||
return { ...doc, folder_id: target };
|
||||
});
|
||||
}
|
||||
|
||||
if (uniqueIdSet.size) {
|
||||
setDocuments((prev) => prev.filter((doc) => !uniqueIdSet.has(doc.id)));
|
||||
setFolderContents((prev) => {
|
||||
if (!prev.size) {
|
||||
return prev;
|
||||
}
|
||||
let changed = false;
|
||||
const next = new Map(prev);
|
||||
movedDocs.forEach(({ id, sourceFolderId }) => {
|
||||
const sourceKey = sourceFolderId || 'root';
|
||||
const entry = next.get(sourceKey);
|
||||
if (!entry?.documents?.length) {
|
||||
return;
|
||||
}
|
||||
const filteredDocs = entry.documents.filter((doc) => doc.id !== id);
|
||||
if (filteredDocs.length !== entry.documents.length) {
|
||||
changed = true;
|
||||
next.set(sourceKey, { ...entry, documents: filteredDocs });
|
||||
}
|
||||
});
|
||||
return changed ? next : prev;
|
||||
});
|
||||
|
||||
setSelectedRowKeys((prev) => pruneRowCollection(prev));
|
||||
setSelectionOrder((prev) => pruneRowCollection(prev));
|
||||
selectionOrderRef.current = pruneRowCollection(selectionOrderRef.current);
|
||||
if (
|
||||
selectionAnchorRef.current &&
|
||||
isDocumentRowKey(selectionAnchorRef.current) &&
|
||||
uniqueIdSet.has(getRowId(selectionAnchorRef.current))
|
||||
) {
|
||||
selectionAnchorRef.current = null;
|
||||
}
|
||||
if (focusedDocumentId && uniqueIdSet.has(focusedDocumentId)) {
|
||||
setFocusedDocumentId(null);
|
||||
}
|
||||
if (
|
||||
focusedRowKey &&
|
||||
isDocumentRowKey(focusedRowKey) &&
|
||||
uniqueIdSet.has(getRowId(focusedRowKey))
|
||||
) {
|
||||
setFocusedRowKey(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetFolderId && targetFolderId !== selectedFolder) {
|
||||
await ensureFolderData(targetFolderId, { force: true, prefetchDepth: 1 });
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error.response?.data?.error || 'Failed to move documents.';
|
||||
notifyApiError(error, message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error.response?.data?.error || 'Failed to move documents.';
|
||||
notifyApiError(error, message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
ensureFolderData,
|
||||
refreshCurrentFolder,
|
||||
selectedFolder,
|
||||
folderLabelMap,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
],
|
||||
},
|
||||
[
|
||||
documentLookup,
|
||||
ensureFolderData,
|
||||
folderLabelMap,
|
||||
focusedDocumentId,
|
||||
mapDocumentCaches,
|
||||
notifyApiError,
|
||||
selectedFolder,
|
||||
setDocuments,
|
||||
setFolderContents,
|
||||
setFocusedDocumentId,
|
||||
focusedRowKey,
|
||||
setFocusedRowKey,
|
||||
setSelectionOrder,
|
||||
setSelectedRowKeys,
|
||||
setStatusMessage,
|
||||
],
|
||||
);
|
||||
|
||||
const handleThumbnailRegeneration = useCallback(
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
EditIcon,
|
||||
ViewListIcon,
|
||||
ViewGridIcon,
|
||||
DesktopIcon,
|
||||
IconFileStack,
|
||||
FolderIcon,
|
||||
TrashIcon,
|
||||
RefreshIcon,
|
||||
@@ -539,7 +539,7 @@ const DocumentsTable = ({
|
||||
aria-pressed={isDeskView}
|
||||
title="Desk view"
|
||||
>
|
||||
<DesktopIcon className="view-toggle__icon" size={18} />
|
||||
<IconFileStack className="view-toggle__icon" size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<button className="secondary" onClick={onRefresh}>
|
||||
@@ -1113,7 +1113,7 @@ export const createDocumentsTableHeaderActions = ({
|
||||
aria-pressed={isDeskView}
|
||||
title="Desk view"
|
||||
>
|
||||
<DesktopIcon className="view-toggle__icon" size={18} />
|
||||
<IconFileStack className="view-toggle__icon" size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<span className="main-content__actions-divider" aria-hidden="true">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useId } from 'react';
|
||||
import {
|
||||
IconChevronRight as TablerChevronRight,
|
||||
IconDownload as TablerDownload,
|
||||
@@ -202,6 +203,42 @@ export const MinusVerticalIcon = ({ className, size = '1em', stroke = 1.6, ...re
|
||||
/>
|
||||
);
|
||||
|
||||
export const IconFileStack = ({ className, size = 24, stroke = 160, ...rest }) => {
|
||||
return (
|
||||
<svg
|
||||
className={composeClassName('icon', className)}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={stroke}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...rest}
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
id="icon-file-stack-front"
|
||||
d="M 5 5.173 l 0 -1.673 c 0 -1.1 0.9 -2 2 -2 l 10 0 c 1.1 0 2 0.9 2 2 l 0 14 c 0 0.7611 -0.4309 1.4265 -1.0608 1.7642 c 0.0548 -0.2682 0.0567 -0.5513 -0.0036 -0.835 l -2.8267 -13.2989 c -0.2356 -1.1082 -1.3351 -1.8223 -2.4433 -1.5867 l -7.6656 1.6294 Z"
|
||||
/>
|
||||
<path
|
||||
id="icon-file-stack-left"
|
||||
d="M 16.349 20.8725 l -10.075 2.1415 c -1.1082 0.2355 -2.2077 -0.4785 -2.4432 -1.5867 l -2.8268 -13.2989 c -0.2356 -1.1083 0.4784 -2.2077 1.5867 -2.4433 l 10.0749 -2.1415 c 1.1082 -0.2356 2.2077 0.4785 2.4433 1.5867 l 2.8267 13.2989 c 0.2356 1.1082 -0.4784 2.2077 -1.5866 2.4433 Z"
|
||||
/>
|
||||
<path
|
||||
id="icon-file-stack-right"
|
||||
d="M 17.9392 19.2642 c 0.6299 -0.3377 1.0608 -1.0031 1.0608 -1.7642 l 0 -11.4909 l 2.502 0.5318 c 1.0329 0.2195 1.6984 1.2443 1.4788 2.2772 l -2.6346 12.3951 c -0.2196 1.0329 -1.2443 1.6984 -2.2772 1.4789 l -5.1403 -1.0926 l 3.4203 -0.727 c 0.8245 -0.1753 1.4308 -0.8288 1.5902 -1.6083 Z"
|
||||
/>
|
||||
</defs>
|
||||
|
||||
<use href="#icon-file-stack-right" fill="none" />
|
||||
<use href="#icon-file-stack-front" fill="none" />
|
||||
<use href="#icon-file-stack-left" fill="none" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const CloseIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconX
|
||||
className={composeClassName('icon', className)}
|
||||
|
||||
Reference in New Issue
Block a user