Consolidate all group call creation into useLoadGroupCall

This enables us to automatically create a group call in rooms that
exist, but contain no calls.
This commit is contained in:
Robin Townsend 2022-07-15 12:58:53 -04:00
parent b4a56f6dd7
commit fae4c504c9
4 changed files with 124 additions and 130 deletions

View file

@ -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<string> {
let setExpectedRoomId: (roomId: string) => void;
const expectedRoomId = new Promise<string>(
@ -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);
}

View file

@ -30,7 +30,6 @@ export function GroupCallLoader({
client,
roomId,
viaServers,
true,
createPtt
);

View file

@ -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;
}

View file

@ -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<GroupCallLoadState>({ loading: true });
useEffect(() => {
setState({ loading: true });
const fetchOrCreateRoom = async (): Promise<Room> => {
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<GroupCall> => {
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;
};