mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
introduce error boundary in call member
This commit is contained in:
parent
f0d2c19184
commit
b1687d7115
@ -19,6 +19,7 @@ import {makeTxnId, makeId} from "../../common";
|
|||||||
import {EventType, CallErrorCode} from "../callEventTypes";
|
import {EventType, CallErrorCode} from "../callEventTypes";
|
||||||
import {formatToDeviceMessagesPayload} from "../../common";
|
import {formatToDeviceMessagesPayload} from "../../common";
|
||||||
import {sortedIndex} from "../../../utils/sortedIndex";
|
import {sortedIndex} from "../../../utils/sortedIndex";
|
||||||
|
import { ErrorBoundary } from "../../../utils/ErrorBoundary";
|
||||||
|
|
||||||
import type {MuteSettings} from "../common";
|
import type {MuteSettings} from "../common";
|
||||||
import type {Options as PeerCallOptions, RemoteMedia} from "../PeerCall";
|
import type {Options as PeerCallOptions, RemoteMedia} from "../PeerCall";
|
||||||
@ -94,6 +95,9 @@ class MemberConnection {
|
|||||||
export class Member {
|
export class Member {
|
||||||
private connection?: MemberConnection;
|
private connection?: MemberConnection;
|
||||||
private expireTimeout?: Timeout;
|
private expireTimeout?: Timeout;
|
||||||
|
private errorBoundary = new ErrorBoundary(err => {
|
||||||
|
this.options.emitUpdate(this, "error");
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public member: RoomMember,
|
public member: RoomMember,
|
||||||
@ -104,6 +108,10 @@ export class Member {
|
|||||||
this._renewExpireTimeout(updateMemberLog);
|
this._renewExpireTimeout(updateMemberLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get error(): Error | undefined {
|
||||||
|
return this.errorBoundary.error;
|
||||||
|
}
|
||||||
|
|
||||||
private _renewExpireTimeout(log: ILogItem) {
|
private _renewExpireTimeout(log: ILogItem) {
|
||||||
this.expireTimeout?.dispose();
|
this.expireTimeout?.dispose();
|
||||||
this.expireTimeout = undefined;
|
this.expireTimeout = undefined;
|
||||||
@ -166,23 +174,26 @@ export class Member {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
connect(localMedia: LocalMedia, localMuteSettings: MuteSettings, turnServer: BaseObservableValue<RTCIceServer>, memberLogItem: ILogItem): ILogItem | undefined {
|
connect(localMedia: LocalMedia, localMuteSettings: MuteSettings, turnServer: BaseObservableValue<RTCIceServer>, memberLogItem: ILogItem): ILogItem | undefined {
|
||||||
if (this.connection) {
|
return this.errorBoundary.try(() => {
|
||||||
return;
|
if (this.connection) {
|
||||||
}
|
return;
|
||||||
// Safari can't send a MediaStream to multiple sources, so clone it
|
}
|
||||||
const connection = new MemberConnection(
|
// Safari can't send a MediaStream to multiple sources, so clone it
|
||||||
localMedia.clone(),
|
const connection = new MemberConnection(
|
||||||
localMuteSettings,
|
localMedia.clone(),
|
||||||
turnServer,
|
localMuteSettings,
|
||||||
memberLogItem
|
turnServer,
|
||||||
);
|
memberLogItem
|
||||||
this.connection = connection;
|
);
|
||||||
let connectLogItem;
|
this.connection = connection;
|
||||||
connection.logItem.wrap("connect", async log => {
|
let connectLogItem: ILogItem | undefined;
|
||||||
connectLogItem = log;
|
connection.logItem.wrap("connect", async log => {
|
||||||
await this.callIfNeeded(log);
|
connectLogItem = log;
|
||||||
|
await this.callIfNeeded(log);
|
||||||
|
});
|
||||||
|
throw new Error("connect failed!");
|
||||||
|
return connectLogItem;
|
||||||
});
|
});
|
||||||
return connectLogItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private callIfNeeded(log: ILogItem): Promise<void> {
|
private callIfNeeded(log: ILogItem): Promise<void> {
|
||||||
@ -211,30 +222,34 @@ export class Member {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
disconnect(hangup: boolean): ILogItem | undefined {
|
disconnect(hangup: boolean): ILogItem | undefined {
|
||||||
const {connection} = this;
|
return this.errorBoundary.try(() => {
|
||||||
if (!connection) {
|
const {connection} = this;
|
||||||
return;
|
if (!connection) {
|
||||||
}
|
return;
|
||||||
let disconnectLogItem;
|
|
||||||
// if if not sending the hangup, still log disconnect
|
|
||||||
connection.logItem.wrap("disconnect", async log => {
|
|
||||||
disconnectLogItem = log;
|
|
||||||
if (hangup && connection.peerCall) {
|
|
||||||
await connection.peerCall.hangup(CallErrorCode.UserHangup, log);
|
|
||||||
}
|
}
|
||||||
|
let disconnectLogItem;
|
||||||
|
// if if not sending the hangup, still log disconnect
|
||||||
|
connection.logItem.wrap("disconnect", async log => {
|
||||||
|
disconnectLogItem = log;
|
||||||
|
if (hangup && connection.peerCall) {
|
||||||
|
await connection.peerCall.hangup(CallErrorCode.UserHangup, log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connection.dispose();
|
||||||
|
this.connection = undefined;
|
||||||
|
return disconnectLogItem;
|
||||||
});
|
});
|
||||||
connection.dispose();
|
|
||||||
this.connection = undefined;
|
|
||||||
return disconnectLogItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
updateCallInfo(callDeviceMembership: CallDeviceMembership, causeItem: ILogItem) {
|
updateCallInfo(callDeviceMembership: CallDeviceMembership, causeItem: ILogItem) {
|
||||||
this.callDeviceMembership = callDeviceMembership;
|
this.errorBoundary.try(() => {
|
||||||
this._renewExpireTimeout(causeItem);
|
this.callDeviceMembership = callDeviceMembership;
|
||||||
if (this.connection) {
|
this._renewExpireTimeout(causeItem);
|
||||||
this.connection.logItem.refDetached(causeItem);
|
if (this.connection) {
|
||||||
}
|
this.connection.logItem.refDetached(causeItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@ -308,49 +323,51 @@ export class Member {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
handleDeviceMessage(message: SignallingMessage<MGroupCallBase>, syncLog: ILogItem): void {
|
handleDeviceMessage(message: SignallingMessage<MGroupCallBase>, syncLog: ILogItem): void {
|
||||||
const {connection} = this;
|
this.errorBoundary.try(() => {
|
||||||
if (connection) {
|
const {connection} = this;
|
||||||
const destSessionId = message.content.dest_session_id;
|
if (connection) {
|
||||||
if (destSessionId !== this.options.sessionId) {
|
const destSessionId = message.content.dest_session_id;
|
||||||
const logItem = connection.logItem.log({l: "ignoring to_device event with wrong session_id", destSessionId, type: message.type});
|
if (destSessionId !== this.options.sessionId) {
|
||||||
syncLog.refDetached(logItem);
|
const logItem = connection.logItem.log({l: "ignoring to_device event with wrong session_id", destSessionId, type: message.type});
|
||||||
return;
|
syncLog.refDetached(logItem);
|
||||||
}
|
return;
|
||||||
// if there is no peerCall, we either create it with an invite and Handle is implied or we'll ignore it
|
|
||||||
let action = IncomingMessageAction.Handle;
|
|
||||||
if (connection.peerCall) {
|
|
||||||
action = connection.peerCall.getMessageAction(message);
|
|
||||||
// deal with glare and replacing the call before creating new calls
|
|
||||||
if (action === IncomingMessageAction.InviteGlare) {
|
|
||||||
const {shouldReplace, log} = connection.peerCall.handleInviteGlare(message, this.deviceId, connection.logItem);
|
|
||||||
if (log) {
|
|
||||||
syncLog.refDetached(log);
|
|
||||||
}
|
|
||||||
if (shouldReplace) {
|
|
||||||
connection.peerCall = undefined;
|
|
||||||
action = IncomingMessageAction.Handle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
// if there is no peerCall, we either create it with an invite and Handle is implied or we'll ignore it
|
||||||
if (message.type === EventType.Invite && !connection.peerCall) {
|
let action = IncomingMessageAction.Handle;
|
||||||
connection.peerCall = this._createPeerCall(message.content.call_id);
|
|
||||||
}
|
|
||||||
if (action === IncomingMessageAction.Handle) {
|
|
||||||
const idx = sortedIndex(connection.queuedSignallingMessages, message, (a, b) => a.content.seq - b.content.seq);
|
|
||||||
connection.queuedSignallingMessages.splice(idx, 0, message);
|
|
||||||
if (connection.peerCall) {
|
if (connection.peerCall) {
|
||||||
const hasNewMessageBeenDequeued = this.dequeueSignallingMessages(connection, connection.peerCall, message, syncLog);
|
action = connection.peerCall.getMessageAction(message);
|
||||||
if (!hasNewMessageBeenDequeued) {
|
// deal with glare and replacing the call before creating new calls
|
||||||
syncLog.refDetached(connection.logItem.log({l: "queued signalling message", type: message.type, seq: message.content.seq}));
|
if (action === IncomingMessageAction.InviteGlare) {
|
||||||
|
const {shouldReplace, log} = connection.peerCall.handleInviteGlare(message, this.deviceId, connection.logItem);
|
||||||
|
if (log) {
|
||||||
|
syncLog.refDetached(log);
|
||||||
|
}
|
||||||
|
if (shouldReplace) {
|
||||||
|
connection.peerCall = undefined;
|
||||||
|
action = IncomingMessageAction.Handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (action === IncomingMessageAction.Ignore && connection.peerCall) {
|
if (message.type === EventType.Invite && !connection.peerCall) {
|
||||||
const logItem = connection.logItem.log({l: "ignoring to_device event with wrong call_id", callId: message.content.call_id, type: message.type});
|
connection.peerCall = this._createPeerCall(message.content.call_id);
|
||||||
syncLog.refDetached(logItem);
|
}
|
||||||
|
if (action === IncomingMessageAction.Handle) {
|
||||||
|
const idx = sortedIndex(connection.queuedSignallingMessages, message, (a, b) => a.content.seq - b.content.seq);
|
||||||
|
connection.queuedSignallingMessages.splice(idx, 0, message);
|
||||||
|
if (connection.peerCall) {
|
||||||
|
const hasNewMessageBeenDequeued = this.dequeueSignallingMessages(connection, connection.peerCall, message, syncLog);
|
||||||
|
if (!hasNewMessageBeenDequeued) {
|
||||||
|
syncLog.refDetached(connection.logItem.log({l: "queued signalling message", type: message.type, seq: message.content.seq}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (action === IncomingMessageAction.Ignore && connection.peerCall) {
|
||||||
|
const logItem = connection.logItem.log({l: "ignoring to_device event with wrong call_id", callId: message.content.call_id, type: message.type});
|
||||||
|
syncLog.refDetached(logItem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
syncLog.log({l: "member not connected", userId: this.userId, deviceId: this.deviceId});
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
syncLog.log({l: "member not connected", userId: this.userId, deviceId: this.deviceId});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private dequeueSignallingMessages(connection: MemberConnection, peerCall: PeerCall, newMessage: SignallingMessage<MGroupCallBase>, syncLog: ILogItem): boolean {
|
private dequeueSignallingMessages(connection: MemberConnection, peerCall: PeerCall, newMessage: SignallingMessage<MGroupCallBase>, syncLog: ILogItem): boolean {
|
||||||
@ -373,19 +390,23 @@ export class Member {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
async setMedia(localMedia: LocalMedia, previousMedia: LocalMedia): Promise<void> {
|
async setMedia(localMedia: LocalMedia, previousMedia: LocalMedia): Promise<void> {
|
||||||
const {connection} = this;
|
return this.errorBoundary.try(async () => {
|
||||||
if (connection) {
|
const {connection} = this;
|
||||||
connection.localMedia = localMedia.replaceClone(connection.localMedia, previousMedia);
|
if (connection) {
|
||||||
await connection.peerCall?.setMedia(connection.localMedia, connection.logItem);
|
connection.localMedia = localMedia.replaceClone(connection.localMedia, previousMedia);
|
||||||
}
|
await connection.peerCall?.setMedia(connection.localMedia, connection.logItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setMuted(muteSettings: MuteSettings): Promise<void> {
|
async setMuted(muteSettings: MuteSettings): Promise<void> {
|
||||||
const {connection} = this;
|
return this.errorBoundary.try(async () => {
|
||||||
if (connection) {
|
const {connection} = this;
|
||||||
connection.localMuteSettings = muteSettings;
|
if (connection) {
|
||||||
await connection.peerCall?.setMuted(muteSettings, connection.logItem);
|
connection.localMuteSettings = muteSettings;
|
||||||
}
|
await connection.peerCall?.setMuted(muteSettings, connection.logItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createPeerCall(callId: string): PeerCall {
|
private _createPeerCall(callId: string): PeerCall {
|
||||||
|
Loading…
Reference in New Issue
Block a user