-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
WebUI: Add checkboxes for easy selection on mobile #22892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
2e6d301
to
538116e
Compare
I wouldn't expect WebUI exclusive feature touches main (server) app in any way. Why not store this setting at the WebUI side? |
538116e
to
5629b07
Compare
Agreed, latest version introduces this option only in the WebUI and does not change any server/app files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md#opening-a-pull-request
Provide screenshots for UI related changes.
@@ -585,6 +587,8 @@ window.qBittorrent.DynamicTable ??= (() => { | |||
column["onVisibilityChange"] = null; | |||
column["staticWidth"] = null; | |||
column["calculateBuffer"] = () => 0; | |||
column["jsManaged"] = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean jsManaged
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah im on board if there are better design ideas.
jsManaged
for me means Managed by JS code and not provided from C++/QT/APP
The default columns are being supplied from the src/webui/api/serialize/serialize_torrent.cpp
.
The other approach (PR22131) is to have a "js" column (not supplied by serialize
) with dataProperties
the properties that when changed would force it to update.
My introduction of jsManaged
is because
- We do not get info from
serialize
for which torrent is selected. - The checkbox is independent of the other torrent properties (no
dataProperties
to show when a refresh of the checkbox is needed)
So i went with a quick flag that would show that this column is managed by JS code, and its update function (updateTd
) should be called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsManaged
bears no meaning of how it is used or its context. Nobody thinks about C++/QT/APP in this context or reading this code. It is a bad name.
Furthermore, why do you need a property when you can just look up LocalPreferences.get("use_checkbox")
within updateRow()
?
htmlInput.type = "checkbox"; | ||
htmlInput.className = "selectRowCheckbox"; | ||
htmlInput.checked = self.isRowSelected(row.rowId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't you put these in if (htmlInput === null)
body?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree for type
and className
, once we render the checkbox it would already have them.
I do think that checked
must remain outside of htmlInput === null
as we need to update the state of existing checkboxes too to represent the currently selected torrents.
htmlInput.className = "selectRowCheckbox"; | ||
htmlInput.checked = self.isRowSelected(row.rowId); | ||
|
||
if (td.firstElementChild === null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be merged with if (htmlInput === null)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think so.
It is outside of htmlInput === null
so that it can append the newly created checkbox if it does not exist.
td.firstElementChild === null
- if true
that row did not have a checkbox and we add one to it, otherwise the changes were already applied to the checkbox through the htmlInput
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't follow. Let me ask another way.
Please point out what is wrong with the following code:
if (LocalPreferences.get("use_checkbox", "false") === "true") {
this.columns["selected"].updateTd = (td, row) => {
let htmlInput = td.firstElementChild;
if (htmlInput === null) {
htmlInput = document.createElement("input");
htmlInput.type = "checkbox";
htmlInput.className = "selectRowCheckbox";
htmlInput.addEventListener("click", (e) => {
e.stopPropagation();
if (htmlInput.checked)
this.selectRow(row.rowId);
else
this.deselectRow(row.rowId);
});
td.append(htmlInput);
}
htmlInput.checked = this.isRowSelected(row.rowId);
};
}
Adds a setting that when turned `ON` renders a left most column that represents a checkbox in the torrents table The state of this checkbox updates on torrent select/deselect to highlight the currently selected torrents Checking/Unchecking the box allows for easier management of multiple torrents through a touch-screen device where selecting multiple non sequential torrents differs from using a mouse.
5629b07
to
37bf7b3
Compare
@@ -163,6 +163,9 @@ window.qBittorrent.DynamicTable ??= (() => { | |||
}, true); | |||
|
|||
this.dynamicTableDiv.addEventListener("touchstart", (e) => { | |||
// ignore touch events on checkboxes, otherwise breaks on mobile | |||
if (e.target.classList.contains("selectRowCheckbox")) | |||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return; | |
return; | |
@@ -3031,6 +3036,8 @@ | |||
} | |||
settings["alternative_webui_enabled"] = alternative_webui_enabled; | |||
settings["alternative_webui_path"] = webui_files_location_textarea; | |||
settings["use_checkbox"] = document.getElementById("useCheckbox").checked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are you sending this setting to qbt server?
htmlInput.className = "selectRowCheckbox"; | ||
htmlInput.checked = self.isRowSelected(row.rowId); | ||
|
||
if (td.firstElementChild === null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't follow. Let me ask another way.
Please point out what is wrong with the following code:
if (LocalPreferences.get("use_checkbox", "false") === "true") {
this.columns["selected"].updateTd = (td, row) => {
let htmlInput = td.firstElementChild;
if (htmlInput === null) {
htmlInput = document.createElement("input");
htmlInput.type = "checkbox";
htmlInput.className = "selectRowCheckbox";
htmlInput.addEventListener("click", (e) => {
e.stopPropagation();
if (htmlInput.checked)
this.selectRow(row.rowId);
else
this.deselectRow(row.rowId);
});
td.append(htmlInput);
}
htmlInput.checked = this.isRowSelected(row.rowId);
};
}
@@ -585,6 +587,8 @@ window.qBittorrent.DynamicTable ??= (() => { | |||
column["onVisibilityChange"] = null; | |||
column["staticWidth"] = null; | |||
column["calculateBuffer"] = () => 0; | |||
column["jsManaged"] = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsManaged
bears no meaning of how it is used or its context. Nobody thinks about C++/QT/APP in this context or reading this code. It is a bad name.
Furthermore, why do you need a property when you can just look up LocalPreferences.get("use_checkbox")
within updateRow()
?
Closes #22701
I assume tasks refers to torrents until told otherwise.
Screenshots
Desktop

Mobile

Summary
Adds a setting that when turned
ON
renders a left most column that represents a checkbox in the torrents tableThe state of this checkbox updates on torrent select/deselect to highlight the currently selected torrents
Checking/Unchecking the box allows for easier management of multiple torrents through a touch-screen device where selecting multiple non sequential torrents is more difficult than when using a mouse.
Notes
jsManaged
(boolean) attribute in to thecolumn
object, which would befalse
by default, but in case it istrue
we will trigger the column'supdateTd
regardless of change in server response.DynamicTable.selectAll
&DynamicTable.deselectAll
did not trigger the event (onSelectedRowChanged
) thatselectRow
and its related do, might have been done with performance in mind, dont have that much torrents to test with. Made it fire the events in order to be able to detect selection of all torrents.touchstart
event ofdynamicTableDiv
. On desktop all was good, but on mobile this event fires together with the checkbox click and effectively deselects the selected row causing the checkbox to "blink" on for a second and then become unchecked again.