Add upload overwrite dialog
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
PLUGIN="u-navigator"
|
PLUGIN="u-navigator"
|
||||||
VERSION="2026.06.24.r001"
|
VERSION="2026.06.24.r002"
|
||||||
PKG_DIR="packages"
|
PKG_DIR="packages"
|
||||||
WORK_DIR=".plugin-build"
|
WORK_DIR=".plugin-build"
|
||||||
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+80
-3
@@ -12,6 +12,7 @@ const state = {
|
|||||||
selectedEntry: null,
|
selectedEntry: null,
|
||||||
contextMenu: null,
|
contextMenu: null,
|
||||||
permissionsDialog: null,
|
permissionsDialog: null,
|
||||||
|
uploadConflictDialog: null,
|
||||||
draggedEntry: null,
|
draggedEntry: null,
|
||||||
pointerDrag: null,
|
pointerDrag: null,
|
||||||
suppressClickPath: null,
|
suppressClickPath: null,
|
||||||
@@ -197,6 +198,9 @@ function render() {
|
|||||||
if (state.permissionsDialog) {
|
if (state.permissionsDialog) {
|
||||||
workspace.appendChild(renderPermissionsDialog());
|
workspace.appendChild(renderPermissionsDialog());
|
||||||
}
|
}
|
||||||
|
if (state.uploadConflictDialog) {
|
||||||
|
workspace.appendChild(renderUploadConflictDialog());
|
||||||
|
}
|
||||||
renderTransferPanel();
|
renderTransferPanel();
|
||||||
renderSettingsPanel();
|
renderSettingsPanel();
|
||||||
}
|
}
|
||||||
@@ -928,6 +932,7 @@ async function uploadFileList(win, files, targetPath) {
|
|||||||
if (!files.length) return;
|
if (!files.length) return;
|
||||||
const uploadItems = normalizeUploadItems(files);
|
const uploadItems = normalizeUploadItems(files);
|
||||||
const transfer = createUploadTransfer(targetPath, uploadItems);
|
const transfer = createUploadTransfer(targetPath, uploadItems);
|
||||||
|
let overwriteAll = false;
|
||||||
for (const [index, item] of uploadItems.entries()) {
|
for (const [index, item] of uploadItems.entries()) {
|
||||||
updateUploadTransfer(transfer, {
|
updateUploadTransfer(transfer, {
|
||||||
status: 'running',
|
status: 'running',
|
||||||
@@ -955,11 +960,21 @@ async function uploadFileList(win, files, targetPath) {
|
|||||||
}, true);
|
}, true);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
const overwrite = window.confirm(`"${item.path}" existiert bereits.\n\nVorhandene Datei überschreiben?`);
|
const conflictAction = overwriteAll ? 'overwrite' : await askUploadConflict({
|
||||||
recordDebug('upload.overwrite.prompt', { path: item.path, overwrite });
|
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) {
|
if (!overwrite) {
|
||||||
updateUploadTransfer(transfer, {
|
updateUploadTransfer(transfer, {
|
||||||
skippedFiles: transfer.skippedFiles + 1,
|
skippedFiles: transfer.skippedFiles + 1,
|
||||||
|
skippedBytes: transfer.skippedBytes + Number(item.file.size || 0),
|
||||||
currentFile: item.path,
|
currentFile: item.path,
|
||||||
progress: uploadProgressPercent(transfer)
|
progress: uploadProgressPercent(transfer)
|
||||||
});
|
});
|
||||||
@@ -1051,6 +1066,7 @@ function createUploadTransfer(targetPath, uploadItems) {
|
|||||||
overwrittenFiles: 0,
|
overwrittenFiles: 0,
|
||||||
totalBytes: uploadItems.reduce((sum, item) => sum + Number(item.file?.size || 0), 0),
|
totalBytes: uploadItems.reduce((sum, item) => sum + Number(item.file?.size || 0), 0),
|
||||||
uploadedBytes: 0,
|
uploadedBytes: 0,
|
||||||
|
skippedBytes: 0,
|
||||||
currentFile: '',
|
currentFile: '',
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
@@ -1084,7 +1100,7 @@ function addUploadProgress(transfer, bytes) {
|
|||||||
|
|
||||||
function uploadProgressPercent(transfer) {
|
function uploadProgressPercent(transfer) {
|
||||||
if (transfer.totalBytes > 0) {
|
if (transfer.totalBytes > 0) {
|
||||||
return (transfer.uploadedBytes / transfer.totalBytes) * 100;
|
return ((transfer.uploadedBytes + transfer.skippedBytes) / transfer.totalBytes) * 100;
|
||||||
}
|
}
|
||||||
if (transfer.totalFiles > 0) {
|
if (transfer.totalFiles > 0) {
|
||||||
return ((transfer.completedFiles + transfer.skippedFiles) / transfer.totalFiles) * 100;
|
return ((transfer.completedFiles + transfer.skippedFiles) / transfer.totalFiles) * 100;
|
||||||
@@ -1424,6 +1440,67 @@ function renderPermissionsDialog() {
|
|||||||
return overlay;
|
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) {
|
function permissionMatrixMarkup(mode) {
|
||||||
const digits = modeToDigits(mode);
|
const digits = modeToDigits(mode);
|
||||||
const rows = [
|
const rows = [
|
||||||
|
|||||||
@@ -581,6 +581,31 @@
|
|||||||
opacity: 0.48;
|
opacity: 0.48;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.u-nav .conflict-modal {
|
||||||
|
width: 520px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-nav .conflict-details {
|
||||||
|
background: var(--surface-2);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: grid;
|
||||||
|
gap: 8px 12px;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-nav .conflict-details dt {
|
||||||
|
color: var(--muted);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-nav .conflict-details dd {
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.u-nav .check-row {
|
.u-nav .check-row {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE PLUGIN [
|
<!DOCTYPE PLUGIN [
|
||||||
<!ENTITY name "u-navigator">
|
<!ENTITY name "u-navigator">
|
||||||
<!ENTITY author "Michael Roll">
|
<!ENTITY author "Michael Roll">
|
||||||
<!ENTITY version "2026.06.24.r001">
|
<!ENTITY version "2026.06.24.r002">
|
||||||
<!ENTITY package "&name;-&version;.tgz">
|
<!ENTITY package "&name;-&version;.tgz">
|
||||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
||||||
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<FILE Name="/boot/config/plugins/&name;/&package;">
|
<FILE Name="/boot/config/plugins/&name;/&package;">
|
||||||
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
||||||
<MD5>5d36280f152114104067f4d3e3a97234</MD5>
|
<MD5>d2034d7eb911d38a0b4cb5044c26f404</MD5>
|
||||||
</FILE>
|
</FILE>
|
||||||
|
|
||||||
<FILE Run="/bin/bash">
|
<FILE Run="/bin/bash">
|
||||||
|
|||||||
Reference in New Issue
Block a user