diff --git a/src/domain/session/room/timeline/tiles/CallTile.js b/src/domain/session/room/timeline/tiles/CallTile.js index ffb9ef59..05762bc0 100644 --- a/src/domain/session/room/timeline/tiles/CallTile.js +++ b/src/domain/session/room/timeline/tiles/CallTile.js @@ -94,7 +94,7 @@ export class CallTile extends SimpleTile { } get canJoin() { - return this._call && !this._call.hasJoined; + return this._call && !this._call.hasJoined && !this._call.usesFoci; } get canLeave() { @@ -118,11 +118,14 @@ export class CallTile extends SimpleTile { } get typeLabel() { + if (this._call && this._call.usesFoci) { + return `This call uses a stream-forwarding unit, which isn't supported yet, so you can't join this call.`; + } if (this.type === CallType.Video) { - return `Video call`; - } else { - return `Voice call`; - } + return `Video call`; + } else { + return `Voice call`; + } } get type() { diff --git a/src/domain/session/toast/ToastCollectionViewModel.ts b/src/domain/session/toast/ToastCollectionViewModel.ts index 18b58b76..709f5b81 100644 --- a/src/domain/session/toast/ToastCollectionViewModel.ts +++ b/src/domain/session/toast/ToastCollectionViewModel.ts @@ -79,7 +79,7 @@ export class ToastCollectionViewModel extends ViewModel { private _shouldShowNotification(call: GroupCall): boolean { const currentlyOpenedRoomId = this.navigation.path.get("room")?.value; - if (!call.isLoadedFromStorage && call.roomId !== currentlyOpenedRoomId) { + if (!call.isLoadedFromStorage && call.roomId !== currentlyOpenedRoomId && !call.usesFoci) { return true; } return false; diff --git a/src/matrix/calls/callEventTypes.ts b/src/matrix/calls/callEventTypes.ts index a0eb986c..879d0c11 100644 --- a/src/matrix/calls/callEventTypes.ts +++ b/src/matrix/calls/callEventTypes.ts @@ -22,11 +22,17 @@ export enum EventType { // TODO: Change to "sdp_stream_metadata" when MSC3077 is merged export const SDPStreamMetadataKey = "org.matrix.msc3077.sdp_stream_metadata"; +export interface FocusConfig { + user_id: string, + device_id: string +} + export interface CallDeviceMembership { device_id: string, session_id: string, ["expires_ts"]?: number, feeds?: Array<{purpose: string}> + ["m.foci.active"]?: Array } export interface CallMembership { diff --git a/src/matrix/calls/group/GroupCall.ts b/src/matrix/calls/group/GroupCall.ts index e0099f4e..0460c212 100644 --- a/src/matrix/calls/group/GroupCall.ts +++ b/src/matrix/calls/group/GroupCall.ts @@ -145,6 +145,15 @@ export class GroupCall extends EventEmitter<{change: never}> { return !!this.callContent?.["m.terminated"]; } + get usesFoci(): boolean { + for (const member of this._members.values()) { + if (member.usesFoci) { + return true; + } + } + return false; + } + get duration(): number | undefined { if (typeof this.startTime === "number") { return (this.options.clock.now() - this.startTime); @@ -181,7 +190,8 @@ export class GroupCall extends EventEmitter<{change: never}> { join(localMedia: LocalMedia, log?: ILogItem): Promise { return this.options.logger.wrapOrRun(log, "Call.join", async joinLog => { - if (this._state !== GroupCallState.Created || this.joinedData) { + if (this._state !== GroupCallState.Created || this.joinedData || this.usesFoci) { + localMedia.dispose(); return; } const logItem = this.options.logger.child({ @@ -217,7 +227,7 @@ export class GroupCall extends EventEmitter<{change: never}> { this.emitChange(); }); // send invite to all members that are < my userId - for (const [,member] of this._members) { + for (const member of this._members.values()) { this.connectToMember(member, joinedData, log); } }); @@ -521,7 +531,7 @@ export class GroupCall extends EventEmitter<{change: never}> { disconnect(log: ILogItem): boolean { return this.errorBoundary.try(() => { if (this.hasJoined) { - for (const [,member] of this._members) { + for (const member of this._members.values()) { const disconnectLogItem = member.disconnect(true); if (disconnectLogItem) { log.refDetached(disconnectLogItem); diff --git a/src/matrix/calls/group/Member.ts b/src/matrix/calls/group/Member.ts index e097804c..ab99c6b8 100644 --- a/src/matrix/calls/group/Member.ts +++ b/src/matrix/calls/group/Member.ts @@ -117,6 +117,11 @@ export class Member { return this.errorBoundary.error; } + get usesFoci(): boolean { + const activeFoci = this.callDeviceMembership["m.foci.active"]; + return Array.isArray(activeFoci) && activeFoci.length > 0; + } + private _renewExpireTimeout(log: ILogItem) { this.expireTimeout?.dispose(); this.expireTimeout = undefined;