feat: upload - deel 03.02 - Skipp all toegevoegd
This commit is contained in:
+185
-6
@@ -62,6 +62,12 @@ let uploadState = {
|
||||
targetPath: "",
|
||||
files: [],
|
||||
index: 0,
|
||||
overwriteAll: false,
|
||||
skipAll: false,
|
||||
successfulCount: 0,
|
||||
skippedCount: 0,
|
||||
cancelled: false,
|
||||
conflictResolver: null,
|
||||
};
|
||||
let settingsState = {
|
||||
activeTab: "general",
|
||||
@@ -319,6 +325,72 @@ function uploadElements() {
|
||||
};
|
||||
}
|
||||
|
||||
function uploadConflictElements() {
|
||||
return {
|
||||
overlay: document.getElementById("upload-conflict-modal"),
|
||||
title: document.getElementById("upload-conflict-title"),
|
||||
target: document.getElementById("upload-conflict-target"),
|
||||
fileName: document.getElementById("upload-conflict-file-name"),
|
||||
message: document.getElementById("upload-conflict-message"),
|
||||
overwriteButton: document.getElementById("upload-conflict-overwrite-btn"),
|
||||
overwriteAllButton: document.getElementById("upload-conflict-overwrite-all-btn"),
|
||||
skipButton: document.getElementById("upload-conflict-skip-btn"),
|
||||
skipAllButton: document.getElementById("upload-conflict-skip-all-btn"),
|
||||
cancelButton: document.getElementById("upload-conflict-cancel-btn"),
|
||||
};
|
||||
}
|
||||
|
||||
function ensureUploadConflictModal() {
|
||||
if (document.getElementById("upload-conflict-modal")) {
|
||||
return uploadConflictElements();
|
||||
}
|
||||
const overlay = document.createElement("div");
|
||||
overlay.id = "upload-conflict-modal";
|
||||
overlay.className = "popup-overlay hidden";
|
||||
|
||||
const card = document.createElement("div");
|
||||
card.className = "popup-card";
|
||||
card.setAttribute("role", "dialog");
|
||||
card.setAttribute("aria-modal", "true");
|
||||
card.setAttribute("aria-labelledby", "upload-conflict-title");
|
||||
|
||||
const title = document.createElement("h3");
|
||||
title.id = "upload-conflict-title";
|
||||
title.textContent = "Upload conflict";
|
||||
|
||||
const target = document.createElement("div");
|
||||
target.id = "upload-conflict-target";
|
||||
target.className = "popup-meta";
|
||||
|
||||
const fileName = document.createElement("div");
|
||||
fileName.id = "upload-conflict-file-name";
|
||||
fileName.className = "popup-meta";
|
||||
|
||||
const message = document.createElement("div");
|
||||
message.id = "upload-conflict-message";
|
||||
message.className = "popup-meta";
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "popup-actions";
|
||||
|
||||
const overwriteButton = createButton("Overwrite", () => resolveUploadConflict("overwrite"));
|
||||
overwriteButton.id = "upload-conflict-overwrite-btn";
|
||||
const overwriteAllButton = createButton("Overwrite all", () => resolveUploadConflict("overwrite_all"));
|
||||
overwriteAllButton.id = "upload-conflict-overwrite-all-btn";
|
||||
const skipButton = createButton("Skip", () => resolveUploadConflict("skip"));
|
||||
skipButton.id = "upload-conflict-skip-btn";
|
||||
const skipAllButton = createButton("Skip all", () => resolveUploadConflict("skip_all"));
|
||||
skipAllButton.id = "upload-conflict-skip-all-btn";
|
||||
const cancelButton = createButton("Cancel", () => resolveUploadConflict("cancel"));
|
||||
cancelButton.id = "upload-conflict-cancel-btn";
|
||||
|
||||
actions.append(overwriteButton, overwriteAllButton, skipButton, skipAllButton, cancelButton);
|
||||
card.append(title, target, fileName, message, actions);
|
||||
overlay.append(card);
|
||||
document.body.append(overlay);
|
||||
return uploadConflictElements();
|
||||
}
|
||||
|
||||
async function apiRequest(method, url, body) {
|
||||
const options = { method, headers: {} };
|
||||
if (body !== undefined) {
|
||||
@@ -334,9 +406,19 @@ async function apiRequest(method, url, body) {
|
||||
return data;
|
||||
}
|
||||
|
||||
async function uploadFileRequest(targetPath, file) {
|
||||
function createApiError(response, data) {
|
||||
const error = data.error || {};
|
||||
const err = new Error(error.message || `HTTP ${response.status}`);
|
||||
err.code = error.code || null;
|
||||
err.status = response.status;
|
||||
err.details = error.details || {};
|
||||
return err;
|
||||
}
|
||||
|
||||
async function uploadFileRequest(targetPath, file, overwrite = false) {
|
||||
const formData = new FormData();
|
||||
formData.append("target_path", targetPath);
|
||||
formData.append("overwrite", overwrite ? "true" : "false");
|
||||
formData.append("file", file, file.name);
|
||||
|
||||
const response = await fetch("/api/files/upload", {
|
||||
@@ -345,8 +427,7 @@ async function uploadFileRequest(targetPath, file) {
|
||||
});
|
||||
const data = await response.json().catch(() => ({}));
|
||||
if (!response.ok) {
|
||||
const error = data.error || {};
|
||||
throw new Error(error.message || `HTTP ${response.status}`);
|
||||
throw createApiError(response, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -377,6 +458,12 @@ function resetUploadProgress() {
|
||||
uploadState.targetPath = "";
|
||||
uploadState.files = [];
|
||||
uploadState.index = 0;
|
||||
uploadState.overwriteAll = false;
|
||||
uploadState.skipAll = false;
|
||||
uploadState.successfulCount = 0;
|
||||
uploadState.skippedCount = 0;
|
||||
uploadState.cancelled = false;
|
||||
uploadState.conflictResolver = null;
|
||||
elements.button.disabled = false;
|
||||
elements.target.textContent = "";
|
||||
elements.currentFile.textContent = "";
|
||||
@@ -407,6 +494,34 @@ function openUploadPicker() {
|
||||
elements.input.click();
|
||||
}
|
||||
|
||||
function isUploadConflictOpen() {
|
||||
const overlay = document.getElementById("upload-conflict-modal");
|
||||
return Boolean(overlay) && !overlay.classList.contains("hidden");
|
||||
}
|
||||
|
||||
function resolveUploadConflict(choice) {
|
||||
const resolver = uploadState.conflictResolver;
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
uploadState.conflictResolver = null;
|
||||
const elements = uploadConflictElements();
|
||||
elements.overlay.classList.add("hidden");
|
||||
resolver(choice);
|
||||
}
|
||||
|
||||
function promptUploadConflict(fileName, targetPath, message) {
|
||||
const elements = ensureUploadConflictModal();
|
||||
elements.title.textContent = "Upload conflict";
|
||||
elements.target.textContent = `Upload to: ${targetPath}`;
|
||||
elements.fileName.textContent = `File: ${fileName}`;
|
||||
elements.message.textContent = message || "Target path already exists.";
|
||||
elements.overlay.classList.remove("hidden");
|
||||
return new Promise((resolve) => {
|
||||
uploadState.conflictResolver = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
async function handleUploadSelection(event) {
|
||||
const files = Array.from(event.target.files || []);
|
||||
event.target.value = "";
|
||||
@@ -419,18 +534,71 @@ async function handleUploadSelection(event) {
|
||||
uploadState.targetPath = targetPath;
|
||||
uploadState.files = files;
|
||||
uploadState.index = 0;
|
||||
uploadState.overwriteAll = false;
|
||||
uploadState.skipAll = false;
|
||||
uploadState.successfulCount = 0;
|
||||
uploadState.skippedCount = 0;
|
||||
uploadState.cancelled = false;
|
||||
setError("actions-error", "");
|
||||
updateUploadProgress();
|
||||
|
||||
try {
|
||||
outer:
|
||||
for (let index = 0; index < files.length; index += 1) {
|
||||
uploadState.index = index;
|
||||
updateUploadProgress();
|
||||
await uploadFileRequest(targetPath, files[index]);
|
||||
let overwrite = uploadState.overwriteAll;
|
||||
while (true) {
|
||||
try {
|
||||
await uploadFileRequest(targetPath, files[index], overwrite);
|
||||
uploadState.successfulCount += 1;
|
||||
break;
|
||||
} catch (err) {
|
||||
if (err.code !== "already_exists") {
|
||||
throw err;
|
||||
}
|
||||
if (uploadState.skipAll) {
|
||||
uploadState.skippedCount += 1;
|
||||
break;
|
||||
}
|
||||
const choice = await promptUploadConflict(files[index].name, targetPath, err.message);
|
||||
if (choice === "overwrite") {
|
||||
overwrite = true;
|
||||
continue;
|
||||
}
|
||||
if (choice === "overwrite_all") {
|
||||
uploadState.overwriteAll = true;
|
||||
overwrite = true;
|
||||
continue;
|
||||
}
|
||||
if (choice === "skip") {
|
||||
uploadState.skippedCount += 1;
|
||||
break;
|
||||
}
|
||||
if (choice === "skip_all") {
|
||||
uploadState.skipAll = true;
|
||||
uploadState.skippedCount += 1;
|
||||
break;
|
||||
}
|
||||
uploadState.cancelled = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uploadState.successfulCount > 0) {
|
||||
await loadBrowsePane(state.activePane);
|
||||
}
|
||||
if (uploadState.cancelled) {
|
||||
setStatus(`Upload: ${uploadState.successfulCount}/${files.length} file${files.length === 1 ? "" : "s"} uploaded before cancel`);
|
||||
} else if (uploadState.skippedCount > 0) {
|
||||
setStatus(`Upload: ${uploadState.successfulCount} uploaded, ${uploadState.skippedCount} skipped`);
|
||||
} else {
|
||||
setStatus(`Upload: ${uploadState.successfulCount} file${uploadState.successfulCount === 1 ? "" : "s"} uploaded`);
|
||||
}
|
||||
await loadBrowsePane(state.activePane);
|
||||
setStatus(`Upload: ${files.length} file${files.length === 1 ? "" : "s"} uploaded`);
|
||||
} catch (err) {
|
||||
if (uploadState.successfulCount > 0) {
|
||||
await loadBrowsePane(state.activePane);
|
||||
}
|
||||
setActionError("Upload", err);
|
||||
} finally {
|
||||
resetUploadProgress();
|
||||
@@ -1558,6 +1726,10 @@ function isBatchMovePopupOpen() {
|
||||
return !batchMoveElements().overlay.classList.contains("hidden");
|
||||
}
|
||||
|
||||
function isUploadConflictModalOpen() {
|
||||
return isUploadConflictOpen();
|
||||
}
|
||||
|
||||
function isViewerOpen() {
|
||||
return !viewerElements().overlay.classList.contains("hidden");
|
||||
}
|
||||
@@ -2664,6 +2836,13 @@ function handleKeyboardShortcuts(event) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isUploadConflictModalOpen()) {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
resolveUploadConflict("cancel");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isMovePopupOpen()) {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user