feat: delete non empty folders

This commit is contained in:
kodi
2026-03-14 07:48:29 +01:00
parent f092007998
commit d84b3da561
16 changed files with 218 additions and 11 deletions
+141 -2
View File
@@ -47,6 +47,9 @@ let renameState = {
source: null,
name: "",
};
let deleteConfirmState = {
path: null,
};
let batchMoveState = {
destinationBase: "",
count: 0,
@@ -164,6 +167,15 @@ function setStatus(msg) {
}
function setError(id, msg) {
if (id === "actions-error") {
document.getElementById(id).textContent = "";
if (msg) {
openFeedbackModal(msg);
} else {
closeFeedbackModal();
}
return;
}
document.getElementById(id).textContent = msg || "";
}
@@ -281,6 +293,24 @@ function batchMoveElements() {
};
}
function deleteConfirmElements() {
return {
overlay: document.getElementById("delete-confirm-modal"),
path: document.getElementById("delete-confirm-path"),
error: document.getElementById("delete-confirm-error"),
applyButton: document.getElementById("delete-confirm-apply-btn"),
cancelButton: document.getElementById("delete-confirm-cancel-btn"),
};
}
function feedbackElements() {
return {
overlay: document.getElementById("feedback-modal"),
message: document.getElementById("feedback-message"),
closeButton: document.getElementById("feedback-close-btn"),
};
}
function settingsElements() {
return {
overlay: document.getElementById("settings-modal"),
@@ -347,6 +377,28 @@ function uploadModalElements() {
};
}
function isFeedbackModalOpen() {
return !feedbackElements().overlay.classList.contains("hidden");
}
function openFeedbackModal(message) {
const elements = feedbackElements();
if (!elements.overlay) {
return;
}
elements.message.textContent = message || "";
elements.overlay.classList.remove("hidden");
}
function closeFeedbackModal() {
const elements = feedbackElements();
if (!elements.overlay) {
return;
}
elements.message.textContent = "";
elements.overlay.classList.add("hidden");
}
function setUploadModalVisible(visible) {
const elements = uploadModalElements();
if (!elements.overlay) {
@@ -486,8 +538,7 @@ async function apiRequest(method, url, body) {
const response = await fetch(url, options);
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;
}
@@ -1831,6 +1882,40 @@ async function renameSelected() {
}
}
function closeDeleteConfirmModal() {
const elements = deleteConfirmElements();
deleteConfirmState.path = null;
elements.error.textContent = "";
elements.overlay.classList.add("hidden");
}
function openDeleteConfirmModal(path) {
const elements = deleteConfirmElements();
deleteConfirmState.path = path;
elements.path.textContent = path;
elements.error.textContent = "";
elements.overlay.classList.remove("hidden");
}
async function submitDeleteConfirmModal() {
const path = deleteConfirmState.path;
if (!path) {
return;
}
const elements = deleteConfirmElements();
elements.error.textContent = "";
try {
await apiRequest("POST", "/api/files/delete", { path, recursive: true });
closeDeleteConfirmModal();
setSelectedItem(state.activePane, null);
await loadBrowsePane(state.activePane);
setStatus("Delete: 1 success, 0 failed");
setError("actions-error", "");
} catch (err) {
elements.error.textContent = err.message;
}
}
async function deleteSelected() {
const pane = state.activePane;
const selectedItems = [...paneState(pane).selectedItems];
@@ -1849,6 +1934,16 @@ async function deleteSelected() {
await apiRequest("POST", "/api/files/delete", { path: item.path });
successes += 1;
} catch (err) {
if (
err.code === "directory_not_empty"
&& selectedItems.length === 1
&& item.kind === "directory"
) {
failures = 0;
firstError = null;
openDeleteConfirmModal(item.path);
return;
}
failures += 1;
if (!firstError) {
firstError = `${item.path}: ${err.message}`;
@@ -2089,6 +2184,10 @@ function isBatchMovePopupOpen() {
return !batchMoveElements().overlay.classList.contains("hidden");
}
function isDeleteConfirmModalOpen() {
return !deleteConfirmElements().overlay.classList.contains("hidden");
}
function isUploadConflictModalOpen() {
return isUploadConflictOpen();
}
@@ -3150,6 +3249,13 @@ function handleKeyboardShortcuts(event) {
closeUploadMenu();
return;
}
if (isFeedbackModalOpen()) {
if (event.key === "Escape" || event.key === "Enter") {
event.preventDefault();
closeFeedbackModal();
}
return;
}
if (isInfoOpen()) {
if (event.key === "Escape") {
event.preventDefault();
@@ -3204,6 +3310,19 @@ function handleKeyboardShortcuts(event) {
}
return;
}
if (isDeleteConfirmModalOpen()) {
if (event.key === "Escape") {
event.preventDefault();
closeDeleteConfirmModal();
return;
}
if (event.key === "Enter") {
event.preventDefault();
submitDeleteConfirmModal();
return;
}
return;
}
if (isUploadConflictModalOpen()) {
if (event.key === "Escape") {
event.preventDefault();
@@ -3393,6 +3512,17 @@ function setupEvents() {
if (modalCancel) {
modalCancel.onclick = requestUploadCancel;
}
const feedback = feedbackElements();
if (feedback.closeButton) {
feedback.closeButton.onclick = closeFeedbackModal;
}
if (feedback.overlay) {
feedback.overlay.onclick = (event) => {
if (event.target === feedback.overlay) {
closeFeedbackModal();
}
};
}
document.addEventListener("click", (event) => {
const elements = uploadElements();
if (!elements.menu || elements.menu.contains(event.target)) {
@@ -3514,6 +3644,15 @@ function setupEvents() {
}
};
const deleteConfirm = deleteConfirmElements();
deleteConfirm.cancelButton.onclick = closeDeleteConfirmModal;
deleteConfirm.applyButton.onclick = submitDeleteConfirmModal;
deleteConfirm.overlay.onclick = (event) => {
if (event.target === deleteConfirm.overlay) {
closeDeleteConfirmModal();
}
};
const viewer = viewerElements();
viewer.closeButton.onclick = closeViewer;
viewer.overlay.onclick = (event) => {