diff --git a/apps/client/src/common/api/constants.ts b/apps/client/src/common/api/constants.ts index aad81975f..3998e9800 100644 --- a/apps/client/src/common/api/constants.ts +++ b/apps/client/src/common/api/constants.ts @@ -13,6 +13,7 @@ export const RUNDOWN = ['rundown']; export const RUNTIME = ['runtimeStore']; export const URL_PRESETS = ['urlpresets']; export const VIEW_SETTINGS = ['viewSettings']; +export const CSS_OVERRIDE = ['cssOverride']; export const CLIENT_LIST = ['clientList']; export const REPORT = ['report']; export const TRANSLATION = ['translation']; @@ -21,9 +22,7 @@ export const TRANSLATION = ['translation']; export const apiEntryUrl = `${serverURL}/data`; const userAssetsPath = 'user'; -const cssOverridePath = 'styles/override.css'; const customTranslationsPath = 'translations/translations.json'; -export const overrideStylesURL = `${serverURL}/${userAssetsPath}/${cssOverridePath}`; export const projectLogoPath = `${serverURL}/${userAssetsPath}/logo`; export const customTranslationsURL = `${serverURL}/${userAssetsPath}/${customTranslationsPath}`; diff --git a/apps/client/src/common/hooks-query/useCssOverride.ts b/apps/client/src/common/hooks-query/useCssOverride.ts new file mode 100644 index 000000000..0ee332362 --- /dev/null +++ b/apps/client/src/common/hooks-query/useCssOverride.ts @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query'; +import { MILLIS_PER_HOUR } from 'ontime-utils'; + +import { getCSSContents } from '../api/assets'; +import { CSS_OVERRIDE } from '../api/constants'; + +export default function useCssOverride(enabled: boolean) { + const { data, status } = useQuery({ + queryKey: CSS_OVERRIDE, + queryFn: ({ signal }) => getCSSContents({ signal }), + staleTime: MILLIS_PER_HOUR, + enabled + }); + + return { + data: data ?? '', status + }; +} diff --git a/apps/client/src/common/utils/socket.ts b/apps/client/src/common/utils/socket.ts index 52416977b..ce7520e98 100644 --- a/apps/client/src/common/utils/socket.ts +++ b/apps/client/src/common/utils/socket.ts @@ -13,6 +13,7 @@ import { isProduction, websocketUrl } from '../../externals'; import { APP_SETTINGS, CLIENT_LIST, + CSS_OVERRIDE, CUSTOM_FIELDS, PROJECT_DATA, REPORT, @@ -199,6 +200,9 @@ export const connectSocket = () => { case RefetchKey.ViewSettings: ontimeQueryClient.invalidateQueries({ queryKey: VIEW_SETTINGS }); break; + case RefetchKey.CssOverride: + ontimeQueryClient.invalidateQueries({ queryKey: CSS_OVERRIDE }); + break; case RefetchKey.Translation: ontimeQueryClient.invalidateQueries({ queryKey: TRANSLATION }); break; diff --git a/apps/client/src/views/ViewLoader.tsx b/apps/client/src/views/ViewLoader.tsx index 2b72c03a7..6743492d9 100644 --- a/apps/client/src/views/ViewLoader.tsx +++ b/apps/client/src/views/ViewLoader.tsx @@ -1,35 +1,39 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, Suspense } from 'react'; -import { overrideStylesURL } from '../common/api/constants'; +import useCssOverride from '../common/hooks-query/useCssOverride'; import useViewSettings from '../common/hooks-query/useViewSettings'; -import { useRuntimeStylesheet } from '../common/hooks/useRuntimeStylesheet'; import Loader from './common/loader/Loader'; -export default function ViewLoader({ children }: PropsWithChildren) { - const { data } = useViewSettings(); - const { shouldRender } = useRuntimeStylesheet(data.overrideStyles ? overrideStylesURL : undefined); +const scriptTagId = 'ontime-stylesheet-override'; +function OverrideStyles() { + 'use memo'; + const { data: settings } = useViewSettings(); + const { overrideStyles } = settings; + const { data: css } = useCssOverride(overrideStyles); + const cssBlob = URL.createObjectURL(new Blob([css], { type: 'text/css' })); + + //@ts-expect-error disabled exists on link when rel='stylesheet' https://react.dev/reference/react-dom/components/link#props + return ; +} + +export default function ViewLoader({ children }: PropsWithChildren) { + 'use memo'; // we need to be able to override the background colour with the key param const searchParams = new URLSearchParams(window.location.search); const colourFromParams = searchParams.get('keyColour') ?? '#101010'; - // eventually we would want to leverage suspense here - // while the feature is not ready, we simply trigger a loader - // suspense would have the advantage of being triggered also by react-query - - if (!shouldRender) { - return ( - <> - - - - ); - } - return ( - <> - + + + + + } + > + {children} - + ); } diff --git a/apps/server/src/api-data/assets/assets.router.ts b/apps/server/src/api-data/assets/assets.router.ts index 2a3ca199d..c664edccc 100644 --- a/apps/server/src/api-data/assets/assets.router.ts +++ b/apps/server/src/api-data/assets/assets.router.ts @@ -24,6 +24,7 @@ router.post('/css', validatePostCss, async (req: Request, res: Response) => { try { await writeCssFile(defaultCss); + sendRefetch(RefetchKey.CssOverride); res.status(200).send(defaultCss); } catch (error) { const message = getErrorMessage(error); diff --git a/packages/types/src/api/websocket/refetch.type.ts b/packages/types/src/api/websocket/refetch.type.ts index fdaac6b75..b78f5bf9a 100644 --- a/packages/types/src/api/websocket/refetch.type.ts +++ b/packages/types/src/api/websocket/refetch.type.ts @@ -6,6 +6,7 @@ export enum RefetchKey { Rundown = 'rundown', UrlPresets = 'url-presets', ViewSettings = 'view-settings', + CssOverride = 'css-override', Translation = 'translation', Settings = 'settings', }