Merge remote-tracking branch 'origin/livekit' into dbkr/matrixrtcsession

This commit is contained in:
David Baker
2023-08-16 18:53:00 +01:00
20 changed files with 334 additions and 150 deletions

View File

@@ -31,7 +31,6 @@ import { MatrixInfo } from "./VideoPreview";
import { CallEndedView } from "./CallEndedView";
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
import { useProfile } from "../profile/useProfile";
import { E2EEConfig } from "../livekit/useLiveKit";
import { findDeviceByName } from "../media-utils";
//import { OpenIDLoader } from "../livekit/OpenIDLoader";
import { ActiveCall } from "./InCallView";
@@ -41,6 +40,11 @@ import { LivekitFocus } from "../livekit/LivekitFocus";
import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships";
import { enterRTCSession, leaveRTCSession } from "../rtcSessionHelpers";
import { useMatrixRTCSessionJoinState } from "../useMatrixRTCSessionJoinState";
import {
useManageRoomSharedKey,
useIsRoomE2EE,
} from "../e2ee/sharedKeyManagement";
import { useEnableE2EE } from "../settings/useSetting";
declare global {
interface Window {
@@ -78,6 +82,9 @@ export function GroupCallView({
const memberships = useMatrixRTCSessionMemberships(rtcSession);
const isJoined = useMatrixRTCSessionJoinState(rtcSession);
const e2eeSharedKey = useManageRoomSharedKey(groupCall.room.roomId);
const isRoomE2EE = useIsRoomE2EE(groupCall.room.roomId);
const { t } = useTranslation();
useEffect(() => {
@@ -243,8 +250,11 @@ export function GroupCallView({
}
}, [isJoined, rtcSession]);
const [e2eeConfig, setE2EEConfig] = useState<E2EEConfig | undefined>(
undefined
const [e2eeEnabled] = useEnableE2EE();
const e2eeConfig = useMemo(
() => (e2eeSharedKey ? { sharedKey: e2eeSharedKey } : undefined),
[e2eeSharedKey]
);
const onReconnect = useCallback(() => {
@@ -266,6 +276,22 @@ export function GroupCallView({
return <ErrorView error={new Error("Call focus is not compatible!")} />;
}
if (e2eeEnabled && isRoomE2EE && !e2eeSharedKey) {
return (
<ErrorView
error={
new Error(
"No E2EE key provided: please make sure the URL you're using to join this call has been retrieved using the in-app button."
)
}
/>
);
}
if (!e2eeEnabled && isRoomE2EE) {
return <ErrorView error={new Error("You need to enable E2EE to join.")} />;
}
if (isJoined) {
return (
/*<OpenIDLoader
@@ -325,10 +351,7 @@ export function GroupCallView({
<LobbyView
matrixInfo={matrixInfo}
muteStates={muteStates}
onEnter={(e2eeConfig?: E2EEConfig) => {
setE2EEConfig(e2eeConfig);
enterRTCSession(rtcSession);
}}
onEnter={() => enter()}
isEmbedded={isEmbedded}
hideHeader={hideHeader}
/>

View File

@@ -78,6 +78,7 @@ import { useEventEmitterThree } from "../useEvents";
import { useWakeLock } from "../useWakeLock";
import { useMergedRefs } from "../useMergedRefs";
import { MuteStates } from "./MuteStates";
import { useIsRoomE2EE } from "../e2ee/sharedKeyManagement";
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
@@ -133,6 +134,8 @@ export function InCallView({
usePreventScroll();
useWakeLock();
const isRoomE2EE = useIsRoomE2EE(rtcSession.room.roomId);
const containerRef1 = useRef<HTMLDivElement | null>(null);
const [containerRef2, bounds] = useMeasure({ polyfill: ResizeObserver });
const boundsValid = bounds.height > 0;
@@ -408,7 +411,7 @@ export function InCallView({
<Header>
<LeftNav>
<RoomHeaderInfo roomName={rtcSession.room.name} />
<E2EELock />
{!isRoomE2EE && <E2EELock />}
</LeftNav>
<RightNav>
<GridLayoutMenu layout={layout} setLayout={setLayout} />
@@ -445,12 +448,7 @@ export function InCallView({
/>
)}
{inviteModalState.isOpen && (
<InviteModal
roomIdOrAlias={
rtcSession.room.getCanonicalAlias() ?? rtcSession.room.roomId
}
{...inviteModalProps}
/>
<InviteModal roomId={rtcSession.room.roomId} {...inviteModalProps} />
)}
</div>
);

View File

@@ -1,5 +1,5 @@
/*
Copyright 2022 New Vector Ltd
Copyright 2022 - 2023 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.
@@ -21,13 +21,15 @@ import { Modal, ModalContent, ModalProps } from "../Modal";
import { CopyButton } from "../button";
import { getRoomUrl } from "../matrix-utils";
import styles from "./InviteModal.module.css";
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
interface Props extends Omit<ModalProps, "title" | "children"> {
roomIdOrAlias: string;
roomId: string;
}
export const InviteModal: FC<Props> = ({ roomIdOrAlias, ...rest }) => {
export const InviteModal: FC<Props> = ({ roomId, ...rest }) => {
const { t } = useTranslation();
const roomSharedKey = useRoomSharedKey(roomId);
return (
<Modal
@@ -40,7 +42,7 @@ export const InviteModal: FC<Props> = ({ roomIdOrAlias, ...rest }) => {
<p>{t("Copy and share this call link")}</p>
<CopyButton
className={styles.copyButton}
value={getRoomUrl(roomIdOrAlias)}
value={getRoomUrl(roomId, roomSharedKey ?? undefined)}
data-testid="modal_inviteLink"
/>
</ModalContent>

View File

@@ -14,14 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {
useRef,
useEffect,
useState,
useCallback,
ChangeEvent,
FC,
} from "react";
import { useRef, useEffect, FC } from "react";
import { Trans, useTranslation } from "react-i18next";
import styles from "./LobbyView.module.css";
@@ -32,15 +25,13 @@ import { UserMenuContainer } from "../UserMenuContainer";
import { Body, Link } from "../typography/Typography";
import { useLocationNavigation } from "../useLocationNavigation";
import { MatrixInfo, VideoPreview } from "./VideoPreview";
import { E2EEConfig } from "../livekit/useLiveKit";
import { InputField } from "../input/Input";
import { useEnableE2EE } from "../settings/useSetting";
import { MuteStates } from "./MuteStates";
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
interface Props {
matrixInfo: MatrixInfo;
muteStates: MuteStates;
onEnter: (e2eeConfig?: E2EEConfig) => void;
onEnter: () => void;
isEmbedded: boolean;
hideHeader: boolean;
}
@@ -53,10 +44,9 @@ export const LobbyView: FC<Props> = ({
hideHeader,
}) => {
const { t } = useTranslation();
const roomSharedKey = useRoomSharedKey(matrixInfo.roomId);
useLocationNavigation();
const [enableE2EE] = useEnableE2EE();
const joinCallButtonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (joinCallButtonRef.current) {
@@ -64,18 +54,6 @@ export const LobbyView: FC<Props> = ({
}
}, [joinCallButtonRef]);
const [e2eeSharedKey, setE2EESharedKey] = useState<string | undefined>(
undefined
);
const onE2EESharedKeyChanged = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setE2EESharedKey(value === "" ? undefined : value);
},
[setE2EESharedKey]
);
return (
<div className={styles.room}>
{!hideHeader && (
@@ -91,25 +69,12 @@ export const LobbyView: FC<Props> = ({
<div className={styles.joinRoom}>
<div className={styles.joinRoomContent}>
<VideoPreview matrixInfo={matrixInfo} muteStates={muteStates} />
{enableE2EE && (
<InputField
className={styles.passwordField}
label={t("Password (if none, E2EE is disabled)")}
type="text"
onChange={onE2EESharedKeyChanged}
value={e2eeSharedKey}
/>
)}
<Trans>
<Button
ref={joinCallButtonRef}
className={styles.copyButton}
size="lg"
onPress={() =>
onEnter(
e2eeSharedKey ? { sharedKey: e2eeSharedKey } : undefined
)
}
onPress={() => onEnter()}
data-testid="lobby_joinCall"
>
Join call now
@@ -117,7 +82,7 @@ export const LobbyView: FC<Props> = ({
<Body>Or</Body>
<CopyButton
variant="secondaryCopy"
value={getRoomUrl(matrixInfo.roomAlias ?? matrixInfo.roomId)}
value={getRoomUrl(matrixInfo.roomId, roomSharedKey ?? undefined)}
className={styles.copyButton}
copiedMessage={t("Call link copied")}
data-testid="lobby_inviteLink"

View File

@@ -20,10 +20,14 @@ import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client";
import { SyncState } from "matrix-js-sdk/src/sync";
import { useTranslation } from "react-i18next";
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
import { randomString } from "matrix-js-sdk/src/randomstring";
import type { Room } from "matrix-js-sdk/src/models/room";
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { setLocalStorageItem } from "../useLocalStorage";
import { isLocalRoomId, createRoom, roomNameFromRoomId } from "../matrix-utils";
import { useEnableE2EE } from "../settings/useSetting";
import { getRoomSharedKeyLocalStorageKey } from "../e2ee/sharedKeyManagement";
export type GroupCallLoaded = {
kind: "loaded";
@@ -58,6 +62,8 @@ export const useLoadGroupCall = (
const { t } = useTranslation();
const [state, setState] = useState<GroupCallStatus>({ kind: "loading" });
const [e2eeEnabled] = useEnableE2EE();
useEffect(() => {
const fetchOrCreateRoom = async (): Promise<Room> => {
try {
@@ -95,8 +101,17 @@ export const useLoadGroupCall = (
const [, roomId] = await createRoom(
client,
roomNameFromRoomId(roomIdOrAlias),
createPtt
createPtt,
e2eeEnabled ?? false
);
if (e2eeEnabled) {
setLocalStorageItem(
getRoomSharedKeyLocalStorageKey(roomId),
randomString(32)
);
}
// likewise, wait for the room
await client.waitUntilRoomReadyForGroupCalls(roomId);
return client.getRoom(roomId)!;
@@ -136,7 +151,7 @@ export const useLoadGroupCall = (
.then(fetchOrCreateGroupCall)
.then((rtcSession) => setState({ kind: "loaded", rtcSession }))
.catch((error) => setState({ kind: "failed", error }));
}, [client, roomIdOrAlias, viaServers, createPtt, t]);
}, [client, roomIdOrAlias, viaServers, createPtt, t, e2eeEnabled]);
return state;
};