mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 12:16:53 +02:00
Refactor Jitsi widget to use compound styles.
This commit is contained in:
parent
c7f07f4c29
commit
df2fe0feba
@ -7,16 +7,6 @@
|
||||
<body>
|
||||
<div id="jitsiContainer"><!-- the js will put the conference here --></div>
|
||||
<div id="joinButtonContainer">
|
||||
<div class="joinConferenceFloating">
|
||||
<div class="joinConferencePrompt">
|
||||
<span class="icon"><!-- managed by CSS --></span>
|
||||
<!-- TODO: i18n -->
|
||||
<h2>Jitsi Video Conference</h2>
|
||||
<div id="widgetActionContainer">
|
||||
<button type="button" id="joinButton">Join Conference</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- This script is not webpacked, and the script is downloaded at build time -->
|
||||
<script src="./jitsi_external_api.min.js"></script>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<typeof getVectorConfig>}): JSX.Element => {
|
||||
const [shouldRender, setShouldRender] = useState(false);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const instanceConfig = new SnakedObject<IConfigOptions>((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 <div></div>;
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className="joinConferencePrompt">
|
||||
<VideoCallIcon width={"32px"} height={"32px"}/>
|
||||
<h2>Jitsi Video Conference</h2>
|
||||
<div id="widgetActionContainer">
|
||||
<Button onClick={() => joinConference()}>Join conference</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
};
|
||||
|
||||
const setupCompleted = (async (): Promise<string | void> => {
|
||||
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(<StrictMode><JoinButtonContainer configPromise={configPromise} /></StrictMode>);
|
||||
|
||||
|
||||
// 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<string | void> => {
|
||||
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<string | void> => {
|
||||
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<IConfigOptions>((await configPromise) ?? <IConfigOptions>{});
|
||||
const instanceConfig = new SnakedObject<IConfigOptions>((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<string | void> => {
|
||||
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<void> => joinConference();
|
||||
}
|
||||
|
||||
function switchVisibleContainers(): void {
|
||||
inConference = !inConference;
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user