From 8f3a4ac597f8e3183959ba1941cfb5f0f1674bcf Mon Sep 17 00:00:00 2001 From: Timo Date: Mon, 11 Nov 2024 15:49:31 +0100 Subject: [PATCH] Skip lobby if when coming from waitForInvite state. --- README.md | 2 +- public/locales/en-GB/app.json | 8 +- src/room/GroupCallLoader.tsx | 77 ------------- src/room/RoomPage.tsx | 211 ++++++++++++++++++++-------------- src/room/useLoadGroupCall.ts | 7 +- 5 files changed, 134 insertions(+), 171 deletions(-) delete mode 100644 src/room/GroupCallLoader.tsx diff --git a/README.md b/README.md index f05c2d2d..c4c252ad 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ rc_message: MSC3266 allows to request a room summary of rooms you are not joined. The summary contains the room join rules. We need that to decide if the user gets -prompted with the option to knock ("ask to join"), a cannot join error or the +prompted with the option to knock ("Request to join"), a cannot join error or the join view. Element Call requires a Livekit SFU alongside a [Livekit JWT diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 3c09aa82..0729841d 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -81,8 +81,8 @@ "call_ended_heading": "Call ended", "failed_heading": "Failed to join", "failed_text": "Call not found or is not accessible.", - "knock_reject_body": "The room members declined your request to join.", - "knock_reject_heading": "Not allowed to join", + "knock_reject_body": "Your request was declined. Try again later or reach out for more info.", + "knock_reject_heading": "Access Denied", "reason": "Reason" }, "hangup_button_label": "End call", @@ -100,11 +100,11 @@ "layout_grid_label": "Grid", "layout_spotlight_label": "Spotlight", "lobby": { - "ask_to_join": "Ask to join call", + "ask_to_join": "Request to join call", "join_as_guest": "Join as guest", "join_button": "Join call", "leave_button": "Back to recents", - "waiting_for_invite": "Request sent" + "waiting_for_invite": "Request Sent! Waiting for permission to join..." }, "log_in": "Log In", "logging_in": "Logging in…", diff --git a/src/room/GroupCallLoader.tsx b/src/room/GroupCallLoader.tsx deleted file mode 100644 index f843f3f4..00000000 --- a/src/room/GroupCallLoader.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2022-2024 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only -Please see LICENSE in the repository root for full details. -*/ - -import { MatrixClient } from "matrix-js-sdk/src/client"; -import { useTranslation } from "react-i18next"; -import { MatrixError } from "matrix-js-sdk/src/matrix"; -import { Heading, Text } from "@vector-im/compound-web"; - -import { Link } from "../button/Link"; -import { - useLoadGroupCall, - GroupCallStatus, - CallTerminatedMessage, -} from "./useLoadGroupCall"; -import { ErrorView, FullScreenView } from "../FullScreenView"; - -interface Props { - client: MatrixClient; - roomIdOrAlias: string; - viaServers: string[]; - children: (groupCallState: GroupCallStatus) => JSX.Element; -} - -export function GroupCallLoader({ - client, - roomIdOrAlias, - viaServers, - children, -}: Props): JSX.Element { - const { t } = useTranslation(); - const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers); - - switch (groupCallState.kind) { - case "loaded": - case "waitForInvite": - case "canKnock": - return children(groupCallState); - case "loading": - return ( - -

{t("common.loading")}

-
- ); - case "failed": - if ((groupCallState.error as MatrixError).errcode === "M_NOT_FOUND") { - return ( - - {t("group_call_loader.failed_heading")} - {t("group_call_loader.failed_text")} - {/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have - dupes of this flow, let's make a common component and put it here. */} - {t("common.home")} - - ); - } else if (groupCallState.error instanceof CallTerminatedMessage) { - return ( - - {groupCallState.error.message} - {groupCallState.error.messageBody} - {groupCallState.error.reason && ( - <> - {t("group_call_loader.reason")}: - "{groupCallState.error.reason}" - - )} - {t("common.home")} - - ); - } else { - return ; - } - } -} diff --git a/src/room/RoomPage.tsx b/src/room/RoomPage.tsx index ce6c9f70..bce3cfef 100644 --- a/src/room/RoomPage.tsx +++ b/src/room/RoomPage.tsx @@ -5,15 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { FC, useEffect, useState, useCallback, ReactNode } from "react"; +import { FC, useEffect, useState, useCallback, ReactNode, useRef } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { useTranslation } from "react-i18next"; import { CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { MatrixError } from "matrix-js-sdk/src/http-api"; +import { Heading, Text } from "@vector-im/compound-web"; import { useClientLegacy } from "../ClientContext"; -import { ErrorView, LoadingView } from "../FullScreenView"; +import { ErrorView, FullScreenView, LoadingView } from "../FullScreenView"; import { RoomAuthView } from "./RoomAuthView"; -import { GroupCallLoader } from "./GroupCallLoader"; import { GroupCallView } from "./GroupCallView"; import { useRoomIdentifier, useUrlParams } from "../UrlParams"; import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser"; @@ -21,13 +22,14 @@ import { HomePage } from "../home/HomePage"; import { platform } from "../Platform"; import { AppSelectionModal } from "./AppSelectionModal"; import { widget } from "../widget"; -import { GroupCallStatus } from "./useLoadGroupCall"; +import { CallTerminatedMessage, useLoadGroupCall } from "./useLoadGroupCall"; import { LobbyView } from "./LobbyView"; import { E2eeType } from "../e2ee/e2eeType"; import { useProfile } from "../profile/useProfile"; import { useMuteStates } from "./MuteStates"; import { useOptInAnalytics } from "../settings/settings"; import { Config } from "../config/Config"; +import { Link } from "../button/Link"; export const RoomPage: FC = () => { const { @@ -53,6 +55,7 @@ export const RoomPage: FC = () => { useClientLegacy(); const { avatarUrl, displayName: userDisplayName } = useProfile(client); + const groupCallState = useLoadGroupCall(roomIdOrAlias, viaServers, client); const muteStates = useMuteStates(); useEffect(() => { @@ -82,82 +85,124 @@ export const RoomPage: FC = () => { if (optInAnalytics === null && setOptInAnalytics) setOptInAnalytics(true); }, [optInAnalytics, setOptInAnalytics]); - const groupCallView = useCallback( - (groupCallState: GroupCallStatus): JSX.Element => { - switch (groupCallState.kind) { - case "loaded": - return ( - + const wasInWaitForInviteState = useRef(false); + + useEffect(() => { + if (groupCallState.kind === "loaded" && wasInWaitForInviteState) { + logger.log("Play join sound 'Not yet implemented'"); + } + }); + + const groupCallView = useCallback((): JSX.Element => { + switch (groupCallState.kind) { + case "loaded": + return ( + + ); + case "waitForInvite": + case "canKnock": { + wasInWaitForInviteState.current = + wasInWaitForInviteState.current || + "waitForInvite" === groupCallState.kind; + const knock = + groupCallState.kind === "canKnock" ? groupCallState.knock : null; + const label: string | JSX.Element = + groupCallState.kind === "canKnock" ? ( + t("lobby.ask_to_join") + ) : ( + <> + {t("lobby.waiting_for_invite")} + + ); - case "waitForInvite": - case "canKnock": { - const knock = - groupCallState.kind === "canKnock" ? groupCallState.knock : null; - const label: string | JSX.Element = - groupCallState.kind === "canKnock" ? ( - t("lobby.ask_to_join") - ) : ( - <> - {t("lobby.waiting_for_invite")} - - - ); - return ( - knock?.()} - enterLabel={label} - waitingForInvite={groupCallState.kind === "waitForInvite"} - confineToRoom={confineToRoom} - hideHeader={hideHeader} - participantCount={null} - muteStates={muteStates} - onShareClick={null} - /> - ); - } - default: - return <> ; + return ( + knock?.()} + enterLabel={label} + waitingForInvite={groupCallState.kind === "waitForInvite"} + confineToRoom={confineToRoom} + hideHeader={hideHeader} + participantCount={null} + muteStates={muteStates} + onShareClick={null} + /> + ); } - }, - [ - client, - passwordlessUser, - confineToRoom, - preload, - skipLobby, - hideHeader, - muteStates, - t, - userDisplayName, - avatarUrl, - ], - ); + case "loading": + return ( + +

{t("common.loading")}

+
+ ); + case "failed": + wasInWaitForInviteState.current = false; + if ((groupCallState.error as MatrixError).errcode === "M_NOT_FOUND") { + return ( + + {t("group_call_loader.failed_heading")} + {t("group_call_loader.failed_text")} + {/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have + dupes of this flow, let's make a common component and put it here. */} + {t("common.home")} + + ); + } else if (groupCallState.error instanceof CallTerminatedMessage) { + return ( + + {groupCallState.error.message} + {groupCallState.error.messageBody} + {groupCallState.error.reason && ( + <> + {t("group_call_loader.reason")}: + "{groupCallState.error.reason}" + + )} + {t("common.home")} + + ); + } else { + return ; + } + default: + return <> ; + } + }, [ + groupCallState, + client, + passwordlessUser, + confineToRoom, + preload, + skipLobby, + hideHeader, + muteStates, + t, + userDisplayName, + avatarUrl, + ]); let content: ReactNode; if (loading || isRegistering) { @@ -170,15 +215,7 @@ export const RoomPage: FC = () => { // TODO: This doesn't belong here, the app routes need to be reworked content = ; } else { - content = ( - - {groupCallView} - - ); + content = groupCallView(); } return ( diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts index 6e07aa52..5fd4ad38 100644 --- a/src/room/useLoadGroupCall.ts +++ b/src/room/useLoadGroupCall.ts @@ -117,9 +117,9 @@ export class CallTerminatedMessage extends Error { } export const useLoadGroupCall = ( - client: MatrixClient, - roomIdOrAlias: string, + roomIdOrAlias: string | null, viaServers: string[], + client?: MatrixClient, ): GroupCallStatus => { const [state, setState] = useState({ kind: "loading" }); const activeRoom = useRef(); @@ -159,6 +159,9 @@ export const useLoadGroupCall = ( ?.getContent().reason; useEffect(() => { + if (!client || !roomIdOrAlias) { + return; + } const getRoomByAlias = async (alias: string): Promise => { // We lowercase the localpart when we create the room, so we must lowercase // it here too (we just do the whole alias). We can't do the same to room IDs