2024-02-22 16:15:46 +01:00
|
|
|
/*
|
2024-09-06 10:22:13 +02:00
|
|
|
Copyright 2024 New Vector Ltd.
|
2024-02-22 16:15:46 +01:00
|
|
|
|
2025-02-18 17:59:58 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-06 10:22:13 +02:00
|
|
|
Please see LICENSE in the repository root for full details.
|
2024-02-22 16:15:46 +01:00
|
|
|
*/
|
|
|
|
|
|
2024-12-17 19:42:04 -05:00
|
|
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
2025-03-13 18:15:58 +01:00
|
|
|
import { WidgetApiToWidgetAction } from "matrix-widget-api";
|
|
|
|
|
import { type IThemeChangeActionRequest } from "matrix-widget-api";
|
2026-09-13 01:22:20 -04:00
|
|
|
import { logger } from "matrix-js-sdk/lib/logger";
|
2024-02-22 16:15:46 +01:00
|
|
|
|
2024-12-17 19:42:04 -05:00
|
|
|
import { getUrlParams } from "./UrlParams";
|
|
|
|
|
import { widget } from "./widget";
|
2026-06-29 23:30:57 -04:00
|
|
|
import { lotusFlag } from "./lotus/lotusWidget";
|
2024-02-22 16:15:46 +01:00
|
|
|
|
|
|
|
|
export const useTheme = (): void => {
|
2024-12-17 19:42:04 -05:00
|
|
|
const [requestedTheme, setRequestedTheme] = useState(
|
|
|
|
|
() => getUrlParams().theme,
|
|
|
|
|
);
|
2024-02-22 16:15:46 +01:00
|
|
|
const previousTheme = useRef<string | null>(document.body.classList.item(0));
|
2024-12-17 19:42:04 -05:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (widget) {
|
|
|
|
|
const onThemeChange = (
|
|
|
|
|
ev: CustomEvent<IThemeChangeActionRequest>,
|
|
|
|
|
): void => {
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
if ("name" in ev.detail.data && typeof ev.detail.data.name === "string")
|
|
|
|
|
setRequestedTheme(ev.detail.data.name);
|
|
|
|
|
widget!.api.transport.reply(ev.detail, {});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
widget.lazyActions.on(WidgetApiToWidgetAction.ThemeChange, onThemeChange);
|
|
|
|
|
return (): void => {
|
|
|
|
|
widget!.lazyActions.off(
|
|
|
|
|
WidgetApiToWidgetAction.ThemeChange,
|
|
|
|
|
onThemeChange,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
2024-02-22 16:15:46 +01:00
|
|
|
useLayoutEffect(() => {
|
2024-12-17 19:42:04 -05:00
|
|
|
// If no theme has been explicitly requested we default to dark
|
|
|
|
|
const theme = requestedTheme?.includes("light") ? "light" : "dark";
|
|
|
|
|
const themeHighContrast = requestedTheme?.includes("high-contrast")
|
|
|
|
|
? "-hc"
|
|
|
|
|
: "";
|
2024-02-22 16:15:46 +01:00
|
|
|
const themeString = "cpd-theme-" + theme + themeHighContrast;
|
|
|
|
|
if (themeString !== previousTheme.current) {
|
|
|
|
|
document.body.classList.remove(
|
|
|
|
|
"cpd-theme-light",
|
|
|
|
|
"cpd-theme-dark",
|
|
|
|
|
"cpd-theme-light-hc",
|
|
|
|
|
"cpd-theme-dark-hc",
|
|
|
|
|
);
|
|
|
|
|
document.body.classList.add(themeString);
|
|
|
|
|
previousTheme.current = themeString;
|
|
|
|
|
}
|
2024-03-13 14:58:21 +01:00
|
|
|
document.body.classList.remove("no-theme");
|
2026-06-29 23:30:57 -04:00
|
|
|
// [lotus #5] Native theming hooks, opted in by the host via URL flags, so
|
|
|
|
|
// it no longer has to inject CSS into the iframe after load.
|
2026-09-13 01:22:20 -04:00
|
|
|
const lotusTransparent = lotusFlag("lotusTransparent");
|
|
|
|
|
const lotusTheme = lotusFlag("lotusTheme");
|
|
|
|
|
if (lotusTransparent) document.body.classList.add("lotus-transparent");
|
|
|
|
|
// [lotus #21] The two flags are only meaningful together: a transparent
|
|
|
|
|
// canvas with no matching theme leaves text/icon colours computed against
|
|
|
|
|
// the default dark canvas token while the real pixels behind them are
|
|
|
|
|
// whatever the host painted. If a host sets lotusTransparent without
|
|
|
|
|
// lotusTheme, warn and apply lotusTheme anyway rather than silently
|
|
|
|
|
// risking unreadable UI.
|
|
|
|
|
if (lotusTransparent && !lotusTheme) {
|
|
|
|
|
logger.warn(
|
|
|
|
|
"[lotus] lotusTransparent is set without lotusTheme; applying " +
|
|
|
|
|
"lotusTheme anyway since the two flags are only meaningful together.",
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-09-26 13:39:18 -04:00
|
|
|
if (lotusTheme || lotusTransparent) {
|
2026-09-13 01:22:20 -04:00
|
|
|
document.body.classList.add("lotus-theme");
|
2026-09-26 13:39:18 -04:00
|
|
|
// [cinny #43] The frame's root colour scheme must match the host's, or
|
|
|
|
|
// the browser paints an opaque backdrop behind the transparent frame.
|
|
|
|
|
// (The host used to inject `:root { color-scheme }` for this.)
|
|
|
|
|
document.documentElement.style.colorScheme = theme;
|
|
|
|
|
}
|
|
|
|
|
// [cinny #43] The host draws its own call bar: hide EC's footer, but keep
|
|
|
|
|
// it in the DOM (the host still clicks its screenshare button on engines
|
|
|
|
|
// without Capability Delegation). Replaces the host's injected styles.
|
|
|
|
|
if (lotusFlag("lotusHostControls"))
|
|
|
|
|
document.body.classList.add("lotus-host-controls");
|
2024-12-17 19:42:04 -05:00
|
|
|
}, [previousTheme, requestedTheme]);
|
2024-02-22 16:15:46 +01:00
|
|
|
};
|