chore(deps): update dependency element-call from 0.19.1 to v0.20.1 (#2992)
* chore(deps): update dependency @element-hq/element-call-embedded from 0.19.1 to v0.20.1 * fix reaction button click * fix call settings button query * fix screenshare and spotlight button state update * fix incomming call text alignment * prevent displaying call notification for voice rooms * disable mic/video icon when toggle in progress * update matrix-js-sdk --------- Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
This commit is contained in:
Generated
+21
-17
@@ -43,7 +43,7 @@
|
||||
"jotai": "2.6.0",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"matrix-js-sdk": "41.5.0",
|
||||
"matrix-js-sdk": "41.7.0",
|
||||
"matrix-widget-api": "1.16.1",
|
||||
"millify": "6.1.0",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
@@ -66,7 +66,7 @@
|
||||
"ua-parser-js": "1.0.35"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@element-hq/element-call-embedded": "0.19.1",
|
||||
"@element-hq/element-call-embedded": "0.20.1",
|
||||
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
|
||||
"@rollup/plugin-inject": "5.0.3",
|
||||
"@rollup/plugin-wasm": "6.1.1",
|
||||
@@ -1774,9 +1774,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@element-hq/element-call-embedded": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.19.1.tgz",
|
||||
"integrity": "sha512-RDZY3P3LTx10ACaGhzkwh2+boNB3x54zHF/7v/cCyoQlAVfEYMhgMEb4CRTwJFwwYFe1r++6Higa0A0G5XxZ8Q==",
|
||||
"version": "0.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.20.1.tgz",
|
||||
"integrity": "sha512-ODg2r7UmR8UjRpapLKbn6v1PS8fu/r58zdbvXMYaAlUEAC2f6L/9Moc9S4noG1+ARgWxY+m2vLmNDK9G9uFZYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
@@ -2386,9 +2386,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-18.3.0.tgz",
|
||||
"integrity": "sha512-9a4feyt8QLysARu7PHKaRWT+wcCd+IYH074LXp9QK5WqfN4zUXueRhiSSMNT18Bm+8q3sBR/4zxDxOSDR0M8Kg==",
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-18.3.1.tgz",
|
||||
"integrity": "sha512-VRjWhE1UgHnPpJ3b9B5+8z71ZC/HICFngPPFIN6ktzmUBKI5RusPujzbAQUoB3CgZ0yU58L99AfSQS4YTztSWw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
@@ -6216,12 +6216,16 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/conventional-commit-types": {
|
||||
@@ -10106,16 +10110,16 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/matrix-js-sdk": {
|
||||
"version": "41.5.0",
|
||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-41.5.0.tgz",
|
||||
"integrity": "sha512-CK3h+qQJ4wkVEUgEWc5MdLjccXyiFqncCC53P+auqOhnX2U6tAFsRfnbML1QQiKIsFMzqTrAnF/4a5LUUOIeXg==",
|
||||
"version": "41.7.0",
|
||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-41.7.0.tgz",
|
||||
"integrity": "sha512-MP0xNv/VVRbshq00TE6EVo77IIXsQk0KjiVtgKV0t9j/V77a6Klt00QrrO0XykkTUsNC0+mQeBMxnx75rZO86Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^18.2.0",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^18.3.1",
|
||||
"another-json": "^0.2.0",
|
||||
"bs58": "^6.0.0",
|
||||
"content-type": "^1.0.4",
|
||||
"content-type": "^2.0.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"loglevel": "^1.9.2",
|
||||
"matrix-events-sdk": "0.0.1",
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@
|
||||
"jotai": "2.6.0",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"matrix-js-sdk": "41.5.0",
|
||||
"matrix-js-sdk": "41.7.0",
|
||||
"matrix-widget-api": "1.16.1",
|
||||
"millify": "6.1.0",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
@@ -90,7 +90,7 @@
|
||||
"ua-parser-js": "1.0.35"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@element-hq/element-call-embedded": "0.19.1",
|
||||
"@element-hq/element-call-embedded": "0.20.1",
|
||||
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
|
||||
"@rollup/plugin-inject": "5.0.3",
|
||||
"@rollup/plugin-wasm": "6.1.1",
|
||||
|
||||
@@ -148,11 +148,13 @@ function IncomingCall({ dm, info, onIgnore, onAnswer, onReject }: IncomingCallPr
|
||||
/>
|
||||
</Avatar>
|
||||
</Box>
|
||||
<Box grow="Yes" direction="Column" gap="100">
|
||||
<Box grow="Yes" direction="Column" gap="100" alignItems="Center">
|
||||
<Text size="H3" align="Center" truncate>
|
||||
{roomName}
|
||||
</Text>
|
||||
<Text size="T300">Incoming Call</Text>
|
||||
<Text size="T300" align="Center">
|
||||
Incoming Call
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
{!livekitSupported && (
|
||||
@@ -237,6 +239,7 @@ function IncomingCallListener({ callEmbed, joined }: IncomingCallListenerProps)
|
||||
// only process rtc notification reference events.
|
||||
// we do not want to wait to decrypt all events.
|
||||
if (event.getRelation()?.rel_type !== RelationType.Reference) return;
|
||||
if (room?.isCallRoom()) return;
|
||||
|
||||
if (event.isEncrypted()) {
|
||||
if (!event.isBeingDecrypted()) {
|
||||
|
||||
@@ -12,6 +12,9 @@ type MicrophoneButtonProps = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
function MicrophoneButton({ enabled, onToggle, disabled }: MicrophoneButtonProps) {
|
||||
const [micState, toggleMic] = useAsyncCallback(onToggle);
|
||||
const loading = micState.status === AsyncStatus.Loading;
|
||||
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
@@ -28,9 +31,9 @@ function MicrophoneButton({ enabled, onToggle, disabled }: MicrophoneButtonProps
|
||||
fill="Soft"
|
||||
radii="300"
|
||||
size="300"
|
||||
onClick={() => onToggle()}
|
||||
onClick={toggleMic}
|
||||
outlined
|
||||
disabled={disabled}
|
||||
disabled={disabled || loading}
|
||||
>
|
||||
<Icon size="100" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
|
||||
</IconButton>
|
||||
@@ -82,6 +85,9 @@ type VideoButtonProps = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) {
|
||||
const [videoState, toggleVideo] = useAsyncCallback(onToggle);
|
||||
const loading = videoState.status === AsyncStatus.Loading;
|
||||
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
@@ -98,9 +104,9 @@ function VideoButton({ enabled, onToggle, disabled }: VideoButtonProps) {
|
||||
fill="Soft"
|
||||
radii="300"
|
||||
size="300"
|
||||
onClick={() => onToggle()}
|
||||
onClick={toggleVideo}
|
||||
outlined
|
||||
disabled={disabled}
|
||||
disabled={disabled || loading}
|
||||
>
|
||||
<Icon
|
||||
size="100"
|
||||
@@ -158,6 +164,9 @@ export function CallControl({
|
||||
const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control);
|
||||
const setCallEmbed = useSetAtom(callEmbedAtom);
|
||||
|
||||
const handleMicrophoneToggle = useCallback(() => callEmbed.control.toggleMicrophone(), [callEmbed]);
|
||||
const handleVideoToggle = useCallback(() => callEmbed.control.toggleVideo(), [callEmbed]);
|
||||
|
||||
const [hangupState, hangup] = useAsyncCallback(
|
||||
useCallback(() => callEmbed.hangup(), [callEmbed])
|
||||
);
|
||||
@@ -177,7 +186,7 @@ export function CallControl({
|
||||
<Box alignItems="Inherit" gap="200">
|
||||
<MicrophoneButton
|
||||
enabled={microphone}
|
||||
onToggle={() => callEmbed.control.toggleMicrophone()}
|
||||
onToggle={handleMicrophoneToggle}
|
||||
disabled={!callJoined}
|
||||
/>
|
||||
<SoundButton
|
||||
@@ -188,7 +197,7 @@ export function CallControl({
|
||||
{!compact && <StatusDivider />}
|
||||
<VideoButton
|
||||
enabled={video}
|
||||
onToggle={() => callEmbed.control.toggleVideo()}
|
||||
onToggle={handleVideoToggle}
|
||||
disabled={!callJoined}
|
||||
/>
|
||||
{!compact && (
|
||||
|
||||
@@ -71,6 +71,9 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
setCords(undefined);
|
||||
};
|
||||
|
||||
const handleMicrophoneToggle = useCallback(() => callEmbed.control.toggleMicrophone(), [callEmbed]);
|
||||
const handleVideoToggle = useCallback(() => callEmbed.control.toggleVideo(), [callEmbed]);
|
||||
|
||||
const [hangupState, hangup] = useAsyncCallback(
|
||||
useCallback(() => callEmbed.hangup(), [callEmbed])
|
||||
);
|
||||
@@ -96,13 +99,13 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
<Box shrink="No" alignItems="Inherit" justifyContent="Inherit" gap="200">
|
||||
<MicrophoneButton
|
||||
enabled={microphone}
|
||||
onToggle={() => callEmbed.control.toggleMicrophone()}
|
||||
onToggle={handleMicrophoneToggle}
|
||||
/>
|
||||
<SoundButton enabled={sound} onToggle={() => callEmbed.control.toggleSound()} />
|
||||
</Box>
|
||||
{!compact && <ControlDivider />}
|
||||
<Box shrink="No" alignItems="Inherit" justifyContent="Inherit" gap="200">
|
||||
<VideoButton enabled={video} onToggle={() => callEmbed.control.toggleVideo()} />
|
||||
<VideoButton enabled={video} onToggle={handleVideoToggle} />
|
||||
<ScreenShareButton
|
||||
enabled={screenshare}
|
||||
onToggle={() => callEmbed.control.toggleScreenshare()}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Icon, IconButton, Icons, Line, Text, Tooltip, TooltipProvider } from 'f
|
||||
import { useAtom } from 'jotai';
|
||||
import * as css from './styles.css';
|
||||
import { callChatAtom } from '../../state/callEmbed';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
|
||||
export function ControlDivider() {
|
||||
return (
|
||||
@@ -12,9 +13,12 @@ export function ControlDivider() {
|
||||
|
||||
type MicrophoneButtonProps = {
|
||||
enabled: boolean;
|
||||
onToggle: () => void;
|
||||
onToggle: () => Promise<unknown>;
|
||||
};
|
||||
export function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
|
||||
const [micState, toggleMic] = useAsyncCallback(onToggle);
|
||||
const loading = micState.status === AsyncStatus.Loading;
|
||||
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
@@ -32,8 +36,9 @@ export function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
|
||||
fill="Soft"
|
||||
radii="400"
|
||||
size="400"
|
||||
onClick={() => onToggle()}
|
||||
onClick={toggleMic}
|
||||
outlined
|
||||
disabled={loading}
|
||||
>
|
||||
<Icon size="400" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
|
||||
</IconButton>
|
||||
@@ -80,9 +85,12 @@ export function SoundButton({ enabled, onToggle }: SoundButtonProps) {
|
||||
|
||||
type VideoButtonProps = {
|
||||
enabled: boolean;
|
||||
onToggle: () => void;
|
||||
onToggle: () => Promise<unknown>;
|
||||
};
|
||||
export function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
||||
const [videoState, toggleVideo] = useAsyncCallback(onToggle);
|
||||
const loading = videoState.status === AsyncStatus.Loading;
|
||||
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
@@ -100,8 +108,9 @@ export function VideoButton({ enabled, onToggle }: VideoButtonProps) {
|
||||
fill="Soft"
|
||||
radii="400"
|
||||
size="400"
|
||||
onClick={() => onToggle()}
|
||||
onClick={toggleVideo}
|
||||
outlined
|
||||
disabled={loading}
|
||||
>
|
||||
<Icon
|
||||
size="400"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Box, Button, Icon, Icons, Spinner, Text } from 'folds';
|
||||
import { SequenceCard } from '../../components/sequence-card';
|
||||
import * as css from './styles.css';
|
||||
@@ -26,6 +26,10 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) {
|
||||
const { microphone, video, sound, toggleMicrophone, toggleVideo, toggleSound } =
|
||||
useCallPreferences();
|
||||
|
||||
|
||||
const handleMicrophoneToggle = useCallback(async () => toggleMicrophone(), [toggleMicrophone]);
|
||||
const handleVideoToggle = useCallback(async () => toggleVideo(), [toggleVideo]);
|
||||
|
||||
return (
|
||||
<SequenceCard
|
||||
className={css.ControlCard}
|
||||
@@ -37,12 +41,12 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) {
|
||||
wrap="Wrap"
|
||||
>
|
||||
<Box shrink="No" alignItems="Inherit" justifyContent="SpaceBetween" gap="200">
|
||||
<MicrophoneButton enabled={microphone} onToggle={toggleMicrophone} />
|
||||
<MicrophoneButton enabled={microphone} onToggle={handleMicrophoneToggle} />
|
||||
<SoundButton enabled={sound} onToggle={toggleSound} />
|
||||
</Box>
|
||||
<ControlDivider />
|
||||
<Box shrink="No" alignItems="Inherit" justifyContent="SpaceBetween" gap="200">
|
||||
<VideoButton enabled={video} onToggle={toggleVideo} />
|
||||
<VideoButton enabled={video} onToggle={handleVideoToggle} />
|
||||
<ChatButton />
|
||||
</Box>
|
||||
<Box grow="Yes" direction="Column">
|
||||
|
||||
@@ -14,8 +14,12 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
|
||||
private iframe: HTMLIFrameElement;
|
||||
|
||||
private bodyMutationObserver: MutationObserver;
|
||||
|
||||
private controlMutationObserver: MutationObserver;
|
||||
|
||||
private mediaStatePromiseResolver: undefined | (() => void);
|
||||
|
||||
private get document(): Document | undefined {
|
||||
return this.iframe.contentDocument ?? this.iframe.contentWindow?.document;
|
||||
}
|
||||
@@ -28,16 +32,25 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
return screenshareBtn ?? undefined;
|
||||
}
|
||||
|
||||
private get settingsButton(): HTMLElement | undefined {
|
||||
private get leaveButton(): Element | undefined {
|
||||
const leaveBtn = this.document?.querySelector('[data-testid="incall_leave"]');
|
||||
|
||||
const settingsButton = leaveBtn?.previousElementSibling as HTMLElement | null;
|
||||
return leaveBtn ?? undefined;
|
||||
}
|
||||
|
||||
return settingsButton ?? undefined;
|
||||
private get settingsButton(): HTMLElement | undefined {
|
||||
const settingsButtonLeft = this.document?.querySelector(
|
||||
'[data-testid="settings-bottom-left"]'
|
||||
) as HTMLButtonElement | undefined;
|
||||
const settingsButtonCenter = this.document?.querySelector(
|
||||
'[data-testid="settings-bottom-center"]'
|
||||
) as HTMLButtonElement | undefined;
|
||||
|
||||
return settingsButtonLeft ?? settingsButtonCenter ?? undefined;
|
||||
}
|
||||
|
||||
private get reactionsButton(): HTMLElement | undefined {
|
||||
const reactionsButton = this.settingsButton?.previousElementSibling as HTMLElement | null;
|
||||
const reactionsButton = this.leaveButton?.previousElementSibling as HTMLElement | null;
|
||||
|
||||
return reactionsButton ?? undefined;
|
||||
}
|
||||
@@ -65,6 +78,7 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.call = call;
|
||||
this.iframe = iframe;
|
||||
|
||||
this.bodyMutationObserver = new MutationObserver(this.onBodyMutation.bind(this));
|
||||
this.controlMutationObserver = new MutationObserver(this.onControlMutation.bind(this));
|
||||
}
|
||||
|
||||
@@ -102,6 +116,30 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
|
||||
public startObserving() {
|
||||
if (!this.document) return;
|
||||
|
||||
this.bodyMutationObserver.observe(this.document.body, {
|
||||
childList: true,
|
||||
subtree: false, // only direct children of body
|
||||
});
|
||||
this.onBodyMutation();
|
||||
}
|
||||
|
||||
private onBodyMutation() {
|
||||
if (!this.document) return;
|
||||
|
||||
this.document.body.style.setProperty('background', 'none', 'important');
|
||||
|
||||
const controls = this.leaveButton?.parentElement?.parentElement;
|
||||
if (controls) {
|
||||
controls.style.setProperty('position', 'absolute');
|
||||
controls.style.setProperty('visibility', 'hidden');
|
||||
}
|
||||
|
||||
this.observeControls();
|
||||
}
|
||||
|
||||
private observeControls() {
|
||||
this.controlMutationObserver.disconnect();
|
||||
|
||||
const screenshareBtn = this.screenshareButton;
|
||||
@@ -125,8 +163,14 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.setSound(this.sound);
|
||||
}
|
||||
|
||||
private setMediaState(state: ElementMediaStatePayload) {
|
||||
return this.call.transport.send(ElementWidgetActions.DeviceMute, state);
|
||||
private async setMediaState(state: ElementMediaStatePayload) {
|
||||
const data = await this.call.transport.send(ElementWidgetActions.DeviceMute, state);
|
||||
return new Promise<typeof data>(resolve => {
|
||||
if (this.mediaStatePromiseResolver) {
|
||||
this.mediaStatePromiseResolver();
|
||||
}
|
||||
this.mediaStatePromiseResolver = () => resolve(data);
|
||||
});
|
||||
}
|
||||
|
||||
private setSound(sound: boolean): void {
|
||||
@@ -157,9 +201,14 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
if (this.microphone && !this.sound) {
|
||||
this.toggleSound();
|
||||
}
|
||||
|
||||
if (this.mediaStatePromiseResolver) {
|
||||
this.mediaStatePromiseResolver();
|
||||
this.mediaStatePromiseResolver = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public onControlMutation() {
|
||||
private onControlMutation() {
|
||||
const screenshare: boolean = this.screenshareButton?.getAttribute('data-kind') === 'primary';
|
||||
const spotlight: boolean = this.spotlightButton?.checked ?? false;
|
||||
|
||||
@@ -230,6 +279,7 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.bodyMutationObserver.disconnect();
|
||||
this.controlMutationObserver.disconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +172,10 @@ export class CallEmbed {
|
||||
|
||||
const controlState = initialControlState ?? new CallControlState(true, false, true);
|
||||
this.control = new CallControl(controlState, call, iframe);
|
||||
this.control.startObserving();
|
||||
iframe.onload = () => {
|
||||
this.control.startObserving();
|
||||
};
|
||||
|
||||
let initialMediaEvent = true;
|
||||
this.disposables.push(
|
||||
@@ -272,21 +276,6 @@ export class CallEmbed {
|
||||
|
||||
private onCallJoined(): void {
|
||||
this.joined = true;
|
||||
this.applyStyles();
|
||||
this.control.startObserving();
|
||||
}
|
||||
|
||||
private applyStyles(): void {
|
||||
const doc = this.document;
|
||||
if (!doc) return;
|
||||
|
||||
doc.body.style.setProperty('background', 'none', 'important');
|
||||
const controls = doc.body.querySelector('[data-testid="incall_leave"]')?.parentElement
|
||||
?.parentElement;
|
||||
if (controls) {
|
||||
controls.style.setProperty('position', 'absolute');
|
||||
controls.style.setProperty('visibility', 'hidden');
|
||||
}
|
||||
}
|
||||
|
||||
private onEvent(ev: MatrixEvent): void {
|
||||
|
||||
Reference in New Issue
Block a user