Compare commits

..

2 Commits

Author SHA1 Message Date
Administrator
9ea9212637 release: v2.2.2 2026-03-21 09:03:48 +01:00
Administrator
a64ebd1587 feat(queue): add "Hoster entfernen" submenu to context menu
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) <noreply@anthropic.com>
2026-03-21 09:03:13 +01:00
3 changed files with 48 additions and 1 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.2.1", "version": "2.2.2",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@ -1019,6 +1019,25 @@ function showContextMenu(x, y) {
cancelSep.style.display = 'none'; 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'; menu.style.display = 'block';
const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5); const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5);
menu.style.left = menuX + 'px'; menu.style.left = menuX + 'px';
@ -1026,7 +1045,12 @@ function showContextMenu(x, y) {
// Flip submenus if they would overflow the viewport right edge // Flip submenus if they would overflow the viewport right edge
menu.querySelectorAll('.ctx-submenu-items').forEach(sub => { 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.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(); renderQueueTable();
updateStatusBar(); updateStatusBar();
updateQueueActionButtons(); 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-')) { } else if (action.startsWith('shutdown-')) {
const mode = action.replace('shutdown-', ''); const mode = action.replace('shutdown-', '');
await window.api.setShutdownAfterFinish(mode); await window.api.setShutdownAfterFinish(mode);

View File

@ -265,6 +265,10 @@
<div class="ctx-separator"></div> <div class="ctx-separator"></div>
<div class="ctx-item" data-action="delete-selected">Entfernen</div> <div class="ctx-item" data-action="delete-selected">Entfernen</div>
<div class="ctx-item" data-action="delete-all">Alle entfernen</div> <div class="ctx-item" data-action="delete-all">Alle entfernen</div>
<div class="ctx-submenu ctx-hoster-delete-submenu" style="display:none">
<div class="ctx-item ctx-item-danger">Hoster entfernen &#9656;</div>
<div class="ctx-submenu-items ctx-hoster-delete-items"></div>
</div>
<div class="ctx-separator ctx-hoster-cancel-sep" style="display:none"></div> <div class="ctx-separator ctx-hoster-cancel-sep" style="display:none"></div>
<div class="ctx-hoster-cancel-items"></div> <div class="ctx-hoster-cancel-items"></div>
</div> </div>