Skip to content
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

WebUI: Add color scheme switcher #21613

Merged

Conversation

sledgehammer999
Copy link
Member

@sledgehammer999 sledgehammer999 commented Oct 14, 2024

Closes #21600

Sometimes I am bored and want to try new things. This time I wanted to code the WebUI.

If user hasn't explicitly set validly his preference -> Use system/browser preference and react to changes to it
If user has explicitly and validly set his preference -> Use the user's settings and don't react to system/browser preference change

Obligatory pic:
Untitled

@sledgehammer999 sledgehammer999 added the WebUI WebUI-related issues/changes label Oct 14, 2024
@Pentaphon
Copy link

Pentaphon commented Oct 15, 2024

Honestly, this would look a lot better if you simply had a sun/moon toggle button. No need to take up all that space with text and a checkbox. I'd go with that instead and put the toggle at the bottom right corner. If you want to keep it using text, I'd put it under the view dropdown. Anything but the checkbox and text, which looks very out of place.

@glassez glassez changed the title Add color scheme switcher WebUI: Add color scheme switcher Oct 15, 2024
@HanabishiRecca
Copy link
Contributor

HanabishiRecca commented Oct 15, 2024

I would vote for not inventing anything special here. Just make a regular selector in the settings.
With clear and obvious options: System, Light and Dark. Same as in #21615.

@sledgehammer999
Copy link
Member Author

I would vote for not inventing anything special here. Just make a regular selector in the settings.

Do you mean in the Settings/Preferences dialog or in the menu (eg View)?

@HanabishiRecca
Copy link
Contributor

HanabishiRecca commented Oct 15, 2024

A regular option in the settings dialog. For example:

image

@wolfdael
Copy link

Its a humble request. Please merge this for webui ASAP. I have chronic floaters in my eye (what are floaters?) and I cant see well in situations like inverted dark mode. Seemingly, the current webui torrent list and rss list look like inverted dark mode and I am so lost when looking at the screen because I cant read. Currently, I am using a android client to connect to the local docker. Due to my setup, I am not able to downgrade as well. Thank you @sledgehammer999 @HanabishiRecca

@HanabishiRecca
Copy link
Contributor

Wdym by "inverted dark mode"? And if you have issues with dark modes, why do you have the dark mode enabled in your system/browser?

@sledgehammer999
Copy link
Member Author

I have addressed the comments.

@sledgehammer999 sledgehammer999 force-pushed the webui_color_switcher branch 3 times, most recently from 9cbec54 to e2fb7f3 Compare October 21, 2024 01:03
src/webui/www/private/views/preferences.html Outdated Show resolved Hide resolved
src/webui/www/private/scripts/client.js Outdated Show resolved Hide resolved
src/webui/www/private/scripts/client.js Outdated Show resolved Hide resolved
src/webui/www/private/views/preferences.html Outdated Show resolved Hide resolved
src/webui/www/private/views/preferences.html Outdated Show resolved Hide resolved
src/webui/www/private/views/preferences.html Outdated Show resolved Hide resolved
src/webui/www/private/scripts/client.js Outdated Show resolved Hide resolved
@sledgehammer999 sledgehammer999 force-pushed the webui_color_switcher branch 2 times, most recently from f386261 to 784a8c7 Compare October 21, 2024 09:12
if (colorScheme === 0)
LocalPreferences.remove("color_scheme");
else if (colorScheme === 1)
LocalPreferences.set("color_scheme", "light");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I use camelCase for the settings' key too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have rules for it. Snake case is fine for now.

@sledgehammer999
Copy link
Member Author

I think I am done with the modifications now.

@skomerko
Copy link
Contributor

skomerko commented Oct 21, 2024

I think I am done with the modifications now.

There is still one issue present - white theme flash on page load when using a dark theme (and vice versa). I can guarantee users will complain about that right after release if not further changes are made.

@sledgehammer999
Copy link
Member Author

I think I am done with the modifications now.

There is still one issue present - white theme flash on page load when using a dark theme. I can guarantee users will complain about that right after release if not further changes are made.

I am not well versed in the webui world.
Currently we set the theme inside the "load" signal handler. Aka it runs immediately after the page is rendered. Each time the user changes the setting, the page is reloaded and the signal handler is run again.

What's the preferred solution here for time run and subsequent user change?

@HanabishiRecca
Copy link
Contributor

HanabishiRecca commented Oct 21, 2024

Currently we set the theme inside the "load" signal handler. Aka it runs immediately after the page is rendered.

It's not.

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets, scripts, iframes, and images, except those that are loaded lazily.

Which could have a large delay, depending on network connection speed.

If you want to fire immediately after the page is rendered, use document's DOMContentLoaded event.

It is a common mistake to use load where DOMContentLoaded would be more appropriate.

@sledgehammer999
Copy link
Member Author

I think I am done with the modifications now.

There is still one issue present - white theme flash on page load when using a dark theme. I can guarantee users will complain about that right after release if not further changes are made.

I am not well versed in the webui world. Currently we set the theme inside the "load" signal handler. Aka it runs immediately after the page is rendered. Each time the user changes the setting, the page is reloaded and the signal handler is run again.

What's the preferred solution here for time run and subsequent user change?

I don't know if it advisable to do so but a solution is:

  1. Drop the defer script attribute from client.js in index.html
  2. Do the setup of updateColorScheme() outside the signal handler for load, in the global scope. It will run as soon as the script loads and before the page renders.
  3. I am not sure how to handle the user changing the setting afterwards. Will the page reload re-run the scripts?

