From a64ebd15878ad17634efab1133b931e28ff1178d Mon Sep 17 00:00:00 2001 From: Administrator Date: Sat, 21 Mar 2026 09:03:13 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(queue):=20add=20"Hoster=20entf?= =?UTF-8?q?ernen"=20submenu=20to=20context=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right-click on queue now shows a "Hoster entfernen ▸" submenu listing all hosters with job count (e.g. "Vidmoly (3)"). Clicking removes all jobs for that hoster, cancels active uploads, and saves immediately. Also fixes submenu viewport flip measurement (was reading offsetWidth on display:none elements). Co-Authored-By: Claude Opus 4.6 (1M context) --- renderer/app.js | 43 +++++++++++++++++++++++++++++++++++++++++++ renderer/index.html | 4 ++++ 2 files changed, 47 insertions(+) diff --git a/renderer/app.js b/renderer/app.js index d08f847..0795b0b 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -1019,6 +1019,25 @@ function showContextMenu(x, y) { cancelSep.style.display = 'none'; } + // Dynamic "delete by hoster" submenu + const deleteHosterSubmenu = menu.querySelector('.ctx-hoster-delete-submenu'); + const deleteHosterContainer = menu.querySelector('.ctx-hoster-delete-items'); + const hosterCounts = new Map(); + queueJobs.forEach(j => hosterCounts.set(j.hoster, (hosterCounts.get(j.hoster) || 0) + 1)); + deleteHosterContainer.innerHTML = ''; + if (hosterCounts.size > 0) { + deleteHosterSubmenu.style.display = ''; + hosterCounts.forEach((count, hoster) => { + const item = document.createElement('div'); + item.className = 'ctx-item ctx-item-danger'; + item.dataset.action = `delete-hoster:${hoster}`; + item.textContent = `${getHosterLabel(hoster)} (${count})`; + deleteHosterContainer.appendChild(item); + }); + } else { + deleteHosterSubmenu.style.display = 'none'; + } + menu.style.display = 'block'; const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5); menu.style.left = menuX + 'px'; @@ -1026,7 +1045,12 @@ function showContextMenu(x, y) { // Flip submenus if they would overflow the viewport right edge menu.querySelectorAll('.ctx-submenu-items').forEach(sub => { + // Temporarily show to measure actual width (display:none → offsetWidth=0) + sub.style.visibility = 'hidden'; + sub.style.display = 'block'; sub.classList.toggle('flip-left', menuX + menu.offsetWidth + sub.offsetWidth > window.innerWidth); + sub.style.display = ''; + sub.style.visibility = ''; }); } @@ -1252,6 +1276,25 @@ async function handleContextAction(action) { renderQueueTable(); updateStatusBar(); updateQueueActionButtons(); + } else if (action.startsWith('delete-hoster:')) { + const hoster = action.replace('delete-hoster:', ''); + // Cancel active uploads for this hoster + const activeIds = queueJobs + .filter(j => j.hoster === hoster && (j.status === 'uploading' || j.status === 'queued' || j.status === 'retrying' || j.status === 'getting-server' || j.status === 'preview')) + .map(j => j.id); + if (activeIds.length > 0) await window.api.cancelSelectedJobs(activeIds); + // Remove ALL jobs for this hoster + queueJobs = queueJobs.filter(j => { + if (j.hoster === hoster) { removeJobFromIndex(j); return false; } + return true; + }); + selectedJobIds.clear(); + syncSelectedFilesFromQueue(); + renderQueueTable(); + if (queueJobs.length === 0) { selectedFiles = []; updateUploadView(); } + updateStatusBar(); + updateQueueActionButtons(); + persistQueueStateSoon(true); } else if (action.startsWith('shutdown-')) { const mode = action.replace('shutdown-', ''); await window.api.setShutdownAfterFinish(mode); diff --git a/renderer/index.html b/renderer/index.html index 157d622..91c5c3b 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -265,6 +265,10 @@
Entfernen
Alle entfernen
+