From 550c45b69e6c5203d0f86ee8d2d9492424b089c0 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Jan 2022 15:06:51 -0800 Subject: [PATCH] Clean up room-related components --- src/App.jsx | 43 +-- src/Room.jsx | 447 ------------------------------ src/Room.module.css | 217 --------------- src/room/CallEndedView.jsx | 50 ++++ src/room/CallEndedView.module.css | 73 +++++ src/room/InCallView.jsx | 166 +++++++++++ src/room/InCallView.module.css | 68 +++++ src/room/RoomPage.jsx | 209 ++++++++++++++ src/room/RoomRedirect.jsx | 25 ++ 9 files changed, 598 insertions(+), 700 deletions(-) delete mode 100644 src/Room.jsx delete mode 100644 src/Room.module.css create mode 100644 src/room/CallEndedView.jsx create mode 100644 src/room/CallEndedView.module.css create mode 100644 src/room/InCallView.jsx create mode 100644 src/room/InCallView.module.css create mode 100644 src/room/RoomPage.jsx create mode 100644 src/room/RoomRedirect.jsx diff --git a/src/App.jsx b/src/App.jsx index d462330..dc5c6e6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -14,26 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useEffect, useState } from "react"; -import { - BrowserRouter as Router, - Switch, - Route, - useLocation, - useHistory, -} from "react-router-dom"; +import React from "react"; +import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import * as Sentry from "@sentry/react"; import { OverlayProvider } from "@react-aria/overlays"; import { HomePage } from "./home/HomePage"; import { LoginPage } from "./LoginPage"; import { RegisterPage } from "./RegisterPage"; -import { Room } from "./Room"; -import { - ClientProvider, - defaultHomeserverHost, -} from "./ConferenceCallManagerHooks"; -import { LoadingView } from "./FullScreenView"; +import { RoomPage } from "./room/RoomPage"; +import { RoomRedirect } from "./room/RoomRedirect"; +import { ClientProvider } from "./ConferenceCallManagerHooks"; import { usePageFocusStyle } from "./usePageFocusStyle"; + const SentryRoute = Sentry.withSentryRouting(Route); export default function App({ history }) { @@ -54,7 +46,7 @@ export default function App({ history }) { - + @@ -65,24 +57,3 @@ export default function App({ history }) { ); } - -function RoomRedirect() { - const { pathname } = useLocation(); - const history = useHistory(); - - useEffect(() => { - let roomId = pathname; - - if (pathname.startsWith("/")) { - roomId = roomId.substr(1, roomId.length); - } - - if (!roomId.startsWith("#") && !roomId.startsWith("!")) { - roomId = `#${roomId}:${defaultHomeserverHost}`; - } - - history.replace(`/room/${roomId}`); - }, [pathname, history]); - - return ; -} diff --git a/src/Room.jsx b/src/Room.jsx deleted file mode 100644 index ac35f02..0000000 --- a/src/Room.jsx +++ /dev/null @@ -1,447 +0,0 @@ -/* -Copyright 2021 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. -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. -*/ - -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import styles from "./Room.module.css"; -import { useLocation, useParams, useHistory, Link } from "react-router-dom"; -import { - Button, - CopyButton, - HangupButton, - MicButton, - VideoButton, - ScreenshareButton, - LinkButton, -} from "./button"; -import { Header, LeftNav, RightNav, RoomHeaderInfo } from "./Header"; -import { GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall"; -import VideoGrid, { - useVideoGridLayout, -} from "matrix-react-sdk/src/components/views/voip/GroupCallView/VideoGrid"; -import SimpleVideoGrid from "matrix-react-sdk/src/components/views/voip/GroupCallView/SimpleVideoGrid"; -import "matrix-react-sdk/res/css/views/voip/GroupCallView/_VideoGrid.scss"; -import { useGroupCall } from "matrix-react-sdk/src/hooks/useGroupCall"; -import { useCallFeed } from "matrix-react-sdk/src/hooks/useCallFeed"; -import { useMediaStream } from "matrix-react-sdk/src/hooks/useMediaStream"; -import { - getAvatarUrl, - getRoomUrl, - useClient, - useLoadGroupCall, - useProfile, -} from "./ConferenceCallManagerHooks"; -import { ErrorView, LoadingView, FullScreenView } from "./FullScreenView"; -import { GroupCallInspector } from "./GroupCallInspector"; -import * as Sentry from "@sentry/react"; -import { OverflowMenu } from "./OverflowMenu"; -import { GridLayoutMenu } from "./GridLayoutMenu"; -import { UserMenu } from "./UserMenu"; -import classNames from "classnames"; -import { Avatar } from "./Avatar"; -import { UserMenuContainer } from "./UserMenuContainer"; -import { LobbyView } from "./room/LobbyView"; - -const canScreenshare = "getDisplayMedia" in navigator.mediaDevices; -// There is currently a bug in Safari our our code with cloning and sending MediaStreams -// or with getUsermedia and getDisplaymedia being used within the same session. -// For now we can disable screensharing in Safari. -const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - -export function Room() { - const [registrationError, setRegistrationError] = useState(); - const { loading, isAuthenticated, error, client, isPasswordlessUser } = - useClient(); - - useEffect(() => { - if (!loading && !isAuthenticated) { - setRegistrationError(new Error("Must be registered")); - } - }, [loading, isAuthenticated]); - - if (loading) { - return ; - } - - if (registrationError || error) { - return ; - } - - return ; -} - -export function GroupCall({ client, isPasswordlessUser }) { - const { roomId: maybeRoomId } = useParams(); - const { hash, search } = useLocation(); - const [simpleGrid, viaServers] = useMemo(() => { - const params = new URLSearchParams(search); - return [params.has("simple"), params.getAll("via")]; - }, [search]); - const roomId = maybeRoomId || hash; - const { loading, error, groupCall } = useLoadGroupCall( - client, - roomId, - viaServers - ); - - useEffect(() => { - window.groupCall = groupCall; - }, [groupCall]); - - if (loading) { - return ; - } - - if (error) { - return ; - } - - return ( - - ); -} - -export function GroupCallView({ - client, - isPasswordlessUser, - roomId, - groupCall, - simpleGrid, -}) { - const [showInspector, setShowInspector] = useState(false); - const { - state, - error, - activeSpeaker, - userMediaFeeds, - microphoneMuted, - localVideoMuted, - localCallFeed, - initLocalCallFeed, - enter, - leave, - toggleLocalVideoMuted, - toggleMicrophoneMuted, - toggleScreensharing, - isScreensharing, - localScreenshareFeed, - screenshareFeeds, - hasLocalParticipant, - } = useGroupCall(groupCall); - - useEffect(() => { - function onHangup(call) { - if (call.hangupReason === "ice_failed") { - Sentry.captureException(new Error("Call hangup due to ICE failure.")); - } - } - - function onError(error) { - Sentry.captureException(error); - } - - if (groupCall) { - groupCall.on("hangup", onHangup); - groupCall.on("error", onError); - } - - return () => { - if (groupCall) { - groupCall.removeListener("hangup", onHangup); - groupCall.removeListener("error", onError); - } - }; - }, [groupCall]); - - const [left, setLeft] = useState(false); - const history = useHistory(); - - const onLeave = useCallback(() => { - leave(); - - if (!isPasswordlessUser) { - history.push("/"); - } else { - setLeft(true); - } - }, [leave, history]); - - if (error) { - return ; - } else if (state === GroupCallState.Entered) { - return ( - - ); - } else if (state === GroupCallState.Entering) { - return ; - } else if (left) { - if (isPasswordlessUser) { - return ; - } else { - return ; - } - } else { - return ( - - ); - } -} - -export function LoadingRoomView() { - return ( - -

Loading room...

-
- ); -} - -export function EnteringRoomView() { - return ( - -

Entering room...

-
- ); -} - -function InRoomView({ - client, - groupCall, - roomName, - microphoneMuted, - localVideoMuted, - toggleLocalVideoMuted, - toggleMicrophoneMuted, - userMediaFeeds, - activeSpeaker, - onLeave, - toggleScreensharing, - isScreensharing, - screenshareFeeds, - simpleGrid, - setShowInspector, - showInspector, - roomId, -}) { - const [layout, setLayout] = useVideoGridLayout(); - - const items = useMemo(() => { - const participants = []; - - for (const callFeed of userMediaFeeds) { - participants.push({ - id: callFeed.stream.id, - usermediaCallFeed: callFeed, - isActiveSpeaker: - screenshareFeeds.length === 0 - ? callFeed.userId === activeSpeaker - : false, - }); - } - - for (const callFeed of screenshareFeeds) { - const participant = participants.find( - (p) => p.usermediaCallFeed.userId === callFeed.userId - ); - - if (participant) { - participant.screenshareCallFeed = callFeed; - } - } - - return participants; - }, [userMediaFeeds, activeSpeaker, screenshareFeeds]); - - const onFocusTile = useCallback( - (tiles, focusedTile) => { - if (layout === "freedom") { - return tiles.map((tile) => { - if (tile === focusedTile) { - return { ...tile, presenter: !tile.presenter }; - } - - return tile; - }); - } else { - return tiles; - } - }, - [layout, setLayout] - ); - - const renderAvatar = useCallback( - (roomMember, width, height) => { - const avatarUrl = roomMember.user?.avatarUrl; - const size = Math.round(Math.min(width, height) / 2); - - return ( - - ); - }, - [client] - ); - - return ( -
-
- - - - - - - -
- {items.length === 0 ? ( -
-

Waiting for other participants...

-
- ) : simpleGrid ? ( - - ) : ( - - )} -
- - - {canScreenshare && !isSafari && ( - - )} - - -
- -
- ); -} - -export function GuestCallEndedScreen() { - return ( - -

Your call is now ended

-
-

Why not finish by creating an account?

-

You'll be able to:

-
    -
  • Easily access all your previous call links
  • -
  • Set a username and avatar
  • -
- - Create account - -
- Not now, return to home screen -
- ); -} - -export function PasswordlessUserCallEndedScreen({ client }) { - const { displayName } = useProfile(client); - - return ( - -

{displayName}, your call is now ended

-
-

Why not finish by setting up a password to keep your account?

-

- You'll be able to keep your name and set an avatar for use on future - calls -

- - Create account - -
- Not now, return to home screen -
- ); -} diff --git a/src/Room.module.css b/src/Room.module.css deleted file mode 100644 index d6ec2c0..0000000 --- a/src/Room.module.css +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright 2021 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. -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. -*/ - -.room { - position: relative; - display: flex; - flex-direction: column; - overflow: hidden; - min-height: 100%; -} - -.inRoom { - position: fixed; - height: 100%; - width: 100%; -} - -.joinRoom { - display: flex; - flex-direction: column; - align-items: center; - flex: 1; - overflow: hidden; - height: 100%; -} - -.joinRoomContent { - display: flex; - flex-direction: column; - align-items: center; - flex: 1; -} - -.joinRoomContent h1 { - display: none; - margin: 0; -} - -.joinRoomFooter { - margin: 20px 0; -} - -.homeLink { - margin-top: 50px; - color: #0dbd8b; - text-decoration: none; - font-weight: normal; - font-size: 15px; -} - -.preview { - position: relative; - min-height: 280px; - height: 50vh; - border-radius: 24px; - overflow: hidden; - background-color: var(--bgColor3); - margin: 40px 20px 20px 20px; -} - -.preview video { - width: 100%; - height: 100%; - object-fit: contain; - background-color: black; - transform: scaleX(-1); -} - -.webcamPermissions { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - margin: 0; - font-size: 13px; - font-weight: 600; - text-align: center; -} - -.previewButtons { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 66px; - display: flex; - justify-content: center; - align-items: center; - background-color: rgba(23, 25, 28, 0.9); -} - -.joinCallButton { - position: absolute; - width: 100%; - max-width: 222px; - height: 40px; - bottom: 86px; - left: 50%; - font-weight: 600; - font-size: 15px; - transform: translateX(-50%); -} - -.copyButton { - width: 320px !important; -} - -.previewButtons > * { - margin-right: 30px; -} - -.previewButtons > :last-child { - margin-right: 0px; -} - -.centerMessage { - display: flex; - flex: 1; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.centerMessage p { - display: block; - margin-bottom: 0; -} - -.roomContainer { - overflow: hidden; - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; - min-height: 0; -} - -.footer { - position: relative; - display: flex; - justify-content: center; - align-items: center; - height: 64px; -} - -.footer > * { - margin-right: 30px; -} - -.footer > :last-child { - margin-right: 0px; -} - -.callEndedScreen h1 { - text-align: center; - margin-bottom: 60px; -} - -.callEndedScreen h2 { - font-size: 24px; - font-weight: 600; - margin-bottom: 32px; -} - -.callEndedScreen p { - margin: 0 0 16px 0; -} - -.callEndedScreen ul { - padding: 0; - margin-bottom: 40px; - text-align: initial; - padding-left: 20px; -} - -.callEndedButton { - width: 100%; -} - -.callEndedContent { - text-align: center; - max-width: 360px; -} - -.avatar { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -@media (min-width: 800px) { - .roomContainer { - flex-direction: row; - } - - .footer { - height: 118px; - } - - .joinRoomContent h1 { - display: block; - } -} diff --git a/src/room/CallEndedView.jsx b/src/room/CallEndedView.jsx new file mode 100644 index 0000000..77d085d --- /dev/null +++ b/src/room/CallEndedView.jsx @@ -0,0 +1,50 @@ +import React from "react"; +import styles from "./CallEndedView.module.css"; +import { LinkButton } from "../button"; +import { useProfile } from "../ConferenceCallManagerHooks"; +import { Subtitle, Body, Link, Headline } from "../typography/Typography"; +import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; + +export function CallEndedView({ client }) { + const { displayName } = useProfile(client); + + return ( + <> +
+ + + + +
+
+
+ + {displayName}, your call is now ended + +
+ + Why not finish by setting up a password to keep your account? + + + You'll be able to keep your name and set an avatar for use on + future calls + + + Create account + +
+
+ + + Not now, return to home screen + + +
+ + ); +} diff --git a/src/room/CallEndedView.module.css b/src/room/CallEndedView.module.css new file mode 100644 index 0000000..f08f1d2 --- /dev/null +++ b/src/room/CallEndedView.module.css @@ -0,0 +1,73 @@ +/* +Copyright 2021 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. +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. +*/ + +.headline { + text-align: center; + margin-bottom: 60px; +} + +.callEndedContent { + text-align: center; + max-width: 360px; +} + +.callEndedContent h3 { + margin-bottom: 32px; +} + +.callEndedButton { + width: 100%; + margin-top: 54px; +} + +.container { + display: flex; + min-height: calc(100% - 64px); + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.main { + display: flex; + flex: 1; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.logo { + display: flex; + margin-bottom: 54px; +} + +.headline { + margin-bottom: 40px; +} + +.footer { + margin-bottom: 44px; +} + +@media (min-width: 800px) { + .logo { + display: none; + } + + .container { + min-height: calc(100% - 76px); + } +} diff --git a/src/room/InCallView.jsx b/src/room/InCallView.jsx new file mode 100644 index 0000000..8c22d98 --- /dev/null +++ b/src/room/InCallView.jsx @@ -0,0 +1,166 @@ +import React, { useCallback, useMemo } from "react"; +import styles from "./InCallView.module.css"; +import { + HangupButton, + MicButton, + VideoButton, + ScreenshareButton, +} from "../button"; +import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header"; +import VideoGrid, { + useVideoGridLayout, +} from "matrix-react-sdk/src/components/views/voip/GroupCallView/VideoGrid"; +import SimpleVideoGrid from "matrix-react-sdk/src/components/views/voip/GroupCallView/SimpleVideoGrid"; +import "matrix-react-sdk/res/css/views/voip/GroupCallView/_VideoGrid.scss"; +import { getAvatarUrl } from "../ConferenceCallManagerHooks"; +import { GroupCallInspector } from "../GroupCallInspector"; +import { OverflowMenu } from "../OverflowMenu"; +import { GridLayoutMenu } from "../GridLayoutMenu"; +import { Avatar } from "../Avatar"; +import { UserMenuContainer } from "../UserMenuContainer"; + +const canScreenshare = "getDisplayMedia" in navigator.mediaDevices; +// There is currently a bug in Safari our our code with cloning and sending MediaStreams +// or with getUsermedia and getDisplaymedia being used within the same session. +// For now we can disable screensharing in Safari. +const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + +export function InCallView({ + client, + groupCall, + roomName, + microphoneMuted, + localVideoMuted, + toggleLocalVideoMuted, + toggleMicrophoneMuted, + userMediaFeeds, + activeSpeaker, + onLeave, + toggleScreensharing, + isScreensharing, + screenshareFeeds, + simpleGrid, + setShowInspector, + showInspector, + roomId, +}) { + const [layout, setLayout] = useVideoGridLayout(); + + const items = useMemo(() => { + const participants = []; + + for (const callFeed of userMediaFeeds) { + participants.push({ + id: callFeed.stream.id, + usermediaCallFeed: callFeed, + isActiveSpeaker: + screenshareFeeds.length === 0 + ? callFeed.userId === activeSpeaker + : false, + }); + } + + for (const callFeed of screenshareFeeds) { + const participant = participants.find( + (p) => p.usermediaCallFeed.userId === callFeed.userId + ); + + if (participant) { + participant.screenshareCallFeed = callFeed; + } + } + + return participants; + }, [userMediaFeeds, activeSpeaker, screenshareFeeds]); + + const onFocusTile = useCallback( + (tiles, focusedTile) => { + if (layout === "freedom") { + return tiles.map((tile) => { + if (tile === focusedTile) { + return { ...tile, presenter: !tile.presenter }; + } + + return tile; + }); + } else { + return tiles; + } + }, + [layout, setLayout] + ); + + const renderAvatar = useCallback( + (roomMember, width, height) => { + const avatarUrl = roomMember.user?.avatarUrl; + const size = Math.round(Math.min(width, height) / 2); + + return ( + + ); + }, + [client] + ); + + return ( +
+
+ + + + + + + +
+ {items.length === 0 ? ( +
+

Waiting for other participants...

+
+ ) : simpleGrid ? ( + + ) : ( + + )} +
+ + + {canScreenshare && !isSafari && ( + + )} + + +
+ +
+ ); +} diff --git a/src/room/InCallView.module.css b/src/room/InCallView.module.css new file mode 100644 index 0000000..054e27d --- /dev/null +++ b/src/room/InCallView.module.css @@ -0,0 +1,68 @@ +/* +Copyright 2021 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. +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. +*/ + +.inRoom { + position: relative; + display: flex; + flex-direction: column; + overflow: hidden; + min-height: 100%; + position: fixed; + height: 100%; + width: 100%; +} + +.centerMessage { + display: flex; + flex: 1; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.centerMessage p { + display: block; + margin-bottom: 0; +} + +.footer { + position: relative; + display: flex; + justify-content: center; + align-items: center; + height: 64px; +} + +.footer > * { + margin-right: 30px; +} + +.footer > :last-child { + margin-right: 0px; +} + +.avatar { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +@media (min-width: 800px) { + .footer { + height: 118px; + } +} diff --git a/src/room/RoomPage.jsx b/src/room/RoomPage.jsx new file mode 100644 index 0000000..450ca54 --- /dev/null +++ b/src/room/RoomPage.jsx @@ -0,0 +1,209 @@ +/* +Copyright 2021 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. +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. +*/ + +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { useLocation, useParams, useHistory } from "react-router-dom"; +import { GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall"; +import { useGroupCall } from "matrix-react-sdk/src/hooks/useGroupCall"; +import { useClient, useLoadGroupCall } from "../ConferenceCallManagerHooks"; +import { ErrorView, LoadingView, FullScreenView } from "../FullScreenView"; +import * as Sentry from "@sentry/react"; +import { LobbyView } from "./LobbyView"; +import { InCallView } from "./InCallView"; +import { CallEndedView } from "./CallEndedView"; + +export function RoomPage() { + const [registrationError, setRegistrationError] = useState(); + const { loading, isAuthenticated, error, client, isPasswordlessUser } = + useClient(); + + useEffect(() => { + if (!loading && !isAuthenticated) { + setRegistrationError(new Error("Must be registered")); + } + }, [loading, isAuthenticated]); + + if (loading) { + return ; + } + + if (registrationError || error) { + return ; + } + + return ; +} + +export function GroupCall({ client, isPasswordlessUser }) { + const { roomId: maybeRoomId } = useParams(); + const { hash, search } = useLocation(); + const [simpleGrid, viaServers] = useMemo(() => { + const params = new URLSearchParams(search); + return [params.has("simple"), params.getAll("via")]; + }, [search]); + const roomId = maybeRoomId || hash; + const { loading, error, groupCall } = useLoadGroupCall( + client, + roomId, + viaServers + ); + + useEffect(() => { + window.groupCall = groupCall; + }, [groupCall]); + + if (loading) { + return ( + +

Loading room...

+
+ ); + } + + if (error) { + return ; + } + + return ( + + ); +} + +export function GroupCallView({ + client, + isPasswordlessUser, + roomId, + groupCall, + simpleGrid, +}) { + const [showInspector, setShowInspector] = useState(false); + const { + state, + error, + activeSpeaker, + userMediaFeeds, + microphoneMuted, + localVideoMuted, + localCallFeed, + initLocalCallFeed, + enter, + leave, + toggleLocalVideoMuted, + toggleMicrophoneMuted, + toggleScreensharing, + isScreensharing, + localScreenshareFeed, + screenshareFeeds, + hasLocalParticipant, + } = useGroupCall(groupCall); + + useEffect(() => { + function onHangup(call) { + if (call.hangupReason === "ice_failed") { + Sentry.captureException(new Error("Call hangup due to ICE failure.")); + } + } + + function onError(error) { + Sentry.captureException(error); + } + + if (groupCall) { + groupCall.on("hangup", onHangup); + groupCall.on("error", onError); + } + + return () => { + if (groupCall) { + groupCall.removeListener("hangup", onHangup); + groupCall.removeListener("error", onError); + } + }; + }, [groupCall]); + + const [left, setLeft] = useState(false); + const history = useHistory(); + + const onLeave = useCallback(() => { + leave(); + + if (!isPasswordlessUser) { + history.push("/"); + } else { + setLeft(true); + } + }, [leave, history]); + + if (error) { + return ; + } else if (state === GroupCallState.Entered) { + return ( + + ); + } else if (state === GroupCallState.Entering) { + return ( + +

Entering room...

+
+ ); + } else if (left) { + return ; + } else { + return ( + + ); + } +} diff --git a/src/room/RoomRedirect.jsx b/src/room/RoomRedirect.jsx new file mode 100644 index 0000000..1725a74 --- /dev/null +++ b/src/room/RoomRedirect.jsx @@ -0,0 +1,25 @@ +import React, { useEffect } from "react"; +import { useLocation, useHistory } from "react-router-dom"; +import { defaultHomeserverHost } from "../ConferenceCallManagerHooks"; +import { LoadingView } from "../FullScreenView"; + +export function RoomRedirect() { + const { pathname } = useLocation(); + const history = useHistory(); + + useEffect(() => { + let roomId = pathname; + + if (pathname.startsWith("/")) { + roomId = roomId.substr(1, roomId.length); + } + + if (!roomId.startsWith("#") && !roomId.startsWith("!")) { + roomId = `#${roomId}:${defaultHomeserverHost}`; + } + + history.replace(`/room/${roomId}`); + }, [pathname, history]); + + return ; +}