diff --git a/docs/url-params.md b/docs/url-params.md index 46caa671..4a22359a 100644 --- a/docs/url-params.md +++ b/docs/url-params.md @@ -1,57 +1,76 @@ -## Url Format and parameters +# Url Format and parameters There are two formats for Element Call urls. - **Current Format** - ``` + + ```text https://element_call.domain/room/# /?roomId=!id:domain&password=1234& ``` - The url is split into two sections. The `https://element_call.domain/room/#` contains the app and the intend that the link brings you into a specific room (`https://call.element.io/#` would be the homepage). The fragment is used for query parameters to make sure they never get sent to the element_call.domain server. Here we have the actual matrix roomId and the password which are used to connect all participants with e2ee. This allows that `` does not need to be unique. Multiple meetings with the label weekly-sync can be created without collisions. + + The url is split into two sections. The `https://element_call.domain/room/#` + contains the app and the intend that the link brings you into a specific room + (`https://call.element.io/#` would be the homepage). The fragment is used for + query parameters to make sure they never get sent to the element_call.domain + server. Here we have the actual matrix roomId and the password which are used + to connect all participants with e2ee. This allows that `` does + not need to be unique. Multiple meetings with the label weekly-sync can be created + without collisions. + - **deprecated** - ``` + + ```text https://element_call.domain/ ``` - With this format the livekit alias that will be used is the ``. All ppl connecting to this url will end up in the same unencrypted room. This does not scale, is super unsecure (ppl could end up in the same room by accident) and it also is not really possible to support encryption. + + With this format the livekit alias that will be used is the ``. + All ppl connecting to this url will end up in the same unencrypted room. + This does not scale, is super unsecure + (ppl could end up in the same room by accident) and it also is not really + possible to support encryption. The url parameters are spit into two categories: **general** and **widget related**. -### Widget related params +## Widget related params **widgetId** -The id used by the widget. The presence of this parameter inplis that elemetn call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl` +The id used by the widget. The presence of this parameter implies that element +call will not connect to a homeserver directly and instead tries to establish +postMessage communication via the `parentUrl`. -``` +```ts widgetId: string | null; ``` **parentUrl** -The url used to send widget action postMessages. This should be the domain of the client -or the webview the widget is hosted in. (in case the widget is not in an Iframe but in a -dedicated webview we send the postMessages same webview the widget lives in. Filtering is -done in the widget so it ignores the messages it receives from itself) +The url used to send widget action postMessages. This should be the domain of +the client or the webview the widget is hosted in. (in case the widget is not +in an Iframe but in a dedicated webview we send the postMessages same webview +the widget lives in. Filtering is done in the widget so it ignores the messages +it receives from itself) -``` +```ts parentUrl: string | null; ``` **userId** The user's ID (only used in matryoshka mode). -``` +```ts userId: string | null; ``` **deviceId** The device's ID (only used in matryoshka mode). -``` +```ts deviceId: string | null; ``` **baseUrl** The base URL of the homeserver to use for media lookups in matryoshka mode. -``` +```ts baseUrl: string | null; ``` @@ -64,14 +83,14 @@ roomId is an exception as we need the room ID in embedded (matroyska) mode, and the room alias (or even the via params because we are not trying to join it). This is also not validated, where it is in useRoomIdentifier(). -``` +```ts roomId: string | null; ``` **confineToRoom** Whether the app should keep the user confined to the current call/room. -``` +```ts confineToRoom: boolean; (default: false) ``` @@ -79,7 +98,7 @@ confineToRoom: boolean; (default: false) Whether upon entering a room, the user should be prompted to launch the native mobile app. (Affects only Android and iOS.) -``` +```ts appPrompt: boolean; (default: true) ``` @@ -87,28 +106,29 @@ appPrompt: boolean; (default: true) Whether the app should pause before joining the call until it sees an io.element.join widget action, allowing it to be preloaded. -``` +```ts preload: boolean; (default: false) ``` **hideHeader** Whether to hide the room header when in a call. -``` +```ts hideHeader: boolean; (default: false) ``` **showControls** -Whether to show the buttons to mute, screen-share, invite, hangup are shown when in a call. +Whether to show the buttons to mute, screen-share, invite, hangup are shown +when in a call. -``` +```ts showControls: boolean; (default: true) ``` **hideScreensharing** Whether to hide the screen-sharing button. -``` +```ts hideScreensharing: boolean; (default: false) ``` @@ -116,7 +136,7 @@ hideScreensharing: boolean; (default: false) Whether to use end-to-end encryption. This is a legacy flag for the full mesh branch. It is not used on the livekit branch and has no impact there! -``` +```ts enableE2EE: boolean; (default: true) ``` @@ -124,28 +144,29 @@ enableE2EE: boolean; (default: true) Whether to use per participant encryption. Keys will be exchanged over encrypted matrix room messages. -``` +```ts perParticipantE2EE: boolean; (default: false) ``` **password** -E2EE password when using a shared secret. (For individual sender keys in embedded mode this is not required.) +E2EE password when using a shared secret. +(For individual sender keys in embedded mode this is not required.) -``` +```ts password: string | null; ``` **displayName** The display name to use for auto-registration. -``` +```ts displayName: string | null; ``` **lang** The BCP 47 code of the language the app should use. -``` +```ts lang: string | null; ``` @@ -153,7 +174,7 @@ lang: string | null; The font/fonts which the interface should use. There can be multiple font url parameters: `?font=font-one&font=font-two...` -``` +```ts font: string; font: string; ... @@ -162,14 +183,15 @@ font: string; **fontScale** The factor by which to scale the interface's font size. -``` +```ts fontScale: number | null; ``` **analyticsID** -The Posthog analytics ID. It is only available if the user has given consent for sharing telemetry in element web. +The Posthog analytics ID. It is only available if the user has given consent for +sharing telemetry in element web. -``` +```ts analyticsID: string | null; ``` @@ -177,7 +199,7 @@ analyticsID: string | null; Whether the app is allowed to use fallback STUN servers for ICE in case the user's homeserver doesn't provide any. -``` +```ts allowIceFallback: boolean; (default: false) ``` @@ -186,7 +208,7 @@ Setting this flag skips the lobby and brings you in the call directly. In the widget this can be combined with preload to pass the device settings with the join widget action. -``` +```ts skipLobby: boolean; (default: false) ``` @@ -196,17 +218,26 @@ a call. This is useful for video rooms. If set to false, the widget will show a blank page after leaving the call. -``` +```ts returnToLobby: boolean; (default: false) ``` +**theme** +The theme to use for element call. +can be "light", "dark", "light-high-contrast" or "dark-high-contrast". +If not set element call will use the dark theme. + +```ts +theme: string | null; +``` + **viaServers** This defines the homeserver that is going to be used when joining a room. It has to be set to a non default value for links to rooms that are not on the default homeserver, that is in use for the current user. -``` +```ts viaServers: string; (default: undefined) ``` @@ -216,6 +247,6 @@ a new (guest) user. This can be user to configure a non default guest user server when creating a spa link. -``` +```ts homeserver: string; (default: undefined) ``` diff --git a/src/App.tsx b/src/App.tsx index f3482252..d4df1d09 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,19 +35,21 @@ import { CrashView, LoadingView } from "./FullScreenView"; import { DisconnectedBanner } from "./DisconnectedBanner"; import { Initializer } from "./initializer"; import { MediaDevicesProvider } from "./livekit/MediaDevicesContext"; +import { widget } from "./widget"; +import { useTheme } from "./useTheme"; const SentryRoute = Sentry.withSentryRouting(Route); -interface BackgroundProviderProps { +interface SimpleProviderProps { children: JSX.Element; } -const BackgroundProvider: FC = ({ children }) => { +const BackgroundProvider: FC = ({ children }) => { const { pathname } = useLocation(); useEffect(() => { let backgroundImage = ""; - if (!["/login", "/register"].includes(pathname)) { + if (!["/login", "/register"].includes(pathname) && !widget) { backgroundImage = "var(--background-gradient)"; } @@ -57,6 +59,10 @@ const BackgroundProvider: FC = ({ children }) => { return <>{children}; }; +const ThemeProvider: FC = ({ children }) => { + useTheme(); + return children; +}; interface AppProps { history: History; @@ -64,7 +70,6 @@ interface AppProps { export const App: FC = ({ history }) => { const [loaded, setLoaded] = useState(false); - useEffect(() => { Initializer.init()?.then(() => { setLoaded(true); @@ -78,37 +83,39 @@ export const App: FC = ({ history }) => { // @ts-ignore - - {loaded ? ( - - - - - - - - - - - - - - - - - - - - - - - - - - ) : ( - - )} - + + + {loaded ? ( + + + + + + + + + + + + + + + + + + + + + + + + + + ) : ( + + )} + + ); diff --git a/src/Header.module.css b/src/Header.module.css index bb66034b..1c9a7225 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -33,6 +33,7 @@ limitations under the License. } .headerLogo { + color: var(--cpd-color-text-primary); display: none; align-items: center; text-decoration: none; diff --git a/src/UrlParams.ts b/src/UrlParams.ts index fcc91d97..78735a8e 100644 --- a/src/UrlParams.ts +++ b/src/UrlParams.ts @@ -131,7 +131,11 @@ export interface UrlParams { */ returnToLobby: boolean; /** - * This defines the homeserver that is going to be used when joining a room. + * The theme to use for element call. + * can be "light", "dark", "light-high-contrast" or "dark-high-contrast". + */ + theme: string | null; + /** This defines the homeserver that is going to be used when joining a room. * It has to be set to a non default value for links to rooms * that are not on the default homeserver, * that is in use for the current user. @@ -243,6 +247,7 @@ export const getUrlParams = ( perParticipantE2EE: parser.getFlagParam("perParticipantE2EE"), skipLobby: parser.getFlagParam("skipLobby"), returnToLobby: parser.getFlagParam("returnToLobby"), + theme: parser.getParam("theme"), viaServers: parser.getParam("viaServers"), homeserver: parser.getParam("homeserver"), }; diff --git a/src/icons/Logo.svg b/src/icons/Logo.svg index 4d64175f..8ee78ca7 100644 --- a/src/icons/Logo.svg +++ b/src/icons/Logo.svg @@ -2,17 +2,17 @@ Element Call (Beta) - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/index.css b/src/index.css index fc05247c..90794ddc 100644 --- a/src/index.css +++ b/src/index.css @@ -56,10 +56,6 @@ limitations under the License. --cpd-color-border-accent: var(--cpd-color-green-1100); --stopgap-color-on-solid-accent: var(--cpd-color-text-primary); --stopgap-background-85: rgba(16, 19, 23, 0.85); - - background-size: calc(max(1440px, 100vw)) calc(max(800px, 100vh)); - background-repeat: no-repeat; - background-position: center; } @font-face { @@ -152,6 +148,9 @@ limitations under the License. body { background-color: var(--cpd-color-bg-canvas-default); + background-size: calc(max(1440px, 100vw)) calc(max(800px, 100vh)); + background-repeat: no-repeat; + background-position: center; color: var(--cpd-color-text-primary); color-scheme: dark; margin: 0; diff --git a/src/useTheme.ts b/src/useTheme.ts new file mode 100644 index 00000000..99a7e9cd --- /dev/null +++ b/src/useTheme.ts @@ -0,0 +1,41 @@ +/* +Copyright 2024 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { useLayoutEffect, useRef } from "react"; + +import { useUrlParams } from "./UrlParams"; + +export const useTheme = (): void => { + const { theme: themeName } = useUrlParams(); + const previousTheme = useRef(document.body.classList.item(0)); + useLayoutEffect(() => { + // Don't update the current theme if the url does not contain a theme prop. + if (!themeName) return; + const theme = themeName.includes("light") ? "light" : "dark"; + const themeHighContrast = themeName.includes("high-contrast") ? "-hc" : ""; + 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; + } + }, [previousTheme, themeName]); +};