WebUI: Improve torrent deletion dialog

This PR improves torrent deletion dialog.

1. Now shows different message depending on the number of selected torrents
2. Visually pretty much inline with the GUI
3. Adjusts to content on load
4. Now uses XHR load method. Panels / windows loaded using this method become part of the current document so there is no need to import styles or scripts (they should load marginally faster now).

PR #21185.

---------

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
This commit is contained in:
skomerko 2024-08-25 08:11:36 +02:00 committed by GitHub
parent 9a9c375b9d
commit 39dd415d43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 249 additions and 142 deletions

View file

@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="${LANG}">
<head>
<meta charset="UTF-8">
<title>QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]</title>
<link rel="stylesheet" href="css/style.css?v=${CACHEID}" type="text/css">
<script src="scripts/lib/MooTools-Core-1.6.0-compat-compressed.js"></script>
<script src="scripts/lib/MooTools-More-1.6.0-compat-compressed.js"></script>
<script>
"use strict";
function setRememberBtnEnabled(enable) {
const btn = $("rememberBtn");
btn.disabled = !enable;
const icon = btn.getElementsByTagName("path")[0];
if (enable)
icon.style.removeProperty("fill");
else
icon.style.fill = "var(--color-border-default)";
}
window.addEventListener("DOMContentLoaded", () => {
new Request({
url: "images/object-locked.svg",
method: "get",
onSuccess: function(text, xml) {
const newIcon = xml.childNodes[0];
newIcon.style.height = "24px";
newIcon.style.width = "24px";
$("rememberBtn").appendChild(newIcon);
setRememberBtnEnabled(false);
}
}).send();
const isDeletingFiles = (new URI().getData("deleteFiles") === "true");
$("deleteFromDiskCB").checked = isDeletingFiles;
const prefCache = window.parent.qBittorrent.Cache.preferences.get();
let prefDeleteContentFiles = prefCache.delete_torrent_content_files;
$("deleteFromDiskCB").checked ||= prefDeleteContentFiles;
$("deleteFromDiskCB").addEventListener("click", (e) => {
setRememberBtnEnabled($("deleteFromDiskCB").checked !== prefDeleteContentFiles);
});
// Set current "Delete files" choice as the default
$("rememberBtn").addEventListener("click", (e) => {
window.parent.qBittorrent.Cache.preferences.set({
data: {
"delete_torrent_content_files": $("deleteFromDiskCB").checked
},
onSuccess: function() {
prefDeleteContentFiles = $("deleteFromDiskCB").checked;
setRememberBtnEnabled(false);
}
});
});
const hashes = new URI().getData("hashes").split("|");
$("cancelBtn").focus();
$("cancelBtn").addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
window.parent.qBittorrent.Client.closeWindows();
});
$("confirmBtn").addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
parent.torrentsTable.deselectAll();
const cmd = "api/v2/torrents/delete";
const deleteFiles = $("deleteFromDiskCB").checked;
new Request({
url: cmd,
method: "post",
data: {
"hashes": hashes.join("|"),
"deleteFiles": deleteFiles
},
onComplete: function() {
window.parent.qBittorrent.Client.closeWindows();
}
}).send();
});
});
</script>
</head>
<body>
<br>
<p>&nbsp;&nbsp;QBT_TR(Are you sure you want to remove the selected torrents from the transfer list?)QBT_TR[CONTEXT=HttpServer]</p>
&nbsp;&nbsp;&nbsp;&nbsp;<button type="button" id="rememberBtn" title="QBT_TR(Remember choice)QBT_TR[CONTEXT=HttpServer]" aria-label="QBT_TR(Remember choice)QBT_TR[CONTEXT=HttpServer]" style="vertical-align: middle; padding: 4px 6px;"></button>
<input type="checkbox" id="deleteFromDiskCB"> <label for="deleteFromDiskCB"><i>QBT_TR(Also remove the content files)QBT_TR[CONTEXT=confirmDeletionDlg]</i></label><br><br>
<div style="text-align: right;">
<input type="button" id="cancelBtn" value="QBT_TR(Cancel)QBT_TR[CONTEXT=MainWindow]">&nbsp;&nbsp;<input type="button" id="confirmBtn" value="QBT_TR(Remove)QBT_TR[CONTEXT=MainWindow]">&nbsp;&nbsp;
</div>
</body>
</html>