@HanabishiRecca
Copy link
Contributor

HanabishiRecca commented Oct 21, 2024

No, that's a terrible advice. Just use DOMContentLoaded event instead.
I.e. move your init code to somewhere under the

window.addEventListener("DOMContentLoaded", () => {

But actually, as we use defer on scripts anyway, even DOMContentLoaded is not really needed.
You can just do your stuff inside the main script body.

@skomerko
Copy link
Contributor

skomerko commented Oct 21, 2024

No, that's a terrible advice. Just use DOMContentLoaded event instead. I.e. move your init code to somewhere under the

window.addEventListener("DOMContentLoaded", () => {

But actually, as we use defer on scripts anyway, even DOMContentLoaded is not really needed. You can just do your stuff inside the main script body.

I can't really check it right now but IIRC by the time DOMContentLoaded will fire, the browser may already have painted page's background. Adding a tiny render blocking script in the head will work for sure.

@sledgehammer999
Copy link
Member Author

Applying the following patch I notice 2 things:

  1. On the very first render I don't see a white flash (with browser set to dark mode)
  2. After a reload/refresh there's a flash of white background
diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js
index d3a8b783b..684c2cf95 100644
--- a/src/webui/www/private/scripts/client.js
+++ b/src/webui/www/private/scripts/client.js
@@ -25,6 +25,19 @@
 
 "use strict";
 
+// Setup color scheme switching
+const updateColorScheme = () => {
+    const root = document.documentElement;
+    const colorScheme = LocalPreferences.get("color_scheme");
+    const validScheme = (colorScheme === "light") || (colorScheme === "dark");
+    const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
+    root.classList.toggle("dark", ((!validScheme && isDark) || (colorScheme === "dark")));
+};
+
+const colorSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)");
+colorSchemeQuery.addEventListener("change", updateColorScheme);
+updateColorScheme();
+
 window.qBittorrent ??= {};
 window.qBittorrent.Client ??= (() => {
     const exports = () => {
@@ -1681,19 +1694,6 @@ window.addEventListener("load", () => {
     window.qBittorrent.Cache.preferences.init();
     window.qBittorrent.Cache.qbtVersion.init();
 
-    // Setup color scheme switching
-    const updateColorScheme = () => {
-        const root = document.documentElement;
-        const colorScheme = LocalPreferences.get("color_scheme");
-        const validScheme = (colorScheme === "light") || (colorScheme === "dark");
-        const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
-        root.classList.toggle("dark", ((!validScheme && isDark) || (colorScheme === "dark")));
-    };
-
-    const colorSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)");
-    colorSchemeQuery.addEventListener("change", updateColorScheme);
-    updateColorScheme();
-
     // switch to previously used tab
     const previouslyUsedTab = LocalPreferences.get("selected_window_tab", "transfers");
     switch (previouslyUsedTab) {

@HanabishiRecca
Copy link
Contributor

That's a bit complicated topic yeah.

DOMContentLoaded does not wait for stylesheets to load, however deferred scripts do wait for stylesheets, and the DOMContentLoaded event is queued after deferred scripts.

@HanabishiRecca
Copy link
Contributor

We could do it vice versa though: load the dark theme initially, then switch to the light.
I.e. instead of .dark class there would be .light.
Light mode users are not afraid of black screen flashes afaik.

@HanabishiRecca
Copy link
Contributor

Another way: simply setting class="dark" inside the html also works.

--- a/src/webui/www/private/index.html
+++ b/src/webui/www/private/index.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="${LANG}">
+<html lang="${LANG}" class="dark">
 
 <head>
     <meta charset="UTF-8">

@sledgehammer999
Copy link
Member Author

@Chocobo1 should I follow @HanabishiRecca recommendation (apply dark class) or do you have something else to propose?

@Chocobo1
Copy link
Member

@Chocobo1 should I follow @HanabishiRecca recommendation (apply dark class) or do you have something else to propose?

I think it is fine. I would also add a code comment about avoiding the bright flash.

@sledgehammer999 sledgehammer999 force-pushed the webui_color_switcher branch 2 times, most recently from 537bf28 to 4ab3218 Compare November 1, 2024 20:03
@sledgehammer999
Copy link
Member Author

I added the dark class by default to the html.

src/webui/www/private/scripts/client.js Outdated Show resolved Hide resolved
if (colorScheme === 0)
LocalPreferences.remove("color_scheme");
else if (colorScheme === 1)
LocalPreferences.set("color_scheme", "light");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have rules for it. Snake case is fine for now.

@sledgehammer999 sledgehammer999 merged commit 0771970 into qbittorrent:master Nov 2, 2024
14 checks passed
@sledgehammer999 sledgehammer999 deleted the webui_color_switcher branch November 2, 2024 14:31
@HanabishiRecca
Copy link
Contributor

HanabishiRecca commented Nov 2, 2024

Hmm, seems like all iframe dialogs (download.html, upload.html etc.) ignore selected color scheme.
Unfortunately I didn't notice that earlier.

@HanabishiRecca
Copy link
Contributor

Addressed in a new PR: #21750

Chocobo1 pushed a commit that referenced this pull request Nov 9, 2024
Applies the color scheme for iframe dialogs.

Fixup for #21613.
PR #21750.
sledgehammer999 added a commit that referenced this pull request Nov 9, 2024
…witcher

WebUI: Add color scheme switcher (v5_0_x)

Bacport of #21613
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WebUI WebUI-related issues/changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot toggle to lightmode on WebUI
6 participants