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