View file

@ -720,3 +720,66 @@ td.statusBarSeparator {
background-color: var(--color-background-hover);
color: var(--color-text-white);
}
/* Confirm deletion dialog */
#confirmDeletionPage * {
box-sizing: border-box;
}
#confirmDeletionPage_content {
display: flex !important; /* override for default mocha inline style */
flex-direction: column;
height: 100%;
}
#confirmDeletionPage_content > :last-child {
align-self: flex-end;
}
#confirmDeletionDialog {
margin: auto 0;
}
#rememberBtn {
background: url("../images/object-locked.svg") center center / 24px
no-repeat var(--color-background-popup);
height: 38px;
padding: 4px 6px;
width: 38px;
}
#rememberBtn.disabled {
filter: grayscale(100%);
}
#deleteFromDiskCB {
margin: 0 2px 0 0;
vertical-align: -1px;
}
#deleteTorrentMessage {
overflow-wrap: anywhere;
}
.confirmDeletionGrid {
align-items: center;
display: grid;
gap: 3px 4px;
grid-template-columns: max-content 1fr;
margin-bottom: 10px;
}
.deletionGridItem {
padding: 3px;
}
.deletionGridItem:first-child {
justify-self: center;
}
.confirmDialogWarning {
background: url("../images/dialog-warning.svg") center center no-repeat;
height: 38px;
width: 38px;
}

View file

