Control the color scheme through the user option
authorAlexander Ebert <ebert@woltlab.com>
Thu, 10 Aug 2023 14:26:26 +0000 (16:26 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 10 Aug 2023 14:26:26 +0000 (16:26 +0200)
14 files changed:
com.woltlab.wcf/templates/headIncludeJavaScript.tpl
com.woltlab.wcf/templates/pageFooter.tpl
ts/WoltLabSuite/Core/Acp/Bootstrap.ts
ts/WoltLabSuite/Core/Bootstrap.ts
ts/WoltLabSuite/Core/BootstrapFrontend.ts
ts/WoltLabSuite/Core/Controller/Style/ColorScheme.ts
wcfsetup/install/files/acp/templates/header.tpl
wcfsetup/install/files/acp/templates/pageHeaderUser.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Bootstrap.js
wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js
wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js
wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Style/ColorScheme.js
wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php
wcfsetup/install/lang/de.xml

index fd8a12e10800a263aa6d085eb1ff1705001c4f61..d85341947e5f25eca467537e5d15b121e9a0acda 100644 (file)
@@ -75,7 +75,7 @@ window.addEventListener('pageshow', function(event) {
                                url: '{link controller="BackgroundQueuePerform"}{/link}',
                                force: {if $forceBackgroundQueuePerform|isset}true{else}false{/if}
                        },
-                       colorScheme: '{@$__wcf->getStyleHandler()->getColorScheme()|encodeJS}',
+                       dynamicColorScheme: {if $__wcf->getStyleHandler()->getColorScheme() === 'system'}true{else}false{/if},
                        enableUserPopover: {if $__wcf->getSession()->getPermission('user.profile.canViewUserProfile')}true{else}false{/if},
                        executeCronjobs: {if $executeCronjobs}'{link controller="CronjobPerform"}{/link}'{else}undefined{/if},
                        {if ENABLE_SHARE_BUTTONS}
index 00301f3642b57731ef6414856524100ac297b430..bdf5b1abed6c77920eebffc0a9d4bdd2edacb691 100644 (file)
                {assign var=__showStyleChanger value=false}
        {/if}
 
-       {if $__boxesFooter|count || !$boxesFooter|empty || $__showStyleChanger || $__wcf->getStyleHandler()->showColorSchemeSelector()}
+       {if $__boxesFooter|count || !$boxesFooter|empty || $__showStyleChanger}
                <div class="boxesFooter">
-                       <div class="layoutBoundary{if $__showStyleChanger || $__wcf->getStyleHandler()->showColorSchemeSelector()} clearfix{/if}">
-                               {hascontent}
+                       <div class="layoutBoundary{if $__showStyleChanger} clearfix{/if}">
+                               {if $__showStyleChanger}
                                        <div class="styleChanger jsOnly">
-                                               {content}
-                                                       {if $__showStyleChanger}
-                                                               <button type="button" class="jsButtonStyleChanger">{lang}wcf.style.changeStyle{/lang}</button>
-                                                       {/if}
-                                                       {if $__wcf->getStyleHandler()->showColorSchemeSelector()}
-                                                               <button type="button" class="page__colorScheme jsButtonStyleColorScheme jsTooltip" title="{lang}wcf.style.setColorScheme{/lang}">
-                                                                       <span class="page__colorScheme--dark">
-                                                                               {icon name='moon' type='solid'}
-                                                                       </span>
-                                                                       <span class="page__colorScheme--light">
-                                                                               {icon name='sun' type='solid'}
-                                                                       </span>
-                                                               </button>
-                                                       {/if}
-                                               {/content}
+                                               <button type="button" class="jsButtonStyleChanger">{lang}wcf.style.changeStyle{/lang}</button>
                                        </div>
-                               {/hascontent}
+                               {/if}
                                {hascontent}
                                        <div class="boxContainer">
                                                {content}
index 2437dfab244c3963d91262db2d37deacdc56c2a2..9eaa9540a66291d4a289405461f96bce2bd3defb 100644 (file)
@@ -24,7 +24,6 @@ export function setup(options: AcpBootstrapOptions): void {
   options = Core.extend(
     {
       bootstrap: {
-        colorScheme: "system",
         enableMobileMenu: true,
         pageMenuMainProvider: new AcpUiPageMenuMainBackend(),
       },
index 13a57e954ad844a04b82f496e0209e8d1498a997..c290f507d792666c37a434082c94d2058eeb0472 100644 (file)
@@ -32,9 +32,6 @@ import { init as initSearch } from "./Ui/Search";
 import { PageMenuMainProvider } from "./Ui/Page/Menu/Main/Provider";
 import { whenFirstSeen } from "./LazyLoader";
 import { adoptPageOverlayContainer } from "./Helper/PageOverlay";
-import User from "./User"
-
-import type { ColorScheme } from "./Controller/Style/ColorScheme";
 
 // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
 import "perfect-scrollbar";
@@ -53,7 +50,7 @@ window.WCF.Language.addObject = Language.addObject;
 window.__wcf_bc_eventHandler = EventHandler;
 
 export interface BoostrapOptions {
-  colorScheme: ColorScheme;
+  dynamicColorScheme: boolean;
   enableMobileMenu: boolean;
   pageMenuMainProvider: PageMenuMainProvider;
 }
@@ -151,8 +148,10 @@ export function setup(options: BoostrapOptions): void {
 
   DomChangeListener.add("WoltLabSuite/Core/Bootstrap", () => initA11y);
 
-  if (options.colorScheme === "system" && User.userId) {
-    void import("./Controller/Style/ColorScheme").then(({ setup }) => setup(options.colorScheme));
+  if (options.dynamicColorScheme) {
+    void import("./Controller/Style/ColorScheme").then(({ setup }) => {
+      setup();
+    });
   }
 
   whenFirstSeen("[data-report-content]", () => {
index 1969599d9c2259ec839cf4a291adb209ccd4b500..2ed6d376dfda2f042e3d512a8a4880919e342dcc 100644 (file)
@@ -20,14 +20,12 @@ import UiPageMenuMainFrontend from "./Ui/Page/Menu/Main/Frontend";
 import { whenFirstSeen } from "./LazyLoader";
 import { prepareRequest } from "./Ajax/Backend";
 
-import type { ColorScheme } from "./Controller/Style/ColorScheme";
-
 interface BootstrapOptions {
   backgroundQueue: {
     url: string;
     force: boolean;
   };
-  colorScheme: ColorScheme;
+  dynamicColorScheme: boolean;
   enableUserPopover: boolean;
   executeCronjobs: string | undefined;
   shareButtonProviders?: ShareProvider[];
@@ -63,7 +61,7 @@ export function setup(options: BootstrapOptions): void {
   options.backgroundQueue.url = window.WSC_API_URL + options.backgroundQueue.url.substr(window.WCF_PATH.length);
 
   Bootstrap.setup({
-    colorScheme: options.colorScheme,
+    dynamicColorScheme: options.dynamicColorScheme,
     enableMobileMenu: true,
     pageMenuMainProvider: new UiPageMenuMainFrontend(),
   });
index 256fc5635c4d329284536a182605d815dd402c9b..dd3e37cefa023f0016e3a1630c6ead679eac39e4 100644 (file)
@@ -1,92 +1,18 @@
 /**
- * Offer users the ability to enforce a specific color scheme.
+ * Dynamically updates the color scheme to match the system preference.
  *
  * @author Alexander Ebert
  * @copyright 2001-2023 WoltLab GmbH
  * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @since 6.0
- * @woltlabExcludeBundle tiny
  */
 
-import { getPhrase } from "WoltLabSuite/Core/Language";
-import { attach, create } from "../../Ui/Dropdown/Builder";
-import { registerCallback } from "../../Ui/Dropdown/Simple";
-
-export type ColorScheme = "dark" | "light" | "system";
-
-let currentScheme: ColorScheme = "system";
-let mediaQuery: MediaQueryList;
-let themeColor: HTMLMetaElement;
-
-function setScheme(scheme: ColorScheme): void {
-  currentScheme = scheme;
-
-  if (currentScheme === "light" || currentScheme === "dark") {
-    document.documentElement.dataset.colorScheme = currentScheme;
-    updateThemeColor();
-  } else {
-    applySystemScheme();
-  }
-}
-
-function applySystemScheme(): void {
-  if (currentScheme === "system") {
-    document.documentElement.dataset.colorScheme = mediaQuery.matches ? "dark" : "light";
-    updateThemeColor();
-  }
-}
-
-function updateThemeColor(): void {
+export function setup(): void {
+  const themeColor = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')!;
   themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
-}
-
-function initializeButton(button: HTMLElement): void {
-  const dropdownMenu = create([
-    {
-      identifier: "light",
-      label: getPhrase("wcf.style.setColorScheme.light"),
-      callback() {
-        setScheme("light");
-      },
-    },
-    {
-      identifier: "dark",
-      label: getPhrase("wcf.style.setColorScheme.dark"),
-      callback() {
-        setScheme("dark");
-      },
-    },
-    "divider",
-    {
-      identifier: "system",
-      label: getPhrase("wcf.style.setColorScheme.system"),
-      callback() {
-        setScheme("system");
-      },
-    },
-  ]);
-
-  attach(dropdownMenu, button);
-
-  registerCallback(button.id, (_containerId, action) => {
-    if (action === "open") {
-      dropdownMenu.querySelectorAll(".active").forEach((element) => element.classList.remove("active"));
-      dropdownMenu.querySelector(`[data-identifier="${currentScheme}"]`)!.classList.add("active");
-    }
-  });
-}
-
-export function setup(colorScheme: ColorScheme): void {
-  const button = document.querySelector<HTMLElement>(".jsButtonStyleColorScheme");
-  if (button) {
-    //initializeButton(button);
-  }
-
-  currentScheme = colorScheme;
-  themeColor = document.querySelector('meta[name="theme-color"]')!;
 
-  mediaQuery = matchMedia("(prefers-color-scheme: dark)");
-  mediaQuery.addEventListener("change", () => {
-    applySystemScheme();
+  window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
+    document.documentElement.dataset.colorScheme = event.matches ? "dark" : "light";
+    themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
   });
 }
index 4240ffe6c0245c9caaf8cc0ef901d6644ac553e8..ece8e46a6f9587d4cd47930f01bb4bf2b7cacca8 100644 (file)
@@ -94,7 +94,7 @@
                        
                        AcpBootstrap.setup({
                                bootstrap: {
-                                       colorScheme: '{@$__wcf->getStyleHandler()->getColorScheme()|encodeJS}',
+                                       dynamicColorScheme: {if $__wcf->getStyleHandler()->getColorScheme() === 'system'}true{else}false{/if},
                                        enableMobileMenu: {if PACKAGE_ID && $__isLogin|empty}true{else}false{/if},
                                }
                        });
index 4163fe39ac4f281448d347912db879e8f9e6a069..742cd6410b0dda076a0b07243610e4b934906ed4 100644 (file)
                                        <li><a class="externalURL" href="https://www.woltlab.com/pluginstore/"{if EXTERNAL_LINK_TARGET_BLANK} target="_blank" rel="noopener"{/if}>{lang}wcf.acp.index.woltlab.pluginStore{/lang}</a></li>
                                </ul>
                        </li>
-
-                       <li>
-                               <a
-                                       href="#"
-                                       role="button"
-                                       class="page__colorScheme jsButtonStyleColorScheme jsTooltip"
-                                       title="{lang}wcf.style.setColorScheme{/lang}"
-                               >
-                                       <span class="iconWrapper page__colorScheme--dark">
-                                               {icon size=16 name='moon' type='solid'}
-                                       </span>
-                                       <span class="iconWrapper page__colorScheme--light">
-                                               {icon size=16 name='sun' type='solid'}
-                                       </span>
-                               </a>
-                       </li>
                {/if}
                
                {event name='menuItems'}
index bafdc3a250ce6abe1c31cc5c77d6472840999f8d..828a9409ddd6bdab0687f3ec494d687a8606f1be 100644 (file)
@@ -20,7 +20,6 @@ define(["require", "exports", "tslib", "../Core", "../Bootstrap", "./Ui/Page/Men
     function setup(options) {
         options = Core.extend({
             bootstrap: {
-                colorScheme: "system",
                 enableMobileMenu: true,
                 pageMenuMainProvider: new Backend_1.default(),
             },
index 80333d98ef43761b60eb36c8391c00d56329cc28..b7cd0cb62814c477235da333915c4301548de3eb 100644 (file)
@@ -7,7 +7,7 @@
  * @copyright  2001-2022 WoltLab GmbH
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Form/XsrfToken", "./Language", "./Ui/Dialog", "./Ui/Dropdown/Simple", "./Ui/Mobile", "./Ui/Page/Action", "./Ui/TabMenu", "./Ui/Tooltip", "./Ui/Page/JumpTo", "./Ui/Password", "./Ui/Empty", "./Ui/Object/Action", "./Ui/Object/Action/Delete", "./Ui/Object/Action/Toggle", "./Ui/Search", "./LazyLoader", "./Helper/PageOverlay", "./User", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, Devtools_1, Listener_1, Environment, EventHandler, XsrfToken, Language, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty, UiObjectAction, UiObjectActionDelete, UiObjectActionToggle, Search_1, LazyLoader_1, PageOverlay_1, User_1) {
+define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Form/XsrfToken", "./Language", "./Ui/Dialog", "./Ui/Dropdown/Simple", "./Ui/Mobile", "./Ui/Page/Action", "./Ui/TabMenu", "./Ui/Tooltip", "./Ui/Page/JumpTo", "./Ui/Password", "./Ui/Empty", "./Ui/Object/Action", "./Ui/Object/Action/Delete", "./Ui/Object/Action/Toggle", "./Ui/Search", "./LazyLoader", "./Helper/PageOverlay", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, Devtools_1, Listener_1, Environment, EventHandler, XsrfToken, Language, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty, UiObjectAction, UiObjectActionDelete, UiObjectActionToggle, Search_1, LazyLoader_1, PageOverlay_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
@@ -31,7 +31,6 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools",
     UiObjectAction = tslib_1.__importStar(UiObjectAction);
     UiObjectActionDelete = tslib_1.__importStar(UiObjectActionDelete);
     UiObjectActionToggle = tslib_1.__importStar(UiObjectActionToggle);
-    User_1 = tslib_1.__importDefault(User_1);
     // non strict equals by intent
     if (window.WCF == null) {
         window.WCF = {};
@@ -117,8 +116,10 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools",
         });
         initA11y();
         Listener_1.default.add("WoltLabSuite/Core/Bootstrap", () => initA11y);
-        if (options.colorScheme === "system" && User_1.default.userId) {
-            void new Promise((resolve_1, reject_1) => { require(["./Controller/Style/ColorScheme"], resolve_1, reject_1); }).then(tslib_1.__importStar).then(({ setup }) => setup(options.colorScheme));
+        if (options.dynamicColorScheme) {
+            void new Promise((resolve_1, reject_1) => { require(["./Controller/Style/ColorScheme"], resolve_1, reject_1); }).then(tslib_1.__importStar).then(({ setup }) => {
+                setup();
+            });
         }
         (0, LazyLoader_1.whenFirstSeen)("[data-report-content]", () => {
             void new Promise((resolve_2, reject_2) => { require(["./Ui/Moderation/Report"], resolve_2, reject_2); }).then(tslib_1.__importStar).then(({ setup }) => setup());
index f61197b30975ca8cc5723caf941401154a0d8520..8ae7bbcc04bc1b9e4903507b645baffcd31f129b 100644 (file)
@@ -43,7 +43,7 @@ define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Co
         // Modify the URL of the background queue URL to always target the current domain to avoid CORS.
         options.backgroundQueue.url = window.WSC_API_URL + options.backgroundQueue.url.substr(window.WCF_PATH.length);
         Bootstrap.setup({
-            colorScheme: options.colorScheme,
+            dynamicColorScheme: options.dynamicColorScheme,
             enableMobileMenu: true,
             pageMenuMainProvider: new Frontend_1.default(),
         });
index ab100190cd04b87d6efbe06ccf182106e8d22ba7..416486bb50db396aeec1d0860ffbc318e1ee3a12 100644 (file)
@@ -1,81 +1,21 @@
 /**
- * Offer users the ability to enforce a specific color scheme.
+ * Dynamically updates the color scheme to match the system preference.
  *
  * @author Alexander Ebert
  * @copyright 2001-2023 WoltLab GmbH
  * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @since 6.0
- * @woltlabExcludeBundle tiny
  */
-define(["require", "exports", "WoltLabSuite/Core/Language", "../../Ui/Dropdown/Builder", "../../Ui/Dropdown/Simple"], function (require, exports, Language_1, Builder_1, Simple_1) {
+define(["require", "exports"], function (require, exports) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
-    let currentScheme = "system";
-    let mediaQuery;
-    let themeColor;
-    function setScheme(scheme) {
-        currentScheme = scheme;
-        if (currentScheme === "light" || currentScheme === "dark") {
-            document.documentElement.dataset.colorScheme = currentScheme;
-            updateThemeColor();
-        }
-        else {
-            applySystemScheme();
-        }
-    }
-    function applySystemScheme() {
-        if (currentScheme === "system") {
-            document.documentElement.dataset.colorScheme = mediaQuery.matches ? "dark" : "light";
-            updateThemeColor();
-        }
-    }
-    function updateThemeColor() {
+    function setup() {
+        const themeColor = document.querySelector('meta[name="theme-color"]');
         themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
-    }
-    function initializeButton(button) {
-        const dropdownMenu = (0, Builder_1.create)([
-            {
-                identifier: "light",
-                label: (0, Language_1.getPhrase)("wcf.style.setColorScheme.light"),
-                callback() {
-                    setScheme("light");
-                },
-            },
-            {
-                identifier: "dark",
-                label: (0, Language_1.getPhrase)("wcf.style.setColorScheme.dark"),
-                callback() {
-                    setScheme("dark");
-                },
-            },
-            "divider",
-            {
-                identifier: "system",
-                label: (0, Language_1.getPhrase)("wcf.style.setColorScheme.system"),
-                callback() {
-                    setScheme("system");
-                },
-            },
-        ]);
-        (0, Builder_1.attach)(dropdownMenu, button);
-        (0, Simple_1.registerCallback)(button.id, (_containerId, action) => {
-            if (action === "open") {
-                dropdownMenu.querySelectorAll(".active").forEach((element) => element.classList.remove("active"));
-                dropdownMenu.querySelector(`[data-identifier="${currentScheme}"]`).classList.add("active");
-            }
-        });
-    }
-    function setup(colorScheme) {
-        const button = document.querySelector(".jsButtonStyleColorScheme");
-        if (button) {
-            //initializeButton(button);
-        }
-        currentScheme = colorScheme;
-        themeColor = document.querySelector('meta[name="theme-color"]');
-        mediaQuery = matchMedia("(prefers-color-scheme: dark)");
-        mediaQuery.addEventListener("change", () => {
-            applySystemScheme();
+        window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
+            document.documentElement.dataset.colorScheme = event.matches ? "dark" : "light";
+            themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
         });
     }
     exports.setup = setup;
index 62df1ecf15f6f5238bab67c4db03a600a9ee3954..6ddd4f95b41ca1423b6e99b21d234612672e0a2f 100644 (file)
@@ -144,9 +144,6 @@ final class PreloadPhrasesCollectingListener
         $event->preload('wcf.reactions.summary.listReactions');
 
         $event->preload('wcf.style.changeStyle');
-        $event->preload('wcf.style.setColorScheme.dark');
-        $event->preload('wcf.style.setColorScheme.light');
-        $event->preload('wcf.style.setColorScheme.system');
 
         $event->preload('wcf.user.activityPoint');
         $event->preload('wcf.user.language');
index a5f4df91e266b375e02e41a2a9e5cbf6de57da4f..65cd124f961520628572be5ee04dec9bcca735a1 100644 (file)
@@ -4660,7 +4660,7 @@ Dateianhänge:
                <item name="wcf.user.styles"><![CDATA[Stile]]></item>
                <item name="wcf.user.style.description"><![CDATA[Stil der Benutzeroberfläche]]></item>
                <item name="wcf.user.style.colorScheme"><![CDATA[Bevorzugtes Farbschema]]></item>
-               <item name="wcf.user.style.colorScheme.description"><![CDATA[Die automatische Erkennung wendet das Farbschema basierend auf den Präferenzen Ihres Geräts an. Einige Stile verfügen möglicherweise nicht über angepasste Farbschemas.]]></item>
+               <item name="wcf.user.style.colorScheme.description"><![CDATA[Die automatische Erkennung wendet das Farbschema basierend auf den Präferenzen Ihres Geräts an. Einige Stile verfügen möglicherweise nicht über angepasste Farbschemata.]]></item>
                <item name="wcf.user.username.description"><![CDATA[Der Benutzername muss mindestens {REGISTER_USERNAME_MIN_LENGTH} und darf maximal {REGISTER_USERNAME_MAX_LENGTH} Zeichen lang sein.]]></item>
                <item name="wcf.user.password.description"><![CDATA[Ein sicheres Kennwort sollte mindestens 10 Zeichen lang sein.]]></item>
                <item name="wcf.user.password.strength"><![CDATA[Kennwortstärke]]></item>