diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 81da90c..e7bf120 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -10,10 +10,6 @@ import { ICreateClientOpts } from "matrix-js-sdk/src/matrix"; import { ClientEvent } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials"; -import { - GroupCallIntent, - GroupCallType, -} from "matrix-js-sdk/src/webrtc/groupCall"; import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import { logger } from "matrix-js-sdk/src/logger"; @@ -224,8 +220,7 @@ export function isLocalRoomId(roomId: string): boolean { export async function createRoom( client: MatrixClient, - name: string, - isPtt = false + name: string ): Promise { let setExpectedRoomId: (roomId: string) => void; const expectedRoomId = new Promise( @@ -278,15 +273,6 @@ export async function createRoom( setExpectedRoomId(createRoomResult.room_id); await roomReceived; - console.log(`Creating ${isPtt ? "PTT" : "video"} group call room`); - - await client.createGroupCall( - createRoomResult.room_id, - isPtt ? GroupCallType.Voice : GroupCallType.Video, - isPtt, - GroupCallIntent.Prompt - ); - return fullAliasFromRoomName(name, client); } diff --git a/src/room/GroupCallLoader.jsx b/src/room/GroupCallLoader.jsx index f074128..70791a2 100644 --- a/src/room/GroupCallLoader.jsx +++ b/src/room/GroupCallLoader.jsx @@ -30,7 +30,6 @@ export function GroupCallLoader({ client, roomId, viaServers, - true, createPtt ); diff --git a/src/room/useLoadGroupCall.js b/src/room/useLoadGroupCall.js deleted file mode 100644 index b4ec628..0000000 --- a/src/room/useLoadGroupCall.js +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright 2022 Matrix.org Foundation C.I.C. - -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 { useState, useEffect } from "react"; -import { isLocalRoomId, createRoom, roomNameFromRoomId } from "../matrix-utils"; - -async function fetchGroupCall( - client, - roomIdOrAlias, - viaServers = undefined, - timeout = 5000 -) { - const { roomId } = await client.joinRoom(roomIdOrAlias, { viaServers }); - - return new Promise((resolve, reject) => { - let timeoutId; - - function onGroupCallIncoming(groupCall) { - if (groupCall && groupCall.room.roomId === roomId) { - clearTimeout(timeoutId); - client.removeListener("GroupCall.incoming", onGroupCallIncoming); - resolve(groupCall); - } - } - - const groupCall = client.getGroupCallForRoom(roomId); - - if (groupCall) { - resolve(groupCall); - } - - client.on("GroupCall.incoming", onGroupCallIncoming); - - if (timeout) { - timeoutId = setTimeout(() => { - client.removeListener("GroupCall.incoming", onGroupCallIncoming); - reject(new Error("Fetching group call timed out.")); - }, timeout); - } - }); -} - -export function useLoadGroupCall( - client, - roomId, - viaServers, - createIfNotFound, - createPtt -) { - const [state, setState] = useState({ - loading: true, - error: undefined, - groupCall: undefined, - }); - - useEffect(() => { - async function fetchOrCreateGroupCall() { - try { - const groupCall = await fetchGroupCall( - client, - roomId, - viaServers, - 30000 - ); - return groupCall; - } catch (error) { - if ( - createIfNotFound && - (error.errcode === "M_NOT_FOUND" || - (error.message && - error.message.indexOf("Failed to fetch alias") !== -1)) && - isLocalRoomId(roomId) - ) { - const roomName = roomNameFromRoomId(roomId); - await createRoom(client, roomName, createPtt); - const groupCall = await fetchGroupCall( - client, - roomId, - viaServers, - 30000 - ); - return groupCall; - } - - throw error; - } - } - - setState({ loading: true }); - - fetchOrCreateGroupCall() - .then((groupCall) => - setState((prevState) => ({ ...prevState, loading: false, groupCall })) - ) - .catch((error) => - setState((prevState) => ({ ...prevState, loading: false, error })) - ); - }, [client, roomId, state.reloadId, createIfNotFound, viaServers, createPtt]); - - return state; -} diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts new file mode 100644 index 0000000..27744d7 --- /dev/null +++ b/src/room/useLoadGroupCall.ts @@ -0,0 +1,123 @@ +/* +Copyright 2022 Matrix.org Foundation C.I.C. + +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 { useState, useEffect } from "react"; +import { EventType } from "matrix-js-sdk/src/@types/event"; +import { + GroupCallType, + GroupCallIntent, +} from "matrix-js-sdk/src/webrtc/groupCall"; +import { GroupCallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/groupCallEventHandler"; + +import type { MatrixClient } from "matrix-js-sdk/src/client"; +import type { Room } from "matrix-js-sdk/src/models/room"; +import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall"; +import { isLocalRoomId, createRoom, roomNameFromRoomId } from "../matrix-utils"; + +export interface GroupCallLoadState { + loading: boolean; + error?: Error; + groupCall?: GroupCall; +} + +export const useLoadGroupCall = ( + client: MatrixClient, + roomIdOrAlias: string, + viaServers: string[], + createPtt: boolean +) => { + const [state, setState] = useState({ loading: true }); + + useEffect(() => { + setState({ loading: true }); + + const fetchOrCreateRoom = async (): Promise => { + try { + return await client.joinRoom(roomIdOrAlias, { viaServers }); + } catch (error) { + if ( + isLocalRoomId(roomIdOrAlias) && + (error.errcode === "M_NOT_FOUND" || + (error.message && + error.message.indexOf("Failed to fetch alias") !== -1)) + ) { + // The room doesn't exist, but we can create it + await createRoom(client, roomNameFromRoomId(roomIdOrAlias)); + return await client.joinRoom(roomIdOrAlias, { viaServers }); + } else { + throw error; + } + } + }; + + const fetchOrCreateGroupCall = async (): Promise => { + const room = await fetchOrCreateRoom(); + const groupCall = client.getGroupCallForRoom(room.roomId); + + if (groupCall) { + return groupCall; + } else if ( + room.currentState.mayClientSendStateEvent( + EventType.GroupCallPrefix, + client + ) + ) { + // The call doesn't exist, but we can create it + console.log(`Creating ${createPtt ? "PTT" : "video"} group call room`); + return await client.createGroupCall( + room.roomId, + createPtt ? GroupCallType.Voice : GroupCallType.Video, + createPtt, + GroupCallIntent.Room + ); + } else { + // We don't have permission to create the call, so all we can do is wait + // for one to come in + return new Promise((resolve, reject) => { + const onGroupCallIncoming = (groupCall: GroupCall) => { + if (groupCall?.room.roomId === room.roomId) { + clearTimeout(timeout); + client.off( + GroupCallEventHandlerEvent.Incoming, + onGroupCallIncoming + ); + resolve(groupCall); + } + }; + client.on(GroupCallEventHandlerEvent.Incoming, onGroupCallIncoming); + + const timeout = setTimeout(() => { + client.off( + GroupCallEventHandlerEvent.Incoming, + onGroupCallIncoming + ); + reject(new Error("Fetching group call timed out.")); + }, 30000); + }); + } + }; + + fetchOrCreateGroupCall() + .then((groupCall) => + setState((prevState) => ({ ...prevState, loading: false, groupCall })) + ) + .catch((error) => + setState((prevState) => ({ ...prevState, loading: false, error })) + ); + }, [client, roomIdOrAlias, viaServers, createPtt]); + + return state; +};