2022-05-04 17:09:48 +01:00
|
|
|
/*
|
2023-01-03 16:55:26 +00:00
|
|
|
Copyright 2022 New Vector Ltd
|
2022-05-04 17:09:48 +01:00
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2023-09-22 18:05:13 -04:00
|
|
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
2022-01-05 15:35:12 -08:00
|
|
|
import { useHistory } from "react-router-dom";
|
2022-08-12 16:46:53 -04:00
|
|
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
2024-04-23 15:15:13 +02:00
|
|
|
import {
|
|
|
|
|
Room,
|
|
|
|
|
isE2EESupported as isE2EESupportedBrowser,
|
|
|
|
|
} from "livekit-client";
|
2023-06-23 14:17:51 -04:00
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2023-08-16 18:41:27 +01:00
|
|
|
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
2023-09-27 18:12:04 -04:00
|
|
|
import { JoinRule } from "matrix-js-sdk/src/matrix";
|
2023-09-20 13:05:11 +01:00
|
|
|
import { Heading, Link, Text } from "@vector-im/compound-web";
|
|
|
|
|
import { useTranslation } from "react-i18next";
|
2022-08-02 00:46:16 +02:00
|
|
|
|
2022-09-09 02:10:45 -04:00
|
|
|
import type { IWidgetApiRequest } from "matrix-widget-api";
|
2023-06-23 14:17:51 -04:00
|
|
|
import { widget, ElementWidgetActions, JoinCallData } from "../widget";
|
2024-04-23 15:15:13 +02:00
|
|
|
import { FullScreenView } from "../FullScreenView";
|
2022-01-05 15:35:12 -08:00
|
|
|
import { LobbyView } from "./LobbyView";
|
2023-05-26 20:41:32 +02:00
|
|
|
import { MatrixInfo } from "./VideoPreview";
|
2022-01-05 15:35:12 -08:00
|
|
|
import { CallEndedView } from "./CallEndedView";
|
2023-03-01 13:47:36 +01:00
|
|
|
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
2023-05-26 20:41:32 +02:00
|
|
|
import { useProfile } from "../profile/useProfile";
|
2023-06-23 14:17:51 -04:00
|
|
|
import { findDeviceByName } from "../media-utils";
|
2023-06-30 18:12:58 +01:00
|
|
|
import { ActiveCall } from "./InCallView";
|
2024-04-23 15:15:13 +02:00
|
|
|
import { MUTE_PARTICIPANT_COUNT, MuteStates } from "./MuteStates";
|
2023-08-02 15:29:37 -04:00
|
|
|
import { useMediaDevices, MediaDevices } from "../livekit/MediaDevicesContext";
|
2023-08-16 18:41:27 +01:00
|
|
|
import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships";
|
|
|
|
|
import { enterRTCSession, leaveRTCSession } from "../rtcSessionHelpers";
|
|
|
|
|
import { useMatrixRTCSessionJoinState } from "../useMatrixRTCSessionJoinState";
|
2024-04-23 15:15:13 +02:00
|
|
|
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
|
2023-09-08 15:39:10 -04:00
|
|
|
import { useRoomAvatar } from "./useRoomAvatar";
|
|
|
|
|
import { useRoomName } from "./useRoomName";
|
|
|
|
|
import { useJoinRule } from "./useJoinRule";
|
2023-09-27 15:17:04 -04:00
|
|
|
import { InviteModal } from "./InviteModal";
|
2023-10-16 17:45:06 +01:00
|
|
|
import { useUrlParams } from "../UrlParams";
|
2023-10-30 17:06:59 +00:00
|
|
|
import { E2eeType } from "../e2ee/e2eeType";
|
2022-09-09 02:10:45 -04:00
|
|
|
|
2022-08-02 00:46:16 +02:00
|
|
|
declare global {
|
|
|
|
|
interface Window {
|
2023-08-16 18:41:27 +01:00
|
|
|
rtcSession?: MatrixRTCSession;
|
2022-08-02 00:46:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-09 02:04:53 -04:00
|
|
|
|
2022-08-02 00:46:16 +02:00
|
|
|
interface Props {
|
|
|
|
|
client: MatrixClient;
|
|
|
|
|
isPasswordlessUser: boolean;
|
2023-09-18 20:47:47 -04:00
|
|
|
confineToRoom: boolean;
|
2022-09-09 02:10:45 -04:00
|
|
|
preload: boolean;
|
2023-10-25 13:49:18 +02:00
|
|
|
skipLobby: boolean;
|
2022-09-09 02:04:53 -04:00
|
|
|
hideHeader: boolean;
|
2023-08-16 18:41:27 +01:00
|
|
|
rtcSession: MatrixRTCSession;
|
2024-04-23 15:15:13 +02:00
|
|
|
muteStates: MuteStates;
|
2022-08-02 00:46:16 +02:00
|
|
|
}
|
2022-09-09 02:04:53 -04:00
|
|
|
|
2023-09-22 18:05:13 -04:00
|
|
|
export const GroupCallView: FC<Props> = ({
|
2022-01-05 15:35:12 -08:00
|
|
|
client,
|
|
|
|
|
isPasswordlessUser,
|
2023-09-18 20:47:47 -04:00
|
|
|
confineToRoom,
|
2022-09-09 02:10:45 -04:00
|
|
|
preload,
|
2023-10-25 13:49:18 +02:00
|
|
|
skipLobby,
|
2022-09-09 02:04:53 -04:00
|
|
|
hideHeader,
|
2023-08-16 18:41:27 +01:00
|
|
|
rtcSession,
|
2024-04-23 15:15:13 +02:00
|
|
|
muteStates,
|
2023-09-22 18:05:13 -04:00
|
|
|
}) => {
|
2023-08-16 18:41:27 +01:00
|
|
|
const memberships = useMatrixRTCSessionMemberships(rtcSession);
|
|
|
|
|
const isJoined = useMatrixRTCSessionJoinState(rtcSession);
|
2022-01-05 15:35:12 -08:00
|
|
|
|
2024-04-23 15:15:13 +02:00
|
|
|
// The mute state reactively gets updated once the participant count reaches the threshold.
|
|
|
|
|
// The user then still is able to unmute again.
|
|
|
|
|
// The more common case is that the user is muted from the start (participant count is already over the threshold).
|
|
|
|
|
const autoMuteHappened = useRef(false);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (autoMuteHappened.current) return;
|
|
|
|
|
if (memberships.length >= MUTE_PARTICIPANT_COUNT) {
|
|
|
|
|
muteStates.audio.setEnabled?.(false);
|
|
|
|
|
autoMuteHappened.current = true;
|
|
|
|
|
}
|
|
|
|
|
}, [autoMuteHappened, memberships, muteStates.audio]);
|
|
|
|
|
|
2022-01-05 15:35:12 -08:00
|
|
|
useEffect(() => {
|
2023-08-16 18:41:27 +01:00
|
|
|
window.rtcSession = rtcSession;
|
2024-06-04 11:20:25 -04:00
|
|
|
return (): void => {
|
2023-08-16 18:41:27 +01:00
|
|
|
delete window.rtcSession;
|
2022-09-09 02:10:45 -04:00
|
|
|
};
|
2023-08-16 18:41:27 +01:00
|
|
|
}, [rtcSession]);
|
2022-07-08 20:55:18 +01:00
|
|
|
|
2023-05-26 20:41:32 +02:00
|
|
|
const { displayName, avatarUrl } = useProfile(client);
|
2023-09-12 11:30:46 +01:00
|
|
|
const roomName = useRoomName(rtcSession.room);
|
|
|
|
|
const roomAvatar = useRoomAvatar(rtcSession.room);
|
2024-01-26 10:03:08 +01:00
|
|
|
const { perParticipantE2EE, returnToLobby } = useUrlParams();
|
2024-04-23 15:15:13 +02:00
|
|
|
const e2eeSystem = useRoomEncryptionSystem(rtcSession.room.roomId);
|
2023-09-08 15:39:10 -04:00
|
|
|
|
2023-07-04 18:46:27 +01:00
|
|
|
const matrixInfo = useMemo((): MatrixInfo => {
|
|
|
|
|
return {
|
2023-08-31 15:46:09 +02:00
|
|
|
userId: client.getUserId()!,
|
2023-07-04 18:46:27 +01:00
|
|
|
displayName: displayName!,
|
|
|
|
|
avatarUrl: avatarUrl!,
|
2023-08-16 18:41:27 +01:00
|
|
|
roomId: rtcSession.room.roomId,
|
2023-09-08 15:39:10 -04:00
|
|
|
roomName,
|
2023-08-16 18:41:27 +01:00
|
|
|
roomAlias: rtcSession.room.getCanonicalAlias(),
|
2023-09-08 15:39:10 -04:00
|
|
|
roomAvatar,
|
2024-04-23 15:15:13 +02:00
|
|
|
e2eeSystem,
|
2023-07-04 18:46:27 +01:00
|
|
|
};
|
2023-09-08 15:39:10 -04:00
|
|
|
}, [
|
2024-04-23 15:15:13 +02:00
|
|
|
client,
|
2023-09-08 15:39:10 -04:00
|
|
|
displayName,
|
|
|
|
|
avatarUrl,
|
2024-04-23 15:15:13 +02:00
|
|
|
rtcSession.room,
|
2023-09-08 15:39:10 -04:00
|
|
|
roomName,
|
|
|
|
|
roomAvatar,
|
2024-04-23 15:15:13 +02:00
|
|
|
e2eeSystem,
|
2023-09-08 15:39:10 -04:00
|
|
|
]);
|
|
|
|
|
|
2023-09-27 18:12:04 -04:00
|
|
|
// Count each member only once, regardless of how many devices they use
|
|
|
|
|
const participantCount = useMemo(
|
2023-10-09 20:49:03 +01:00
|
|
|
() => new Set<string>(memberships.map((m) => m.sender!)).size,
|
2023-10-11 10:42:04 -04:00
|
|
|
[memberships],
|
2023-09-27 18:12:04 -04:00
|
|
|
);
|
2023-05-26 20:41:32 +02:00
|
|
|
|
2023-08-02 15:29:37 -04:00
|
|
|
const deviceContext = useMediaDevices();
|
|
|
|
|
const latestDevices = useRef<MediaDevices>();
|
|
|
|
|
latestDevices.current = deviceContext;
|
|
|
|
|
|
|
|
|
|
const latestMuteStates = useRef<MuteStates>();
|
|
|
|
|
latestMuteStates.current = muteStates;
|
|
|
|
|
|
2022-09-09 02:10:45 -04:00
|
|
|
useEffect(() => {
|
2023-10-25 13:49:18 +02:00
|
|
|
const defaultDeviceSetup = async (
|
|
|
|
|
requestedDeviceData: JoinCallData,
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
// XXX: I think this is broken currently - LiveKit *won't* request
|
|
|
|
|
// permissions and give you device names unless you specify a kind, but
|
|
|
|
|
// here we want all kinds of devices. This needs a fix in livekit-client
|
|
|
|
|
// for the following name-matching logic to do anything useful.
|
|
|
|
|
const devices = await Room.getLocalDevices(undefined, true);
|
|
|
|
|
const { audioInput, videoInput } = requestedDeviceData;
|
|
|
|
|
if (audioInput === null) {
|
|
|
|
|
latestMuteStates.current!.audio.setEnabled?.(false);
|
|
|
|
|
} else {
|
|
|
|
|
const deviceId = await findDeviceByName(
|
|
|
|
|
audioInput,
|
|
|
|
|
"audioinput",
|
|
|
|
|
devices,
|
|
|
|
|
);
|
|
|
|
|
if (!deviceId) {
|
|
|
|
|
logger.warn("Unknown audio input: " + audioInput);
|
2023-08-02 15:29:37 -04:00
|
|
|
latestMuteStates.current!.audio.setEnabled?.(false);
|
|
|
|
|
} else {
|
2023-10-25 13:49:18 +02:00
|
|
|
logger.debug(
|
|
|
|
|
`Found audio input ID ${deviceId} for name ${audioInput}`,
|
2023-06-23 14:17:51 -04:00
|
|
|
);
|
2023-10-25 13:49:18 +02:00
|
|
|
latestDevices.current!.audioInput.select(deviceId);
|
|
|
|
|
latestMuteStates.current!.audio.setEnabled?.(true);
|
2023-06-23 14:17:51 -04:00
|
|
|
}
|
2023-10-25 13:49:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (videoInput === null) {
|
|
|
|
|
latestMuteStates.current!.video.setEnabled?.(false);
|
|
|
|
|
} else {
|
|
|
|
|
const deviceId = await findDeviceByName(
|
|
|
|
|
videoInput,
|
|
|
|
|
"videoinput",
|
|
|
|
|
devices,
|
|
|
|
|
);
|
|
|
|
|
if (!deviceId) {
|
|
|
|
|
logger.warn("Unknown video input: " + videoInput);
|
2023-08-09 17:34:53 -04:00
|
|
|
latestMuteStates.current!.video.setEnabled?.(false);
|
2023-08-02 15:29:37 -04:00
|
|
|
} else {
|
2023-10-25 13:49:18 +02:00
|
|
|
logger.debug(
|
|
|
|
|
`Found video input ID ${deviceId} for name ${videoInput}`,
|
2023-06-23 14:17:51 -04:00
|
|
|
);
|
2023-10-25 13:49:18 +02:00
|
|
|
latestDevices.current!.videoInput.select(deviceId);
|
|
|
|
|
latestMuteStates.current!.video.setEnabled?.(true);
|
2023-06-23 14:17:51 -04:00
|
|
|
}
|
2023-10-25 13:49:18 +02:00
|
|
|
}
|
|
|
|
|
};
|
2024-01-26 10:03:08 +01:00
|
|
|
|
|
|
|
|
if (widget && preload && skipLobby) {
|
|
|
|
|
// In preload mode without lobby we wait for a join action before entering
|
2023-10-25 13:49:18 +02:00
|
|
|
const onJoin = async (
|
|
|
|
|
ev: CustomEvent<IWidgetApiRequest>,
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
defaultDeviceSetup(ev.detail.data as unknown as JoinCallData);
|
2023-10-16 17:45:06 +01:00
|
|
|
enterRTCSession(rtcSession, perParticipantE2EE);
|
2024-01-26 10:03:08 +01:00
|
|
|
await widget!.api.transport.reply(ev.detail, {});
|
2022-09-09 02:10:45 -04:00
|
|
|
};
|
|
|
|
|
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
|
2024-06-04 11:20:25 -04:00
|
|
|
return (): void => {
|
2023-07-04 11:26:54 +01:00
|
|
|
widget!.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
|
2022-09-09 02:10:45 -04:00
|
|
|
};
|
2024-01-26 10:03:08 +01:00
|
|
|
} else if (widget && !preload && skipLobby) {
|
|
|
|
|
// No lobby and no preload: we enter the rtc session right away
|
2023-10-25 13:49:18 +02:00
|
|
|
defaultDeviceSetup({ audioInput: null, videoInput: null });
|
2023-10-26 10:29:12 +01:00
|
|
|
enterRTCSession(rtcSession, perParticipantE2EE);
|
2022-09-09 02:10:45 -04:00
|
|
|
}
|
2023-10-26 10:29:12 +01:00
|
|
|
}, [rtcSession, preload, skipLobby, perParticipantE2EE]);
|
2022-09-09 02:10:45 -04:00
|
|
|
|
2022-01-05 15:35:12 -08:00
|
|
|
const [left, setLeft] = useState(false);
|
2023-07-20 17:55:50 +01:00
|
|
|
const [leaveError, setLeaveError] = useState<Error | undefined>(undefined);
|
2022-01-05 15:35:12 -08:00
|
|
|
const history = useHistory();
|
|
|
|
|
|
2023-07-20 17:55:50 +01:00
|
|
|
const onLeave = useCallback(
|
|
|
|
|
async (leaveError?: Error) => {
|
|
|
|
|
setLeaveError(leaveError);
|
|
|
|
|
setLeft(true);
|
2022-11-04 13:07:14 +01:00
|
|
|
|
2023-07-20 17:55:50 +01:00
|
|
|
// In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent,
|
|
|
|
|
// therefore we want the event to be sent instantly without getting queued/batched.
|
|
|
|
|
const sendInstantly = !!widget;
|
|
|
|
|
PosthogAnalytics.instance.eventCallEnded.track(
|
2023-08-16 18:41:27 +01:00
|
|
|
rtcSession.room.roomId,
|
|
|
|
|
rtcSession.memberships.length,
|
2023-10-11 10:42:04 -04:00
|
|
|
sendInstantly,
|
2023-07-20 17:55:50 +01:00
|
|
|
);
|
2022-11-04 13:07:14 +01:00
|
|
|
|
2023-11-28 19:07:08 +01:00
|
|
|
// Only sends matrix leave event. The Livekit session will disconnect once the ActiveCall-view unmounts.
|
|
|
|
|
await leaveRTCSession(rtcSession);
|
2022-01-05 15:35:12 -08:00
|
|
|
|
2023-07-20 17:55:50 +01:00
|
|
|
if (
|
|
|
|
|
!isPasswordlessUser &&
|
2023-09-18 20:47:47 -04:00
|
|
|
!confineToRoom &&
|
2023-07-20 17:55:50 +01:00
|
|
|
!PosthogAnalytics.instance.isEnabled()
|
|
|
|
|
) {
|
|
|
|
|
history.push("/");
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-11-28 19:07:08 +01:00
|
|
|
[rtcSession, isPasswordlessUser, confineToRoom, history],
|
2023-07-20 17:55:50 +01:00
|
|
|
);
|
2022-09-09 02:10:45 -04:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-08-16 18:41:27 +01:00
|
|
|
if (widget && isJoined) {
|
2024-01-26 10:03:08 +01:00
|
|
|
// set widget to sticky once joined.
|
|
|
|
|
widget!.api.setAlwaysOnScreen(true);
|
|
|
|
|
|
2023-09-22 18:05:13 -04:00
|
|
|
const onHangup = async (
|
2023-10-11 10:42:04 -04:00
|
|
|
ev: CustomEvent<IWidgetApiRequest>,
|
2023-09-22 18:05:13 -04:00
|
|
|
): Promise<void> => {
|
|
|
|
|
widget!.api.transport.reply(ev.detail, {});
|
2023-11-28 19:07:08 +01:00
|
|
|
// Only sends matrix leave event. The Livekit session will disconnect once the ActiveCall-view unmounts.
|
|
|
|
|
await leaveRTCSession(rtcSession);
|
2022-09-09 02:10:45 -04:00
|
|
|
};
|
|
|
|
|
widget.lazyActions.once(ElementWidgetActions.HangupCall, onHangup);
|
2024-06-04 11:20:25 -04:00
|
|
|
return (): void => {
|
2023-07-04 11:26:54 +01:00
|
|
|
widget!.lazyActions.off(ElementWidgetActions.HangupCall, onHangup);
|
2022-09-09 02:10:45 -04:00
|
|
|
};
|
|
|
|
|
}
|
2023-11-28 19:07:08 +01:00
|
|
|
}, [isJoined, rtcSession]);
|
2023-06-16 18:07:13 +02:00
|
|
|
|
2023-07-20 17:55:50 +01:00
|
|
|
const onReconnect = useCallback(() => {
|
|
|
|
|
setLeft(false);
|
|
|
|
|
setLeaveError(undefined);
|
2023-10-16 17:45:06 +01:00
|
|
|
enterRTCSession(rtcSession, perParticipantE2EE);
|
|
|
|
|
}, [rtcSession, perParticipantE2EE]);
|
2023-08-16 18:41:27 +01:00
|
|
|
|
2023-09-12 11:30:46 +01:00
|
|
|
const joinRule = useJoinRule(rtcSession.room);
|
2023-09-08 15:39:10 -04:00
|
|
|
|
2023-09-27 15:17:04 -04:00
|
|
|
const [shareModalOpen, setInviteModalOpen] = useState(false);
|
|
|
|
|
const onDismissInviteModal = useCallback(
|
|
|
|
|
() => setInviteModalOpen(false),
|
2023-10-11 10:42:04 -04:00
|
|
|
[setInviteModalOpen],
|
2023-09-17 14:35:35 -04:00
|
|
|
);
|
2023-09-08 15:39:10 -04:00
|
|
|
|
|
|
|
|
const onShareClickFn = useCallback(
|
2023-09-27 15:17:04 -04:00
|
|
|
() => setInviteModalOpen(true),
|
2023-10-11 10:42:04 -04:00
|
|
|
[setInviteModalOpen],
|
2023-09-08 15:39:10 -04:00
|
|
|
);
|
|
|
|
|
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
|
|
|
|
|
|
2023-09-20 13:05:11 +01:00
|
|
|
const onHomeClick = useCallback(
|
|
|
|
|
(ev: React.MouseEvent) => {
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
history.push("/");
|
|
|
|
|
},
|
2023-10-11 10:42:04 -04:00
|
|
|
[history],
|
2023-09-20 13:05:11 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
|
2024-04-23 15:15:13 +02:00
|
|
|
if (!isE2EESupportedBrowser() && e2eeSystem.kind !== E2eeType.NONE) {
|
|
|
|
|
// If we have a encryption system but the browser does not support it.
|
2023-09-20 13:05:11 +01:00
|
|
|
return (
|
|
|
|
|
<FullScreenView>
|
2023-11-22 20:07:15 +00:00
|
|
|
<Heading>{t("browser_media_e2ee_unsupported_heading")}</Heading>
|
```
move "{{count, number}}_one" "participant_count_one"
move "{{count, number}}_other" "participant_count_other"
move "{{count}} stars_one" "star_rating_input_label_one"
move "{{count}} stars_other" "star_rating_input_label_other"
move "{{displayName}} is presenting" "video_tile.presenter_label"
move "{{displayName}}, your call has ended." "call_ended_view.headline"
move "<0></0><1></1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call." "settings.opt_in_description"
move "<0>Already have an account?</0><1><0>Log in</0> Or <2>Access as a guest</2></1>" "register_auth_links"
move "<0>Create an account</0> Or <2>Access as a guest</2>" "login_auth_links"
move "<0>Oops, something's gone wrong.</0>" "full_screen_view_h1"
move "<0>Submitting debug logs will help us track down the problem.</0>" "full_screen_view_description"
move "<0>Thanks for your feedback!</0>" "call_ended_view.feedback_done"
move "<0>We'd love to hear your feedback so we can improve your experience.</0>" "call_ended_view.feedback_prompt"
move "<0>Why not finish by setting up a password to keep your account?</0><1>You'll be able to keep your name and set an avatar for use on future calls</1>" "call_ended_view.create_account_prompt"
move "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log." "rageshake_request_modal.body"
move "Back to recents" "lobby.leave_button"
move "By participating in this beta, you consent to the collection of anonymous data, which we use to improve the product. You can find more information about which data we track in our <2>Privacy Policy</2> and our <5>Cookie Policy</5>." "analytics_notice"
move "Call not found" "group_call_loader_failed_heading"
move "Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key." "group_call_loader_failed_text"
move "Confirm password" "register_confirm_password_label"
move "Connectivity to the server has been lost." "disconnected_banner"
move "Continue in browser" "app_selection_modal.continue_in_browser"
move "Create account" "call_ended_view.create_account_button"
move "Debug log request" "rageshake_request_modal.title"
move "Developer" "settings.developer_tab_title"
move "Developer Settings" "settings.developer_settings_label"
move "Element Call Home" "header_label"
move "End call" "hangup_button_label"
move "Full screen" "fullscreen_button_label"
move "Exit full screen" "exit_fullscreen_button_label"
move "Expose developer settings in the settings window." "settings.developer_settings_label_description"
move "Feedback" "settings.feedback_tab_title"
move "Grid" "layout_grid_label"
move "Spotlight" "layout_spotlight_label"
move "How did it go?" "call_ended_view.survey_prompt"
move "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below." "settings.feedback_tab_body"
move "Include debug logs" "settings.feedback_tab_send_logs_label"
move "Invite to this call" "invite_modal.title"
move "Join call" "lobby.join_button"
move "Join call now" "room_auth_view_join_button"
move "Join existing call?" "join_existing_call_modal.title"
move "Link copied to clipboard" "invite_modal.link_copied_toast"
move "Local volume" "local_volume_label"
move "Logging in…" "logging_in"
move "Login" "login_title"
move "Login to your account" "unauthenticated_view_login_button"
move "Microphone off" "microphone_off"
move "Microphone on" "microphone_on"
move "More" "settings.more_tab_title"
move "Mute microphone" "mute_microphone_button_label"
move "Name of call" "call_name"
move "Not now, return to home screen" "call_ended_view.not_now_button"
move "Open in the app" "app_selection_modal.open_in_app"
move "Not registered yet? <2>Create an account</2>" "unauthenticated_view_body"
move "Participants" "header_participants_label"
move "Passwords must match" "register.passwords_must_match"
move "Ready to join?" "app_selection_modal.text"
move "Recaptcha dismissed" "recaptcha_dismissed"
move "Recaptcha not loaded" "recaptcha_not_loaded"
move "Reconnect" "call_ended_view.reconnect_button"
move "Registering…" "register.registering"
move "Retry sending logs" "rageshake_button_error_caption"
move "Return to home screen" "return_home_button"
move "Select an option" "select_input_unset_button"
move "Select app" "app_selection_modal.title"
move "Send debug logs" "rageshake_send_logs"
move "Sending debug logs…" "rageshake_sending_logs"
move "Sending…" "rageshake_sending"
move "Share screen" "screenshare_button_label"
move "Sharing screen" "stop_screenshare_button_label"
move "Show connection stats" "settings.show_connection_stats_label"
move "Speaker" "settings.speaker_device_selection_label"
move "Start new call" "start_new_call"
move "Start video" "start_video_button_label"
move "Stop video" "stop_video_button_label"
move "Submit feedback" "settings.feedback_tab_h4"
move "Submitting…" "submitting"
move "Thanks, we received your feedback!" "settings.feedback_tab_thank_you"
move "Thanks!" "rageshake_sent"
move "This application has been opened in another tab." "application_opened_another_tab"
move "This call already exists, would you like to join?" "join_existing_call_modal.text"
move "Unmute microphone" "unmute_microphone_button_label"
move "Version: {{version}}" "version"
move "Waiting for other participants…" "waiting_for_participants"
move "Yes, join call" "join_existing_call_modal.join_button"
move "You" "video_tile.sfu_participant_local"
move "You were disconnected from the call" "call_ended_view.body"
move "Your feedback" "settings.feedback_tab_description_label"
move "Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117" "browser_media_e2ee_unsupported"
move "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)</2>" "unauthenticated_view_eula_caption"
move "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)</2>" "room_auth_view_eula_caption"
move "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>" "register.recaptcha_caption"
```
2023-11-20 13:00:43 +00:00
|
|
|
<Text>{t("browser_media_e2ee_unsupported")}</Text>
|
2023-09-20 13:05:11 +01:00
|
|
|
<Link href="/" onClick={onHomeClick}>
|
2023-11-20 11:05:18 +00:00
|
|
|
{t("common.home")}
|
2023-09-20 13:05:11 +01:00
|
|
|
</Link>
|
|
|
|
|
</FullScreenView>
|
|
|
|
|
);
|
2023-08-11 16:59:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-17 14:35:35 -04:00
|
|
|
const shareModal = (
|
2023-09-27 15:17:04 -04:00
|
|
|
<InviteModal
|
2023-09-19 18:23:44 +01:00
|
|
|
room={rtcSession.room}
|
2023-09-17 14:35:35 -04:00
|
|
|
open={shareModalOpen}
|
2023-09-27 15:17:04 -04:00
|
|
|
onDismiss={onDismissInviteModal}
|
2023-09-17 14:35:35 -04:00
|
|
|
/>
|
2023-09-08 15:39:10 -04:00
|
|
|
);
|
2024-01-26 10:03:08 +01:00
|
|
|
const lobbyView = (
|
|
|
|
|
<>
|
|
|
|
|
{shareModal}
|
|
|
|
|
<LobbyView
|
|
|
|
|
client={client}
|
|
|
|
|
matrixInfo={matrixInfo}
|
|
|
|
|
muteStates={muteStates}
|
|
|
|
|
onEnter={(): void => enterRTCSession(rtcSession, perParticipantE2EE)}
|
|
|
|
|
confineToRoom={confineToRoom}
|
|
|
|
|
hideHeader={hideHeader}
|
|
|
|
|
participantCount={participantCount}
|
|
|
|
|
onShareClick={onShareClick}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2023-09-08 15:39:10 -04:00
|
|
|
|
2023-11-28 19:07:08 +01:00
|
|
|
if (isJoined) {
|
2023-05-26 20:41:32 +02:00
|
|
|
return (
|
2023-09-12 11:30:46 +01:00
|
|
|
<>
|
2023-09-08 15:39:10 -04:00
|
|
|
{shareModal}
|
2023-06-30 18:12:58 +01:00
|
|
|
<ActiveCall
|
|
|
|
|
client={client}
|
2023-09-08 15:39:10 -04:00
|
|
|
matrixInfo={matrixInfo}
|
2023-09-12 11:30:46 +01:00
|
|
|
rtcSession={rtcSession}
|
2023-09-27 18:12:04 -04:00
|
|
|
participantCount={participantCount}
|
2023-06-30 18:12:58 +01:00
|
|
|
onLeave={onLeave}
|
|
|
|
|
hideHeader={hideHeader}
|
2023-08-02 15:29:37 -04:00
|
|
|
muteStates={muteStates}
|
2024-04-23 15:15:13 +02:00
|
|
|
e2eeSystem={e2eeSystem}
|
2023-09-12 11:30:46 +01:00
|
|
|
//otelGroupCallMembership={otelGroupCallMembership}
|
2023-09-08 15:39:10 -04:00
|
|
|
onShareClick={onShareClick}
|
2023-06-30 18:12:58 +01:00
|
|
|
/>
|
2023-09-12 11:30:46 +01:00
|
|
|
</>
|
2023-05-26 20:41:32 +02:00
|
|
|
);
|
2024-01-26 10:03:08 +01:00
|
|
|
} else if (left && widget === null) {
|
|
|
|
|
// Left in SPA mode:
|
|
|
|
|
|
2023-06-07 14:19:53 -04:00
|
|
|
// The call ended view is shown for two reasons: prompting guests to create
|
|
|
|
|
// an account, and prompting users that have opted into analytics to provide
|
|
|
|
|
// feedback. We don't show a feedback prompt to widget users however (at
|
|
|
|
|
// least for now), because we don't yet have designs that would allow widget
|
|
|
|
|
// users to dismiss the feedback prompt and close the call window without
|
|
|
|
|
// submitting anything.
|
|
|
|
|
if (
|
|
|
|
|
isPasswordlessUser ||
|
2023-09-18 20:47:47 -04:00
|
|
|
(PosthogAnalytics.instance.isEnabled() && widget === null) ||
|
2023-07-20 17:55:50 +01:00
|
|
|
leaveError
|
2023-06-07 14:19:53 -04:00
|
|
|
) {
|
2023-06-07 16:22:44 +02:00
|
|
|
return (
|
|
|
|
|
<CallEndedView
|
2023-08-16 18:41:27 +01:00
|
|
|
endedCallId={rtcSession.room.roomId}
|
2023-06-07 16:22:44 +02:00
|
|
|
client={client}
|
|
|
|
|
isPasswordlessUser={isPasswordlessUser}
|
2023-09-18 20:47:47 -04:00
|
|
|
confineToRoom={confineToRoom}
|
2023-07-20 17:55:50 +01:00
|
|
|
leaveError={leaveError}
|
|
|
|
|
reconnect={onReconnect}
|
2023-06-07 16:22:44 +02:00
|
|
|
/>
|
|
|
|
|
);
|
2022-09-23 15:35:05 +01:00
|
|
|
} else {
|
|
|
|
|
// If the user is a regular user, we'll have sent them back to the homepage,
|
|
|
|
|
// so just sit here & do nothing: otherwise we would (briefly) mount the
|
|
|
|
|
// LobbyView again which would open capture devices again.
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2024-01-26 10:03:08 +01:00
|
|
|
} else if (left && widget !== null) {
|
|
|
|
|
// Left in widget mode:
|
|
|
|
|
if (!returnToLobby) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
} else if (preload || skipLobby) {
|
2022-09-09 02:10:45 -04:00
|
|
|
return null;
|
2022-01-05 15:35:12 -08:00
|
|
|
}
|
2024-01-26 10:03:08 +01:00
|
|
|
|
|
|
|
|
return lobbyView;
|
2023-09-22 18:05:13 -04:00
|
|
|
};
|