3
This commit is contained in:
+85
-110
@@ -13,9 +13,6 @@ const API_ROOT = (process.env.API_BASE_URL || '').replace(/\/$/, '');
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_ROOT ? `${API_ROOT}/api` : '/api',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const DEFAULT_FOLDER_NAME = 'All Documents';
|
||||
@@ -858,20 +855,27 @@ function App() {
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (file, targetFolderId) => {
|
||||
if (!file || file.size === 0) {
|
||||
setStatusMessage('Skipped empty file.', 'error');
|
||||
return null;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('file', file, file.name);
|
||||
if (targetFolderId && targetFolderId !== 'root') {
|
||||
formData.append('folder_id', targetFolderId);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.post('/documents', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
});
|
||||
if (response.status === 200) {
|
||||
setStatusMessage(`${file.name} already exists; reused existing document.`, 'info');
|
||||
} else {
|
||||
setStatusMessage(`Uploaded ${file.name}`, 'success');
|
||||
}
|
||||
const { data, status } = await api.post('/documents', formData);
|
||||
const duplicate = data?.reused || status === 200;
|
||||
setStatusMessage(
|
||||
duplicate
|
||||
? `${file.name} already exists; reused existing document.`
|
||||
: `Uploaded ${file.name}`,
|
||||
duplicate ? 'info' : 'success',
|
||||
);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const message = error.response?.data?.error || `Failed to upload ${file.name}.`;
|
||||
@@ -911,98 +915,61 @@ function App() {
|
||||
);
|
||||
|
||||
const extractFilesFromDataTransfer = useCallback(async (dataTransfer) => {
|
||||
if (
|
||||
!dataTransfer ||
|
||||
!dataTransfer.items ||
|
||||
!window.isSecureContext ||
|
||||
!Array.from(dataTransfer.items).some((item) => typeof item.getAsFileSystemHandle === 'function')
|
||||
) {
|
||||
throw new Error('File System Access API is required for uploads.');
|
||||
}
|
||||
|
||||
const results = [];
|
||||
|
||||
if (!dataTransfer) {
|
||||
return results;
|
||||
}
|
||||
const pushFile = (file, ancestors) => {
|
||||
if (file) {
|
||||
results.push({
|
||||
file,
|
||||
segments: ancestors.filter(Boolean),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const walkDirectoryHandle = async (handle, ancestors) => {
|
||||
const currentPath = handle.name ? [...ancestors, handle.name] : [...ancestors];
|
||||
for await (const childHandle of handle.values()) {
|
||||
if (childHandle.kind === 'file') {
|
||||
const file = await childHandle.getFile();
|
||||
results.push({ file, segments: currentPath });
|
||||
} else if (childHandle.kind === 'directory') {
|
||||
await walkDirectoryHandle(childHandle, currentPath);
|
||||
const nextAncestors = handle.name ? [...ancestors, handle.name] : [...ancestors];
|
||||
for await (const child of handle.values()) {
|
||||
if (child.kind === 'file') {
|
||||
const file = await child.getFile();
|
||||
pushFile(file, nextAncestors);
|
||||
} else if (child.kind === 'directory') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await walkDirectoryHandle(child, nextAncestors);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const walkEntry = async (entry, ancestors) => {
|
||||
if (entry.isFile) {
|
||||
const file = await new Promise((resolve, reject) => entry.file(resolve, reject));
|
||||
results.push({ file, segments: ancestors });
|
||||
return;
|
||||
for (const item of Array.from(dataTransfer.items)) {
|
||||
if (item.kind !== 'file') continue;
|
||||
const getHandle = item.getAsFileSystemHandle?.bind(item);
|
||||
if (!getHandle) {
|
||||
throw new Error('File System Access API handle missing.');
|
||||
}
|
||||
|
||||
if (entry.isDirectory) {
|
||||
const childAncestors = entry.name ? [...ancestors, entry.name] : [...ancestors];
|
||||
const reader = entry.createReader();
|
||||
const readEntries = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
reader.readEntries(resolve, reject);
|
||||
});
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const handle = await getHandle();
|
||||
if (!handle) continue;
|
||||
|
||||
let entries;
|
||||
do {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
entries = await readEntries();
|
||||
for (const child of entries) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await walkEntry(child, childAncestors);
|
||||
}
|
||||
} while (entries.length);
|
||||
}
|
||||
};
|
||||
|
||||
const items = dataTransfer.items ? Array.from(dataTransfer.items).filter((item) => item.kind === 'file') : [];
|
||||
|
||||
if (items.length) {
|
||||
for (const item of items) {
|
||||
if (handle.kind === 'file') {
|
||||
const file = await handle.getFile();
|
||||
pushFile(file, []);
|
||||
} else if (handle.kind === 'directory') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (item.getAsFileSystemHandle) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const handle = await item.getAsFileSystemHandle();
|
||||
if (!handle) continue;
|
||||
if (handle.kind === 'file') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const file = await handle.getFile();
|
||||
results.push({ file, segments: [] });
|
||||
} else if (handle.kind === 'directory') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await walkDirectoryHandle(handle, []);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : null;
|
||||
if (entry) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await walkEntry(entry, []);
|
||||
continue;
|
||||
}
|
||||
|
||||
const file = item.getAsFile ? item.getAsFile() : null;
|
||||
if (file) {
|
||||
const segments = file.webkitRelativePath
|
||||
? file.webkitRelativePath.split('/').slice(0, -1).filter(Boolean)
|
||||
: [];
|
||||
results.push({ file, segments });
|
||||
continue;
|
||||
}
|
||||
await walkDirectoryHandle(handle, []);
|
||||
}
|
||||
}
|
||||
|
||||
if (!results.length && dataTransfer.files) {
|
||||
results.push(
|
||||
...Array.from(dataTransfer.files).map((file) => ({
|
||||
file,
|
||||
segments: file.webkitRelativePath
|
||||
? file.webkitRelativePath.split('/').slice(0, -1).filter(Boolean)
|
||||
: [],
|
||||
})),
|
||||
);
|
||||
if (!results.length) {
|
||||
throw new Error('No files detected in drop payload.');
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -1014,33 +981,41 @@ function App() {
|
||||
setStatusMessage('Please log in before uploading.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
folderPathCacheRef.current.clear();
|
||||
|
||||
const extracted = await extractFilesFromDataTransfer(dataTransfer);
|
||||
const baseFolderId = targetFolderId && targetFolderId !== 'root' ? targetFolderId : null;
|
||||
|
||||
if (!extracted.length && dataTransfer?.files) {
|
||||
for (const file of Array.from(dataTransfer.files)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await uploadFile(file, targetFolderId ?? 'root');
|
||||
}
|
||||
} else {
|
||||
for (const { file, segments } of extracted) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const destinationId = segments.length
|
||||
? await ensureFolderPathOnServer(baseFolderId, segments)
|
||||
: baseFolderId;
|
||||
|
||||
const uploadTarget = destinationId
|
||||
?? (targetFolderId && targetFolderId !== 'root' ? targetFolderId : 'root');
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await uploadFile(file, uploadTarget);
|
||||
}
|
||||
if (!extracted.length) {
|
||||
setStatusMessage('No files to upload.', 'info');
|
||||
return;
|
||||
}
|
||||
const baseFolderId =
|
||||
targetFolderId && targetFolderId !== 'root' ? targetFolderId : null;
|
||||
|
||||
for (const { file, segments } of extracted) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const destinationId = segments.length
|
||||
? await ensureFolderPathOnServer(baseFolderId, segments)
|
||||
: baseFolderId;
|
||||
|
||||
const uploadTarget =
|
||||
destinationId ??
|
||||
(targetFolderId && targetFolderId !== 'root' ? targetFolderId : 'root');
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await uploadFile(file, uploadTarget);
|
||||
}
|
||||
|
||||
await refreshCurrentFolder();
|
||||
if (targetFolderId && targetFolderId !== 'root' && targetFolderId !== selectedFolder) {
|
||||
|
||||
if (
|
||||
targetFolderId &&
|
||||
targetFolderId !== 'root' &&
|
||||
targetFolderId !== selectedFolder
|
||||
) {
|
||||
await ensureFolderData(targetFolderId, { force: true });
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user