You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Merge remote-tracking branch 'upstream/develop' into fix/call-notifs
This commit is contained in:
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
Changes in [12.4.0](https://github.com/vector-im/element-desktop/releases/tag/v12.4.0) (2021-08-31)
|
||||
===================================================================================================
|
||||
|
||||
## 🦖 Deprecations
|
||||
* Deprecate groups APIs. Groups are no longer supported, only Synapse has support. They are being replaced by Spaces which build off of Rooms and are far more flexible. ([\#1792](https://github.com/matrix-org/matrix-js-sdk/pull/1792)).
|
||||
|
||||
## ✨ Features
|
||||
* Add method for including extra fields when uploading to a tree space ([\#1850](https://github.com/matrix-org/matrix-js-sdk/pull/1850)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix broken voice calls, no ringing and broken call notifications ([\#1858](https://github.com/matrix-org/matrix-js-sdk/pull/1858)). Fixes vector-im/element-web#18578 vector-im/element-web#18538 and vector-im/element-web#18578. Contributed by [SimonBrandner](https://github.com/SimonBrandner).
|
||||
* Revert "Fix glare related regressions" ([\#1857](https://github.com/matrix-org/matrix-js-sdk/pull/1857)).
|
||||
* Fix glare related regressions ([\#1851](https://github.com/matrix-org/matrix-js-sdk/pull/1851)). Fixes vector-im/element-web#18538 and vector-im/element-web#18538. Contributed by [SimonBrandner](https://github.com/SimonBrandner).
|
||||
* Fix temporary call messages being handled without call ([\#1834](https://github.com/matrix-org/matrix-js-sdk/pull/1834)). Contributed by [Palid](https://github.com/Palid).
|
||||
* Fix conditional on returning file tree spaces ([\#1841](https://github.com/matrix-org/matrix-js-sdk/pull/1841)).
|
||||
|
||||
Changes in [12.3.1](https://github.com/vector-im/element-desktop/releases/tag/v12.3.1) (2021-08-17)
|
||||
===================================================================================================
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
console.log("Loading browser sdk");
|
||||
|
||||
var client = matrixcs.createClient("http://matrix.org");
|
||||
var client = matrixcs.createClient("https://matrix.org");
|
||||
client.publicRooms(function (err, data) {
|
||||
if (err) {
|
||||
console.error("err %s", JSON.stringify(err));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Test</title>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="icon" href="data:,">
|
||||
<script src="lib/matrix.js"></script>
|
||||
<script src="browserTest.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "12.3.1",
|
||||
"version": "12.4.0",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"scripts": {
|
||||
"prepublishOnly": "yarn build",
|
||||
|
||||
@@ -55,12 +55,20 @@ export enum JoinRule {
|
||||
* @deprecated Reserved keyword. Should not be used. Not yet implemented.
|
||||
*/
|
||||
Private = "private",
|
||||
Knock = "knock", // MSC2403 - only valid inside experimental room versions at this time.
|
||||
Restricted = "restricted", // MSC3083 - only valid inside experimental room versions at this time.
|
||||
Knock = "knock",
|
||||
Restricted = "restricted",
|
||||
}
|
||||
|
||||
export enum RestrictedAllowType {
|
||||
RoomMembership = "m.room_membership", // MSC3083 - only valid inside experimental room versions at this time.
|
||||
RoomMembership = "m.room_membership",
|
||||
}
|
||||
|
||||
export interface IJoinRuleEventContent {
|
||||
join_rule: JoinRule; // eslint-disable-line camelcase
|
||||
allow?: {
|
||||
type: RestrictedAllowType;
|
||||
room_id: string; // eslint-disable-line camelcase
|
||||
}[];
|
||||
}
|
||||
|
||||
export enum GuestAccess {
|
||||
|
||||
@@ -380,6 +380,11 @@ export interface IStartClientOpts {
|
||||
* This should be in the order of hours. Default: undefined.
|
||||
*/
|
||||
clientWellKnownPollPeriod?: number;
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
experimentalThreadSupport?: boolean;
|
||||
}
|
||||
|
||||
export interface IStoredClientOpts extends IStartClientOpts {
|
||||
|
||||
@@ -28,7 +28,9 @@ import { EventType, MsgType, RelationType } from "../@types/event";
|
||||
import { Crypto } from "../crypto";
|
||||
import { deepSortedObjectEntries } from "../utils";
|
||||
import { RoomMember } from "./room-member";
|
||||
import { Thread } from "./thread";
|
||||
import { IActionsObject } from '../pushprocessor';
|
||||
import { ReEmitter } from '../ReEmitter';
|
||||
|
||||
/**
|
||||
* Enum for event statuses.
|
||||
@@ -192,6 +194,12 @@ export class MatrixEvent extends EventEmitter {
|
||||
*/
|
||||
private txnId: string = null;
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* A reference to the thread this event belongs to
|
||||
*/
|
||||
private thread: Thread = null;
|
||||
|
||||
/* Set an approximate timestamp for the event relative the local clock.
|
||||
* This will inherently be approximate because it doesn't take into account
|
||||
* the time between the server putting the 'age' field on the event as it sent
|
||||
@@ -213,6 +221,8 @@ export class MatrixEvent extends EventEmitter {
|
||||
*/
|
||||
public verificationRequest = null;
|
||||
|
||||
private readonly reEmitter: ReEmitter;
|
||||
|
||||
/**
|
||||
* Construct a Matrix Event object
|
||||
* @constructor
|
||||
@@ -262,6 +272,7 @@ export class MatrixEvent extends EventEmitter {
|
||||
|
||||
this.txnId = event.txn_id || null;
|
||||
this.localTimestamp = Date.now() - this.getAge();
|
||||
this.reEmitter = new ReEmitter(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,6 +393,15 @@ export class MatrixEvent extends EventEmitter {
|
||||
return this.event.content || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* Get the event ID of the replied event
|
||||
*/
|
||||
public get replyEventId(): string {
|
||||
const relations = this.getWireContent()["m.relates_to"];
|
||||
return relations?.["m.in_reply_to"]?.["event_id"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous event content JSON. This will only return something for
|
||||
* state events which exist in the timeline.
|
||||
@@ -1285,6 +1305,21 @@ export class MatrixEvent extends EventEmitter {
|
||||
public getTxnId(): string | undefined {
|
||||
return this.txnId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public setThread(thread: Thread): void {
|
||||
this.thread = thread;
|
||||
this.reEmitter.reEmit(thread, ["Thread.ready", "Thread.update"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public getThread(): Thread {
|
||||
return this.thread;
|
||||
}
|
||||
}
|
||||
|
||||
/* REDACT_KEEP_KEYS gives the keys we keep when an event is redacted
|
||||
|
||||
@@ -26,6 +26,7 @@ import * as utils from "../utils";
|
||||
import { EventType } from "../@types/event";
|
||||
import { MatrixEvent } from "./event";
|
||||
import { MatrixClient } from "../client";
|
||||
import { IJoinRuleEventContent, JoinRule } from "../@types/partials";
|
||||
|
||||
// possible statuses for out-of-band member loading
|
||||
enum OobStatus {
|
||||
@@ -728,10 +729,10 @@ export class RoomState extends EventEmitter {
|
||||
* Returns the join rule based on the m.room.join_rule state event, defaulting to `invite`.
|
||||
* @returns {string} the join_rule applied to this room
|
||||
*/
|
||||
public getJoinRule(): string {
|
||||
public getJoinRule(): JoinRule {
|
||||
const joinRuleEvent = this.getStateEvents(EventType.RoomJoinRules, "");
|
||||
const joinRuleContent = joinRuleEvent ? joinRuleEvent.getContent() : {};
|
||||
return joinRuleContent["join_rule"] || "invite";
|
||||
const joinRuleContent = joinRuleEvent?.getContent<IJoinRuleEventContent>() ?? {};
|
||||
return joinRuleContent["join_rule"] || JoinRule.Invite;
|
||||
}
|
||||
|
||||
private updateThirdPartyTokenCache(memberEvent: MatrixEvent): void {
|
||||
|
||||
@@ -32,9 +32,10 @@ import { logger } from '../logger';
|
||||
import { ReEmitter } from '../ReEmitter';
|
||||
import { EventType, RoomCreateTypeField, RoomType, UNSTABLE_ELEMENT_FUNCTIONAL_USERS } from "../@types/event";
|
||||
import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersionStability } from "../client";
|
||||
import { ResizeMethod } from "../@types/partials";
|
||||
import { JoinRule, ResizeMethod } from "../@types/partials";
|
||||
import { Filter } from "../filter";
|
||||
import { RoomState } from "./room-state";
|
||||
import { Thread } from "./thread";
|
||||
|
||||
// These constants are used as sane defaults when the homeserver doesn't support
|
||||
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
|
||||
@@ -145,6 +146,11 @@ export class Room extends EventEmitter {
|
||||
public oldState: RoomState;
|
||||
public currentState: RoomState;
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public threads = new Set<Thread>();
|
||||
|
||||
/**
|
||||
* Construct a new Room.
|
||||
*
|
||||
@@ -857,13 +863,26 @@ export class Room extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an event which is stored in our unfiltered timeline set
|
||||
* Get an event which is stored in our unfiltered timeline set or in a thread
|
||||
*
|
||||
* @param {string} eventId event ID to look for
|
||||
* @return {?module:models/event.MatrixEvent} the given event, or undefined if unknown
|
||||
*/
|
||||
public findEventById(eventId: string): MatrixEvent | undefined {
|
||||
return this.getUnfilteredTimelineSet().findEventById(eventId);
|
||||
let event = this.getUnfilteredTimelineSet().findEventById(eventId);
|
||||
|
||||
if (event) {
|
||||
return event;
|
||||
} else {
|
||||
const threads = this.getThreads();
|
||||
for (let i = 0; i < threads.length; i++) {
|
||||
const thread = threads[i];
|
||||
event = thread.findEventById(eventId);
|
||||
if (event) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1049,6 +1068,54 @@ export class Room extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public addThread(thread: Thread): Set<Thread> {
|
||||
this.threads.add(thread);
|
||||
if (!thread.ready) {
|
||||
thread.once("Thread.ready", this.dedupeThreads);
|
||||
this.emit("Thread.update", thread);
|
||||
this.reEmitter.reEmit(thread, ["Thread.update", "Thread.ready"]);
|
||||
}
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public getThread(eventId: string): Thread {
|
||||
return this.getThreads().find(thread => {
|
||||
return thread.id === eventId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
public getThreads(): Thread[] {
|
||||
return Array.from(this.threads.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Two threads starting from a different child event can end up
|
||||
* with the same event root. This method ensures that the duplicates
|
||||
* are removed
|
||||
* @experimental
|
||||
*/
|
||||
private dedupeThreads = (readyThread): void => {
|
||||
const threads = Array.from(this.threads);
|
||||
if (threads.includes(readyThread)) {
|
||||
this.threads = new Set(threads.filter(thread => {
|
||||
if (readyThread.id === thread.id && readyThread !== thread) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a member from the current room state.
|
||||
* @param {string} userId The user ID of the member.
|
||||
@@ -1225,6 +1292,28 @@ export class Room extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event to a thread's timeline. Will fire "Thread.update"
|
||||
* @experimental
|
||||
*/
|
||||
public addThreadedEvent(event: MatrixEvent): void {
|
||||
if (event.getUnsigned().transaction_id) {
|
||||
const existingEvent = this.txnToEvent[event.getUnsigned().transaction_id];
|
||||
if (existingEvent) {
|
||||
// remote echo of an event we sent earlier
|
||||
this.handleRemoteEcho(event, existingEvent);
|
||||
}
|
||||
}
|
||||
|
||||
let thread = this.findEventById(event.replyEventId)?.getThread();
|
||||
if (thread) {
|
||||
thread.addEvent(event);
|
||||
} else {
|
||||
thread = new Thread([event], this, this.client);
|
||||
this.addThread(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event to the end of this room's live timelines. Will fire
|
||||
* "Room.timeline".
|
||||
@@ -1973,7 +2062,7 @@ export class Room extends EventEmitter {
|
||||
* Returns the join rule based on the m.room.join_rule state event, defaulting to `invite`.
|
||||
* @returns {string} the join_rule applied to this room
|
||||
*/
|
||||
public getJoinRule(): string {
|
||||
public getJoinRule(): JoinRule {
|
||||
return this.currentState.getJoinRule();
|
||||
}
|
||||
|
||||
|
||||
186
src/models/thread.ts
Normal file
186
src/models/thread.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright 2021 The 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 { EventEmitter } from "events";
|
||||
import { MatrixClient } from "../matrix";
|
||||
import { MatrixEvent } from "./event";
|
||||
import { EventTimelineSet } from './event-timeline-set';
|
||||
import { Room } from './room';
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export class Thread extends EventEmitter {
|
||||
/**
|
||||
* A reference to the event ID at the top of the thread
|
||||
*/
|
||||
private root: string;
|
||||
/**
|
||||
* A reference to all the events ID at the bottom of the threads
|
||||
*/
|
||||
public tail = new Set<string>();
|
||||
private _timelineSet: EventTimelineSet;
|
||||
|
||||
constructor(
|
||||
events: MatrixEvent[] = [],
|
||||
public readonly room: Room,
|
||||
public readonly client: MatrixClient,
|
||||
) {
|
||||
super();
|
||||
this._timelineSet = new EventTimelineSet(room, {
|
||||
unstableClientRelationAggregation: true,
|
||||
timelineSupport: true,
|
||||
});
|
||||
events.forEach(event => this.addEvent(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event to the thread and updates
|
||||
* the tail/root references if needed
|
||||
* Will fire "Thread.update"
|
||||
* @param event The event to add
|
||||
*/
|
||||
public async addEvent(event: MatrixEvent): Promise<void> {
|
||||
if (this._timelineSet.findEventById(event.getId()) || event.status !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.tail.has(event.replyEventId)) {
|
||||
this.tail.delete(event.replyEventId);
|
||||
}
|
||||
this.tail.add(event.getId());
|
||||
|
||||
if (!event.replyEventId || !this._timelineSet.findEventById(event.replyEventId)) {
|
||||
this.root = event.getId();
|
||||
}
|
||||
|
||||
event.setThread(this);
|
||||
this._timelineSet.addLiveEvent(event);
|
||||
|
||||
if (this.ready) {
|
||||
this.client.decryptEventIfNeeded(event, {});
|
||||
this.emit("Thread.update", this);
|
||||
} else {
|
||||
this.emit("Thread.update", this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the reply chain with all events
|
||||
* missing from the current sync data
|
||||
* Will fire "Thread.ready"
|
||||
*/
|
||||
public async fetchReplyChain(): Promise<void> {
|
||||
if (!this.ready) {
|
||||
let mxEvent = this.room.findEventById(this.rootEvent.replyEventId);
|
||||
if (!mxEvent) {
|
||||
mxEvent = await this.fetchEventById(
|
||||
this.rootEvent.getRoomId(),
|
||||
this.rootEvent.replyEventId,
|
||||
);
|
||||
}
|
||||
|
||||
this.addEvent(mxEvent);
|
||||
if (mxEvent.replyEventId) {
|
||||
await this.fetchReplyChain();
|
||||
} else {
|
||||
await this.decryptEvents();
|
||||
this.emit("Thread.ready", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async decryptEvents(): Promise<void> {
|
||||
await Promise.allSettled(
|
||||
Array.from(this._timelineSet.getLiveTimeline().getEvents()).map(event => {
|
||||
return this.client.decryptEventIfNeeded(event, {});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an event over the network
|
||||
*/
|
||||
private async fetchEventById(roomId: string, eventId: string): Promise<MatrixEvent> {
|
||||
const response = await this.client.http.authedRequest(
|
||||
undefined,
|
||||
"GET",
|
||||
`/rooms/${roomId}/event/${eventId}`,
|
||||
);
|
||||
return new MatrixEvent(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an event by ID in the current thread
|
||||
*/
|
||||
public findEventById(eventId: string) {
|
||||
return this._timelineSet.findEventById(eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines thread's ready status
|
||||
*/
|
||||
public get ready(): boolean {
|
||||
return this.rootEvent.replyEventId === undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread ID, which is the same as the root event ID
|
||||
*/
|
||||
public get id(): string {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread root event
|
||||
*/
|
||||
public get rootEvent(): MatrixEvent {
|
||||
return this.findEventById(this.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of messages in the thread
|
||||
*/
|
||||
public get length(): number {
|
||||
return this._timelineSet.getLiveTimeline().getEvents().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of mxid participating to the thread
|
||||
*/
|
||||
public get participants(): Set<string> {
|
||||
const participants = new Set<string>();
|
||||
this._timelineSet.getLiveTimeline().getEvents().forEach(event => {
|
||||
participants.add(event.getSender());
|
||||
});
|
||||
return participants;
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only getter to access the timeline set
|
||||
*/
|
||||
public get timelineSet(): EventTimelineSet {
|
||||
return this._timelineSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the last event added to the thread
|
||||
*/
|
||||
public get replyToEvent(): MatrixEvent {
|
||||
const events = this._timelineSet.getLiveTimeline().getEvents();
|
||||
return events[events.length -1];
|
||||
}
|
||||
}
|
||||
63
src/sync.ts
63
src/sync.ts
@@ -148,6 +148,7 @@ export class SyncApi {
|
||||
this.opts.resolveInvitesToProfiles = this.opts.resolveInvitesToProfiles || false;
|
||||
this.opts.pollTimeout = this.opts.pollTimeout || (30 * 1000);
|
||||
this.opts.pendingEventOrdering = this.opts.pendingEventOrdering || PendingEventOrdering.Chronological;
|
||||
this.opts.experimentalThreadSupport = this.opts.experimentalThreadSupport === true;
|
||||
|
||||
if (!opts.canResetEntireTimeline) {
|
||||
opts.canResetEntireTimeline = (roomId: string) => {
|
||||
@@ -283,8 +284,9 @@ export class SyncApi {
|
||||
return;
|
||||
}
|
||||
leaveObj.timeline = leaveObj.timeline || {};
|
||||
const timelineEvents =
|
||||
this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
const events = this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
const [timelineEvents, threadedEvents] = this.partitionThreadedEvents(events);
|
||||
|
||||
const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room);
|
||||
|
||||
// set the back-pagination token. Do this *before* adding any
|
||||
@@ -293,17 +295,39 @@ export class SyncApi {
|
||||
EventTimeline.BACKWARDS);
|
||||
|
||||
this.processRoomEvents(room, stateEvents, timelineEvents);
|
||||
this.processThreadEvents(room, threadedEvents);
|
||||
|
||||
room.recalculate();
|
||||
client.store.storeRoom(room);
|
||||
client.emit("Room", room);
|
||||
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
this.processEventsForNotifs(room, events);
|
||||
});
|
||||
return rooms;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Split events between the ones that will end up in the main
|
||||
* room timeline versus the one that need to be processed in a thread
|
||||
* @experimental
|
||||
*/
|
||||
public partitionThreadedEvents(events: MatrixEvent[]): [MatrixEvent[], MatrixEvent[]] {
|
||||
if (this.opts.experimentalThreadSupport) {
|
||||
return events.reduce((memo, event: MatrixEvent) => {
|
||||
memo[event.replyEventId ? 1 : 0].push(event);
|
||||
return memo;
|
||||
}, [[], []]);
|
||||
} else {
|
||||
// When `experimentalThreadSupport` is disabled
|
||||
// treat all events as timelineEvents
|
||||
return [
|
||||
events,
|
||||
[],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek into a room. This will result in the room in question being synced so it
|
||||
* is accessible via getRooms(). Live updates for the room will be provided.
|
||||
@@ -1193,7 +1217,7 @@ export class SyncApi {
|
||||
// this helps large account to speed up faster
|
||||
// room::decryptCriticalEvent is in charge of decrypting all the events
|
||||
// required for a client to function properly
|
||||
const timelineEvents = this.mapSyncEventsFormat(joinObj.timeline, room, false);
|
||||
const events = this.mapSyncEventsFormat(joinObj.timeline, room, false);
|
||||
const ephemeralEvents = this.mapSyncEventsFormat(joinObj.ephemeral);
|
||||
const accountDataEvents = this.mapSyncEventsFormat(joinObj.account_data);
|
||||
|
||||
@@ -1234,8 +1258,8 @@ export class SyncApi {
|
||||
// which we'll try to paginate but not get any new events (which
|
||||
// will stop us linking the empty timeline into the chain).
|
||||
//
|
||||
for (let i = timelineEvents.length - 1; i >= 0; i--) {
|
||||
const eventId = timelineEvents[i].getId();
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
const eventId = events[i].getId();
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
debuglog("Already have event " + eventId + " in limited " +
|
||||
"sync - not resetting");
|
||||
@@ -1244,7 +1268,7 @@ export class SyncApi {
|
||||
// we might still be missing some of the events before i;
|
||||
// we don't want to be adding them to the end of the
|
||||
// timeline because that would put them out of order.
|
||||
timelineEvents.splice(0, i);
|
||||
events.splice(0, i);
|
||||
|
||||
// XXX: there's a problem here if the skipped part of the
|
||||
// timeline modifies the state set in stateEvents, because
|
||||
@@ -1274,7 +1298,10 @@ export class SyncApi {
|
||||
}
|
||||
}
|
||||
|
||||
const [timelineEvents, threadedEvents] = this.partitionThreadedEvents(events);
|
||||
|
||||
this.processRoomEvents(room, stateEvents, timelineEvents, syncEventData.fromCache);
|
||||
this.processThreadEvents(room, threadedEvents);
|
||||
|
||||
// set summary after processing events,
|
||||
// because it will trigger a name calculation
|
||||
@@ -1295,7 +1322,7 @@ export class SyncApi {
|
||||
client.emit("Room", room);
|
||||
}
|
||||
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
this.processEventsForNotifs(room, events);
|
||||
|
||||
const processRoomEvent = async (e) => {
|
||||
client.emit("event", e);
|
||||
@@ -1316,6 +1343,7 @@ export class SyncApi {
|
||||
|
||||
await utils.promiseMapSeries(stateEvents, processRoomEvent);
|
||||
await utils.promiseMapSeries(timelineEvents, processRoomEvent);
|
||||
await utils.promiseMapSeries(threadedEvents, processRoomEvent);
|
||||
ephemeralEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
@@ -1335,10 +1363,13 @@ export class SyncApi {
|
||||
leaveRooms.forEach((leaveObj) => {
|
||||
const room = leaveObj.room;
|
||||
const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room);
|
||||
const timelineEvents = this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
const events = this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
const accountDataEvents = this.mapSyncEventsFormat(leaveObj.account_data);
|
||||
|
||||
const [timelineEvents, threadedEvents] = this.partitionThreadedEvents(events);
|
||||
|
||||
this.processRoomEvents(room, stateEvents, timelineEvents);
|
||||
this.processThreadEvents(room, threadedEvents);
|
||||
room.addAccountData(accountDataEvents);
|
||||
|
||||
room.recalculate();
|
||||
@@ -1347,7 +1378,7 @@ export class SyncApi {
|
||||
client.emit("Room", room);
|
||||
}
|
||||
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
this.processEventsForNotifs(room, events);
|
||||
|
||||
stateEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
@@ -1355,6 +1386,9 @@ export class SyncApi {
|
||||
timelineEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
threadedEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
accountDataEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
@@ -1674,6 +1708,15 @@ export class SyncApi {
|
||||
room.addLiveEvents(timelineEventList || [], null, fromCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
private processThreadEvents(room: Room, threadedEvents: MatrixEvent[]): void {
|
||||
threadedEvents.forEach(event => {
|
||||
room.addThreadedEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of timelineEvents and adds and adds to notifEvents
|
||||
* as appropriate.
|
||||
|
||||
@@ -31,14 +31,21 @@ import { randomString } from '../randomstring';
|
||||
import {
|
||||
MCallReplacesEvent,
|
||||
MCallAnswer,
|
||||
MCallOfferNegotiate,
|
||||
MCallInviteNegotiate,
|
||||
CallCapabilities,
|
||||
SDPStreamMetadataPurpose,
|
||||
SDPStreamMetadata,
|
||||
SDPStreamMetadataKey,
|
||||
MCallSDPStreamMetadataChanged,
|
||||
MCallSelectAnswer,
|
||||
MCAllAssertedIdentity,
|
||||
MCallCandidates,
|
||||
MCallBase,
|
||||
MCallHangupReject,
|
||||
} from './callEventTypes';
|
||||
import { CallFeed } from './callFeed';
|
||||
import { MatrixClient } from "../client";
|
||||
import { ISendEventResponse } from "../@types/requests";
|
||||
|
||||
// events: hangup, error(err), replaced(call), state(state, oldState)
|
||||
|
||||
@@ -261,30 +268,33 @@ function genCallID(): string {
|
||||
* @param {MatrixClient} opts.client The Matrix Client instance to send events to.
|
||||
*/
|
||||
export class MatrixCall extends EventEmitter {
|
||||
roomId: string;
|
||||
type: CallType;
|
||||
callId: string;
|
||||
state: CallState;
|
||||
hangupParty: CallParty;
|
||||
hangupReason: string;
|
||||
direction: CallDirection;
|
||||
ourPartyId: string;
|
||||
public roomId: string;
|
||||
public type: CallType = null;
|
||||
public callId: string;
|
||||
public state = CallState.Fledgling;
|
||||
public hangupParty: CallParty;
|
||||
public hangupReason: string;
|
||||
public direction: CallDirection;
|
||||
public ourPartyId: string;
|
||||
|
||||
private client: any; // Fix when client is TSified
|
||||
private client: MatrixClient;
|
||||
private forceTURN: boolean;
|
||||
private turnServers: Array<TurnServer>;
|
||||
private candidateSendQueue: Array<RTCIceCandidate>;
|
||||
private candidateSendTries: number;
|
||||
private sentEndOfCandidates: boolean;
|
||||
// A queue for candidates waiting to go out.
|
||||
// We try to amalgamate candidates into a single candidate message where
|
||||
// possible
|
||||
private candidateSendQueue: Array<RTCIceCandidate> = [];
|
||||
private candidateSendTries = 0;
|
||||
private sentEndOfCandidates = false;
|
||||
private peerConn: RTCPeerConnection;
|
||||
private feeds: Array<CallFeed>;
|
||||
private usermediaSenders: Array<RTCRtpSender>;
|
||||
private screensharingSenders: Array<RTCRtpSender>;
|
||||
private inviteOrAnswerSent: boolean;
|
||||
private feeds: Array<CallFeed> = [];
|
||||
private usermediaSenders: Array<RTCRtpSender> = [];
|
||||
private screensharingSenders: Array<RTCRtpSender> = [];
|
||||
private inviteOrAnswerSent = false;
|
||||
private waitForLocalAVStream: boolean;
|
||||
private successor: MatrixCall;
|
||||
private opponentMember: RoomMember;
|
||||
private opponentVersion: number;
|
||||
private opponentVersion: number | string;
|
||||
// The party ID of the other side: undefined if we haven't chosen a partner
|
||||
// yet, null if we have but they didn't send a party ID.
|
||||
private opponentPartyId: string;
|
||||
@@ -293,7 +303,7 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
// The logic of when & if a call is on hold is nontrivial and explained in is*OnHold
|
||||
// This flag represents whether we want the other party to be on hold
|
||||
private remoteOnHold;
|
||||
private remoteOnHold = false;
|
||||
|
||||
// the stats for the call at the point it ended. We can't get these after we
|
||||
// tear the call down, so we just grab a snapshot before we stop the call.
|
||||
@@ -301,7 +311,7 @@ export class MatrixCall extends EventEmitter {
|
||||
private callStatsAtEnd: any[];
|
||||
|
||||
// Perfect negotiation state: https://www.w3.org/TR/webrtc/#perfect-negotiation-example
|
||||
private makingOffer: boolean;
|
||||
private makingOffer = false;
|
||||
private ignoreOffer: boolean;
|
||||
|
||||
// If candidates arrive before we've picked an opponent (which, in particular,
|
||||
@@ -317,7 +327,6 @@ export class MatrixCall extends EventEmitter {
|
||||
super();
|
||||
this.roomId = opts.roomId;
|
||||
this.client = opts.client;
|
||||
this.type = null;
|
||||
this.forceTURN = opts.forceTURN;
|
||||
this.ourPartyId = this.client.deviceId;
|
||||
// Array of Objects with urls, username, credential keys
|
||||
@@ -330,33 +339,14 @@ export class MatrixCall extends EventEmitter {
|
||||
for (const server of this.turnServers) {
|
||||
utils.checkObjectHasKeys(server, ["urls"]);
|
||||
}
|
||||
|
||||
this.callId = genCallID();
|
||||
this.state = CallState.Fledgling;
|
||||
|
||||
// A queue for candidates waiting to go out.
|
||||
// We try to amalgamate candidates into a single candidate message where
|
||||
// possible
|
||||
this.candidateSendQueue = [];
|
||||
this.candidateSendTries = 0;
|
||||
|
||||
this.sentEndOfCandidates = false;
|
||||
this.inviteOrAnswerSent = false;
|
||||
this.makingOffer = false;
|
||||
|
||||
this.remoteOnHold = false;
|
||||
|
||||
this.feeds = [];
|
||||
|
||||
this.usermediaSenders = [];
|
||||
this.screensharingSenders = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a voice call to this room.
|
||||
* @throws If you have not specified a listener for 'error' events.
|
||||
*/
|
||||
async placeVoiceCall() {
|
||||
public async placeVoiceCall(): Promise<void> {
|
||||
logger.debug("placeVoiceCall");
|
||||
this.checkForErrorListener();
|
||||
const constraints = getUserMediaContraints(ConstraintsType.Audio);
|
||||
@@ -368,7 +358,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Place a video call to this room.
|
||||
* @throws If you have not specified a listener for 'error' events.
|
||||
*/
|
||||
async placeVideoCall() {
|
||||
public async placeVideoCall(): Promise<void> {
|
||||
logger.debug("placeVideoCall");
|
||||
this.checkForErrorListener();
|
||||
const constraints = getUserMediaContraints(ConstraintsType.Video);
|
||||
@@ -376,11 +366,11 @@ export class MatrixCall extends EventEmitter {
|
||||
await this.placeCallWithConstraints(constraints);
|
||||
}
|
||||
|
||||
public getOpponentMember() {
|
||||
public getOpponentMember(): RoomMember {
|
||||
return this.opponentMember;
|
||||
}
|
||||
|
||||
public opponentCanBeTransferred() {
|
||||
public opponentCanBeTransferred(): boolean {
|
||||
return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]);
|
||||
}
|
||||
|
||||
@@ -462,7 +452,7 @@ export class MatrixCall extends EventEmitter {
|
||||
return !this.feeds.some((feed) => !feed.isLocal());
|
||||
}
|
||||
|
||||
private pushRemoteFeed(stream: MediaStream) {
|
||||
private pushRemoteFeed(stream: MediaStream): void {
|
||||
// Fallback to old behavior if the other side doesn't support SDPStreamMetadata
|
||||
if (!this.opponentSupportsSDPStreamMetadata()) {
|
||||
this.pushRemoteFeedWithoutMetadata(stream);
|
||||
@@ -495,7 +485,7 @@ export class MatrixCall extends EventEmitter {
|
||||
/**
|
||||
* This method is used ONLY if the other client doesn't support sending SDPStreamMetadata
|
||||
*/
|
||||
private pushRemoteFeedWithoutMetadata(stream: MediaStream) {
|
||||
private pushRemoteFeedWithoutMetadata(stream: MediaStream): void {
|
||||
const userId = this.getOpponentMember().userId;
|
||||
// We can guess the purpose here since the other client can only send one stream
|
||||
const purpose = SDPStreamMetadataPurpose.Usermedia;
|
||||
@@ -523,7 +513,7 @@ export class MatrixCall extends EventEmitter {
|
||||
logger.info(`Pushed remote stream (id="${stream.id}", active="${stream.active}")`);
|
||||
}
|
||||
|
||||
private pushLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true) {
|
||||
private pushLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true): void {
|
||||
const userId = this.client.getUserId();
|
||||
|
||||
// We try to replace an existing feed if there already is one with the same purpose
|
||||
@@ -562,12 +552,12 @@ export class MatrixCall extends EventEmitter {
|
||||
logger.info(`Pushed local stream (id="${stream.id}", active="${stream.active}", purpose="${purpose}")`);
|
||||
}
|
||||
|
||||
private deleteAllFeeds() {
|
||||
private deleteAllFeeds(): void {
|
||||
this.feeds = [];
|
||||
this.emit(CallEvent.FeedsChanged, this.feeds);
|
||||
}
|
||||
|
||||
private deleteFeedByStream(stream: MediaStream) {
|
||||
private deleteFeedByStream(stream: MediaStream): void {
|
||||
logger.debug(`Removing feed with stream id ${stream.id}`);
|
||||
|
||||
const feed = this.getFeedByStreamId(stream.id);
|
||||
@@ -607,8 +597,8 @@ export class MatrixCall extends EventEmitter {
|
||||
* Configure this call from an invite event. Used by MatrixClient.
|
||||
* @param {MatrixEvent} event The m.call.invite event
|
||||
*/
|
||||
async initWithInvite(event: MatrixEvent) {
|
||||
const invite = event.getContent();
|
||||
public async initWithInvite(event: MatrixEvent): Promise<void> {
|
||||
const invite = event.getContent<MCallInviteNegotiate>();
|
||||
this.direction = CallDirection.Inbound;
|
||||
|
||||
// make sure we have valid turn creds. Unless something's gone wrong, it should
|
||||
@@ -674,7 +664,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Configure this call from a hangup or reject event. Used by MatrixClient.
|
||||
* @param {MatrixEvent} event The m.call.hangup event
|
||||
*/
|
||||
initWithHangup(event: MatrixEvent) {
|
||||
public initWithHangup(event: MatrixEvent): void {
|
||||
// perverse as it may seem, sometimes we want to instantiate a call with a
|
||||
// hangup message (because when getting the state of the room on load, events
|
||||
// come in reverse order and we want to remember that a call has been hung up)
|
||||
@@ -684,7 +674,7 @@ export class MatrixCall extends EventEmitter {
|
||||
/**
|
||||
* Answer a call.
|
||||
*/
|
||||
async answer() {
|
||||
public async answer(): Promise<void> {
|
||||
if (this.inviteOrAnswerSent) {
|
||||
return;
|
||||
}
|
||||
@@ -719,7 +709,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* MatrixClient.
|
||||
* @param {MatrixCall} newCall The new call.
|
||||
*/
|
||||
replacedBy(newCall: MatrixCall) {
|
||||
public replacedBy(newCall: MatrixCall): void {
|
||||
if (this.state === CallState.WaitLocalMedia) {
|
||||
logger.debug("Telling new call to wait for local media");
|
||||
newCall.waitForLocalAVStream = true;
|
||||
@@ -737,7 +727,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* @param {string} reason The reason why the call is being hung up.
|
||||
* @param {boolean} suppressEvent True to suppress emitting an event.
|
||||
*/
|
||||
hangup(reason: CallErrorCode, suppressEvent: boolean) {
|
||||
public hangup(reason: CallErrorCode, suppressEvent: boolean): void {
|
||||
if (this.callHasEnded()) return;
|
||||
|
||||
logger.debug("Ending call " + this.callId);
|
||||
@@ -757,7 +747,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* This used to be done by calling hangup, but is a separate method and protocol
|
||||
* event as of MSC2746.
|
||||
*/
|
||||
reject() {
|
||||
public reject(): void {
|
||||
if (this.state !== CallState.Ringing) {
|
||||
throw Error("Call must be in 'ringing' state to reject!");
|
||||
}
|
||||
@@ -800,7 +790,7 @@ export class MatrixCall extends EventEmitter {
|
||||
public async setScreensharingEnabled(
|
||||
enabled: boolean,
|
||||
selectDesktopCapturerSource?: () => Promise<DesktopCapturerSource>,
|
||||
) {
|
||||
): Promise<boolean> {
|
||||
// Skip if there is nothing to do
|
||||
if (enabled && this.isScreensharing()) {
|
||||
logger.warn(`There is already a screensharing stream - there is nothing to do!`);
|
||||
@@ -850,7 +840,7 @@ export class MatrixCall extends EventEmitter {
|
||||
private async setScreensharingEnabledWithoutMetadataSupport(
|
||||
enabled: boolean,
|
||||
selectDesktopCapturerSource?: () => Promise<DesktopCapturerSource>,
|
||||
) {
|
||||
): Promise<boolean> {
|
||||
logger.debug(`Set screensharing enabled? ${enabled} using replaceTrack()`);
|
||||
if (enabled) {
|
||||
try {
|
||||
@@ -896,7 +886,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Set whether our outbound video should be muted or not.
|
||||
* @param {boolean} muted True to mute the outbound video.
|
||||
*/
|
||||
setLocalVideoMuted(muted: boolean) {
|
||||
public setLocalVideoMuted(muted: boolean): void {
|
||||
this.localUsermediaFeed?.setVideoMuted(muted);
|
||||
this.updateMuteStatus();
|
||||
}
|
||||
@@ -910,7 +900,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* @return {Boolean} True if the local preview video is muted, else false
|
||||
* (including if the call is not set up yet).
|
||||
*/
|
||||
isLocalVideoMuted(): boolean {
|
||||
public isLocalVideoMuted(): boolean {
|
||||
return this.localUsermediaFeed?.isVideoMuted();
|
||||
}
|
||||
|
||||
@@ -918,7 +908,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Set whether the microphone should be muted or not.
|
||||
* @param {boolean} muted True to mute the mic.
|
||||
*/
|
||||
setMicrophoneMuted(muted: boolean) {
|
||||
public setMicrophoneMuted(muted: boolean): void {
|
||||
this.localUsermediaFeed?.setAudioMuted(muted);
|
||||
this.updateMuteStatus();
|
||||
}
|
||||
@@ -932,7 +922,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* @return {Boolean} True if the mic is muted, else false (including if the call
|
||||
* is not set up yet).
|
||||
*/
|
||||
isMicrophoneMuted(): boolean {
|
||||
public isMicrophoneMuted(): boolean {
|
||||
return this.localUsermediaFeed?.isAudioMuted();
|
||||
}
|
||||
|
||||
@@ -940,11 +930,11 @@ export class MatrixCall extends EventEmitter {
|
||||
* @returns true if we have put the party on the other side of the call on hold
|
||||
* (that is, we are signalling to them that we are not listening)
|
||||
*/
|
||||
isRemoteOnHold(): boolean {
|
||||
public isRemoteOnHold(): boolean {
|
||||
return this.remoteOnHold;
|
||||
}
|
||||
|
||||
setRemoteOnHold(onHold: boolean) {
|
||||
public setRemoteOnHold(onHold: boolean): void {
|
||||
if (this.isRemoteOnHold() === onHold) return;
|
||||
this.remoteOnHold = onHold;
|
||||
|
||||
@@ -964,7 +954,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* they cannot hear us).
|
||||
* @returns true if the other party has put us on hold
|
||||
*/
|
||||
isLocalOnHold(): boolean {
|
||||
public isLocalOnHold(): boolean {
|
||||
if (this.state !== CallState.Connected) return false;
|
||||
|
||||
let callOnHold = true;
|
||||
@@ -984,7 +974,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Sends a DTMF digit to the other party
|
||||
* @param digit The digit (nb. string - '#' and '*' are dtmf too)
|
||||
*/
|
||||
sendDtmfDigit(digit: string) {
|
||||
public sendDtmfDigit(digit: string): void {
|
||||
for (const sender of this.peerConn.getSenders()) {
|
||||
if (sender.track.kind === 'audio' && sender.dtmf) {
|
||||
sender.dtmf.insertDTMF(digit);
|
||||
@@ -995,7 +985,7 @@ export class MatrixCall extends EventEmitter {
|
||||
throw new Error("Unable to find a track to send DTMF on");
|
||||
}
|
||||
|
||||
private updateMuteStatus() {
|
||||
private updateMuteStatus(): void {
|
||||
this.sendVoipEvent(EventType.CallSDPStreamMetadataChangedPrefix, {
|
||||
[SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(),
|
||||
});
|
||||
@@ -1011,7 +1001,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Internal
|
||||
* @param {Object} stream
|
||||
*/
|
||||
private gotUserMediaForInvite = async (stream: MediaStream) => {
|
||||
private gotUserMediaForInvite = async (stream: MediaStream): Promise<void> => {
|
||||
if (this.successor) {
|
||||
this.successor.gotUserMediaForAnswer(stream);
|
||||
return;
|
||||
@@ -1028,7 +1018,7 @@ export class MatrixCall extends EventEmitter {
|
||||
// Now we wait for the negotiationneeded event
|
||||
};
|
||||
|
||||
private async sendAnswer() {
|
||||
private async sendAnswer(): Promise<void> {
|
||||
const answerContent = {
|
||||
answer: {
|
||||
sdp: this.peerConn.localDescription.sdp,
|
||||
@@ -1075,7 +1065,7 @@ export class MatrixCall extends EventEmitter {
|
||||
this.sendCandidateQueue();
|
||||
}
|
||||
|
||||
private gotUserMediaForAnswer = async (stream: MediaStream) => {
|
||||
private gotUserMediaForAnswer = async (stream: MediaStream): Promise<void> => {
|
||||
if (this.callHasEnded()) {
|
||||
return;
|
||||
}
|
||||
@@ -1114,7 +1104,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Internal
|
||||
* @param {Object} event
|
||||
*/
|
||||
private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent) => {
|
||||
private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent): Promise<void> => {
|
||||
if (event.candidate) {
|
||||
logger.debug(
|
||||
"Call " + this.callId + " got local ICE " + event.candidate.sdpMid + " candidate: " +
|
||||
@@ -1133,7 +1123,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
private onIceGatheringStateChange = (event: Event) => {
|
||||
private onIceGatheringStateChange = (event: Event): void => {
|
||||
logger.debug("ice gathering state changed to " + this.peerConn.iceGatheringState);
|
||||
if (this.peerConn.iceGatheringState === 'complete' && !this.sentEndOfCandidates) {
|
||||
// If we didn't get an empty-string candidate to signal the end of candidates,
|
||||
@@ -1151,19 +1141,20 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
async onRemoteIceCandidatesReceived(ev: MatrixEvent) {
|
||||
public async onRemoteIceCandidatesReceived(ev: MatrixEvent): Promise<void> {
|
||||
if (this.callHasEnded()) {
|
||||
//debuglog("Ignoring remote ICE candidate because call has ended");
|
||||
return;
|
||||
}
|
||||
|
||||
const candidates = ev.getContent().candidates;
|
||||
const content = ev.getContent<MCallCandidates>();
|
||||
const candidates = content.candidates;
|
||||
if (!candidates) {
|
||||
logger.info("Ignoring candidates event with no candidates!");
|
||||
return;
|
||||
}
|
||||
|
||||
const fromPartyId = ev.getContent().version === 0 ? null : ev.getContent().party_id || null;
|
||||
const fromPartyId = content.version === 0 ? null : content.party_id || null;
|
||||
|
||||
if (this.opponentPartyId === undefined) {
|
||||
// we haven't picked an opponent yet so save the candidates
|
||||
@@ -1174,9 +1165,9 @@ export class MatrixCall extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.partyIdMatches(ev.getContent())) {
|
||||
if (!this.partyIdMatches(content)) {
|
||||
logger.info(
|
||||
`Ignoring candidates from party ID ${ev.getContent().party_id}: ` +
|
||||
`Ignoring candidates from party ID ${content.party_id}: ` +
|
||||
`we have chosen party ID ${this.opponentPartyId}`,
|
||||
);
|
||||
|
||||
@@ -1190,8 +1181,9 @@ export class MatrixCall extends EventEmitter {
|
||||
* Used by MatrixClient.
|
||||
* @param {Object} msg
|
||||
*/
|
||||
async onAnswerReceived(event: MatrixEvent) {
|
||||
logger.debug(`Got answer for call ID ${this.callId} from party ID ${event.getContent().party_id}`);
|
||||
public async onAnswerReceived(event: MatrixEvent): Promise<void> {
|
||||
const content = event.getContent<MCallAnswer>();
|
||||
logger.debug(`Got answer for call ID ${this.callId} from party ID ${content.party_id}`);
|
||||
|
||||
if (this.callHasEnded()) {
|
||||
logger.debug(`Ignoring answer because call ID ${this.callId} has ended`);
|
||||
@@ -1200,7 +1192,7 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
if (this.opponentPartyId !== undefined) {
|
||||
logger.info(
|
||||
`Ignoring answer from party ID ${event.getContent().party_id}: ` +
|
||||
`Ignoring answer from party ID ${content.party_id}: ` +
|
||||
`we already have an answer/reject from ${this.opponentPartyId}`,
|
||||
);
|
||||
return;
|
||||
@@ -1211,7 +1203,7 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
this.setState(CallState.Connecting);
|
||||
|
||||
const sdpStreamMetadata = event.getContent()[SDPStreamMetadataKey];
|
||||
const sdpStreamMetadata = content[SDPStreamMetadataKey];
|
||||
if (sdpStreamMetadata) {
|
||||
this.updateRemoteSDPStreamMetadata(sdpStreamMetadata);
|
||||
} else {
|
||||
@@ -1219,7 +1211,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.peerConn.setRemoteDescription(event.getContent().answer);
|
||||
await this.peerConn.setRemoteDescription(content.answer);
|
||||
} catch (e) {
|
||||
logger.debug("Failed to set remote description", e);
|
||||
this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false);
|
||||
@@ -1242,13 +1234,13 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async onSelectAnswerReceived(event: MatrixEvent) {
|
||||
public async onSelectAnswerReceived(event: MatrixEvent): Promise<void> {
|
||||
if (this.direction !== CallDirection.Inbound) {
|
||||
logger.warn("Got select_answer for an outbound call: ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPartyId = event.getContent().selected_party_id;
|
||||
const selectedPartyId = event.getContent<MCallSelectAnswer>().selected_party_id;
|
||||
|
||||
if (selectedPartyId === undefined || selectedPartyId === null) {
|
||||
logger.warn("Got nonsensical select_answer with null/undefined selected_party_id: ignoring");
|
||||
@@ -1262,8 +1254,9 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async onNegotiateReceived(event: MatrixEvent) {
|
||||
const description = event.getContent().description;
|
||||
public async onNegotiateReceived(event: MatrixEvent): Promise<void> {
|
||||
const content = event.getContent<MCallInviteNegotiate>();
|
||||
const description = content.description;
|
||||
if (!description || !description.sdp || !description.type) {
|
||||
logger.info("Ignoring invalid m.call.negotiate event");
|
||||
return;
|
||||
@@ -1288,7 +1281,7 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
const prevLocalOnHold = this.isLocalOnHold();
|
||||
|
||||
const sdpStreamMetadata = event.getContent()[SDPStreamMetadataKey];
|
||||
const sdpStreamMetadata = content[SDPStreamMetadataKey];
|
||||
if (sdpStreamMetadata) {
|
||||
this.updateRemoteSDPStreamMetadata(sdpStreamMetadata);
|
||||
} else {
|
||||
@@ -1336,12 +1329,13 @@ export class MatrixCall extends EventEmitter {
|
||||
this.updateRemoteSDPStreamMetadata(metadata);
|
||||
}
|
||||
|
||||
async onAssertedIdentityReceived(event: MatrixEvent) {
|
||||
if (!event.getContent().asserted_identity) return;
|
||||
public async onAssertedIdentityReceived(event: MatrixEvent): Promise<void> {
|
||||
const content = event.getContent<MCAllAssertedIdentity>();
|
||||
if (!content.asserted_identity) return;
|
||||
|
||||
this.remoteAssertedIdentity = {
|
||||
id: event.getContent().asserted_identity.id,
|
||||
displayName: event.getContent().asserted_identity.display_name,
|
||||
id: content.asserted_identity.id,
|
||||
displayName: content.asserted_identity.display_name,
|
||||
};
|
||||
this.emit(CallEvent.AssertedIdentityChanged);
|
||||
}
|
||||
@@ -1353,7 +1347,7 @@ export class MatrixCall extends EventEmitter {
|
||||
return this.state === CallState.Ended;
|
||||
}
|
||||
|
||||
private gotLocalOffer = async (description: RTCSessionDescriptionInit) => {
|
||||
private gotLocalOffer = async (description: RTCSessionDescriptionInit): Promise<void> => {
|
||||
logger.debug("Created offer: ", description);
|
||||
|
||||
if (this.callHasEnded()) {
|
||||
@@ -1383,7 +1377,7 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
const content = {
|
||||
lifetime: CALL_TIMEOUT_MS,
|
||||
} as MCallOfferNegotiate;
|
||||
} as MCallInviteNegotiate;
|
||||
|
||||
// clunky because TypeScript can't follow the types through if we use an expression as the key
|
||||
if (this.state === CallState.CreateOffer) {
|
||||
@@ -1442,7 +1436,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
private getLocalOfferFailed = (err: Error) => {
|
||||
private getLocalOfferFailed = (err: Error): void => {
|
||||
logger.error("Failed to get local offer", err);
|
||||
|
||||
this.emit(
|
||||
@@ -1455,7 +1449,7 @@ export class MatrixCall extends EventEmitter {
|
||||
this.terminate(CallParty.Local, CallErrorCode.LocalOfferFailed, false);
|
||||
};
|
||||
|
||||
private getUserMediaFailed = (err: Error) => {
|
||||
private getUserMediaFailed = (err: Error): void => {
|
||||
if (this.successor) {
|
||||
this.successor.getUserMediaFailed(err);
|
||||
return;
|
||||
@@ -1474,7 +1468,7 @@ export class MatrixCall extends EventEmitter {
|
||||
this.terminate(CallParty.Local, CallErrorCode.NoUserMedia, false);
|
||||
};
|
||||
|
||||
onIceConnectionStateChanged = () => {
|
||||
private onIceConnectionStateChanged = (): void => {
|
||||
if (this.callHasEnded()) {
|
||||
return; // because ICE can still complete as we're ending the call
|
||||
}
|
||||
@@ -1490,14 +1484,14 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
private onSignallingStateChanged = () => {
|
||||
private onSignallingStateChanged = (): void => {
|
||||
logger.debug(
|
||||
"call " + this.callId + ": Signalling state changed to: " +
|
||||
this.peerConn.signalingState,
|
||||
);
|
||||
};
|
||||
|
||||
private onTrack = (ev: RTCTrackEvent) => {
|
||||
private onTrack = (ev: RTCTrackEvent): void => {
|
||||
if (ev.streams.length === 0) {
|
||||
logger.warn(`Streamless ${ev.track.kind} found: ignoring.`);
|
||||
return;
|
||||
@@ -1521,7 +1515,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* [96685:23:0518/162603.933377:ERROR:webrtc_video_engine.cc(1171)] GetChangedRecvParameters called without any video codecs.
|
||||
* [96685:23:0518/162603.933430:ERROR:sdp_offer_answer.cc(4302)] Failed to set local video description recv parameters for m-section with mid='2'. (INVALID_PARAMETER)
|
||||
*/
|
||||
private getRidOfRTXCodecs() {
|
||||
private getRidOfRTXCodecs(): void {
|
||||
// RTCRtpReceiver.getCapabilities and RTCRtpSender.getCapabilities don't seem to be supported on FF
|
||||
if (!RTCRtpReceiver.getCapabilities || !RTCRtpSender.getCapabilities) return;
|
||||
|
||||
@@ -1549,7 +1543,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
onNegotiationNeeded = async () => {
|
||||
private onNegotiationNeeded = async (): Promise<void> => {
|
||||
logger.info("Negotiation is needed!");
|
||||
|
||||
if (this.state !== CallState.CreateOffer && this.opponentVersion === 0) {
|
||||
@@ -1570,7 +1564,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
onHangupReceived = (msg) => {
|
||||
public onHangupReceived = (msg: MCallHangupReject): void => {
|
||||
logger.debug("Hangup received for call ID " + this.callId);
|
||||
|
||||
// party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen
|
||||
@@ -1583,7 +1577,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
onRejectReceived = (msg) => {
|
||||
public onRejectReceived = (msg: MCallHangupReject): void => {
|
||||
logger.debug("Reject received for call ID " + this.callId);
|
||||
|
||||
// No need to check party_id for reject because if we'd received either
|
||||
@@ -1605,12 +1599,12 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
onAnsweredElsewhere = (msg) => {
|
||||
public onAnsweredElsewhere = (msg: MCallAnswer): void => {
|
||||
logger.debug("Call ID " + this.callId + " answered elsewhere");
|
||||
this.terminate(CallParty.Remote, CallErrorCode.AnsweredElsewhere, true);
|
||||
};
|
||||
|
||||
setState(state: CallState) {
|
||||
private setState(state: CallState): void {
|
||||
const oldState = this.state;
|
||||
this.state = state;
|
||||
this.emit(CallEvent.State, state, oldState);
|
||||
@@ -1622,7 +1616,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* @param {Object} content
|
||||
* @return {Promise}
|
||||
*/
|
||||
private sendVoipEvent(eventType: string, content: object) {
|
||||
private sendVoipEvent(eventType: string, content: object): Promise<ISendEventResponse> {
|
||||
return this.client.sendEvent(this.roomId, eventType, Object.assign({}, content, {
|
||||
version: VOIP_PROTO_VERSION,
|
||||
call_id: this.callId,
|
||||
@@ -1630,7 +1624,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}));
|
||||
}
|
||||
|
||||
queueCandidate(content: RTCIceCandidate) {
|
||||
private queueCandidate(content: RTCIceCandidate): void {
|
||||
// We partially de-trickle candidates by waiting for `delay` before sending them
|
||||
// amalgamated, in order to avoid sending too many m.call.candidates events and hitting
|
||||
// rate limits in Matrix.
|
||||
@@ -1664,7 +1658,7 @@ export class MatrixCall extends EventEmitter {
|
||||
/*
|
||||
* Transfers this call to another user
|
||||
*/
|
||||
async transfer(targetUserId: string) {
|
||||
public async transfer(targetUserId: string): Promise<void> {
|
||||
// Fetch the target user's global profile info: their room avatar / displayname
|
||||
// could be different in whatever room we share with them.
|
||||
const profileInfo = await this.client.getProfileInfo(targetUserId);
|
||||
@@ -1675,7 +1669,7 @@ export class MatrixCall extends EventEmitter {
|
||||
replacement_id: genCallID(),
|
||||
target_user: {
|
||||
id: targetUserId,
|
||||
display_name: profileInfo.display_name,
|
||||
display_name: profileInfo.displayname,
|
||||
avatar_url: profileInfo.avatar_url,
|
||||
},
|
||||
create_call: replacementId,
|
||||
@@ -1690,7 +1684,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Transfers this call to the target call, effectively 'joining' the
|
||||
* two calls (so the remote parties on each call are connected together).
|
||||
*/
|
||||
async transferToCall(transferTargetCall?: MatrixCall) {
|
||||
public async transferToCall(transferTargetCall?: MatrixCall): Promise<void> {
|
||||
const targetProfileInfo = await this.client.getProfileInfo(transferTargetCall.getOpponentMember().userId);
|
||||
const transfereeProfileInfo = await this.client.getProfileInfo(this.getOpponentMember().userId);
|
||||
|
||||
@@ -1702,7 +1696,7 @@ export class MatrixCall extends EventEmitter {
|
||||
replacement_id: genCallID(),
|
||||
target_user: {
|
||||
id: this.getOpponentMember().userId,
|
||||
display_name: transfereeProfileInfo.display_name,
|
||||
display_name: transfereeProfileInfo.displayname,
|
||||
avatar_url: transfereeProfileInfo.avatar_url,
|
||||
},
|
||||
await_call: newCallId,
|
||||
@@ -1714,7 +1708,7 @@ export class MatrixCall extends EventEmitter {
|
||||
replacement_id: genCallID(),
|
||||
target_user: {
|
||||
id: transferTargetCall.getOpponentMember().userId,
|
||||
display_name: targetProfileInfo.display_name,
|
||||
display_name: targetProfileInfo.displayname,
|
||||
avatar_url: targetProfileInfo.avatar_url,
|
||||
},
|
||||
create_call: newCallId,
|
||||
@@ -1726,7 +1720,7 @@ export class MatrixCall extends EventEmitter {
|
||||
await transferTargetCall.terminate(CallParty.Local, CallErrorCode.Transfered, true);
|
||||
}
|
||||
|
||||
private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) {
|
||||
private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean): Promise<void> {
|
||||
if (this.callHasEnded()) return;
|
||||
|
||||
this.callStatsAtEnd = await this.collectCallStats();
|
||||
@@ -1752,7 +1746,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private stopAllMedia() {
|
||||
private stopAllMedia(): void {
|
||||
logger.debug(`stopAllMedia (stream=${this.localUsermediaStream})`);
|
||||
|
||||
for (const feed of this.feeds) {
|
||||
@@ -1762,7 +1756,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private checkForErrorListener() {
|
||||
private checkForErrorListener(): void {
|
||||
if (this.listeners("error").length === 0) {
|
||||
throw new Error(
|
||||
"You MUST attach an error listener using call.on('error', function() {})",
|
||||
@@ -1770,7 +1764,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async sendCandidateQueue() {
|
||||
private async sendCandidateQueue(): Promise<void> {
|
||||
if (this.candidateSendQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1819,7 +1813,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async placeCallWithConstraints(constraints: MediaStreamConstraints) {
|
||||
private async placeCallWithConstraints(constraints: MediaStreamConstraints): Promise<void> {
|
||||
logger.log("Getting user media with constraints", constraints);
|
||||
// XXX Find a better way to do this
|
||||
this.client.callEventHandler.calls.set(this.callId, this);
|
||||
@@ -1864,7 +1858,7 @@ export class MatrixCall extends EventEmitter {
|
||||
return pc;
|
||||
}
|
||||
|
||||
private partyIdMatches(msg): boolean {
|
||||
private partyIdMatches(msg: MCallBase): boolean {
|
||||
// They must either match or both be absent (in which case opponentPartyId will be null)
|
||||
// Also we ignore party IDs on the invite/offer if the version is 0, so we must do the same
|
||||
// here and use null if the version is 0 (woe betide any opponent sending messages in the
|
||||
@@ -1875,9 +1869,9 @@ export class MatrixCall extends EventEmitter {
|
||||
|
||||
// Commits to an opponent for the call
|
||||
// ev: An invite or answer event
|
||||
private chooseOpponent(ev: MatrixEvent) {
|
||||
private chooseOpponent(ev: MatrixEvent): void {
|
||||
// I choo-choo-choose you
|
||||
const msg = ev.getContent();
|
||||
const msg = ev.getContent<MCallInviteNegotiate | MCallAnswer>();
|
||||
|
||||
logger.debug(`Choosing party ID ${msg.party_id} for call ID ${this.callId}`);
|
||||
|
||||
@@ -1892,11 +1886,11 @@ export class MatrixCall extends EventEmitter {
|
||||
// party ID
|
||||
this.opponentPartyId = msg.party_id || null;
|
||||
}
|
||||
this.opponentCaps = msg.capabilities || {};
|
||||
this.opponentCaps = msg.capabilities || {} as CallCapabilities;
|
||||
this.opponentMember = ev.sender;
|
||||
}
|
||||
|
||||
private async addBufferedIceCandidates() {
|
||||
private async addBufferedIceCandidates(): Promise<void> {
|
||||
const bufferedCandidates = this.remoteCandidateBuffer.get(this.opponentPartyId);
|
||||
if (bufferedCandidates) {
|
||||
logger.info(`Adding ${bufferedCandidates.length} buffered candidates for opponent ${this.opponentPartyId}`);
|
||||
@@ -1905,7 +1899,7 @@ export class MatrixCall extends EventEmitter {
|
||||
this.remoteCandidateBuffer = null;
|
||||
}
|
||||
|
||||
private async addIceCandidates(candidates: RTCIceCandidate[]) {
|
||||
private async addIceCandidates(candidates: RTCIceCandidate[]): Promise<void> {
|
||||
for (const candidate of candidates) {
|
||||
if (
|
||||
(candidate.sdpMid === null || candidate.sdpMid === undefined) &&
|
||||
@@ -1927,7 +1921,7 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public get hasPeerConnection() {
|
||||
public get hasPeerConnection(): boolean {
|
||||
return Boolean(this.peerConn);
|
||||
}
|
||||
}
|
||||
@@ -1949,13 +1943,13 @@ async function getScreensharingStream(
|
||||
}
|
||||
}
|
||||
|
||||
function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean) {
|
||||
function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean): void {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
tracks[i].enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
function getUserMediaContraints(type: ConstraintsType) {
|
||||
function getUserMediaContraints(type: ConstraintsType): MediaStreamConstraints {
|
||||
const isWebkit = !!navigator.webkitGetUserMedia;
|
||||
|
||||
switch (type) {
|
||||
@@ -1986,7 +1980,9 @@ function getUserMediaContraints(type: ConstraintsType) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getScreenshareContraints(selectDesktopCapturerSource?: () => Promise<DesktopCapturerSource>) {
|
||||
async function getScreenshareContraints(
|
||||
selectDesktopCapturerSource?: () => Promise<DesktopCapturerSource>,
|
||||
): Promise<DesktopCapturerConstraints> {
|
||||
if (window.electron?.getDesktopCapturerSources && selectDesktopCapturerSource) {
|
||||
// We have access to getDesktopCapturerSources()
|
||||
logger.debug("Electron getDesktopCapturerSources() is available...");
|
||||
@@ -2020,14 +2016,14 @@ let videoInput: string;
|
||||
* @param {string=} deviceId the identifier for the device
|
||||
* undefined treated as unset
|
||||
*/
|
||||
export function setAudioInput(deviceId: string) { audioInput = deviceId; }
|
||||
export function setAudioInput(deviceId: string): void { audioInput = deviceId; }
|
||||
/**
|
||||
* Set a video input device to use for MatrixCalls
|
||||
* @function
|
||||
* @param {string=} deviceId the identifier for the device
|
||||
* undefined treated as unset
|
||||
*/
|
||||
export function setVideoInput(deviceId: string) { videoInput = deviceId; }
|
||||
export function setVideoInput(deviceId: string): void { videoInput = deviceId; }
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
@@ -2042,7 +2038,7 @@ export function setVideoInput(deviceId: string) { videoInput = deviceId; }
|
||||
* since it's only possible to set this option on outbound calls.
|
||||
* @return {MatrixCall} the call or null if the browser doesn't support calling.
|
||||
*/
|
||||
export function createNewMatrixCall(client: any, roomId: string, options?: CallOpts) {
|
||||
export function createNewMatrixCall(client: any, roomId: string, options?: CallOpts): MatrixCall {
|
||||
// typeof prevents Node from erroring on an undefined reference
|
||||
if (typeof(window) === 'undefined' || typeof(document) === 'undefined') {
|
||||
// NB. We don't log here as apps try to create a call object as a test for
|
||||
|
||||
@@ -19,6 +19,7 @@ import { logger } from '../logger';
|
||||
import { createNewMatrixCall, MatrixCall, CallErrorCode, CallState, CallDirection } from './call';
|
||||
import { EventType } from '../@types/event';
|
||||
import { MatrixClient } from '../client';
|
||||
import { MCallAnswer, MCallHangupReject } from "./callEventTypes";
|
||||
|
||||
// Don't ring unless we'd be ringing for at least 3 seconds: the user needs some
|
||||
// time to press the 'accept' button
|
||||
@@ -252,9 +253,9 @@ export class CallEventHandler {
|
||||
} else {
|
||||
if (call.state !== CallState.Ended) {
|
||||
if (type === EventType.CallHangup) {
|
||||
call.onHangupReceived(content);
|
||||
call.onHangupReceived(content as MCallHangupReject);
|
||||
} else {
|
||||
call.onRejectReceived(content);
|
||||
call.onRejectReceived(content as MCallHangupReject);
|
||||
}
|
||||
this.calls.delete(content.call_id);
|
||||
}
|
||||
@@ -274,7 +275,7 @@ export class CallEventHandler {
|
||||
case EventType.CallAnswer:
|
||||
if (weSentTheEvent) {
|
||||
if (call.state === CallState.Ringing) {
|
||||
call.onAnsweredElsewhere(content);
|
||||
call.onAnsweredElsewhere(content as MCallAnswer);
|
||||
}
|
||||
} else {
|
||||
call.onAnswerReceived(event);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// allow non-camelcase as these are events type that go onto the wire
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import { CallErrorCode } from "./call";
|
||||
|
||||
// TODO: Change to "sdp_stream_metadata" when MSC3077 is merged
|
||||
export const SDPStreamMetadataKey = "org.matrix.msc3077.sdp_stream_metadata";
|
||||
|
||||
@@ -19,11 +21,6 @@ export interface SDPStreamMetadata {
|
||||
[key: string]: SDPStreamMetadataObject;
|
||||
}
|
||||
|
||||
interface CallOfferAnswer {
|
||||
type: string;
|
||||
sdp: string;
|
||||
}
|
||||
|
||||
export interface CallCapabilities {
|
||||
'm.call.transferee': boolean;
|
||||
'm.call.dtmf': boolean;
|
||||
@@ -35,29 +32,56 @@ export interface CallReplacesTarget {
|
||||
avatar_url: string;
|
||||
}
|
||||
|
||||
export interface MCallAnswer {
|
||||
answer: CallOfferAnswer;
|
||||
capabilities: CallCapabilities;
|
||||
export interface MCallBase {
|
||||
call_id: string;
|
||||
version: string | number;
|
||||
party_id?: string;
|
||||
}
|
||||
|
||||
export interface MCallAnswer extends MCallBase {
|
||||
answer: RTCSessionDescription;
|
||||
capabilities?: CallCapabilities;
|
||||
[SDPStreamMetadataKey]: SDPStreamMetadata;
|
||||
}
|
||||
|
||||
export interface MCallOfferNegotiate {
|
||||
offer: CallOfferAnswer;
|
||||
description: CallOfferAnswer;
|
||||
export interface MCallSelectAnswer extends MCallBase {
|
||||
selected_party_id: string;
|
||||
}
|
||||
|
||||
export interface MCallInviteNegotiate extends MCallBase {
|
||||
offer: RTCSessionDescription;
|
||||
description: RTCSessionDescription;
|
||||
lifetime: number;
|
||||
capabilities: CallCapabilities;
|
||||
capabilities?: CallCapabilities;
|
||||
[SDPStreamMetadataKey]: SDPStreamMetadata;
|
||||
}
|
||||
|
||||
export interface MCallSDPStreamMetadataChanged {
|
||||
export interface MCallSDPStreamMetadataChanged extends MCallBase {
|
||||
[SDPStreamMetadataKey]: SDPStreamMetadata;
|
||||
}
|
||||
|
||||
export interface MCallReplacesEvent {
|
||||
export interface MCallReplacesEvent extends MCallBase {
|
||||
replacement_id: string;
|
||||
target_user: CallReplacesTarget;
|
||||
create_call: string;
|
||||
await_call: string;
|
||||
target_room: string;
|
||||
}
|
||||
|
||||
export interface MCAllAssertedIdentity extends MCallBase {
|
||||
asserted_identity: {
|
||||
id: string;
|
||||
display_name: string;
|
||||
avatar_url: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MCallCandidates extends MCallBase {
|
||||
candidates: RTCIceCandidate[];
|
||||
}
|
||||
|
||||
export interface MCallHangupReject extends MCallBase {
|
||||
reason?: CallErrorCode;
|
||||
}
|
||||
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
Reference in New Issue
Block a user