From b3e7c1b188da8db5b4719c7cbe6fc393e039b1ec Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Thu, 15 May 2025 17:53:33 +0530 Subject: [PATCH 1/7] Fix an issue where the light theme briefly appears when loading pgAdmin or opening tools, if the user's UI theme preference is not set to light. #8711 --- web/pgadmin/browser/__init__.py | 17 +++--- .../browser/templates/browser/index.html | 4 ++ web/pgadmin/static/js/Theme/index.jsx | 53 ++++++++++++------- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py index 118bd5f552d..ab82cedc2bb 100644 --- a/web/pgadmin/browser/__init__.py +++ b/web/pgadmin/browser/__init__.py @@ -400,12 +400,6 @@ def index(): 'pass_enc_key' in session: session['allow_save_password'] = False - response = Response(render_template( - MODULE_NAME + "/index.html", - username=current_user.username, - _=gettext - )) - # Set the language cookie after login, so next time the user will have that # same option at the login time. misc_preference = Preferences.module('misc') @@ -416,11 +410,22 @@ def index(): if user_languages: language = user_languages.get() or 'en' + # Get the theme preference + user_theme = misc_preference.preference('theme') + theme = user_theme.get() or 'light' if user_theme else 'light' + domain = dict() if config.COOKIE_DEFAULT_DOMAIN and\ config.COOKIE_DEFAULT_DOMAIN != 'localhost': domain['domain'] = config.COOKIE_DEFAULT_DOMAIN + response = Response(render_template( + MODULE_NAME + "/index.html", + username=current_user.username, + theme=theme, + _=gettext + )) + response.set_cookie("PGADMIN_LANGUAGE", value=language, path=config.SESSION_COOKIE_PATH, secure=config.SESSION_COOKIE_SECURE, diff --git a/web/pgadmin/browser/templates/browser/index.html b/web/pgadmin/browser/templates/browser/index.html index 4a9c589e9e7..cf9db26f01a 100644 --- a/web/pgadmin/browser/templates/browser/index.html +++ b/web/pgadmin/browser/templates/browser/index.html @@ -3,6 +3,10 @@ {% block title %}{{ config.APP_NAME }}{% endblock %} {% block init_script %} +// Set language and theme on the global window object +window.pgAdmin = window.pgAdmin || {}; +window.pgAdmin.theme = "{{ theme }}"; + function parseConsoleArgs(args) { const retData = Array.from(args).map(arg => { try { diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index fd7709d91a0..7346e0e6d67 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -886,8 +886,18 @@ function getFinalTheme(baseTheme) { /* In future, this will be moved to App container */ export default function Theme({children}) { const prefStore = usePreferences(); - const [theme, setTheme] = useState(); + const selectedTheme = prefStore?.getPreferencesForModule('misc')?.theme || window.pgAdmin.theme || 'light'; + // Initialize theme state + const [theme, setTheme] = useState(() => { + if (selectedTheme === 'system') { + const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); + return isSystemInDarkMode.matches ? 'dark' : 'light'; + } + return selectedTheme; + }); + + // Memoize the theme object const themeObj = useMemo(()=>{ let baseTheme = getLightTheme(basicSettings); switch(theme) { @@ -901,31 +911,36 @@ export default function Theme({children}) { return getFinalTheme(baseTheme); }, [theme]); + // Handle theme updates useEffect(() => { - const selectedTheme = prefStore.getPreferencesForModule('misc').theme; - if(theme && theme === selectedTheme) { + if (selectedTheme !== 'system') { + setTheme(selectedTheme); + window.pgAdmin.theme = selectedTheme; // Update global theme return; - }else{ - if (selectedTheme !== 'system') { - setTheme(selectedTheme); - return; - } - const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); - setTheme(isSystemInDarkMode.matches ? 'dark' : 'light'); - const listener = (event) => { - setTheme(event.matches ? 'dark' : 'light'); - }; - isSystemInDarkMode.addEventListener('change',listener); - return () => { - isSystemInDarkMode.removeEventListener('change',listener); - }; } - },[prefStore]); + + const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); + const updateTheme = (event) => { + const newTheme = event.matches ? 'dark' : 'light'; + setTheme(newTheme); + window.pgAdmin.theme = newTheme; // Update global theme + }; + + // Set initial system theme + const initialTheme = isSystemInDarkMode.matches ? 'dark' : 'light'; + setTheme(initialTheme); + window.pgAdmin.theme = initialTheme; // Update global theme + isSystemInDarkMode.addEventListener('change', updateTheme); + + return () => { + isSystemInDarkMode.removeEventListener('change', updateTheme); + }; + },[selectedTheme]); return ( - + {children} From 5dc7c1cc42006c9b890e2c44f2a8e5921fd7247b Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Thu, 15 May 2025 18:50:09 +0530 Subject: [PATCH 2/7] Fixed issues related to jest test cases failure. --- web/pgadmin/static/js/Theme/index.jsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index 7346e0e6d67..912876fc7c7 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -886,8 +886,11 @@ function getFinalTheme(baseTheme) { /* In future, this will be moved to App container */ export default function Theme({children}) { const prefStore = usePreferences(); - const selectedTheme = prefStore?.getPreferencesForModule('misc')?.theme || window.pgAdmin.theme || 'light'; - + const selectedTheme = + (prefStore && prefStore.getPreferencesForModule('misc')?.theme) || + (window.pgAdmin && window.pgAdmin.theme) || + 'light'; + // Initialize theme state const [theme, setTheme] = useState(() => { if (selectedTheme === 'system') { @@ -915,7 +918,10 @@ export default function Theme({children}) { useEffect(() => { if (selectedTheme !== 'system') { setTheme(selectedTheme); - window.pgAdmin.theme = selectedTheme; // Update global theme + if (window.pgAdmin) { + window.pgAdmin.theme = selectedTheme; // Update global theme + } + // window.pgAdmin?.theme = selectedTheme; // Update global theme return; } @@ -923,13 +929,17 @@ export default function Theme({children}) { const updateTheme = (event) => { const newTheme = event.matches ? 'dark' : 'light'; setTheme(newTheme); - window.pgAdmin.theme = newTheme; // Update global theme + if (window.pgAdmin) { + window.pgAdmin.theme = newTheme; // Update global theme + } }; // Set initial system theme const initialTheme = isSystemInDarkMode.matches ? 'dark' : 'light'; setTheme(initialTheme); - window.pgAdmin.theme = initialTheme; // Update global theme + if (window.pgAdmin) { + window.pgAdmin.theme = initialTheme; // Update global theme + } isSystemInDarkMode.addEventListener('change', updateTheme); return () => { From 6236ef740e2cecbd1b4d39b1f209e135341c3942 Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Mon, 19 May 2025 14:03:58 +0530 Subject: [PATCH 3/7] 1. Removed duplicated code 2. Assigned theme to window object 3. Reused system theme extraction code --- .../browser/templates/browser/index.html | 5 +- web/pgadmin/static/js/Theme/index.jsx | 49 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/web/pgadmin/browser/templates/browser/index.html b/web/pgadmin/browser/templates/browser/index.html index cf9db26f01a..f72b7275430 100644 --- a/web/pgadmin/browser/templates/browser/index.html +++ b/web/pgadmin/browser/templates/browser/index.html @@ -3,9 +3,8 @@ {% block title %}{{ config.APP_NAME }}{% endblock %} {% block init_script %} -// Set language and theme on the global window object -window.pgAdmin = window.pgAdmin || {}; -window.pgAdmin.theme = "{{ theme }}"; +// Set theme on the global window object +window.theme = "{{ theme }}"; function parseConsoleArgs(args) { const retData = Array.from(args).map(arg => { diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index 912876fc7c7..b6497d139db 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -882,23 +882,32 @@ function getFinalTheme(baseTheme) { }, baseTheme); } +/* Get the actual system theme is user selected system theme in preferences */ +function getSystemTheme(selectedTheme) { + if (selectedTheme === 'system') { + const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); + return { + 'theme': isSystemInDarkMode.matches ? 'dark' : 'light', + 'isSystemInDarkMode': isSystemInDarkMode, + }; + } + return { + 'theme': selectedTheme, + 'isSystemInDarkMode': null, + }; +} + /* Theme wrapper used by DOM containers to apply theme */ /* In future, this will be moved to App container */ export default function Theme({children}) { const prefStore = usePreferences(); const selectedTheme = - (prefStore && prefStore.getPreferencesForModule('misc')?.theme) || - (window.pgAdmin && window.pgAdmin.theme) || + prefStore?.getPreferencesForModule('misc')?.theme || + window.theme || 'light'; - + // Initialize theme state - const [theme, setTheme] = useState(() => { - if (selectedTheme === 'system') { - const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); - return isSystemInDarkMode.matches ? 'dark' : 'light'; - } - return selectedTheme; - }); + const [theme, setTheme] = useState(getSystemTheme(selectedTheme).theme); // Memoize the theme object const themeObj = useMemo(()=>{ @@ -918,28 +927,18 @@ export default function Theme({children}) { useEffect(() => { if (selectedTheme !== 'system') { setTheme(selectedTheme); - if (window.pgAdmin) { - window.pgAdmin.theme = selectedTheme; // Update global theme - } - // window.pgAdmin?.theme = selectedTheme; // Update global theme + window.theme = selectedTheme; // Update global theme return; } - const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); + let {theme, isSystemInDarkMode} = getSystemTheme(selectedTheme); + setTheme(theme); + const updateTheme = (event) => { const newTheme = event.matches ? 'dark' : 'light'; setTheme(newTheme); - if (window.pgAdmin) { - window.pgAdmin.theme = newTheme; // Update global theme - } + window.theme = newTheme; // Update global theme }; - - // Set initial system theme - const initialTheme = isSystemInDarkMode.matches ? 'dark' : 'light'; - setTheme(initialTheme); - if (window.pgAdmin) { - window.pgAdmin.theme = initialTheme; // Update global theme - } isSystemInDarkMode.addEventListener('change', updateTheme); return () => { From e607a4d45c2174be473e8af2aacb1f15fdc6977c Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Mon, 19 May 2025 17:01:40 +0530 Subject: [PATCH 4/7] Removed global theme assignment from the eventListener function --- web/pgadmin/static/js/Theme/index.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index b6497d139db..8bb26acec63 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -937,7 +937,6 @@ export default function Theme({children}) { const updateTheme = (event) => { const newTheme = event.matches ? 'dark' : 'light'; setTheme(newTheme); - window.theme = newTheme; // Update global theme }; isSystemInDarkMode.addEventListener('change', updateTheme); From 86bf98adf85781b6b420c1317a191bd59a707c41 Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Mon, 19 May 2025 17:05:03 +0530 Subject: [PATCH 5/7] isSystemInDarkMode renamed to systemMatchMedia --- web/pgadmin/static/js/Theme/index.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index 8bb26acec63..fa7e6f48cbc 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -885,15 +885,15 @@ function getFinalTheme(baseTheme) { /* Get the actual system theme is user selected system theme in preferences */ function getSystemTheme(selectedTheme) { if (selectedTheme === 'system') { - const isSystemInDarkMode = matchMedia('(prefers-color-scheme: dark)'); + const systemMatchMedia = matchMedia('(prefers-color-scheme: dark)'); return { - 'theme': isSystemInDarkMode.matches ? 'dark' : 'light', - 'isSystemInDarkMode': isSystemInDarkMode, + 'theme': systemMatchMedia.matches ? 'dark' : 'light', + 'systemMatchMedia': systemMatchMedia, }; } return { 'theme': selectedTheme, - 'isSystemInDarkMode': null, + 'systemMatchMedia': null, }; } @@ -931,17 +931,17 @@ export default function Theme({children}) { return; } - let {theme, isSystemInDarkMode} = getSystemTheme(selectedTheme); + let {theme, systemMatchMedia} = getSystemTheme(selectedTheme); setTheme(theme); const updateTheme = (event) => { const newTheme = event.matches ? 'dark' : 'light'; setTheme(newTheme); }; - isSystemInDarkMode.addEventListener('change', updateTheme); + systemMatchMedia.addEventListener('change', updateTheme); return () => { - isSystemInDarkMode.removeEventListener('change', updateTheme); + systemMatchMedia.removeEventListener('change', updateTheme); }; },[selectedTheme]); From e95f79a077f51d28a731bd1366ff3d1a11a67da9 Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Mon, 19 May 2025 17:34:30 +0530 Subject: [PATCH 6/7] Removed global theme from being reassigned --- web/pgadmin/static/js/Theme/index.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index fa7e6f48cbc..be9be9fce8a 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -927,7 +927,6 @@ export default function Theme({children}) { useEffect(() => { if (selectedTheme !== 'system') { setTheme(selectedTheme); - window.theme = selectedTheme; // Update global theme return; } From 32be879228767a174e1307805a913a337ed7d62d Mon Sep 17 00:00:00 2001 From: Anil Sahoo Date: Mon, 19 May 2025 18:16:46 +0530 Subject: [PATCH 7/7] getSystemTheme renamed to parseSystemTheme --- web/pgadmin/static/js/Theme/index.jsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index be9be9fce8a..29323fba35d 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -883,7 +883,7 @@ function getFinalTheme(baseTheme) { } /* Get the actual system theme is user selected system theme in preferences */ -function getSystemTheme(selectedTheme) { +function parseSystemTheme(selectedTheme) { if (selectedTheme === 'system') { const systemMatchMedia = matchMedia('(prefers-color-scheme: dark)'); return { @@ -907,7 +907,7 @@ export default function Theme({children}) { 'light'; // Initialize theme state - const [theme, setTheme] = useState(getSystemTheme(selectedTheme).theme); + const [theme, setTheme] = useState(parseSystemTheme(selectedTheme).theme); // Memoize the theme object const themeObj = useMemo(()=>{ @@ -925,22 +925,17 @@ export default function Theme({children}) { // Handle theme updates useEffect(() => { - if (selectedTheme !== 'system') { - setTheme(selectedTheme); - return; - } - - let {theme, systemMatchMedia} = getSystemTheme(selectedTheme); + let {theme, systemMatchMedia} = parseSystemTheme(selectedTheme); setTheme(theme); const updateTheme = (event) => { const newTheme = event.matches ? 'dark' : 'light'; setTheme(newTheme); }; - systemMatchMedia.addEventListener('change', updateTheme); + systemMatchMedia?.addEventListener('change', updateTheme); return () => { - systemMatchMedia.removeEventListener('change', updateTheme); + systemMatchMedia?.removeEventListener('change', updateTheme); }; },[selectedTheme]);