@stream-io/video-client 1.18.3 → 1.18.4
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/CHANGELOG.md +7 -0
- package/dist/index.browser.es.js +123 -109
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +123 -109
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +123 -109
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamVideoClient.d.ts +5 -3
- package/dist/src/coordinator/connection/client.d.ts +3 -4
- package/dist/src/coordinator/connection/connection.d.ts +3 -4
- package/dist/src/coordinator/connection/types.d.ts +14 -0
- package/dist/src/coordinator/connection/utils.d.ts +0 -7
- package/package.json +1 -1
- package/src/StreamVideoClient.ts +85 -43
- package/src/__tests__/StreamVideoClient.test.ts +82 -6
- package/src/coordinator/connection/client.ts +17 -37
- package/src/coordinator/connection/connection.ts +26 -28
- package/src/coordinator/connection/token_manager.ts +3 -9
- package/src/coordinator/connection/types.ts +17 -0
- package/src/coordinator/connection/utils.ts +15 -45
- package/src/devices/CameraManager.ts +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.18.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.3...@stream-io/video-client-1.18.4) (2025-03-10)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* retryable client.connectUser() ([#1710](https://github.com/GetStream/stream-video-js/issues/1710)) ([10b6860](https://github.com/GetStream/stream-video-js/commit/10b6860e1d65c38d8eb0ba7d7ea18f0ca30f5abc))
|
|
11
|
+
|
|
5
12
|
## [1.18.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.2...@stream-io/video-client-1.18.3) (2025-03-05)
|
|
6
13
|
|
|
7
14
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -3302,7 +3302,8 @@ function isFunction(value) {
|
|
|
3302
3302
|
*/
|
|
3303
3303
|
const KnownCodes = {
|
|
3304
3304
|
TOKEN_EXPIRED: 40,
|
|
3305
|
-
WS_CLOSED_SUCCESS: 1000
|
|
3305
|
+
WS_CLOSED_SUCCESS: 1000,
|
|
3306
|
+
};
|
|
3306
3307
|
/**
|
|
3307
3308
|
* retryInterval - A retry interval which increases acc to number of failures
|
|
3308
3309
|
*
|
|
@@ -3314,9 +3315,6 @@ function retryInterval(numberOfFailures) {
|
|
|
3314
3315
|
const min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 5000);
|
|
3315
3316
|
return Math.floor(Math.random() * (max - min) + min);
|
|
3316
3317
|
}
|
|
3317
|
-
function randomId() {
|
|
3318
|
-
return generateUUIDv4();
|
|
3319
|
-
}
|
|
3320
3318
|
function hex(bytes) {
|
|
3321
3319
|
let s = '';
|
|
3322
3320
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -3329,33 +3327,24 @@ function generateUUIDv4() {
|
|
|
3329
3327
|
const bytes = getRandomBytes(16);
|
|
3330
3328
|
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version
|
|
3331
3329
|
bytes[8] = (bytes[8] & 0xbf) | 0x80; // variant
|
|
3332
|
-
return
|
|
3333
|
-
|
|
3334
|
-
hex(bytes.subarray(4, 6))
|
|
3335
|
-
|
|
3336
|
-
hex(bytes.subarray(
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
'-' +
|
|
3340
|
-
hex(bytes.subarray(10, 16)));
|
|
3341
|
-
}
|
|
3342
|
-
function getRandomValuesWithMathRandom(bytes) {
|
|
3343
|
-
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
3344
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
3345
|
-
bytes[i] = Math.random() * max;
|
|
3346
|
-
}
|
|
3330
|
+
return [
|
|
3331
|
+
hex(bytes.subarray(0, 4)),
|
|
3332
|
+
hex(bytes.subarray(4, 6)),
|
|
3333
|
+
hex(bytes.subarray(6, 8)),
|
|
3334
|
+
hex(bytes.subarray(8, 10)),
|
|
3335
|
+
hex(bytes.subarray(10, 16)),
|
|
3336
|
+
].join('-');
|
|
3347
3337
|
}
|
|
3348
3338
|
const getRandomValues = (() => {
|
|
3349
|
-
if (typeof crypto !== 'undefined' &&
|
|
3350
|
-
typeof crypto?.getRandomValues !== 'undefined') {
|
|
3339
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
3351
3340
|
return crypto.getRandomValues.bind(crypto);
|
|
3352
3341
|
}
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
}
|
|
3342
|
+
return function getRandomValuesWithMathRandom(bytes) {
|
|
3343
|
+
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
3344
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
3345
|
+
bytes[i] = Math.random() * max;
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3359
3348
|
})();
|
|
3360
3349
|
function getRandomBytes(length) {
|
|
3361
3350
|
const bytes = new Uint8Array(length);
|
|
@@ -7465,7 +7454,7 @@ const aggregate = (stats) => {
|
|
|
7465
7454
|
return report;
|
|
7466
7455
|
};
|
|
7467
7456
|
|
|
7468
|
-
const version = "1.18.
|
|
7457
|
+
const version = "1.18.4";
|
|
7469
7458
|
const [major, minor, patch] = version.split('.');
|
|
7470
7459
|
let sdkInfo = {
|
|
7471
7460
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -9344,10 +9333,9 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
9344
9333
|
this.logger('warn', 'No video track found to do direction selection');
|
|
9345
9334
|
return;
|
|
9346
9335
|
}
|
|
9347
|
-
|
|
9336
|
+
await videoTrack.applyConstraints({
|
|
9348
9337
|
facingMode: direction === 'front' ? 'user' : 'environment',
|
|
9349
|
-
};
|
|
9350
|
-
await videoTrack.applyConstraints(constraints);
|
|
9338
|
+
});
|
|
9351
9339
|
this.state.setDirection(direction);
|
|
9352
9340
|
this.state.setDevice(undefined);
|
|
9353
9341
|
}
|
|
@@ -12011,9 +11999,7 @@ var https = null;
|
|
|
12011
11999
|
class StableWSConnection {
|
|
12012
12000
|
constructor(client) {
|
|
12013
12001
|
this._log = (msg, extra = {}, level = 'info') => {
|
|
12014
|
-
this.client.logger(level,
|
|
12015
|
-
...extra,
|
|
12016
|
-
});
|
|
12002
|
+
this.client.logger(level, `connection:${msg}`, extra);
|
|
12017
12003
|
};
|
|
12018
12004
|
this.setClient = (client) => {
|
|
12019
12005
|
this.client = client;
|
|
@@ -12067,7 +12053,7 @@ class StableWSConnection {
|
|
|
12067
12053
|
this.client.logger('error', `Token not set, can't connect authenticate`);
|
|
12068
12054
|
return;
|
|
12069
12055
|
}
|
|
12070
|
-
const authMessage = {
|
|
12056
|
+
const authMessage = JSON.stringify({
|
|
12071
12057
|
token,
|
|
12072
12058
|
user_details: {
|
|
12073
12059
|
id: user.id,
|
|
@@ -12075,8 +12061,9 @@ class StableWSConnection {
|
|
|
12075
12061
|
image: user.image,
|
|
12076
12062
|
custom: user.custom,
|
|
12077
12063
|
},
|
|
12078
|
-
};
|
|
12079
|
-
this.
|
|
12064
|
+
});
|
|
12065
|
+
this._log(`onopen() - Sending auth message ${authMessage}`, {}, 'trace');
|
|
12066
|
+
this.ws?.send(authMessage);
|
|
12080
12067
|
this._log('onopen() - onopen callback', { wsID });
|
|
12081
12068
|
};
|
|
12082
12069
|
this.onmessage = (wsID, event) => {
|
|
@@ -12089,10 +12076,12 @@ class StableWSConnection {
|
|
|
12089
12076
|
// we wait till the first message before we consider the connection open.
|
|
12090
12077
|
// the reason for this is that auth errors and similar errors trigger a ws.onopen and immediately
|
|
12091
12078
|
// after that a ws.onclose.
|
|
12092
|
-
if (!this.
|
|
12093
|
-
|
|
12079
|
+
if (!this.isConnectionOpenResolved &&
|
|
12080
|
+
data &&
|
|
12081
|
+
data.type === 'connection.error') {
|
|
12082
|
+
this.isConnectionOpenResolved = true;
|
|
12094
12083
|
if (data.error) {
|
|
12095
|
-
this.
|
|
12084
|
+
this.rejectConnectionOpen?.(this._errorFromWSEvent(data, false));
|
|
12096
12085
|
return;
|
|
12097
12086
|
}
|
|
12098
12087
|
}
|
|
@@ -12104,7 +12093,7 @@ class StableWSConnection {
|
|
|
12104
12093
|
this.scheduleNextPing();
|
|
12105
12094
|
}
|
|
12106
12095
|
if (data && data.type === 'connection.ok') {
|
|
12107
|
-
this.
|
|
12096
|
+
this.resolveConnectionOpen?.(data);
|
|
12108
12097
|
this._setHealth(true);
|
|
12109
12098
|
}
|
|
12110
12099
|
if (data && data.type === 'connection.error' && data.error) {
|
|
@@ -12141,7 +12130,7 @@ class StableWSConnection {
|
|
|
12141
12130
|
error.wasClean = event.wasClean;
|
|
12142
12131
|
// @ts-expect-error
|
|
12143
12132
|
error.target = event.target;
|
|
12144
|
-
this.
|
|
12133
|
+
this.rejectConnectionOpen?.(error);
|
|
12145
12134
|
this._log(`onclose() - WS connection reject with error ${event.reason}`, {
|
|
12146
12135
|
event,
|
|
12147
12136
|
});
|
|
@@ -12151,7 +12140,7 @@ class StableWSConnection {
|
|
|
12151
12140
|
this.totalFailures += 1;
|
|
12152
12141
|
this._setHealth(false);
|
|
12153
12142
|
this.isConnecting = false;
|
|
12154
|
-
this.
|
|
12143
|
+
this.rejectConnectionOpen?.(this._errorFromWSEvent(event));
|
|
12155
12144
|
this._log(`onclose() - WS connection closed. Calling reconnect ...`, {
|
|
12156
12145
|
event,
|
|
12157
12146
|
});
|
|
@@ -12166,7 +12155,7 @@ class StableWSConnection {
|
|
|
12166
12155
|
this.totalFailures += 1;
|
|
12167
12156
|
this._setHealth(false);
|
|
12168
12157
|
this.isConnecting = false;
|
|
12169
|
-
this.
|
|
12158
|
+
this.rejectConnectionOpen?.(new Error(`WebSocket error: ${event}`));
|
|
12170
12159
|
this._log(`onerror() - WS connection resulted into error`, { event });
|
|
12171
12160
|
this._reconnect();
|
|
12172
12161
|
};
|
|
@@ -12232,11 +12221,11 @@ class StableWSConnection {
|
|
|
12232
12221
|
* _setupPromise - sets up the this.connectOpen promise
|
|
12233
12222
|
*/
|
|
12234
12223
|
this._setupConnectionPromise = () => {
|
|
12235
|
-
this.
|
|
12224
|
+
this.isConnectionOpenResolved = false;
|
|
12236
12225
|
/** a promise that is resolved once ws.open is called */
|
|
12237
12226
|
this.connectionOpenSafe = makeSafePromise(new Promise((resolve, reject) => {
|
|
12238
|
-
this.
|
|
12239
|
-
this.
|
|
12227
|
+
this.resolveConnectionOpen = resolve;
|
|
12228
|
+
this.rejectConnectionOpen = reject;
|
|
12240
12229
|
}));
|
|
12241
12230
|
};
|
|
12242
12231
|
/**
|
|
@@ -12287,7 +12276,7 @@ class StableWSConnection {
|
|
|
12287
12276
|
/** To avoid reconnect if client is disconnected */
|
|
12288
12277
|
this.isDisconnected = false;
|
|
12289
12278
|
/** Boolean that indicates if the connection promise is resolved */
|
|
12290
|
-
this.
|
|
12279
|
+
this.isConnectionOpenResolved = false;
|
|
12291
12280
|
/** Boolean that indicates if we have a working connection to the server */
|
|
12292
12281
|
this.isHealthy = false;
|
|
12293
12282
|
/** Incremented when a new WS connection is made */
|
|
@@ -12432,9 +12421,8 @@ class StableWSConnection {
|
|
|
12432
12421
|
*/
|
|
12433
12422
|
async _connect() {
|
|
12434
12423
|
if (this.isConnecting)
|
|
12435
|
-
return; //
|
|
12424
|
+
return; // ignore _connect if it's currently trying to connect
|
|
12436
12425
|
this.isConnecting = true;
|
|
12437
|
-
this.requestID = randomId();
|
|
12438
12426
|
let isTokenReady = false;
|
|
12439
12427
|
try {
|
|
12440
12428
|
this._log(`_connect() - waiting for token`);
|
|
@@ -12454,10 +12442,7 @@ class StableWSConnection {
|
|
|
12454
12442
|
}
|
|
12455
12443
|
this._setupConnectionPromise();
|
|
12456
12444
|
const wsURL = this._buildUrl();
|
|
12457
|
-
this._log(`_connect() - Connecting to ${wsURL}
|
|
12458
|
-
wsURL,
|
|
12459
|
-
requestID: this.requestID,
|
|
12460
|
-
});
|
|
12445
|
+
this._log(`_connect() - Connecting to ${wsURL}`);
|
|
12461
12446
|
const WS = this.client.options.WebSocketImpl ?? WebSocket;
|
|
12462
12447
|
this.ws = new WS(wsURL);
|
|
12463
12448
|
this.ws.onopen = this.onopen.bind(this, this.wsID);
|
|
@@ -12601,6 +12586,8 @@ const decodeBase64 = (s) => {
|
|
|
12601
12586
|
*/
|
|
12602
12587
|
class TokenManager {
|
|
12603
12588
|
constructor(secret) {
|
|
12589
|
+
this.loadTokenPromise = null;
|
|
12590
|
+
this.type = 'static';
|
|
12604
12591
|
/**
|
|
12605
12592
|
* Set the static string token or token provider.
|
|
12606
12593
|
* Token provider should return a token string or a promise which resolves to string token.
|
|
@@ -12643,9 +12630,7 @@ class TokenManager {
|
|
|
12643
12630
|
if (!this.secret && !tokenOrProvider) {
|
|
12644
12631
|
throw new Error('User token can not be empty');
|
|
12645
12632
|
}
|
|
12646
|
-
if (tokenOrProvider &&
|
|
12647
|
-
typeof tokenOrProvider !== 'string' &&
|
|
12648
|
-
!isFunction(tokenOrProvider)) {
|
|
12633
|
+
if (typeof tokenOrProvider !== 'string' && !isFunction(tokenOrProvider)) {
|
|
12649
12634
|
throw new Error('User token should either be a string or a function');
|
|
12650
12635
|
}
|
|
12651
12636
|
if (typeof tokenOrProvider === 'string') {
|
|
@@ -12696,9 +12681,7 @@ class TokenManager {
|
|
|
12696
12681
|
throw new Error(`User token is not set. Either client.connectUser wasn't called or client.disconnect was called`);
|
|
12697
12682
|
};
|
|
12698
12683
|
this.isStatic = () => this.type === 'static';
|
|
12699
|
-
this.loadTokenPromise = null;
|
|
12700
12684
|
this.secret = secret;
|
|
12701
|
-
this.type = 'static';
|
|
12702
12685
|
}
|
|
12703
12686
|
}
|
|
12704
12687
|
|
|
@@ -12767,11 +12750,11 @@ class StreamClient {
|
|
|
12767
12750
|
* connectUser - Set the current user and open a WebSocket connection
|
|
12768
12751
|
*
|
|
12769
12752
|
* @param user Data about this user. IE {name: "john"}
|
|
12770
|
-
* @param {TokenOrProvider}
|
|
12753
|
+
* @param {TokenOrProvider} tokenOrProvider Token or provider
|
|
12771
12754
|
*
|
|
12772
12755
|
* @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup
|
|
12773
12756
|
*/
|
|
12774
|
-
this.connectUser = async (user,
|
|
12757
|
+
this.connectUser = async (user, tokenOrProvider) => {
|
|
12775
12758
|
if (!user.id) {
|
|
12776
12759
|
throw new Error('The "id" field on the user is missing');
|
|
12777
12760
|
}
|
|
@@ -12779,9 +12762,9 @@ class StreamClient {
|
|
|
12779
12762
|
* Calling connectUser multiple times is potentially the result of a bad integration, however,
|
|
12780
12763
|
* If the user id remains the same we don't throw error
|
|
12781
12764
|
*/
|
|
12782
|
-
if (this.userID === user.id && this.
|
|
12765
|
+
if (this.userID === user.id && this.connectUserTask) {
|
|
12783
12766
|
this.logger('warn', 'Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.');
|
|
12784
|
-
return this.
|
|
12767
|
+
return this.connectUserTask;
|
|
12785
12768
|
}
|
|
12786
12769
|
if (this.userID) {
|
|
12787
12770
|
throw new Error('Use client.disconnect() before trying to connect as a different user. connectUser was called twice.');
|
|
@@ -12792,26 +12775,24 @@ class StreamClient {
|
|
|
12792
12775
|
// we generate the client id client side
|
|
12793
12776
|
this.userID = user.id;
|
|
12794
12777
|
this.anonymous = false;
|
|
12795
|
-
|
|
12778
|
+
await this.tokenManager.setTokenOrProvider(tokenOrProvider, user, false);
|
|
12796
12779
|
this._setUser(user);
|
|
12797
|
-
|
|
12798
|
-
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then((result) => result[1]);
|
|
12780
|
+
this.connectUserTask = this.openConnection();
|
|
12799
12781
|
try {
|
|
12800
12782
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
12801
|
-
return await this.
|
|
12783
|
+
return await this.connectUserTask;
|
|
12802
12784
|
}
|
|
12803
12785
|
catch (err) {
|
|
12804
12786
|
if (this.persistUserOnConnectionFailure) {
|
|
12805
12787
|
// cleanup client to allow the user to retry connectUser again
|
|
12806
|
-
this.closeConnection();
|
|
12788
|
+
await this.closeConnection();
|
|
12807
12789
|
}
|
|
12808
12790
|
else {
|
|
12809
|
-
this.disconnectUser();
|
|
12791
|
+
await this.disconnectUser();
|
|
12810
12792
|
}
|
|
12811
12793
|
throw err;
|
|
12812
12794
|
}
|
|
12813
12795
|
};
|
|
12814
|
-
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
12815
12796
|
this._setUser = (user) => {
|
|
12816
12797
|
/**
|
|
12817
12798
|
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
@@ -12855,7 +12836,7 @@ class StreamClient {
|
|
|
12855
12836
|
return;
|
|
12856
12837
|
}
|
|
12857
12838
|
this._setupConnectionIdPromise();
|
|
12858
|
-
this.clientID = `${this.userID}--${
|
|
12839
|
+
this.clientID = `${this.userID}--${generateUUIDv4()}`;
|
|
12859
12840
|
const newWsPromise = this.connect();
|
|
12860
12841
|
this.wsPromiseSafe = makeSafePromise(newWsPromise);
|
|
12861
12842
|
return await newWsPromise;
|
|
@@ -12893,7 +12874,7 @@ class StreamClient {
|
|
|
12893
12874
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
12894
12875
|
this._setupConnectionIdPromise();
|
|
12895
12876
|
this.anonymous = true;
|
|
12896
|
-
await this.
|
|
12877
|
+
await this.tokenManager.setTokenOrProvider(tokenOrProvider, user, true);
|
|
12897
12878
|
this._setUser(user);
|
|
12898
12879
|
// some endpoints require a connection_id to be resolved.
|
|
12899
12880
|
// as anonymous users aren't allowed to open WS connections, we just
|
|
@@ -13101,7 +13082,7 @@ class StreamClient {
|
|
|
13101
13082
|
this.getUserAgent = () => {
|
|
13102
13083
|
if (!this.cachedUserAgent) {
|
|
13103
13084
|
const { clientAppIdentifier = {} } = this.options;
|
|
13104
|
-
const { sdkName = 'js', sdkVersion = "1.18.
|
|
13085
|
+
const { sdkName = 'js', sdkVersion = "1.18.4", ...extras } = clientAppIdentifier;
|
|
13105
13086
|
this.cachedUserAgent = [
|
|
13106
13087
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
13107
13088
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -13120,7 +13101,7 @@ class StreamClient {
|
|
|
13120
13101
|
if (!options.headers?.['x-client-request-id']) {
|
|
13121
13102
|
options.headers = {
|
|
13122
13103
|
...options.headers,
|
|
13123
|
-
'x-client-request-id':
|
|
13104
|
+
'x-client-request-id': generateUUIDv4(),
|
|
13124
13105
|
};
|
|
13125
13106
|
}
|
|
13126
13107
|
return {
|
|
@@ -13191,7 +13172,7 @@ class StreamClient {
|
|
|
13191
13172
|
// WS connection is initialized when setUser is called
|
|
13192
13173
|
this.wsConnection = null;
|
|
13193
13174
|
this.wsPromiseSafe = null;
|
|
13194
|
-
this.
|
|
13175
|
+
this.connectUserTask = null;
|
|
13195
13176
|
// mapping between channel groups and configs
|
|
13196
13177
|
this.anonymous = false;
|
|
13197
13178
|
this.persistUserOnConnectionFailure =
|
|
@@ -13200,7 +13181,7 @@ class StreamClient {
|
|
|
13200
13181
|
// generated from secret.
|
|
13201
13182
|
this.tokenManager = new TokenManager(this.secret);
|
|
13202
13183
|
this.consecutiveFailures = 0;
|
|
13203
|
-
this.defaultWSTimeout = 15000;
|
|
13184
|
+
this.defaultWSTimeout = this.options.defaultWsTimeout ?? 15000;
|
|
13204
13185
|
this.logger = isFunction(inputOptions.logger)
|
|
13205
13186
|
? inputOptions.logger
|
|
13206
13187
|
: () => null;
|
|
@@ -13277,6 +13258,7 @@ const createTokenOrProvider = (options) => {
|
|
|
13277
13258
|
*/
|
|
13278
13259
|
class StreamVideoClient {
|
|
13279
13260
|
constructor(apiKeyOrArgs, opts) {
|
|
13261
|
+
this.effectsRegistered = false;
|
|
13280
13262
|
this.eventHandlersToUnregister = [];
|
|
13281
13263
|
this.connectionConcurrencyTag = Symbol('connectionConcurrencyTag');
|
|
13282
13264
|
this.registerClientInstance = (apiKey, user) => {
|
|
@@ -13286,27 +13268,9 @@ class StreamVideoClient {
|
|
|
13286
13268
|
}
|
|
13287
13269
|
StreamVideoClient._instances.set(instanceKey, this);
|
|
13288
13270
|
};
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
* If the connection is successful, the connected user [state variable](#readonlystatestore) will be updated accordingly.
|
|
13293
|
-
*
|
|
13294
|
-
* @param user the user to connect.
|
|
13295
|
-
* @param token a token or a function that returns a token.
|
|
13296
|
-
*/
|
|
13297
|
-
this.connectUser = async (user, token) => {
|
|
13298
|
-
if (user.type === 'anonymous') {
|
|
13299
|
-
user.id = '!anon';
|
|
13300
|
-
return this.connectAnonymousUser(user, token);
|
|
13301
|
-
}
|
|
13302
|
-
const connectUser = user.type === 'guest'
|
|
13303
|
-
? () => this.streamClient.connectGuestUser(user)
|
|
13304
|
-
: () => this.streamClient.connectUser(user, token);
|
|
13305
|
-
const connectUserResponse = await withoutConcurrency(this.connectionConcurrencyTag, () => connectUser());
|
|
13306
|
-
// connectUserResponse will be void if connectUser called twice for the same user
|
|
13307
|
-
if (connectUserResponse?.me) {
|
|
13308
|
-
this.writeableStateStore.setConnectedUser(connectUserResponse.me);
|
|
13309
|
-
}
|
|
13271
|
+
this.registerEffects = () => {
|
|
13272
|
+
if (this.effectsRegistered)
|
|
13273
|
+
return;
|
|
13310
13274
|
this.eventHandlersToUnregister.push(this.on('connection.changed', (event) => {
|
|
13311
13275
|
if (!event.online)
|
|
13312
13276
|
return;
|
|
@@ -13326,7 +13290,7 @@ class StreamVideoClient {
|
|
|
13326
13290
|
}));
|
|
13327
13291
|
this.eventHandlersToUnregister.push(this.on('call.created', (event) => {
|
|
13328
13292
|
const { call, members } = event;
|
|
13329
|
-
if (
|
|
13293
|
+
if (this.state.connectedUser?.id === call.created_by.id) {
|
|
13330
13294
|
this.logger('warn', 'Received `call.created` sent by the current user');
|
|
13331
13295
|
return;
|
|
13332
13296
|
}
|
|
@@ -13343,7 +13307,7 @@ class StreamVideoClient {
|
|
|
13343
13307
|
}));
|
|
13344
13308
|
this.eventHandlersToUnregister.push(this.on('call.ring', async (event) => {
|
|
13345
13309
|
const { call, members } = event;
|
|
13346
|
-
if (
|
|
13310
|
+
if (this.state.connectedUser?.id === call.created_by.id) {
|
|
13347
13311
|
this.logger('debug', 'Received `call.ring` sent by the current user so ignoring the event');
|
|
13348
13312
|
return;
|
|
13349
13313
|
}
|
|
@@ -13367,6 +13331,53 @@ class StreamVideoClient {
|
|
|
13367
13331
|
await newCallInstance.get();
|
|
13368
13332
|
}
|
|
13369
13333
|
}));
|
|
13334
|
+
this.effectsRegistered = true;
|
|
13335
|
+
};
|
|
13336
|
+
/**
|
|
13337
|
+
* Connects the given user to the client.
|
|
13338
|
+
* Only one user can connect at a time, if you want to change users, call `disconnectUser` before connecting a new user.
|
|
13339
|
+
* If the connection is successful, the connected user [state variable](#readonlystatestore) will be updated accordingly.
|
|
13340
|
+
*
|
|
13341
|
+
* @param user the user to connect.
|
|
13342
|
+
* @param tokenOrProvider a token or a function that returns a token.
|
|
13343
|
+
*/
|
|
13344
|
+
this.connectUser = async (user, tokenOrProvider) => {
|
|
13345
|
+
if (user.type === 'anonymous') {
|
|
13346
|
+
user.id = '!anon';
|
|
13347
|
+
return this.connectAnonymousUser(user, tokenOrProvider);
|
|
13348
|
+
}
|
|
13349
|
+
const connectUserResponse = await withoutConcurrency(this.connectionConcurrencyTag, async () => {
|
|
13350
|
+
const client = this.streamClient;
|
|
13351
|
+
const { maxConnectUserRetries = 5, onConnectUserError, persistUserOnConnectionFailure, } = client.options;
|
|
13352
|
+
const errorQueue = [];
|
|
13353
|
+
for (let attempt = 0; attempt < maxConnectUserRetries; attempt++) {
|
|
13354
|
+
try {
|
|
13355
|
+
this.logger('trace', `Connecting user (${attempt})`, user);
|
|
13356
|
+
return user.type === 'guest'
|
|
13357
|
+
? await client.connectGuestUser(user)
|
|
13358
|
+
: await client.connectUser(user, tokenOrProvider);
|
|
13359
|
+
}
|
|
13360
|
+
catch (err) {
|
|
13361
|
+
this.logger('warn', `Failed to connect a user (${attempt})`, err);
|
|
13362
|
+
errorQueue.push(err);
|
|
13363
|
+
if (attempt === maxConnectUserRetries - 1) {
|
|
13364
|
+
onConnectUserError?.(err, errorQueue);
|
|
13365
|
+
throw err;
|
|
13366
|
+
}
|
|
13367
|
+
// we need to force to disconnect the user if the client is
|
|
13368
|
+
// configured to persist the user on connection failure
|
|
13369
|
+
if (persistUserOnConnectionFailure) {
|
|
13370
|
+
await client.disconnectUser();
|
|
13371
|
+
}
|
|
13372
|
+
await sleep(retryInterval(attempt));
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13375
|
+
});
|
|
13376
|
+
// connectUserResponse will be void if connectUser called twice for the same user
|
|
13377
|
+
if (connectUserResponse?.me) {
|
|
13378
|
+
this.writeableStateStore.setConnectedUser(connectUserResponse.me);
|
|
13379
|
+
}
|
|
13380
|
+
this.registerEffects();
|
|
13370
13381
|
return connectUserResponse;
|
|
13371
13382
|
};
|
|
13372
13383
|
/**
|
|
@@ -13378,16 +13389,19 @@ class StreamVideoClient {
|
|
|
13378
13389
|
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
13379
13390
|
*/
|
|
13380
13391
|
this.disconnectUser = async (timeout) => {
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13392
|
+
await withoutConcurrency(this.connectionConcurrencyTag, async () => {
|
|
13393
|
+
const { user, key } = this.streamClient;
|
|
13394
|
+
if (!user)
|
|
13395
|
+
return;
|
|
13396
|
+
await this.streamClient.disconnectUser(timeout);
|
|
13397
|
+
if (user.id) {
|
|
13398
|
+
StreamVideoClient._instances.delete(getInstanceKey(key, user));
|
|
13399
|
+
}
|
|
13400
|
+
this.eventHandlersToUnregister.forEach((unregister) => unregister());
|
|
13401
|
+
this.eventHandlersToUnregister = [];
|
|
13402
|
+
this.effectsRegistered = false;
|
|
13403
|
+
this.writeableStateStore.setConnectedUser(undefined);
|
|
13404
|
+
});
|
|
13391
13405
|
};
|
|
13392
13406
|
/**
|
|
13393
13407
|
* You can subscribe to WebSocket events provided by the API.
|