@ -29,6 +29,7 @@ window.qBittorrent ??= {};
window.qBittorrent.Client ??= (() => {
const exports = () => {
return {
closeWindow: closeWindow,
closeWindows: closeWindows,
genHash: genHash,
getSyncMainDataInterval: getSyncMainDataInterval,
@ -44,6 +45,13 @@ window.qBittorrent.Client ??= (() => {
};
};
const closeWindow = function(windowID) {
const window = document.getElementById(windowID);
if (!window)
return;
MochaUI.closeWindow(window);
};
const closeWindows = function() {
MochaUI.closeAll();
};

View file

@ -657,6 +657,10 @@ window.qBittorrent.DynamicTable ??= (() => {
}
},
getRow: function(rowId) {
return this.rows.get(rowId);
},
getFilteredAndSortedRows: function() {
const filteredRows = [];

View file

@ -393,23 +393,35 @@ const initializeWindows = function() {
}
};
deleteFN = function(deleteFiles = false) {
deleteFN = function(forceDeleteFiles = false) {
const hashes = torrentsTable.selectedRowsIds();
if (hashes.length) {
if (hashes.length > 0) {
window.qBittorrent.Client.closeWindow("confirmDeletionPage");
new MochaUI.Window({
id: "confirmDeletionPage",
icon: "images/qbittorrent-tray.svg",
title: "QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]",
loadMethod: "iframe",
contentURL: new URI("confirmdeletion.html").setData("hashes", hashes.join("|")).setData("deleteFiles", deleteFiles).toString(),
scrollbars: false,
resizable: true,
data: {
hashes: hashes,
forceDeleteFiles: forceDeleteFiles
},
loadMethod: "xhr",
contentURL: "views/confirmdeletion.html",
maximizable: false,
padding: 10,
width: 424,
height: 160
collapsible: false,
padding: {
left: 5,
right: 10,
top: 15,
bottom: 15
},
width: 480,
onContentLoaded: function(w) {
MochaUI.resizeWindow(w, { centered: true });
MochaUI.centerWindow(w);
}
});
updateMainData();
}
};
@ -732,21 +744,30 @@ const initializeWindows = function() {
deleteTorrentsByCategoryFN = function(categoryHash) {
const hashes = torrentsTable.getFilteredTorrentsHashes("all", categoryHash, TAGS_ALL, TRACKERS_ALL);
if (hashes.length) {
if (hashes.length > 0) {
window.qBittorrent.Client.closeWindow("confirmDeletionPage");
new MochaUI.Window({
id: "confirmDeletionPage",
icon: "images/qbittorrent-tray.svg",
title: "QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]",
loadMethod: "iframe",
contentURL: new URI("confirmdeletion.html").setData("hashes", hashes.join("|")).toString(),
scrollbars: false,
resizable: true,
data: { hashes: hashes },
loadMethod: "xhr",
contentURL: "views/confirmdeletion.html",
maximizable: false,
padding: 10,
width: 424,
height: 160
collapsible: false,
padding: {
left: 5,
right: 10,
top: 15,
bottom: 15
},
width: 480,
onContentLoaded: function(w) {
MochaUI.resizeWindow(w, { centered: true });
MochaUI.centerWindow(w);
}
});
updateMainData();
}
};
@ -877,21 +898,30 @@ const initializeWindows = function() {
deleteTorrentsByTagFN = function(tagHash) {
const hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, tagHash, TRACKERS_ALL);
if (hashes.length) {
if (hashes.length > 0) {
window.qBittorrent.Client.closeWindow("confirmDeletionPage");
new MochaUI.Window({
id: "confirmDeletionPage",
icon: "images/qbittorrent-tray.svg",
title: "QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]",
loadMethod: "iframe",
contentURL: new URI("confirmdeletion.html").setData("hashes", hashes.join("|")).toString(),
scrollbars: false,
resizable: true,
data: { hashes: hashes },
loadMethod: "xhr",
contentURL: "views/confirmdeletion.html",
maximizable: false,
padding: 10,
width: 424,
height: 160
collapsible: false,
padding: {
left: 5,
right: 10,
top: 15,
bottom: 15
},
width: 480,
onContentLoaded: function(w) {
MochaUI.resizeWindow(w, { centered: true });
MochaUI.centerWindow(w);
}
});
updateMainData();
}
};
@ -982,22 +1012,31 @@ const initializeWindows = function() {
}
}
if (hashes.length) {
if (hashes.length > 0) {
window.qBittorrent.Client.closeWindow("confirmDeletionPage");
new MochaUI.Window({
id: "confirmDeletionPage",
icon: "images/qbittorrent-tray.svg",
title: "QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]",
loadMethod: "iframe",
contentURL: new URI("confirmdeletion.html").setData("hashes", hashes.join("|")).toString(),
scrollbars: false,
resizable: true,
data: {
hashes: hashes,
filterList: "tracker"
},
loadMethod: "xhr",
contentURL: "views/confirmdeletion.html",
maximizable: false,
padding: 10,
width: 424,
height: 160,
onCloseComplete: function() {
updateMainData();
setTrackerFilter(TRACKERS_ALL);
collapsible: false,
padding: {
left: 5,
right: 10,
top: 15,
bottom: 15
},
width: 480,
onContentLoaded: function(w) {
MochaUI.resizeWindow(w, { centered: true });
MochaUI.centerWindow(w);
}
});
}

View file

@ -0,0 +1,95 @@
<div id="confirmDeletionDialog">
<div class="confirmDeletionGrid">
<span class="deletionGridItem confirmDialogWarning"></span>
<span class="deletionGridItem" id="deleteTorrentMessage"></span>
<span class="deletionGridItem">
<button class="disabled" type="button" id="rememberBtn" title="QBT_TR(Remember choice)QBT_TR[CONTEXT=HttpServer]" aria-label="QBT_TR(Remember choice)QBT_TR[CONTEXT=HttpServer]" disabled></button>
</span>
<span class="deletionGridItem">
<input type="checkbox" id="deleteFromDiskCB">
<label for="deleteFromDiskCB">
<em>QBT_TR(Also remove the content files)QBT_TR[CONTEXT=confirmDeletionDlg]</em>
</label>
</span>
</div>
</div>
<div>
<input type="button" value="QBT_TR(Remove)QBT_TR[CONTEXT=MainWindow]" id="confirmDeletionButton">
<input type="button" value="QBT_TR(Cancel)QBT_TR[CONTEXT=MainWindow]" id="cancelDeletionButton">
</div>
<script>
"use strict";
(() => {
const setRememberBtnEnabled = (enable) => {
rememberButton.disabled = !enable;
rememberButton.classList.toggle("disabled", !enable);
};
const confirmButton = document.getElementById("confirmDeletionButton");
const cancelButton = document.getElementById("cancelDeletionButton");
const rememberButton = document.getElementById("rememberBtn");
const deleteCB = document.getElementById("deleteFromDiskCB");
const deletionText = document.getElementById("deleteTorrentMessage");
const {
hashes,
forceDeleteFiles = false,
filterList = null
} = window.MUI.Windows.instances["confirmDeletionPage"].options.data;
let prefDeleteContentFiles = window.qBittorrent.Cache.preferences.get().delete_torrent_content_files;
deleteCB.checked = forceDeleteFiles || prefDeleteContentFiles;
let deleteMessage;
if (hashes.length === 1) {
const { full_data: { name } } = torrentsTable.getRow(hashes[0]);
deleteMessage = "QBT_TR(Are you sure you want to remove %1 from the transfer list?)QBT_TR[CONTEXT=HttpServer]".replace("%1", `"${name}"`);
}
else {
deleteMessage = "QBT_TR(Are you sure you want to remove these %1 torrents from the transfer list?)QBT_TR[CONTEXT=HttpServer]".replace("%1", hashes.length);
}
deletionText.textContent = deleteMessage;
// Enable "Remember" button if the current choice is different from the saved preference
deleteCB.addEventListener("click", (e) => { setRememberBtnEnabled(deleteCB.checked !== prefDeleteContentFiles); });
// Set current "Delete files" choice as the default
rememberButton.addEventListener("click", (e) => {
window.qBittorrent.Cache.preferences.set({
data: {
"delete_torrent_content_files": deleteCB.checked
},
onSuccess: function() {
prefDeleteContentFiles = deleteCB.checked;
setRememberBtnEnabled(false);
}
});
});
cancelButton.focus();
cancelButton.addEventListener("click", (e) => { window.qBittorrent.Client.closeWindows(); });
confirmButton.addEventListener("click", (e) => {
torrentsTable.deselectAll();
new Request({
url: "api/v2/torrents/delete",
method: "post",
data: {
"hashes": hashes.join("|"),
"deleteFiles": deleteCB.checked
},
onSuccess: function() {
if (filterList === "tracker")
setTrackerFilter(TRACKERS_ALL);
updateMainData();
window.qBittorrent.Client.closeWindows();
},
onFailure: function() {
alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]");
},
}).send();
});
})();
</script>

View file

@ -2,7 +2,6 @@
<qresource prefix="/www">
<file>private/addpeers.html</file>
<file>private/addtrackers.html</file>
<file>private/confirmdeletion.html</file>
<file>private/confirmfeeddeletion.html</file>
<file>private/confirmruleclear.html</file>
<file>private/confirmruledeletion.html</file>
@ -418,6 +417,7 @@
<file>private/uploadlimit.html</file>
<file>private/views/about.html</file>
<file>private/views/aboutToolbar.html</file>
<file>private/views/confirmdeletion.html</file>
<file>private/views/filters.html</file>
<file>private/views/installsearchplugin.html</file>
<file>private/views/log.html</file>