diff --git a/src/apps/dashboard/AppLayout.tsx b/src/apps/dashboard/AppLayout.tsx
index 16c81771cc..44b8ad2caf 100644
--- a/src/apps/dashboard/AppLayout.tsx
+++ b/src/apps/dashboard/AppLayout.tsx
@@ -11,6 +11,7 @@ import AppBody from 'components/AppBody';
import AppToolbar from 'components/toolbar/AppToolbar';
import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
+import ThemeCss from 'components/ThemeCss';
import { useApi } from 'hooks/useApi';
import { useLocale } from 'hooks/useLocale';
@@ -101,6 +102,7 @@ export const Component: FC = () => {
+
);
};
diff --git a/src/apps/experimental/AppLayout.tsx b/src/apps/experimental/AppLayout.tsx
index 167a96eaff..6412115302 100644
--- a/src/apps/experimental/AppLayout.tsx
+++ b/src/apps/experimental/AppLayout.tsx
@@ -6,8 +6,10 @@ import useMediaQuery from '@mui/material/useMediaQuery';
import { Outlet, useLocation } from 'react-router-dom';
import AppBody from 'components/AppBody';
+import CustomCss from 'components/CustomCss';
import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
+import ThemeCss from 'components/ThemeCss';
import { useApi } from 'hooks/useApi';
import AppToolbar from './components/AppToolbar';
@@ -29,52 +31,56 @@ export const Component = () => {
}, [ isDrawerActive, setIsDrawerActive ]);
return (
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
- {
- isDrawerAvailable && (
-
- )
- }
-
+ {
+ isDrawerAvailable && (
+
+ )
+ }
+
-
-
-
-
+
+
+
+
+
-
+
+
+ >
);
};
diff --git a/src/apps/stable/AppLayout.tsx b/src/apps/stable/AppLayout.tsx
index f3b186c8db..1e653ee1a8 100644
--- a/src/apps/stable/AppLayout.tsx
+++ b/src/apps/stable/AppLayout.tsx
@@ -2,11 +2,17 @@ import React from 'react';
import { Outlet } from 'react-router-dom';
import AppBody from 'components/AppBody';
+import ThemeCss from 'components/ThemeCss';
+import CustomCss from 'components/CustomCss';
export default function AppLayout() {
return (
-
-
-
+ <>
+
+
+
+
+
+ >
);
}
diff --git a/src/components/CustomCss.tsx b/src/components/CustomCss.tsx
new file mode 100644
index 0000000000..b9c60b48e6
--- /dev/null
+++ b/src/components/CustomCss.tsx
@@ -0,0 +1,37 @@
+import React, { FC, useEffect, useState } from 'react';
+
+import { useApi } from 'hooks/useApi';
+import { useUserSettings } from 'hooks/useUserSettings';
+
+const CustomCss: FC = () => {
+ const { api } = useApi();
+ const { customCss: userCustomCss, disableCustomCss } = useUserSettings();
+ const [ brandingCssUrl, setBrandingCssUrl ] = useState();
+
+ useEffect(() => {
+ if (!api) return;
+
+ setBrandingCssUrl(api.getUri('/Branding/Css.css'));
+ }, [ api ]);
+
+ if (!api) return null;
+
+ return (
+ <>
+ {!disableCustomCss && brandingCssUrl && (
+
+ )}
+ {userCustomCss && (
+
+ )}
+ >
+ );
+};
+
+export default CustomCss;
diff --git a/src/components/ThemeCss.tsx b/src/components/ThemeCss.tsx
new file mode 100644
index 0000000000..53defc1ddf
--- /dev/null
+++ b/src/components/ThemeCss.tsx
@@ -0,0 +1,34 @@
+import React, { FC, useEffect, useState } from 'react';
+
+import { useUserTheme } from 'hooks/useUserTheme';
+import { getDefaultTheme } from 'scripts/settings/webSettings';
+
+interface ThemeCssProps {
+ dashboard?: boolean
+}
+
+const getThemeUrl = (id: string) => `themes/${id}/theme.css`;;
+
+const DEFAULT_THEME_URL = getThemeUrl(getDefaultTheme().id);
+
+const ThemeCss: FC = ({
+ dashboard = false
+}) => {
+ const { theme, dashboardTheme } = useUserTheme();
+ const [ themeUrl, setThemeUrl ] = useState(DEFAULT_THEME_URL);
+
+ useEffect(() => {
+ const id = dashboard ? dashboardTheme : theme;
+ if (id) setThemeUrl(getThemeUrl(id));
+ }, [dashboard, dashboardTheme, theme]);
+
+ return (
+
+ );
+};
+
+export default ThemeCss;
diff --git a/src/hooks/useUserSettings.tsx b/src/hooks/useUserSettings.tsx
index 84eb49117e..289588669e 100644
--- a/src/hooks/useUserSettings.tsx
+++ b/src/hooks/useUserSettings.tsx
@@ -7,6 +7,8 @@ import Events, { type Event } from 'utils/events';
import { useApi } from './useApi';
interface UserSettings {
+ customCss?: string
+ disableCustomCss: boolean
theme?: string
dashboardTheme?: string
dateTimeLocale?: string
@@ -15,6 +17,9 @@ interface UserSettings {
// NOTE: This is an incomplete list of only the settings that are currently being used
const UserSettingField = {
+ // Custom CSS
+ CustomCss: 'customCss',
+ DisableCustomCss: 'disableCustomCss',
// Theme settings
Theme: 'appTheme',
DashboardTheme: 'dashboardTheme',
@@ -23,11 +28,15 @@ const UserSettingField = {
Language: 'language'
};
-const UserSettingsContext = createContext({});
+const UserSettingsContext = createContext({
+ disableCustomCss: false
+});
export const useUserSettings = () => useContext(UserSettingsContext);
export const UserSettingsProvider: FC> = ({ children }) => {
+ const [ customCss, setCustomCss ] = useState();
+ const [ disableCustomCss, setDisableCustomCss ] = useState(false);
const [ theme, setTheme ] = useState();
const [ dashboardTheme, setDashboardTheme ] = useState();
const [ dateTimeLocale, setDateTimeLocale ] = useState();
@@ -36,14 +45,25 @@ export const UserSettingsProvider: FC> = ({ children
const { user } = useApi();
const context = useMemo(() => ({
+ customCss,
+ disableCustomCss,
theme,
dashboardTheme,
dateTimeLocale,
locale: language
- }), [ theme, dashboardTheme, dateTimeLocale, language ]);
+ }), [
+ customCss,
+ disableCustomCss,
+ theme,
+ dashboardTheme,
+ dateTimeLocale,
+ language
+ ]);
// Update the values of the user settings
const updateUserSettings = useCallback(() => {
+ setCustomCss(userSettings.customCss());
+ setDisableCustomCss(userSettings.disableCustomCss());
setTheme(userSettings.theme());
setDashboardTheme(userSettings.dashboardTheme());
setDateTimeLocale(userSettings.dateTimeLocale());
diff --git a/src/index.jsx b/src/index.jsx
index 169f44857c..81627c4ad8 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -17,7 +17,6 @@ import { loadCoreDictionary } from 'lib/globalize/loader';
import { initialize as initializeAutoCast } from 'scripts/autocast';
import browser from './scripts/browser';
import keyboardNavigation from './scripts/keyboardNavigation';
-import { currentSettings } from './scripts/settings/userSettings';
import { getPlugins } from './scripts/settings/webSettings';
import taskButton from './scripts/taskbutton';
import { pageClassOn, serverAddress } from './utils/dashboard';
@@ -116,9 +115,6 @@ build: ${__JF_BUILD_VERSION__}`);
// Load platform specific features
loadPlatformFeatures();
- // Load custom CSS styles
- loadCustomCss();
-
// Enable navigation controls
keyboardNavigation.enable();
autoFocuser.enable();
@@ -191,54 +187,7 @@ function loadPlatformFeatures() {
}
}
-function loadCustomCss() {
- // Apply custom CSS
- const apiClient = ServerConnections.currentApiClient();
- if (apiClient) {
- const brandingCss = fetch(apiClient.getUrl('Branding/Css'))
- .then(function(response) {
- if (!response.ok) {
- throw new Error(response.status + ' ' + response.statusText);
- }
- return response.text();
- })
- .catch(function(err) {
- console.warn('Error applying custom css', err);
- });
-
- const handleStyleChange = async () => {
- let style = document.querySelector('#cssBranding');
- if (!style) {
- // Inject the branding css as a dom element in body so it will take
- // precedence over other stylesheets
- style = document.createElement('style');
- style.id = 'cssBranding';
- document.body.appendChild(style);
- }
-
- const css = [];
- // Only add branding CSS when enabled
- if (!currentSettings.disableCustomCss()) css.push(await brandingCss);
- // Always add user CSS
- css.push(currentSettings.customCss());
-
- style.textContent = css.join('\n');
- };
-
- Events.on(ServerConnections, 'localusersignedin', handleStyleChange);
- Events.on(ServerConnections, 'localusersignedout', handleStyleChange);
- Events.on(currentSettings, 'change', (e, prop) => {
- if (prop == 'disableCustomCss' || prop == 'customCss') {
- handleStyleChange();
- }
- });
-
- handleStyleChange();
- }
-}
-
function registerServiceWorker() {
- /* eslint-disable compat/compat */
if (navigator.serviceWorker && window.appMode !== 'cordova' && window.appMode !== 'android') {
navigator.serviceWorker.register('serviceworker.js').then(() =>
console.log('serviceWorker registered')
@@ -248,7 +197,6 @@ function registerServiceWorker() {
} else {
console.warn('serviceWorker unsupported');
}
- /* eslint-enable compat/compat */
}
async function renderApp() {
diff --git a/src/scripts/themeManager.js b/src/scripts/themeManager.js
index 70f4810041..f976dba82e 100644
--- a/src/scripts/themeManager.js
+++ b/src/scripts/themeManager.js
@@ -1,16 +1,7 @@
import { getDefaultTheme, getThemes as getConfiguredThemes } from './settings/webSettings';
-let themeStyleElement = document.querySelector('#cssTheme');
let currentThemeId;
-function unloadTheme() {
- const elem = themeStyleElement;
- if (elem) {
- elem.removeAttribute('href');
- currentThemeId = null;
- }
-}
-
function getThemes() {
return getConfiguredThemes();
}
@@ -29,11 +20,7 @@ function getThemeStylesheetInfo(id) {
theme = getDefaultTheme();
}
- return {
- stylesheetPath: 'themes/' + theme.id + '/theme.css',
- themeId: theme.id,
- color: theme.color
- };
+ return theme;
});
}
@@ -45,36 +32,12 @@ function setTheme(id) {
}
getThemeStylesheetInfo(id).then(function (info) {
- if (currentThemeId && currentThemeId === info.themeId) {
+ if (currentThemeId && currentThemeId === info.id) {
resolve();
return;
}
- const linkUrl = info.stylesheetPath;
- unloadTheme();
-
- let link = themeStyleElement;
-
- if (!link) {
- // Inject the theme css as a dom element in body so it will take
- // precedence over other stylesheets
- link = document.createElement('link');
- link.id = 'cssTheme';
- link.setAttribute('rel', 'stylesheet');
- link.setAttribute('type', 'text/css');
- document.body.appendChild(link);
- }
-
- const onLoad = function (e) {
- e.target.removeEventListener('load', onLoad);
- resolve();
- };
-
- link.addEventListener('load', onLoad);
-
- link.setAttribute('href', linkUrl);
- themeStyleElement = link;
- currentThemeId = info.themeId;
+ currentThemeId = info.id;
document.getElementById('themeColor').content = info.color;
});
@@ -82,6 +45,6 @@ function setTheme(id) {
}
export default {
- getThemes: getThemes,
- setTheme: setTheme
+ getThemes,
+ setTheme
};