WebUI: Improve filter lists

This PR adds following improvements: 
* Remove unused tracker entries while processing sync data
* Take into account filter selection & terms when performing 'Start/stop/delete' context actions in filter lists
  Now, only filtered torrents will be affected by them, just like in the GUI.
* Provide better feedback when performing 'Start/stop/delete' context actions in filter lists
  Small improvement over GUI - now these actions will be disabled if it's not possible to use them.
* Add context menu to status filter list
* Fix error when toggling filter title
  Fixup for small bug introduced in https://github.com/qbittorrent/qBittorrent/pull/21269

PR #21438.
This commit is contained in:
skomerko
2024-10-12 07:40:18 +02:00
committed by GitHub
parent b1fd61af3a
commit 81509dfb65
9 changed files with 259 additions and 345 deletions

View File

@@ -35,8 +35,8 @@
const {
hashes,
isDeletingVisibleTorrents = false,
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;
@@ -72,7 +72,11 @@
cancelButton.addEventListener("click", (e) => { window.qBittorrent.Client.closeWindows(); });
confirmButton.addEventListener("click", (e) => {
torrentsTable.deselectAll();
// Some torrents might be removed when waiting for user input, so refetch the torrent list
const hashes = isDeletingVisibleTorrents
? torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker)
: torrentsTable.selectedRowsIds();
new Request({
url: "api/v2/torrents/delete",
method: "post",
@@ -81,9 +85,9 @@
"deleteFiles": deleteCB.checked
},
onSuccess: function() {
if (filterList === "tracker")
setTrackerFilter(TRACKERS_ALL);
torrentsTable.deselectAll();
updateMainData();
updatePropertiesPanel();
window.qBittorrent.Client.closeWindows();
},
onFailure: function() {

View File

@@ -3,20 +3,20 @@
<img src="images/go-down.svg" alt="QBT_TR(Collapse/expand)QBT_TR[CONTEXT=TransferListFiltersWidget]">QBT_TR(Status)QBT_TR[CONTEXT=TransferListFiltersWidget]
</span>
<ul class="filterList" id="statusFilterList">
<li id="all_filter"><span class="link"><img src="images/filter-all.svg" alt="All">QBT_TR(All (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="downloading_filter"><span class="link"><img src="images/downloading.svg" alt="Downloading">QBT_TR(Downloading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="seeding_filter"><span class="link"><img src="images/upload.svg" alt="Seeding">QBT_TR(Seeding (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="completed_filter"><span class="link"><img src="images/checked-completed.svg" alt="Completed">QBT_TR(Completed (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="running_filter"><span class="link"><img src="images/torrent-start.svg" alt="Running">QBT_TR(Running (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="stopped_filter"><span class="link"><img src="images/stopped.svg" alt="Stopped">QBT_TR(Stopped (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="active_filter"><span class="link"><img src="images/filter-active.svg" alt="Active">QBT_TR(Active (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="inactive_filter"><span class="link"><img src="images/filter-inactive.svg" alt="Inactive">QBT_TR(Inactive (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="stalled_filter"><span class="link"><img src="images/filter-stalled.svg" alt="Stalled">QBT_TR(Stalled (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="stalled_uploading_filter"><span class="link"><img src="images/stalledUP.svg" alt="Stalled Uploading">QBT_TR(Stalled Uploading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="stalled_downloading_filter"><span class="link"><img src="images/stalledDL.svg" alt="Stalled Downloading">QBT_TR(Stalled Downloading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="checking_filter"><span class="link"><img src="images/force-recheck.svg" alt="Checking">QBT_TR(Checking (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="moving_filter"><span class="link"><img src="images/set-location.svg" alt="Moving">QBT_TR(Moving (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li id="errored_filter"><span class="link"><img src="images/error.svg" alt="Errored">QBT_TR(Errored (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="all_filter"><span class="link"><img src="images/filter-all.svg" alt="All">QBT_TR(All (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="downloading_filter"><span class="link"><img src="images/downloading.svg" alt="Downloading">QBT_TR(Downloading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="seeding_filter"><span class="link"><img src="images/upload.svg" alt="Seeding">QBT_TR(Seeding (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="completed_filter"><span class="link"><img src="images/checked-completed.svg" alt="Completed">QBT_TR(Completed (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="running_filter"><span class="link"><img src="images/torrent-start.svg" alt="Running">QBT_TR(Running (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="stopped_filter"><span class="link"><img src="images/stopped.svg" alt="Stopped">QBT_TR(Stopped (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="active_filter"><span class="link"><img src="images/filter-active.svg" alt="Active">QBT_TR(Active (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="inactive_filter"><span class="link"><img src="images/filter-inactive.svg" alt="Inactive">QBT_TR(Inactive (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="stalled_filter"><span class="link"><img src="images/filter-stalled.svg" alt="Stalled">QBT_TR(Stalled (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="stalled_uploading_filter"><span class="link"><img src="images/stalledUP.svg" alt="Stalled Uploading">QBT_TR(Stalled Uploading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="stalled_downloading_filter"><span class="link"><img src="images/stalledDL.svg" alt="Stalled Downloading">QBT_TR(Stalled Downloading (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="checking_filter"><span class="link"><img src="images/force-recheck.svg" alt="Checking">QBT_TR(Checking (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="moving_filter"><span class="link"><img src="images/set-location.svg" alt="Moving">QBT_TR(Moving (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
<li class="statusesFilterContextMenuTarget" id="errored_filter"><span class="link"><img src="images/error.svg" alt="Errored">QBT_TR(Errored (0))QBT_TR[CONTEXT=StatusFilterWidget]</span></li>
</ul>
</div>
<div class="filterWrapper">
@@ -83,6 +83,33 @@
LocalPreferences.set(`category_${filterItemID}_collapsed`, filterItem.classList.toggle("collapsedCategory").toString());
};
new window.qBittorrent.ContextMenu.StatusesFilterContextMenu({
targets: ".statusesFilterContextMenuTarget",
menu: "statusesFilterMenu",
actions: {
startTorrents: (element, ref) => {
startVisibleTorrentsFN();
},
stopTorrents: (element, ref) => {
stopVisibleTorrentsFN();
},
deleteTorrents: (element, ref) => {
deleteVisibleTorrentsFN();
}
},
offsets: {
x: -15,
y: 2
},
onShow: function() {
this.startTorrentObserver();
setStatusFilter(this.options.element.id.replace("_filter", ""));
},
onHide: function() {
this.stopTorrentObserver();
}
});
const categoriesFilterContextMenu = new window.qBittorrent.ContextMenu.CategoriesFilterContextMenu({
targets: ".categoriesFilterContextMenuTarget",
menu: "categoriesFilterMenu",
@@ -102,14 +129,14 @@
deleteUnusedCategories: function(element, ref) {
deleteUnusedCategoriesFN();
},
startTorrentsByCategory: function(element, ref) {
startTorrentsByCategoryFN(Number(element.id));
startTorrents: (element, ref) => {
startVisibleTorrentsFN();
},
stopTorrentsByCategory: function(element, ref) {
stopTorrentsByCategoryFN(Number(element.id));
stopTorrents: (element, ref) => {
stopVisibleTorrentsFN();
},
deleteTorrentsByCategory: function(element, ref) {
deleteTorrentsByCategoryFN(Number(element.id));
deleteTorrents: (element, ref) => {
deleteVisibleTorrentsFN();
}
},
offsets: {
@@ -117,7 +144,11 @@
y: 2
},
onShow: function() {
this.options.element.click();
this.startTorrentObserver();
setCategoryFilter(this.options.element.id);
},
onHide: function() {
this.stopTorrentObserver();
}
});
@@ -134,14 +165,14 @@
deleteUnusedTags: function(element, ref) {
deleteUnusedTagsFN();
},
startTorrentsByTag: function(element, ref) {
startTorrentsByTagFN(Number(element.id));
startTorrents: (element, ref) => {
startVisibleTorrentsFN();
},
stopTorrentsByTag: function(element, ref) {
stopTorrentsByTagFN(Number(element.id));
stopTorrents: (element, ref) => {
stopVisibleTorrentsFN();
},
deleteTorrentsByTag: function(element, ref) {
deleteTorrentsByTagFN(Number(element.id));
deleteTorrents: (element, ref) => {
deleteVisibleTorrentsFN();
}
},
offsets: {
@@ -149,7 +180,11 @@
y: 2
},
onShow: function() {
this.options.element.click();
this.startTorrentObserver();
setTagFilter(this.options.element.id);
},
onHide: function() {
this.stopTorrentObserver();
}
});
@@ -160,14 +195,14 @@
deleteTracker: function(element, ref) {
deleteTrackerFN(element.id);
},
startTorrentsByTracker: function(element, ref) {
startTorrentsByTrackerFN(element.id);
startTorrents: (element, ref) => {
startVisibleTorrentsFN();
},
stopTorrentsByTracker: function(element, ref) {
stopTorrentsByTrackerFN(element.id);
stopTorrents: (element, ref) => {
stopVisibleTorrentsFN();
},
deleteTorrentsByTracker: function(element, ref) {
deleteTorrentsByTrackerFN(element.id);
deleteTorrents: (element, ref) => {
deleteVisibleTorrentsFN();
}
},
offsets: {
@@ -175,7 +210,11 @@
y: 2
},
onShow: function() {
this.options.element.click();
this.startTorrentObserver();
setTrackerFilter(this.options.element.id);
},
onHide: function() {
this.stopTorrentObserver();
}
});
@@ -193,7 +232,7 @@
document.getElementById("Filters_pad").addEventListener("click", (event) => {
const filterItem = event.target.closest("li");
if (filterItem?.classList?.contains("selectedFilter"))
if (!filterItem || filterItem.classList.contains("selectedFilter"))
return;
const { id: filterItemID } = filterItem;

View File

@@ -43,7 +43,7 @@
},
delete: function(element, ref) {
deleteFN();
deleteSelectedTorrentsFN();
},
setLocation: function(element, ref) {