diff --git a/src/Header.tsx b/src/Header.tsx index 4c796bf..acad072 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -58,7 +58,7 @@ export function LeftNav({ interface RightNavProps extends HTMLAttributes { children?: ReactNode; className?: string; - hideMobile?: string; + hideMobile?: boolean; } export function RightNav({ diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index ea6aac2..71abb28 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -56,13 +56,13 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { const data = new FormData(e.target as HTMLFormElement); const roomNameData = data.get("callName"); const roomName = typeof roomNameData === "string" ? roomNameData : ""; - // const ptt = callType === CallType.Radio; + const ptt = callType === CallType.Radio; async function submit() { setError(undefined); setLoading(true); - const [roomIdOrAlias] = await createRoom(client, roomName); + const [roomIdOrAlias] = await createRoom(client, roomName, ptt); if (roomIdOrAlias) { history.push(`/room/${roomIdOrAlias}`); @@ -82,7 +82,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { } }); }, - [client, history, modalState] + [client, history, modalState, callType] ); const recentRooms = useGroupCallRooms(client); diff --git a/src/home/UnauthenticatedView.jsx b/src/home/UnauthenticatedView.tsx similarity index 89% rename from src/home/UnauthenticatedView.jsx rename to src/home/UnauthenticatedView.tsx index a6d7a6e..2cf09d3 100644 --- a/src/home/UnauthenticatedView.jsx +++ b/src/home/UnauthenticatedView.tsx @@ -14,45 +14,46 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useCallback, useState } from "react"; +import React, { FC, useCallback, useState, FormEventHandler } from "react"; +import { useHistory } from "react-router-dom"; +import { randomString } from "matrix-js-sdk/src/randomstring"; + import { useClient } from "../ClientContext"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; import { UserMenuContainer } from "../UserMenuContainer"; -import { useHistory } from "react-router-dom"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { Button } from "../button"; -import { randomString } from "matrix-js-sdk/src/randomstring"; import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils"; import { useInteractiveRegistration } from "../auth/useInteractiveRegistration"; import { useModalTriggerState } from "../Modal"; import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { useRecaptcha } from "../auth/useRecaptcha"; -import { Body, Caption, Link, Headline } from "../typography/Typography"; +import { Body, Caption, Link } from "../typography/Typography"; import { Form } from "../form/Form"; import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; import styles from "./UnauthenticatedView.module.css"; import commonStyles from "./common.module.css"; import { generateRandomName } from "../auth/generateRandomName"; -export function UnauthenticatedView() { +export const UnauthenticatedView: FC = () => { const { setClient } = useClient(); const [callType, setCallType] = useState(CallType.Video); const [loading, setLoading] = useState(false); - const [error, setError] = useState(); + const [error, setError] = useState(); const [privacyPolicyUrl, recaptchaKey, register] = useInteractiveRegistration(); const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey); const { modalState, modalProps } = useModalTriggerState(); - const [onFinished, setOnFinished] = useState(); + const [onFinished, setOnFinished] = useState<() => void>(); const history = useHistory(); - const onSubmit = useCallback( + const onSubmit: FormEventHandler = useCallback( (e) => { e.preventDefault(); - const data = new FormData(e.target); - const roomName = data.get("callName"); - const displayName = data.get("displayName"); + const data = new FormData(e.target as HTMLFormElement); + const roomName = data.get("callName") as string; + const displayName = data.get("displayName") as string; const ptt = callType === CallType.Radio; async function submit() { @@ -68,12 +69,12 @@ export function UnauthenticatedView() { true ); - let roomIdOrAlias; + let roomIdOrAlias: string; try { [roomIdOrAlias] = await createRoom(client, roomName, ptt); } catch (error) { if (error.errcode === "M_ROOM_IN_USE") { - setOnFinished(() => () => { + setOnFinished(() => { setClient(client, session); const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName); const [, serverName] = client.getUserId().split(":"); @@ -100,7 +101,7 @@ export function UnauthenticatedView() { reset(); }); }, - [register, reset, execute, history, callType] + [register, reset, execute, history, callType, modalState, setClient] ); const callNameLabel = @@ -177,4 +178,4 @@ export function UnauthenticatedView() { )} ); -} +}; diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index ed3cde3..4c25d17 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -17,7 +17,12 @@ import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials"; import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import { WidgetApi } from "matrix-widget-api"; import { logger } from "matrix-js-sdk/src/logger"; +import { + GroupCallIntent, + GroupCallType, +} from "matrix-js-sdk/src/webrtc/groupCall"; +import type { Room } from "matrix-js-sdk/src/models/room"; import IndexedDBWorker from "./IndexedDBWorker?worker"; import { getRoomParams } from "./room/useRoomParams"; @@ -290,9 +295,10 @@ export function isLocalRoomId(roomId: string): boolean { export async function createRoom( client: MatrixClient, - name: string + name: string, + ptt: boolean ): Promise<[string, string]> { - const result = await client.createRoom({ + const createPromise = client.createRoom({ visibility: Visibility.Private, preset: Preset.PublicChat, name, @@ -322,6 +328,36 @@ export async function createRoom( }, }); + // Wait for the room to arrive + await new Promise((resolve, reject) => { + const onRoom = async (room: Room) => { + if (room.roomId === (await createPromise).room_id) { + resolve(); + cleanUp(); + } + }; + createPromise.catch((e) => { + reject(e); + cleanUp(); + }); + + const cleanUp = () => { + client.off(ClientEvent.Room, onRoom); + }; + client.on(ClientEvent.Room, onRoom); + }); + + const result = await createPromise; + + console.log(`Creating ${ptt ? "PTT" : "video"} group call room`); + + await client.createGroupCall( + result.room_id, + ptt ? GroupCallType.Voice : GroupCallType.Video, + ptt, + GroupCallIntent.Room + ); + return [fullAliasFromRoomName(name, client), result.room_id]; } diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts index 301ba54..a741b90 100644 --- a/src/room/useLoadGroupCall.ts +++ b/src/room/useLoadGroupCall.ts @@ -87,7 +87,8 @@ export const useLoadGroupCall = ( // The room doesn't exist, but we can create it const [, roomId] = await createRoom( client, - roomNameFromRoomId(roomIdOrAlias) + roomNameFromRoomId(roomIdOrAlias), + createPtt ); // likewise, wait for the room return await waitForRoom(roomId);