diff --git a/src/vector/jitsi/index.html b/src/vector/jitsi/index.html
index 7b60e6a22b..79346cfa65 100644
--- a/src/vector/jitsi/index.html
+++ b/src/vector/jitsi/index.html
@@ -7,16 +7,6 @@
diff --git a/src/vector/jitsi/index.pcss b/src/vector/jitsi/index.pcss
index 09299f1b7f..6dd234bcaa 100644
--- a/src/vector/jitsi/index.pcss
+++ b/src/vector/jitsi/index.pcss
@@ -6,7 +6,8 @@ Please see LICENSE files in the repository root for full details.
*/
/* TODO: Match the user's theme: https://github.com/element-hq/element-web/issues/12794 */
-
+@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound);
+@import url("@vector-im/compound-web/dist/style.css");
/* Path to `res` dir in the source tree */
$res: ../../../res;
@@ -47,60 +48,26 @@ html {
}
#joinButtonContainer {
- display: table;
- position: absolute;
- height: 100%;
width: 100%;
-
- /* Hidden by default to avoid flashing the prejoin screen at the user when */
- /* we're supposed to skip it anyways */
- visibility: hidden;
-}
-
-.joinConferenceFloating {
- display: table-cell;
- vertical-align: middle;
+ height: 100%;
+ display: grid;
+ place-items: center;
}
.joinConferencePrompt {
- margin-left: auto;
- margin-right: auto;
- width: 90%;
- text-align: center;
-}
+ display: flex;
+ flex-direction: column;
+ margin: auto;
+ width: fit-content;
-#joinButton {
- /* A mix of AccessibleButton styles */
- cursor: pointer;
- padding: 7px 18px;
- text-align: center;
- border-radius: 4px;
- display: inline-block;
- font-size: 14px;
- color: #ffffff;
- background-color: #03b381;
- border: 0;
-}
+ > * {
+ margin-left: auto;
+ margin-right: auto;
-.icon {
- $icon-size: 42px;
- margin-top: -$icon-size; /* to visually center the form */
-
- &::before {
- content: "";
- background-size: contain;
- background-color: $dark-fg;
- mask-repeat: no-repeat;
- mask-position: center;
- mask-image: url("$(res)/img/element-icons/call/video-call.svg");
- mask-size: $icon-size;
- display: block;
- width: $icon-size;
- height: $icon-size;
- margin: 0 auto; /* center */
}
}
+
body.theme-light .icon::before {
background-color: $light-fg;
}
diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.tsx
similarity index 93%
rename from src/vector/jitsi/index.ts
rename to src/vector/jitsi/index.tsx
index a5ea8cba6d..63a3f3fb73 100644
--- a/src/vector/jitsi/index.ts
+++ b/src/vector/jitsi/index.tsx
@@ -32,6 +32,11 @@ import { type IConfigOptions } from "../../IConfigOptions";
import { SnakedObject } from "../../utils/SnakedObject";
import { ElementWidgetCapabilities } from "../../stores/widgets/ElementWidgetCapabilities";
import { getVectorConfig } from "../getconfig";
+import React, { JSX, StrictMode, useEffect, useState } from "react";
+import { Button } from "@vector-im/compound-web";
+import { createRoot } from "react-dom/client";
+import { VideoCallIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
+
interface Config extends _Config {
// Jitsi's types are missing these fields
@@ -102,11 +107,43 @@ async function checkAudioVideoEnabled(): Promise<[audioEnabled: boolean, videoEn
return [audioEnabled, videoEnabled];
}
+const JoinButtonContainer = ({configPromise}: {configPromise: ReturnType}): JSX.Element => {
+ const [shouldRender, setShouldRender] = useState(false);
+ useEffect(() => {
+ (async () => {
+ const instanceConfig = new SnakedObject((await configPromise) ?? {} as IConfigOptions);
+ const jitsiConfig = instanceConfig.get("jitsi_widget");
+ const skipScreen = new SnakedObject(jitsiConfig ?? {})?.get("skip_built_in_welcome_screen") ?? false;
+ setShouldRender(!skipScreen);
+ })();
+ }, []);
+
+ if (!shouldRender) {
+ return ;
+ }
+
+ return <>
+
+
+ Jitsi Video Conference
+
+
+
+
+ >
+
+};
+
const setupCompleted = (async (): Promise => {
try {
// Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with
// other setup work and therefore do not block.
const configPromise = getVectorConfig();
+ // Render button layout immediately.
+ const domNode = document.querySelector('#joinButtonContainer')!;
+ const root = createRoot(domNode);
+ root.render();
+
// The widget's options are encoded into the fragment to avoid leaking info to the server.
const widgetQuery = new URLSearchParams(window.location.hash.substring(1));
@@ -183,6 +220,7 @@ const setupCompleted = (async (): Promise => {
void joinConference(audioInput as string | null, videoInput as string | null);
});
handleAction(ElementWidgetActions.HangupCall, async ({ force }) => {
+ console.log("Got hangup");
if (force === true) {
meetApi?.dispose();
void notifyHangup();
@@ -249,7 +287,7 @@ const setupCompleted = (async (): Promise => {
supportsScreensharing = qsParam("supportsScreensharing", true) === "true";
// We've reached the point where we have to wait for the config, so do that then parse it.
- const instanceConfig = new SnakedObject((await configPromise) ?? {});
+ const instanceConfig = new SnakedObject((await configPromise) ?? {} as IConfigOptions);
const jitsiConfig = instanceConfig.get("jitsi_widget");
if (jitsiConfig) {
skipOurWelcomeScreen = new SnakedObject(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
@@ -267,18 +305,12 @@ const setupCompleted = (async (): Promise => {
if (skipOurWelcomeScreen) {
skipToJitsiSplashScreen();
}
-
- enableJoinButton(); // always enable the button
} catch (e) {
logger.error("Error setting up Jitsi widget", e);
document.getElementById("widgetActionContainer")!.innerText = "Failed to load Jitsi widget";
}
})();
-function enableJoinButton(): void {
- document.getElementById("joinButton")!.onclick = (): Promise => joinConference();
-}
-
function switchVisibleContainers(): void {
inConference = !inConference;
diff --git a/webpack.config.js b/webpack.config.js
index b979ded2b2..624679f0fd 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -130,7 +130,7 @@ module.exports = (env, argv) => {
entry: {
bundle: "./src/vector/index.ts",
mobileguide: "./src/vector/mobile_guide/index.ts",
- jitsi: "./src/vector/jitsi/index.ts",
+ jitsi: "./src/vector/jitsi/index.tsx",
usercontent: "./src/usercontent/index.ts",
serviceworker: {
import: "./src/serviceworker/index.ts",