From 653005195879b267e54897b75bcae0c76032c614 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 May 2024 17:02:12 -0400 Subject: [PATCH] Closes #15630: Remove server-side color mode preference & simplify toggling --- docs/development/user-preferences.md | 1 - netbox/netbox/preferences.py | 9 ---- netbox/project-static/dist/netbox.js | Bin 375227 -> 375155 bytes netbox/project-static/dist/netbox.js.map | Bin 340202 -> 340141 bytes netbox/project-static/js/setmode.js | 60 ++++++----------------- netbox/project-static/src/colorMode.ts | 5 +- netbox/templates/base/base.html | 3 -- netbox/users/forms/model_forms.py | 2 +- 8 files changed, 17 insertions(+), 63 deletions(-) diff --git a/docs/development/user-preferences.md b/docs/development/user-preferences.md index ceb5321a9..deb469bfb 100644 --- a/docs/development/user-preferences.md +++ b/docs/development/user-preferences.md @@ -11,4 +11,3 @@ The `users.UserConfig` model holds individual preferences for each user in the f | pagination.placement | Where to display the paginator controls relative to the table | | tables.${table}.columns | The ordered list of columns to display when viewing the table | | tables.${table}.ordering | A list of column names by which the table should be ordered | -| ui.colormode | Light or dark mode in the user interface | diff --git a/netbox/netbox/preferences.py b/netbox/netbox/preferences.py index aa37bffae..d560ef1dd 100644 --- a/netbox/netbox/preferences.py +++ b/netbox/netbox/preferences.py @@ -15,15 +15,6 @@ def get_page_lengths(): PREFERENCES = { # User interface - 'ui.colormode': UserPreference( - label=_('Color mode'), - choices=( - ('light', _('Light')), - ('dark', _('Dark')), - ), - default='light', - description=_('Preferred default UI theme') - ), 'ui.htmx_navigation': UserPreference( label=_('HTMX Navigation'), choices=( diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 5a8a9e8335930131dff3d5d36611b84c6fa8380b..7e8feeb186d7497703a30b3fc8aae7f396fa64bf 100644 GIT binary patch delta 3947 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 delta 126 zcmZ4cP~_D^k%kt=7N!>FEiC^d?VSspb=(~tlXZd}9fNh!og5w0opcf%9TRl|9UTL8 zd>vhLTtNyvK}4{QH&`Cb^mGL3oh}%~(kSKZ=m<6iNj_2sD4*jxeNGh1eMa-`HPI}F FR{{2#Cfoo3 diff --git a/netbox/project-static/js/setmode.js b/netbox/project-static/js/setmode.js index ff1c5366b..9418a9f39 100644 --- a/netbox/project-static/js/setmode.js +++ b/netbox/project-static/js/setmode.js @@ -1,13 +1,11 @@ /** * Set the color mode on the `` element and in local storage. * - * @param mode {"dark" | "light"} NetBox Color Mode. - * @param inferred {boolean} Value is inferred from browser/system preference. + * @param mode {"dark" | "light"} UI color mode. */ -function setMode(mode, inferred) { +function setMode(mode) { document.documentElement.setAttribute("data-bs-theme", mode); localStorage.setItem("netbox-color-mode", mode); - localStorage.setItem("netbox-color-mode-inferred", inferred); } /** @@ -15,59 +13,29 @@ function setMode(mode, inferred) { */ function initMode() { try { - // Browser prefers dark color scheme. - var preferDark = window.matchMedia("(prefers-color-scheme: dark)").matches; - // Browser prefers light color scheme. - var preferLight = window.matchMedia("(prefers-color-scheme: light)").matches; - // Client NetBox color-mode override. + // Determine the configured color mode, if any var clientMode = localStorage.getItem("netbox-color-mode"); - // NetBox server-rendered value. - var serverMode = document.documentElement.getAttribute("data-netbox-color-mode"); - // Color mode is inferred from browser/system preference and not deterministically set by - // the client or server. - var inferred = JSON.parse(localStorage.getItem("netbox-color-mode-inferred")); + // Detect browser preference, if set + var preferDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + var preferLight = window.matchMedia("(prefers-color-scheme: light)").matches; - if (inferred === true && (serverMode === "light" || serverMode === "dark")) { - // The color mode was previously inferred from browser/system preference, but - // the server now has a value, so we should use the server's value. - return setMode(serverMode, false); - } - if (clientMode === null && (serverMode === "light" || serverMode === "dark")) { - // If the client mode is not set but the server mode is, use the server mode. - return setMode(serverMode, false); - } - if (clientMode !== null && serverMode === "unset") { - // The color mode has been set, deterministically or otherwise, and the server - // has no preference or has not been set. Use the client mode, but allow it to - /// be overridden by the server if/when a server value exists. - return setMode(clientMode, true); - } - if ( - clientMode !== null && - (serverMode === "light" || serverMode === "dark") && - clientMode !== serverMode - ) { - // If the client mode is set and is different than the server mode (which is also set), - // use the client mode over the server mode, as it should be more recent. + // Use the selected color mode, if any + if (clientMode !== null) { return setMode(clientMode, false); } - if (clientMode === serverMode) { - // If the client and server modes match, use that value. - return setMode(clientMode, false); - } - if (preferDark && serverMode === "unset") { - // If the server mode is not set but the browser prefers dark mode, use dark mode, but - // allow it to be overridden by an explicit preference. + + // Fall back to the mode preferred by the browser, if specified + if (preferDark) { return setMode("dark", true); } - if (preferLight && serverMode === "unset") { - // If the server mode is not set but the browser prefers light mode, use light mode, - // but allow it to be overridden by an explicit preference. + else if (preferLight) { return setMode("light", true); } } catch (error) { // In the event of an error, log it to the console and set the mode to light mode. console.error(error); } + + // Default to light mode return setMode("light", true); } diff --git a/netbox/project-static/src/colorMode.ts b/netbox/project-static/src/colorMode.ts index 894a1e8a5..453617740 100644 --- a/netbox/project-static/src/colorMode.ts +++ b/netbox/project-static/src/colorMode.ts @@ -65,9 +65,8 @@ function handleColorModeToggle(): void { function defaultColorMode(): void { // Get the current color mode value from local storage. const currentValue = localStorage.getItem(COLOR_MODE_KEY) as Nullable; - const serverValue = document.documentElement.getAttribute(`data-${COLOR_MODE_KEY}`); - if (isTruthy(serverValue) && isTruthy(currentValue)) { + if (isTruthy(currentValue)) { return setColorMode(currentValue); } @@ -81,7 +80,7 @@ function defaultColorMode(): void { } } - if (isTruthy(currentValue) && !isTruthy(serverValue) && isColorMode(currentValue)) { + if (isTruthy(currentValue) && isColorMode(currentValue)) { return setColorMode(currentValue); } diff --git a/netbox/templates/base/base.html b/netbox/templates/base/base.html index acaff4295..f7fa3fa50 100644 --- a/netbox/templates/base/base.html +++ b/netbox/templates/base/base.html @@ -8,9 +8,6 @@ lang="en" data-netbox-url-name="{{ request.resolver_match.url_name }}" data-netbox-base-path="{{ settings.BASE_PATH }}" - {% with preferences|get_key:'ui.colormode' as color_mode %} - data-netbox-color-mode="{{ color_mode|default:"unset" }}" - {% endwith %} > diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index b14300d80..e5b9b612e 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -62,7 +62,7 @@ class UserConfigFormMetaclass(forms.models.ModelFormMetaclass): class UserConfigForm(forms.ModelForm, metaclass=UserConfigFormMetaclass): fieldsets = ( FieldSet( - 'locale.language', 'pagination.per_page', 'pagination.placement', 'ui.colormode', 'ui.htmx_navigation', + 'locale.language', 'pagination.per_page', 'pagination.placement', 'ui.htmx_navigation', name=_('User Interface') ), FieldSet('data_format', name=_('Miscellaneous')),