matrix-js-sdk 37.2.0 → 37.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/git-revision.txt +1 -1
- package/lib/client.d.ts +8 -2
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +11 -3
- package/lib/client.js.map +1 -1
- package/lib/common-crypto/CryptoBackend.d.ts +8 -0
- package/lib/common-crypto/CryptoBackend.d.ts.map +1 -1
- package/lib/common-crypto/CryptoBackend.js.map +1 -1
- package/lib/http-api/errors.d.ts +8 -0
- package/lib/http-api/errors.d.ts.map +1 -1
- package/lib/http-api/errors.js +14 -0
- package/lib/http-api/errors.js.map +1 -1
- package/lib/http-api/fetch.d.ts.map +1 -1
- package/lib/http-api/fetch.js +3 -3
- package/lib/http-api/fetch.js.map +1 -1
- package/lib/matrixrtc/LegacyMembershipManager.d.ts +9 -1
- package/lib/matrixrtc/LegacyMembershipManager.d.ts.map +1 -1
- package/lib/matrixrtc/LegacyMembershipManager.js +20 -0
- package/lib/matrixrtc/LegacyMembershipManager.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
- package/lib/matrixrtc/NewMembershipManager.d.ts +7 -58
- package/lib/matrixrtc/NewMembershipManager.d.ts.map +1 -1
- package/lib/matrixrtc/NewMembershipManager.js +130 -137
- package/lib/matrixrtc/NewMembershipManager.js.map +1 -1
- package/lib/matrixrtc/NewMembershipManagerActionScheduler.js +3 -3
- package/lib/matrixrtc/NewMembershipManagerActionScheduler.js.map +1 -1
- package/lib/matrixrtc/index.d.ts +1 -0
- package/lib/matrixrtc/index.d.ts.map +1 -1
- package/lib/matrixrtc/index.js +1 -0
- package/lib/matrixrtc/index.js.map +1 -1
- package/lib/matrixrtc/types.d.ts +77 -1
- package/lib/matrixrtc/types.d.ts.map +1 -1
- package/lib/matrixrtc/types.js +40 -1
- package/lib/matrixrtc/types.js.map +1 -1
- package/lib/models/room-summary.d.ts +30 -0
- package/lib/models/room-summary.d.ts.map +1 -1
- package/lib/models/room-summary.js +11 -0
- package/lib/models/room-summary.js.map +1 -1
- package/lib/models/room.d.ts +37 -1
- package/lib/models/room.d.ts.map +1 -1
- package/lib/models/room.js +133 -28
- package/lib/models/room.js.map +1 -1
- package/lib/oidc/tokenRefresher.d.ts.map +1 -1
- package/lib/oidc/tokenRefresher.js +8 -1
- package/lib/oidc/tokenRefresher.js.map +1 -1
- package/lib/pushprocessor.d.ts +10 -1
- package/lib/pushprocessor.d.ts.map +1 -1
- package/lib/pushprocessor.js +24 -9
- package/lib/pushprocessor.js.map +1 -1
- package/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map +1 -1
- package/lib/rust-crypto/OutgoingRequestProcessor.js +6 -1
- package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -1
- package/lib/rust-crypto/rust-crypto.d.ts +4 -1
- package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
- package/lib/rust-crypto/rust-crypto.js +55 -46
- package/lib/rust-crypto/rust-crypto.js.map +1 -1
- package/lib/sliding-sync-sdk.d.ts.map +1 -1
- package/lib/sliding-sync-sdk.js +86 -75
- package/lib/sliding-sync-sdk.js.map +1 -1
- package/lib/sliding-sync.d.ts +23 -60
- package/lib/sliding-sync.d.ts.map +1 -1
- package/lib/sliding-sync.js +62 -321
- package/lib/sliding-sync.js.map +1 -1
- package/package.json +6 -6
- package/src/client.ts +12 -5
- package/src/common-crypto/CryptoBackend.ts +9 -0
- package/src/http-api/errors.ts +14 -0
- package/src/http-api/fetch.ts +3 -4
- package/src/matrixrtc/LegacyMembershipManager.ts +27 -1
- package/src/matrixrtc/MatrixRTCSession.ts +2 -1
- package/src/matrixrtc/NewMembershipManager.ts +120 -165
- package/src/matrixrtc/NewMembershipManagerActionScheduler.ts +3 -3
- package/src/matrixrtc/index.ts +1 -0
- package/src/matrixrtc/types.ts +84 -1
- package/src/models/room-summary.ts +31 -0
- package/src/models/room.ts +133 -23
- package/src/oidc/tokenRefresher.ts +8 -2
- package/src/pushprocessor.ts +25 -16
- package/src/rust-crypto/OutgoingRequestProcessor.ts +6 -0
- package/src/rust-crypto/rust-crypto.ts +7 -1
- package/src/sliding-sync-sdk.ts +28 -28
- package/src/sliding-sync.ts +38 -329
|
@@ -24,110 +24,80 @@ import { type Room } from "../models/room.ts";
|
|
|
24
24
|
import { defer, type IDeferred } from "../utils.ts";
|
|
25
25
|
import { type CallMembership, DEFAULT_EXPIRE_DURATION, type SessionMembershipData } from "./CallMembership.ts";
|
|
26
26
|
import { type Focus } from "./focus.ts";
|
|
27
|
+
import {
|
|
28
|
+
type IMembershipManager,
|
|
29
|
+
type MembershipManagerEventHandlerMap,
|
|
30
|
+
MembershipManagerEvent,
|
|
31
|
+
Status,
|
|
32
|
+
} from "./types.ts";
|
|
27
33
|
import { isLivekitFocusActive } from "./LivekitFocus.ts";
|
|
28
34
|
import { type MembershipConfig } from "./MatrixRTCSession.ts";
|
|
29
35
|
import { ActionScheduler, type ActionUpdate } from "./NewMembershipManagerActionScheduler.ts";
|
|
36
|
+
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
|
30
37
|
|
|
31
38
|
const logger = rootLogger.getChild("MatrixRTCSession");
|
|
32
39
|
|
|
33
|
-
/**
|
|
34
|
-
* This interface defines what a MembershipManager uses and exposes.
|
|
35
|
-
* This interface is what we use to write tests and allows changing the actual implementation
|
|
36
|
-
* without breaking tests because of some internal method renaming.
|
|
37
|
-
*
|
|
38
|
-
* @internal
|
|
39
|
-
*/
|
|
40
|
-
export interface IMembershipManager {
|
|
41
|
-
/**
|
|
42
|
-
* If we are trying to join, or have successfully joined the session.
|
|
43
|
-
* It does not reflect if the room state is already configured to represent us being joined.
|
|
44
|
-
* It only means that the Manager should be trying to connect or to disconnect running.
|
|
45
|
-
* The Manager is still running right after isJoined becomes false to send the disconnect events.
|
|
46
|
-
* (A more accurate name would be `isActivated`)
|
|
47
|
-
* @returns true if we intend to be participating in the MatrixRTC session
|
|
48
|
-
*/
|
|
49
|
-
isJoined(): boolean;
|
|
50
|
-
/**
|
|
51
|
-
* Start sending all necessary events to make this user participate in the RTC session.
|
|
52
|
-
* @param fociPreferred the list of preferred foci to use in the joined RTC membership event.
|
|
53
|
-
* @param fociActive the active focus to use in the joined RTC membership event.
|
|
54
|
-
* @throws can throw if it exceeds a configured maximum retry.
|
|
55
|
-
*/
|
|
56
|
-
join(fociPreferred: Focus[], fociActive?: Focus, onError?: (error: unknown) => void): void;
|
|
57
|
-
/**
|
|
58
|
-
* Send all necessary events to make this user leave the RTC session.
|
|
59
|
-
* @param timeout the maximum duration in ms until the promise is forced to resolve.
|
|
60
|
-
* @returns It resolves with true in case the leave was sent successfully.
|
|
61
|
-
* It resolves with false in case we hit the timeout before sending successfully.
|
|
62
|
-
*/
|
|
63
|
-
leave(timeout?: number): Promise<boolean>;
|
|
64
|
-
/**
|
|
65
|
-
* Call this if the MatrixRTC session members have changed.
|
|
66
|
-
*/
|
|
67
|
-
onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise<void>;
|
|
68
|
-
/**
|
|
69
|
-
* The used active focus in the currently joined session.
|
|
70
|
-
* @returns the used active focus in the currently joined session or undefined if not joined.
|
|
71
|
-
*/
|
|
72
|
-
getActiveFocus(): Focus | undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
40
|
/* MembershipActionTypes:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
41
|
+
|
|
42
|
+
On Join: ───────────────┐ ┌───────────────(1)───────────┐
|
|
43
|
+
▼ ▼ │
|
|
44
|
+
┌────────────────┐ │
|
|
45
|
+
│SendDelayedEvent│ ──────(2)───┐ │
|
|
46
|
+
└────────────────┘ │ │
|
|
47
|
+
│(3) │ │
|
|
48
|
+
▼ │ │
|
|
49
|
+
┌─────────────┐ │ │
|
|
50
|
+
┌──────(4)───│SendJoinEvent│────(4)─────┐ │ │
|
|
51
|
+
│ └─────────────┘ │ │ │
|
|
52
|
+
│ ┌─────┐ ┌──────┐ │ │ │
|
|
53
|
+
▼ ▼ │ │ ▼ ▼ ▼ │
|
|
87
54
|
┌────────────┐ │ │ ┌───────────────────┐ │
|
|
88
|
-
│UpdateExpiry│
|
|
55
|
+
│UpdateExpiry│ (s) (s)|RestartDelayedEvent│ │
|
|
89
56
|
└────────────┘ │ │ └───────────────────┘ │
|
|
90
|
-
│ │ │ │
|
|
91
|
-
└─────┘ └──────┘
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
│SendMainDelayedEvent│◄───────┘ │
|
|
95
|
-
└───────────────────┬┘ │
|
|
96
|
-
│ │
|
|
97
|
-
└─────────────────────┘
|
|
98
|
-
STOP ALL ABOVE
|
|
57
|
+
│ │ │ │ │ │
|
|
58
|
+
└─────┘ └──────┘ └───────┘
|
|
59
|
+
|
|
60
|
+
On Leave: ───────── STOP ALL ABOVE
|
|
99
61
|
▼
|
|
100
|
-
|
|
101
|
-
│ SendScheduledDelayedLeaveEvent│
|
|
102
|
-
|
|
103
|
-
│
|
|
62
|
+
┌────────────────────────────────┐
|
|
63
|
+
│ SendScheduledDelayedLeaveEvent │
|
|
64
|
+
└────────────────────────────────┘
|
|
65
|
+
│(5)
|
|
104
66
|
▼
|
|
105
67
|
┌──────────────┐
|
|
106
68
|
│SendLeaveEvent│
|
|
107
69
|
└──────────────┘
|
|
108
|
-
|
|
70
|
+
(1) [Not found error] results in resending the delayed event
|
|
71
|
+
(2) [hasMemberEvent = true] Sending the delayed event if we
|
|
72
|
+
already have a call member event results jumping to the
|
|
73
|
+
RestartDelayedEvent loop directly
|
|
74
|
+
(3) [hasMemberEvent = false] if there is not call member event
|
|
75
|
+
sending it is the next step
|
|
76
|
+
(4) Both (UpdateExpiry and RestartDelayedEvent) actions are
|
|
77
|
+
scheduled when successfully sending the state event
|
|
78
|
+
(5) Only if delayed event sending failed (fallback)
|
|
79
|
+
(s) Successful restart/resend
|
|
109
80
|
*/
|
|
81
|
+
|
|
110
82
|
/**
|
|
111
83
|
* The different types of actions the MembershipManager can take.
|
|
112
84
|
* @internal
|
|
113
85
|
*/
|
|
114
86
|
export enum MembershipActionType {
|
|
115
|
-
|
|
87
|
+
SendDelayedEvent = "SendDelayedEvent",
|
|
116
88
|
// -> MembershipActionType.SendJoinEvent if successful
|
|
117
|
-
// -> DelayedLeaveActionType.
|
|
89
|
+
// -> DelayedLeaveActionType.SendDelayedEvent on error, retry sending the first delayed event.
|
|
90
|
+
// -> DelayedLeaveActionType.RestartDelayedEvent on success start updating the delayed event
|
|
118
91
|
SendJoinEvent = "SendJoinEvent",
|
|
119
92
|
// -> MembershipActionType.SendJoinEvent if we run into a rate limit and need to retry
|
|
120
93
|
// -> MembershipActionType.Update if we successfully send the join event then schedule the expire event update
|
|
121
94
|
// -> DelayedLeaveActionType.RestartDelayedEvent to recheck the delayed event
|
|
122
95
|
RestartDelayedEvent = "RestartDelayedEvent",
|
|
123
96
|
// -> DelayedLeaveActionType.SendMainDelayedEvent on missing delay id but there is a rtc state event
|
|
124
|
-
// -> DelayedLeaveActionType.
|
|
97
|
+
// -> DelayedLeaveActionType.SendDelayedEvent on missing delay id and there is no state event
|
|
125
98
|
// -> DelayedLeaveActionType.RestartDelayedEvent on success we schedule the next restart
|
|
126
99
|
UpdateExpiry = "UpdateExpiry",
|
|
127
100
|
// -> MembershipActionType.Update if the timeout has passed so the next update is required.
|
|
128
|
-
SendMainDelayedEvent = "SendMainDelayedEvent",
|
|
129
|
-
// -> DelayedLeaveActionType.RestartDelayedEvent on success start updating the delayed event
|
|
130
|
-
// -> DelayedLeaveActionType.SendMainDelayedEvent on error try again
|
|
131
101
|
SendScheduledDelayedLeaveEvent = "SendScheduledDelayedLeaveEvent",
|
|
132
102
|
// -> MembershipActionType.SendLeaveEvent on failiour (not found) we need to send the leave manually and cannot use the scheduled delayed event
|
|
133
103
|
// -> DelayedLeaveActionType.SendScheduledDelayedLeaveEvent on error we try again.
|
|
@@ -138,7 +108,7 @@ export enum MembershipActionType {
|
|
|
138
108
|
/**
|
|
139
109
|
* @internal
|
|
140
110
|
*/
|
|
141
|
-
export interface
|
|
111
|
+
export interface MembershipManagerState {
|
|
142
112
|
/** The delayId we got when successfully sending the delayed leave event.
|
|
143
113
|
* Gets set to undefined if the server claims it cannot find the delayed event anymore. */
|
|
144
114
|
delayId?: string;
|
|
@@ -158,17 +128,6 @@ export interface ActionSchedulerState {
|
|
|
158
128
|
networkErrorRetries: Map<MembershipActionType, number>;
|
|
159
129
|
}
|
|
160
130
|
|
|
161
|
-
enum Status {
|
|
162
|
-
Disconnected = "Disconnected",
|
|
163
|
-
Connecting = "Connecting",
|
|
164
|
-
ConnectingFailed = "ConnectingFailed",
|
|
165
|
-
Connected = "Connected",
|
|
166
|
-
Reconnecting = "Reconnecting",
|
|
167
|
-
Disconnecting = "Disconnecting",
|
|
168
|
-
Stuck = "Stuck",
|
|
169
|
-
Unknown = "Unknown",
|
|
170
|
-
}
|
|
171
|
-
|
|
172
131
|
/**
|
|
173
132
|
* This class is responsible for sending all events relating to the own membership of a matrixRTC call.
|
|
174
133
|
* It has the following tasks:
|
|
@@ -182,11 +141,18 @@ enum Status {
|
|
|
182
141
|
* - Stop the timer for the delay refresh
|
|
183
142
|
* - Stop the timer for updating the state event
|
|
184
143
|
*/
|
|
185
|
-
export class MembershipManager
|
|
144
|
+
export class MembershipManager
|
|
145
|
+
extends TypedEventEmitter<MembershipManagerEvent, MembershipManagerEventHandlerMap>
|
|
146
|
+
implements IMembershipManager
|
|
147
|
+
{
|
|
186
148
|
private activated = false;
|
|
187
|
-
public
|
|
149
|
+
public isActivated(): boolean {
|
|
188
150
|
return this.activated;
|
|
189
151
|
}
|
|
152
|
+
// DEPRECATED use isActivated
|
|
153
|
+
public isJoined(): boolean {
|
|
154
|
+
return this.isActivated();
|
|
155
|
+
}
|
|
190
156
|
|
|
191
157
|
/**
|
|
192
158
|
* Puts the MembershipManager in a state where it tries to be joined.
|
|
@@ -205,23 +171,27 @@ export class MembershipManager implements IMembershipManager {
|
|
|
205
171
|
this.focusActive = focusActive;
|
|
206
172
|
this.leavePromiseDefer = undefined;
|
|
207
173
|
this.activated = true;
|
|
208
|
-
|
|
174
|
+
this.oldStatus = this.status;
|
|
209
175
|
this.state = MembershipManager.defaultState;
|
|
210
176
|
|
|
211
177
|
this.scheduler
|
|
212
178
|
.startWithJoin()
|
|
213
|
-
.then(() => {
|
|
214
|
-
if (!this.scheduler.running) {
|
|
215
|
-
this.leavePromiseDefer?.resolve(true);
|
|
216
|
-
this.leavePromiseDefer = undefined;
|
|
217
|
-
}
|
|
218
|
-
})
|
|
219
179
|
.catch((e) => {
|
|
220
180
|
logger.error("MembershipManager stopped because: ", e);
|
|
221
181
|
onError?.(e);
|
|
222
182
|
})
|
|
223
|
-
|
|
224
|
-
|
|
183
|
+
.finally(() => {
|
|
184
|
+
// Should already be set to false when calling `leave` in non error cases.
|
|
185
|
+
this.activated = false;
|
|
186
|
+
// Here the scheduler is not running anymore so we the `membershipLoopHandler` is not called to emit.
|
|
187
|
+
if (this.oldStatus && this.oldStatus !== this.status) {
|
|
188
|
+
this.emit(MembershipManagerEvent.StatusChanged, this.oldStatus, this.status);
|
|
189
|
+
}
|
|
190
|
+
if (!this.scheduler.running) {
|
|
191
|
+
this.leavePromiseDefer?.resolve(true);
|
|
192
|
+
this.leavePromiseDefer = undefined;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
225
195
|
}
|
|
226
196
|
|
|
227
197
|
/**
|
|
@@ -256,7 +226,7 @@ export class MembershipManager implements IMembershipManager {
|
|
|
256
226
|
// If one of these actions are scheduled or are getting inserted in the next iteration, we should already
|
|
257
227
|
// take care of our missing membership.
|
|
258
228
|
const sendingMembershipActions = [
|
|
259
|
-
MembershipActionType.
|
|
229
|
+
MembershipActionType.SendDelayedEvent,
|
|
260
230
|
MembershipActionType.SendJoinEvent,
|
|
261
231
|
];
|
|
262
232
|
logger.warn("Missing own membership: force re-join");
|
|
@@ -313,6 +283,7 @@ export class MembershipManager implements IMembershipManager {
|
|
|
313
283
|
>,
|
|
314
284
|
private getOldestMembership: () => CallMembership | undefined,
|
|
315
285
|
) {
|
|
286
|
+
super();
|
|
316
287
|
const [userId, deviceId] = [this.client.getUserId(), this.client.getDeviceId()];
|
|
317
288
|
if (userId === null) throw Error("Missing userId in client");
|
|
318
289
|
if (deviceId === null) throw Error("Missing deviceId in client");
|
|
@@ -322,8 +293,8 @@ export class MembershipManager implements IMembershipManager {
|
|
|
322
293
|
}
|
|
323
294
|
|
|
324
295
|
// MembershipManager mutable state.
|
|
325
|
-
private state:
|
|
326
|
-
private static get defaultState():
|
|
296
|
+
private state: MembershipManagerState;
|
|
297
|
+
private static get defaultState(): MembershipManagerState {
|
|
327
298
|
return {
|
|
328
299
|
hasMemberStateEvent: false,
|
|
329
300
|
delayId: undefined,
|
|
@@ -380,9 +351,13 @@ export class MembershipManager implements IMembershipManager {
|
|
|
380
351
|
private oldStatus?: Status;
|
|
381
352
|
private scheduler = new ActionScheduler((type): Promise<ActionUpdate> => {
|
|
382
353
|
if (this.oldStatus) {
|
|
383
|
-
//
|
|
354
|
+
// we put this at the beginning of the actions scheduler loop handle callback since it is a loop this
|
|
384
355
|
// is equivalent to running it at the end of the loop. (just after applying the status/action list changes)
|
|
356
|
+
// This order is required because this method needs to return the action updates.
|
|
385
357
|
logger.debug(`MembershipManager applied action changes. Status: ${this.oldStatus} -> ${this.status}`);
|
|
358
|
+
if (this.oldStatus !== this.status) {
|
|
359
|
+
this.emit(MembershipManagerEvent.StatusChanged, this.oldStatus, this.status);
|
|
360
|
+
}
|
|
386
361
|
}
|
|
387
362
|
this.oldStatus = this.status;
|
|
388
363
|
logger.debug(`MembershipManager before processing action. status=${this.oldStatus}`);
|
|
@@ -391,17 +366,16 @@ export class MembershipManager implements IMembershipManager {
|
|
|
391
366
|
|
|
392
367
|
// LOOP HANDLER:
|
|
393
368
|
private async membershipLoopHandler(type: MembershipActionType): Promise<ActionUpdate> {
|
|
394
|
-
this.oldStatus = this.status;
|
|
395
369
|
switch (type) {
|
|
396
|
-
case MembershipActionType.
|
|
370
|
+
case MembershipActionType.SendDelayedEvent: {
|
|
397
371
|
// Before we start we check if we come from a state where we have a delay id.
|
|
398
372
|
if (!this.state.delayId) {
|
|
399
|
-
return this.
|
|
373
|
+
return this.sendOrResendDelayedLeaveEvent(); // Normal case without any previous delayed id.
|
|
400
374
|
} else {
|
|
401
375
|
// This can happen if someone else (or another client) removes our own membership event.
|
|
402
376
|
// It will trigger `onRTCSessionMemberUpdate` queue `MembershipActionType.SendFirstDelayedEvent`.
|
|
403
377
|
// We might still have our delayed event from the previous participation and dependent on the server this might not
|
|
404
|
-
// get automatically
|
|
378
|
+
// get removed automatically if the state changes. Hence, it would remove our membership unexpectedly shortly after the rejoin.
|
|
405
379
|
//
|
|
406
380
|
// In this block we will try to cancel this delayed event before setting up a new one.
|
|
407
381
|
|
|
@@ -411,17 +385,10 @@ export class MembershipManager implements IMembershipManager {
|
|
|
411
385
|
case MembershipActionType.RestartDelayedEvent: {
|
|
412
386
|
if (!this.state.delayId) {
|
|
413
387
|
// Delay id got reset. This action was used to check if the hs canceled the delayed event when the join state got sent.
|
|
414
|
-
return createInsertActionUpdate(
|
|
415
|
-
this.state.hasMemberStateEvent
|
|
416
|
-
? MembershipActionType.SendMainDelayedEvent
|
|
417
|
-
: MembershipActionType.SendFirstDelayedEvent,
|
|
418
|
-
);
|
|
388
|
+
return createInsertActionUpdate(MembershipActionType.SendDelayedEvent);
|
|
419
389
|
}
|
|
420
390
|
return this.restartDelayedEvent(this.state.delayId);
|
|
421
391
|
}
|
|
422
|
-
case MembershipActionType.SendMainDelayedEvent: {
|
|
423
|
-
return this.sendMainDelayedEvent();
|
|
424
|
-
}
|
|
425
392
|
case MembershipActionType.SendScheduledDelayedLeaveEvent: {
|
|
426
393
|
// We are already good
|
|
427
394
|
if (!this.state.hasMemberStateEvent) {
|
|
@@ -452,7 +419,11 @@ export class MembershipManager implements IMembershipManager {
|
|
|
452
419
|
}
|
|
453
420
|
|
|
454
421
|
// HANDLERS (used in the membershipLoopHandler)
|
|
455
|
-
private async
|
|
422
|
+
private async sendOrResendDelayedLeaveEvent(): Promise<ActionUpdate> {
|
|
423
|
+
// We can reach this at the start of a call (where we do not yet have a membership: state.hasMemberStateEvent=false)
|
|
424
|
+
// or during a call if the state event canceled our delayed event or caused by an unexpected error that removed our delayed event.
|
|
425
|
+
// (Another client could have canceled it, the homeserver might have removed/lost it due to a restart, ...)
|
|
426
|
+
// In the `then` and `catch` block we treat both cases differently. "if (this.state.hasMemberStateEvent) {} else {}"
|
|
456
427
|
return await this.client
|
|
457
428
|
._unstable_sendDelayedStateEvent(
|
|
458
429
|
this.room.roomId,
|
|
@@ -465,27 +436,46 @@ export class MembershipManager implements IMembershipManager {
|
|
|
465
436
|
)
|
|
466
437
|
.then((response) => {
|
|
467
438
|
// On success we reset retries and set delayId.
|
|
468
|
-
this.
|
|
469
|
-
this.state.networkErrorRetries.set(MembershipActionType.SendFirstDelayedEvent, 0);
|
|
439
|
+
this.resetRateLimitCounter(MembershipActionType.SendDelayedEvent);
|
|
470
440
|
this.state.delayId = response.delay_id;
|
|
471
|
-
|
|
441
|
+
if (this.state.hasMemberStateEvent) {
|
|
442
|
+
// This action was scheduled because the previous delayed event was cancelled
|
|
443
|
+
// due to lack of https://github.com/element-hq/synapse/pull/17810
|
|
444
|
+
return createInsertActionUpdate(
|
|
445
|
+
MembershipActionType.RestartDelayedEvent,
|
|
446
|
+
this.membershipKeepAlivePeriod,
|
|
447
|
+
);
|
|
448
|
+
} else {
|
|
449
|
+
// This action was scheduled because we are in the process of joining
|
|
450
|
+
return createInsertActionUpdate(MembershipActionType.SendJoinEvent);
|
|
451
|
+
}
|
|
472
452
|
})
|
|
473
453
|
.catch((e) => {
|
|
474
|
-
const repeatActionType = MembershipActionType.
|
|
454
|
+
const repeatActionType = MembershipActionType.SendDelayedEvent;
|
|
475
455
|
if (this.manageMaxDelayExceededSituation(e)) {
|
|
476
456
|
return createInsertActionUpdate(repeatActionType);
|
|
477
457
|
}
|
|
478
458
|
const update = this.actionUpdateFromErrors(e, repeatActionType, "sendDelayedStateEvent");
|
|
479
459
|
if (update) return update;
|
|
480
460
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
461
|
+
if (this.state.hasMemberStateEvent) {
|
|
462
|
+
// This action was scheduled because the previous delayed event was cancelled
|
|
463
|
+
// due to lack of https://github.com/element-hq/synapse/pull/17810
|
|
464
|
+
|
|
465
|
+
// Don't do any other delayed event work if its not supported.
|
|
466
|
+
if (this.isUnsupportedDelayedEndpoint(e)) return {};
|
|
467
|
+
throw Error("Could not send delayed event, even though delayed events are supported. " + e);
|
|
484
468
|
} else {
|
|
485
|
-
|
|
469
|
+
// This action was scheduled because we are in the process of joining
|
|
470
|
+
// log and fall through
|
|
471
|
+
if (this.isUnsupportedDelayedEndpoint(e)) {
|
|
472
|
+
logger.info("Not using delayed event because the endpoint is not supported");
|
|
473
|
+
} else {
|
|
474
|
+
logger.info("Not using delayed event because: " + e);
|
|
475
|
+
}
|
|
476
|
+
// On any other error we fall back to not using delayed events and send the join state event immediately
|
|
477
|
+
return createInsertActionUpdate(MembershipActionType.SendJoinEvent);
|
|
486
478
|
}
|
|
487
|
-
// On any other error we fall back to not using delayed events and send the join state event immediately
|
|
488
|
-
return createInsertActionUpdate(MembershipActionType.SendJoinEvent);
|
|
489
479
|
});
|
|
490
480
|
}
|
|
491
481
|
|
|
@@ -495,11 +485,11 @@ export class MembershipManager implements IMembershipManager {
|
|
|
495
485
|
._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Cancel)
|
|
496
486
|
.then(() => {
|
|
497
487
|
this.state.delayId = undefined;
|
|
498
|
-
this.resetRateLimitCounter(MembershipActionType.
|
|
499
|
-
return createReplaceActionUpdate(MembershipActionType.
|
|
488
|
+
this.resetRateLimitCounter(MembershipActionType.SendDelayedEvent);
|
|
489
|
+
return createReplaceActionUpdate(MembershipActionType.SendDelayedEvent);
|
|
500
490
|
})
|
|
501
491
|
.catch((e) => {
|
|
502
|
-
const repeatActionType = MembershipActionType.
|
|
492
|
+
const repeatActionType = MembershipActionType.SendDelayedEvent;
|
|
503
493
|
const update = this.actionUpdateFromErrors(e, repeatActionType, "updateDelayedEvent");
|
|
504
494
|
if (update) return update;
|
|
505
495
|
|
|
@@ -538,7 +528,7 @@ export class MembershipManager implements IMembershipManager {
|
|
|
538
528
|
const repeatActionType = MembershipActionType.RestartDelayedEvent;
|
|
539
529
|
if (this.isNotFoundError(e)) {
|
|
540
530
|
this.state.delayId = undefined;
|
|
541
|
-
return createInsertActionUpdate(MembershipActionType.
|
|
531
|
+
return createInsertActionUpdate(MembershipActionType.SendDelayedEvent);
|
|
542
532
|
}
|
|
543
533
|
// If the HS does not support delayed events we wont reschedule.
|
|
544
534
|
if (this.isUnsupportedDelayedEndpoint(e)) return {};
|
|
@@ -552,40 +542,6 @@ export class MembershipManager implements IMembershipManager {
|
|
|
552
542
|
});
|
|
553
543
|
}
|
|
554
544
|
|
|
555
|
-
private async sendMainDelayedEvent(): Promise<ActionUpdate> {
|
|
556
|
-
return await this.client
|
|
557
|
-
._unstable_sendDelayedStateEvent(
|
|
558
|
-
this.room.roomId,
|
|
559
|
-
{
|
|
560
|
-
delay: this.membershipServerSideExpiryTimeout,
|
|
561
|
-
},
|
|
562
|
-
EventType.GroupCallMemberPrefix,
|
|
563
|
-
{}, // leave event
|
|
564
|
-
this.stateKey,
|
|
565
|
-
)
|
|
566
|
-
.then((response) => {
|
|
567
|
-
this.state.delayId = response.delay_id;
|
|
568
|
-
this.resetRateLimitCounter(MembershipActionType.SendMainDelayedEvent);
|
|
569
|
-
return createInsertActionUpdate(
|
|
570
|
-
MembershipActionType.RestartDelayedEvent,
|
|
571
|
-
this.membershipKeepAlivePeriod,
|
|
572
|
-
);
|
|
573
|
-
})
|
|
574
|
-
.catch((e) => {
|
|
575
|
-
const repeatActionType = MembershipActionType.SendMainDelayedEvent;
|
|
576
|
-
// Don't do any other delayed event work if its not supported.
|
|
577
|
-
if (this.isUnsupportedDelayedEndpoint(e)) return {};
|
|
578
|
-
|
|
579
|
-
if (this.manageMaxDelayExceededSituation(e)) {
|
|
580
|
-
return createInsertActionUpdate(repeatActionType);
|
|
581
|
-
}
|
|
582
|
-
const update = this.actionUpdateFromErrors(e, repeatActionType, "updateDelayedEvent");
|
|
583
|
-
if (update) return update;
|
|
584
|
-
|
|
585
|
-
throw Error("Could not send delayed event, even though delayed events are supported. " + e);
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
|
|
589
545
|
private async sendScheduledDelayedLeaveEventOrFallbackToSendLeaveEvent(delayId: string): Promise<ActionUpdate> {
|
|
590
546
|
return await this.client
|
|
591
547
|
._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Send)
|
|
@@ -887,9 +843,8 @@ export class MembershipManager implements IMembershipManager {
|
|
|
887
843
|
if (actions.length === 1) {
|
|
888
844
|
const { type } = actions[0];
|
|
889
845
|
switch (type) {
|
|
890
|
-
case MembershipActionType.
|
|
846
|
+
case MembershipActionType.SendDelayedEvent:
|
|
891
847
|
case MembershipActionType.SendJoinEvent:
|
|
892
|
-
case MembershipActionType.SendMainDelayedEvent:
|
|
893
848
|
return Status.Connecting;
|
|
894
849
|
case MembershipActionType.UpdateExpiry: // where no delayed events
|
|
895
850
|
return Status.Connected;
|
|
@@ -904,7 +859,7 @@ export class MembershipManager implements IMembershipManager {
|
|
|
904
859
|
// normal state for connected with delayed events
|
|
905
860
|
if (
|
|
906
861
|
(types.includes(MembershipActionType.RestartDelayedEvent) ||
|
|
907
|
-
types.includes(MembershipActionType.
|
|
862
|
+
(types.includes(MembershipActionType.SendDelayedEvent) && this.state.hasMemberStateEvent)) &&
|
|
908
863
|
types.includes(MembershipActionType.UpdateExpiry)
|
|
909
864
|
) {
|
|
910
865
|
return Status.Connected;
|
|
@@ -73,7 +73,7 @@ export class ActionScheduler {
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
this.running = true;
|
|
76
|
-
this._actions = [{ ts: Date.now(), type: MembershipActionType.
|
|
76
|
+
this._actions = [{ ts: Date.now(), type: MembershipActionType.SendDelayedEvent }];
|
|
77
77
|
try {
|
|
78
78
|
while (this._actions.length > 0) {
|
|
79
79
|
// Sort so next (smallest ts) action is at the beginning
|
|
@@ -98,7 +98,7 @@ export class ActionScheduler {
|
|
|
98
98
|
`\nDate.now: "${Date.now()}`,
|
|
99
99
|
);
|
|
100
100
|
try {
|
|
101
|
-
// `this.wakeup` can also be called and sets the `
|
|
101
|
+
// `this.wakeup` can also be called and sets the `wakeupUpdate` object while we are in the handler.
|
|
102
102
|
handlerResult = await this.membershipLoopHandler(nextAction.type as MembershipActionType);
|
|
103
103
|
} catch (e) {
|
|
104
104
|
throw Error(`The MembershipManager shut down because of the end condition: ${e}`);
|
|
@@ -125,7 +125,7 @@ export class ActionScheduler {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
public initiateJoin(): void {
|
|
128
|
-
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.
|
|
128
|
+
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.SendDelayedEvent }] });
|
|
129
129
|
}
|
|
130
130
|
public initiateLeave(): void {
|
|
131
131
|
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.SendScheduledDelayedLeaveEvent }] });
|
package/src/matrixrtc/index.ts
CHANGED
package/src/matrixrtc/types.ts
CHANGED
|
@@ -13,7 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
16
|
+
import type { IMentions } from "../matrix.ts";
|
|
17
|
+
import type { CallMembership } from "./CallMembership.ts";
|
|
18
|
+
import type { Focus } from "./focus.ts";
|
|
19
|
+
|
|
17
20
|
export interface EncryptionKeyEntry {
|
|
18
21
|
index: number;
|
|
19
22
|
key: string;
|
|
@@ -34,3 +37,83 @@ export interface ICallNotifyContent {
|
|
|
34
37
|
"notify_type": CallNotifyType;
|
|
35
38
|
"call_id": string;
|
|
36
39
|
}
|
|
40
|
+
|
|
41
|
+
export enum Status {
|
|
42
|
+
Disconnected = "Disconnected",
|
|
43
|
+
Connecting = "Connecting",
|
|
44
|
+
ConnectingFailed = "ConnectingFailed",
|
|
45
|
+
Connected = "Connected",
|
|
46
|
+
Reconnecting = "Reconnecting",
|
|
47
|
+
Disconnecting = "Disconnecting",
|
|
48
|
+
Stuck = "Stuck",
|
|
49
|
+
Unknown = "Unknown",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export enum MembershipManagerEvent {
|
|
53
|
+
StatusChanged = "StatusChanged",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type MembershipManagerEventHandlerMap = {
|
|
57
|
+
[MembershipManagerEvent.StatusChanged]: (prefStatus: Status, newStatus: Status) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* This interface defines what a MembershipManager uses and exposes.
|
|
62
|
+
* This interface is what we use to write tests and allows changing the actual implementation
|
|
63
|
+
* without breaking tests because of some internal method renaming.
|
|
64
|
+
*
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export interface IMembershipManager {
|
|
68
|
+
/**
|
|
69
|
+
* If we are trying to join, or have successfully joined the session.
|
|
70
|
+
* It does not reflect if the room state is already configured to represent us being joined.
|
|
71
|
+
* It only means that the Manager should be trying to connect or to disconnect running.
|
|
72
|
+
* The Manager is still running right after isJoined becomes false to send the disconnect events.
|
|
73
|
+
* @returns true if we intend to be participating in the MatrixRTC session
|
|
74
|
+
* @deprecated This name is confusing and replaced by `isActivated()`. (Returns the same as `isActivated()`)
|
|
75
|
+
*/
|
|
76
|
+
isJoined(): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* If the manager is activated. This means it tries to do its job to join the call, resend state events...
|
|
79
|
+
* It does not imply that the room state is already configured to represent being joined.
|
|
80
|
+
* It means that the Manager tries to connect or is connected. ("the manager is still active")
|
|
81
|
+
* Once `leave()` is called the manager is not activated anymore but still running until `leave()` resolves.
|
|
82
|
+
* @returns `true` if we intend to be participating in the MatrixRTC session
|
|
83
|
+
*/
|
|
84
|
+
isActivated(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Get the actual connection status of the manager.
|
|
87
|
+
*/
|
|
88
|
+
get status(): Status;
|
|
89
|
+
/**
|
|
90
|
+
* The current status while the manager is activated
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* Start sending all necessary events to make this user participate in the RTC session.
|
|
94
|
+
* @param fociPreferred the list of preferred foci to use in the joined RTC membership event.
|
|
95
|
+
* @param fociActive the active focus to use in the joined RTC membership event.
|
|
96
|
+
* @throws can throw if it exceeds a configured maximum retry.
|
|
97
|
+
*/
|
|
98
|
+
join(fociPreferred: Focus[], fociActive?: Focus, onError?: (error: unknown) => void): void;
|
|
99
|
+
/**
|
|
100
|
+
* Send all necessary events to make this user leave the RTC session.
|
|
101
|
+
* @param timeout the maximum duration in ms until the promise is forced to resolve.
|
|
102
|
+
* @returns It resolves with true in case the leave was sent successfully.
|
|
103
|
+
* It resolves with false in case we hit the timeout before sending successfully.
|
|
104
|
+
*/
|
|
105
|
+
leave(timeout?: number): Promise<boolean>;
|
|
106
|
+
/**
|
|
107
|
+
* Call this if the MatrixRTC session members have changed.
|
|
108
|
+
*/
|
|
109
|
+
onRTCSessionMemberUpdate(memberships: CallMembership[]): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* The used active focus in the currently joined session.
|
|
112
|
+
* @returns the used active focus in the currently joined session or undefined if not joined.
|
|
113
|
+
*/
|
|
114
|
+
getActiveFocus(): Focus | undefined;
|
|
115
|
+
|
|
116
|
+
// TypedEventEmitter methods:
|
|
117
|
+
on(event: MembershipManagerEvent.StatusChanged, listener: (oldStatus: Status, newStatus: Status) => void): this;
|
|
118
|
+
off(event: MembershipManagerEvent.StatusChanged, listener: (oldStatus: Status, newStatus: Status) => void): this;
|
|
119
|
+
}
|
|
@@ -14,9 +14,40 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* A stripped m.room.member event which contains the key renderable fields from the event,
|
|
19
|
+
* sent only in simplified sliding sync (not `/v3/sync`).
|
|
20
|
+
* This is very similar to MSC4186Hero from sliding-sync.ts but an internal format with
|
|
21
|
+
* camelCase rather than underscores.
|
|
22
|
+
*/
|
|
23
|
+
export type Hero = {
|
|
24
|
+
userId: string;
|
|
25
|
+
displayName?: string;
|
|
26
|
+
avatarUrl?: string;
|
|
27
|
+
/**
|
|
28
|
+
* If true, the hero is from an MSC4186 summary, in which case `displayName` and `avatarUrl` will
|
|
29
|
+
* have been set by the server if available. If false, the `Hero` has been constructed from a `/v3/sync` response,
|
|
30
|
+
* so these fields will always be undefined.
|
|
31
|
+
*/
|
|
32
|
+
fromMSC4186: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* High level summary information for a room, as returned by `/v3/sync`.
|
|
37
|
+
*/
|
|
17
38
|
export interface IRoomSummary {
|
|
39
|
+
/**
|
|
40
|
+
* The room heroes: a selected set of members that can be used when summarising or
|
|
41
|
+
* generating a name for a room. List of user IDs.
|
|
42
|
+
*/
|
|
18
43
|
"m.heroes": string[];
|
|
44
|
+
/**
|
|
45
|
+
* The number of joined members in the room.
|
|
46
|
+
*/
|
|
19
47
|
"m.joined_member_count"?: number;
|
|
48
|
+
/**
|
|
49
|
+
* The number of invited members in the room.
|
|
50
|
+
*/
|
|
20
51
|
"m.invited_member_count"?: number;
|
|
21
52
|
}
|
|
22
53
|
|