Add upload overwrite dialog

This commit is contained in:
Mikei386
2026-06-24 20:20:57 +02:00
parent 6df1c7ea97
commit 9a1d54ca88
6 changed files with 108 additions and 6 deletions
+80 -3
View File
@@ -12,6 +12,7 @@ const state = {
selectedEntry: null,
contextMenu: null,
permissionsDialog: null,
uploadConflictDialog: null,
draggedEntry: null,
pointerDrag: null,
suppressClickPath: null,
@@ -197,6 +198,9 @@ function render() {
if (state.permissionsDialog) {
workspace.appendChild(renderPermissionsDialog());
}
if (state.uploadConflictDialog) {
workspace.appendChild(renderUploadConflictDialog());
}
renderTransferPanel();
renderSettingsPanel();
}
@@ -928,6 +932,7 @@ async function uploadFileList(win, files, targetPath) {
if (!files.length) return;
const uploadItems = normalizeUploadItems(files);
const transfer = createUploadTransfer(targetPath, uploadItems);
let overwriteAll = false;
for (const [index, item] of uploadItems.entries()) {
updateUploadTransfer(transfer, {
status: 'running',
@@ -955,11 +960,21 @@ async function uploadFileList(win, files, targetPath) {
}, true);
throw error;
}
const overwrite = window.confirm(`"${item.path}" existiert bereits.\n\nVorhandene Datei überschreiben?`);
recordDebug('upload.overwrite.prompt', { path: item.path, overwrite });
const conflictAction = overwriteAll ? 'overwrite' : await askUploadConflict({
path: item.path,
targetPath,
index: index + 1,
total: uploadItems.length
});
if (conflictAction === 'overwrite-all') {
overwriteAll = true;
}
const overwrite = overwriteAll || conflictAction === 'overwrite';
recordDebug('upload.overwrite.prompt', { path: item.path, action: conflictAction, overwrite });
if (!overwrite) {
updateUploadTransfer(transfer, {
skippedFiles: transfer.skippedFiles + 1,
skippedBytes: transfer.skippedBytes + Number(item.file.size || 0),
currentFile: item.path,
progress: uploadProgressPercent(transfer)
});
@@ -1051,6 +1066,7 @@ function createUploadTransfer(targetPath, uploadItems) {
overwrittenFiles: 0,
totalBytes: uploadItems.reduce((sum, item) => sum + Number(item.file?.size || 0), 0),
uploadedBytes: 0,
skippedBytes: 0,
currentFile: '',
currentIndex: 0,
progress: 0,
@@ -1084,7 +1100,7 @@ function addUploadProgress(transfer, bytes) {
function uploadProgressPercent(transfer) {
if (transfer.totalBytes > 0) {
return (transfer.uploadedBytes / transfer.totalBytes) * 100;
return ((transfer.uploadedBytes + transfer.skippedBytes) / transfer.totalBytes) * 100;
}
if (transfer.totalFiles > 0) {
return ((transfer.completedFiles + transfer.skippedFiles) / transfer.totalFiles) * 100;
@@ -1424,6 +1440,67 @@ function renderPermissionsDialog() {
return overlay;
}
function askUploadConflict(details) {
return new Promise((resolve) => {
state.uploadConflictDialog = { ...details, resolve };
render();
});
}
function renderUploadConflictDialog() {
const dialog = state.uploadConflictDialog;
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.innerHTML = `
<form class="permissions-modal conflict-modal" aria-label="Datei existiert bereits">
<header>
<strong>Datei existiert bereits</strong>
<button type="button" class="icon" data-action="skip" title="Überspringen" aria-label="Überspringen">×</button>
</header>
<div class="modal-body">
<p>Im Zielordner existiert bereits eine Datei mit diesem Namen.</p>
<dl class="conflict-details">
<dt>Datei</dt>
<dd>${escapeHtml(dialog.path)}</dd>
<dt>Ziel</dt>
<dd>${escapeHtml(dialog.targetPath)}</dd>
<dt>Upload</dt>
<dd>${escapeHtml(`${dialog.index}/${dialog.total}`)}</dd>
</dl>
</div>
<footer>
<button type="button" data-action="skip">Überspringen</button>
<button type="button" data-action="overwrite">Überschreiben</button>
<button type="submit" class="primary">Alle überschreiben</button>
</footer>
</form>
`;
const form = overlay.querySelector('form');
form.addEventListener('click', (event) => event.stopPropagation());
overlay.addEventListener('click', () => closeUploadConflictDialog('skip'));
overlay.querySelectorAll('[data-action="skip"]').forEach((button) => {
button.addEventListener('click', () => closeUploadConflictDialog('skip'));
});
overlay.querySelector('[data-action="overwrite"]').addEventListener('click', () => closeUploadConflictDialog('overwrite'));
form.addEventListener('submit', (event) => {
event.preventDefault();
closeUploadConflictDialog('overwrite-all');
});
setTimeout(() => {
form.querySelector('.primary')?.focus();
}, 0);
return overlay;
}
function closeUploadConflictDialog(action) {
const dialog = state.uploadConflictDialog;
if (!dialog) return;
state.uploadConflictDialog = null;
dialog.resolve(action);
render();
}
function permissionMatrixMarkup(mode) {
const digits = modeToDigits(mode);
const rows = [