@ermis-network/ermis-chat-sdk 1.0.2 → 1.0.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/bin/init-call.js +52 -0
- package/dist/index.browser.cjs +284 -92
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.full-bundle.min.js +15 -15
- package/dist/index.browser.full-bundle.min.js.map +1 -1
- package/dist/index.browser.mjs +283 -92
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +284 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +199 -7
- package/dist/index.d.ts +199 -7
- package/dist/index.mjs +283 -92
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -2
- package/public/call_incoming.mp3 +0 -0
- package/public/call_outgoing.mp3 +0 -0
- package/public/ermis_call_node_wasm_bg.wasm +0 -0
- package/src/channel.ts +42 -0
- package/src/channel_state.ts +27 -0
- package/src/client.ts +104 -0
- package/src/ermis_call_node.ts +77 -47
- package/src/index.ts +2 -1
- package/src/signal_message.ts +121 -71
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CallAction: () => CallAction,
|
|
34
34
|
CallStatus: () => CallStatus,
|
|
35
|
+
CallType: () => CallType,
|
|
35
36
|
Channel: () => Channel,
|
|
36
37
|
ChannelState: () => ChannelState,
|
|
37
38
|
ClientState: () => ClientState,
|
|
@@ -413,15 +414,25 @@ var createForwardMessagePayload = (message, targetCid, activeCid) => {
|
|
|
413
414
|
// src/channel_state.ts
|
|
414
415
|
var ChannelState = class {
|
|
415
416
|
_channel;
|
|
417
|
+
/** The current count of users actively watching (having an open WebSocket) this channel. */
|
|
416
418
|
watcher_count;
|
|
419
|
+
/** A dictionary of active typing events gracefully keyed by the user's ID. */
|
|
417
420
|
typing;
|
|
421
|
+
/** A dictionary of read states mapped per user's ID detailing the last viewed message. */
|
|
418
422
|
read;
|
|
423
|
+
/** The locally cached array of pinned messages across the channel. */
|
|
419
424
|
pinnedMessages;
|
|
425
|
+
/** A directory of users actively watching the channel, keyed by User ID. */
|
|
420
426
|
watchers;
|
|
427
|
+
/** A comprehensive directory mapping user IDs to their member status in the channel. */
|
|
421
428
|
members;
|
|
429
|
+
/** The count of messages not yet read by the currently authenticated user. */
|
|
422
430
|
unreadCount;
|
|
431
|
+
/** Information detailing the authenticated user's own membership relation to this channel. */
|
|
423
432
|
membership;
|
|
433
|
+
/** Timestamp indicating when the very last message was created in this chat. */
|
|
424
434
|
last_message_at;
|
|
435
|
+
/** Designates if the local channel state is entirely synchronized with the backend history. */
|
|
425
436
|
isUpToDate;
|
|
426
437
|
messageSets = [];
|
|
427
438
|
topics = [];
|
|
@@ -453,6 +464,15 @@ var ChannelState = class {
|
|
|
453
464
|
const index = this.messageSets.findIndex((s) => s.isLatest);
|
|
454
465
|
this.messageSets[index].messages = messages;
|
|
455
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Pushes a new message directly into the sorted array of the local tracking state.
|
|
469
|
+
* Useful to achieve optimistic UI updates locally.
|
|
470
|
+
*
|
|
471
|
+
* @param newMessage - The message context payload to insert.
|
|
472
|
+
* @param timestampChanged - Specifies if the underlying `created_at` timestamp mutated.
|
|
473
|
+
* @param addIfDoesNotExist - Append it strictly if its ID doesn't already exist.
|
|
474
|
+
* @param messageSetToAddToIfDoesNotExist - Specifies which message set scope to manipulate.
|
|
475
|
+
*/
|
|
456
476
|
addMessageSorted(newMessage, timestampChanged = false, addIfDoesNotExist = true, messageSetToAddToIfDoesNotExist = "latest") {
|
|
457
477
|
return this.addMessagesSorted(
|
|
458
478
|
[newMessage],
|
|
@@ -640,6 +660,12 @@ var ChannelState = class {
|
|
|
640
660
|
const result = msgArray.filter((message) => !(!!message.id && !!msg.id && message.id === msg.id));
|
|
641
661
|
return { removed: result.length < msgArray.length, result };
|
|
642
662
|
};
|
|
663
|
+
/**
|
|
664
|
+
* Refreshes internal user references cascading across all presently active messages.
|
|
665
|
+
* Invoked instantly whenever an underlying user's profile metadata updates (e.g. name or avatar changes).
|
|
666
|
+
*
|
|
667
|
+
* @param user - The newly formatted and populated User details object.
|
|
668
|
+
*/
|
|
643
669
|
updateUserMessages = (user) => {
|
|
644
670
|
const _updateUserMessages = (messages, user2) => {
|
|
645
671
|
for (let i = 0; i < messages.length; i++) {
|
|
@@ -911,6 +937,15 @@ var Channel = class {
|
|
|
911
937
|
lastTypingEvent;
|
|
912
938
|
isTyping;
|
|
913
939
|
disconnected;
|
|
940
|
+
/**
|
|
941
|
+
* Initializes a new Channel class instance.
|
|
942
|
+
* Normally you should not call this directly; use `client.channel(type, id)` instead.
|
|
943
|
+
*
|
|
944
|
+
* @param client - The shared ErmisChat client instance initializing this channel.
|
|
945
|
+
* @param type - The type of channel (`messaging`, `team`, `livestream`, etc.).
|
|
946
|
+
* @param id - The unique ID of the channel.
|
|
947
|
+
* @param data - Initial arbitrary metadata stored within this channel.
|
|
948
|
+
*/
|
|
914
949
|
constructor(client, type, id, data) {
|
|
915
950
|
const validTypeRe = /^[\w_-]+$/;
|
|
916
951
|
const validIDRe = /^[\w!:_-]+$/;
|
|
@@ -937,6 +972,13 @@ var Channel = class {
|
|
|
937
972
|
getClient() {
|
|
938
973
|
return this._client;
|
|
939
974
|
}
|
|
975
|
+
/**
|
|
976
|
+
* Sends a message to this channel.
|
|
977
|
+
* By default, it pushes the message eagerly (optimistically) to the local UI state before the server replies.
|
|
978
|
+
*
|
|
979
|
+
* @param message - The constructed text/attachment object payload representing the message.
|
|
980
|
+
* @returns A Promise resolving to the exact API response encompassing message details.
|
|
981
|
+
*/
|
|
940
982
|
async sendMessage(message) {
|
|
941
983
|
if (!message.id) {
|
|
942
984
|
message = { ...message, id: randomId() };
|
|
@@ -1166,6 +1208,11 @@ var Channel = class {
|
|
|
1166
1208
|
const url = this.getClient().baseURL + `/invites/${this.type}/${this.id}/skip`;
|
|
1167
1209
|
return this.getClient().post(url);
|
|
1168
1210
|
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Directly invites or adds registered users into this channel.
|
|
1213
|
+
*
|
|
1214
|
+
* @param members - Array of user IDs explicitly selected to be added.
|
|
1215
|
+
*/
|
|
1169
1216
|
async addMembers(members) {
|
|
1170
1217
|
return await this._update({ add_members: members });
|
|
1171
1218
|
}
|
|
@@ -1231,6 +1278,11 @@ var Channel = class {
|
|
|
1231
1278
|
})
|
|
1232
1279
|
};
|
|
1233
1280
|
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Expels specified currently participating users out of the channel.
|
|
1283
|
+
*
|
|
1284
|
+
* @param members - Array of user IDs to strictly remove from this chat.
|
|
1285
|
+
*/
|
|
1234
1286
|
async removeMembers(members) {
|
|
1235
1287
|
return await this._update({ remove_members: members });
|
|
1236
1288
|
}
|
|
@@ -1310,6 +1362,10 @@ var Channel = class {
|
|
|
1310
1362
|
messageSlice.sort((a, b) => b.created_at.getTime() - a.created_at.getTime());
|
|
1311
1363
|
return messageSlice[0];
|
|
1312
1364
|
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Emits a mark-read event, updating the backend that the authenticated user has viewed up to the latest known message.
|
|
1367
|
+
* @returns Successful acknowledgement from the server.
|
|
1368
|
+
*/
|
|
1313
1369
|
async markRead() {
|
|
1314
1370
|
return await this.getClient().post(this._channelURL() + "/read");
|
|
1315
1371
|
}
|
|
@@ -1323,6 +1379,13 @@ var Channel = class {
|
|
|
1323
1379
|
}
|
|
1324
1380
|
this.state.clean();
|
|
1325
1381
|
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Subscribes to realtime events (WebSocket) for this channel, grabs the latest available metadata,
|
|
1384
|
+
* loads the most recent messages, and initializes the local state.
|
|
1385
|
+
*
|
|
1386
|
+
* @param options - Pagination limits like `{ watch: true, presence: true, state: true }`.
|
|
1387
|
+
* @returns The synchronized comprehensive channel state.
|
|
1388
|
+
*/
|
|
1326
1389
|
async watch(options) {
|
|
1327
1390
|
await this.getClient().wsPromise;
|
|
1328
1391
|
const combined = { ...options };
|
|
@@ -2760,32 +2823,55 @@ function isString(x) {
|
|
|
2760
2823
|
var ErmisChat = class _ErmisChat {
|
|
2761
2824
|
static _instance;
|
|
2762
2825
|
// type is undefined|ErmisChat, unknown is due to TS limitations with statics
|
|
2826
|
+
/** A map of active channels currently tracked by the client, keyed by Channel ID. */
|
|
2763
2827
|
activeChannels;
|
|
2828
|
+
/** The internal configured Axios instance used for REST API requests. */
|
|
2764
2829
|
axiosInstance;
|
|
2830
|
+
/** The primary base URL for REST API communication. */
|
|
2765
2831
|
baseURL;
|
|
2832
|
+
/** The specific base URL for standard user-focused REST calls. */
|
|
2766
2833
|
userBaseURL;
|
|
2834
|
+
/** True when the SDK is executed inside a browser environment. */
|
|
2767
2835
|
browser;
|
|
2768
2836
|
cleaningIntervalRef;
|
|
2769
2837
|
clientID;
|
|
2770
2838
|
apiKey;
|
|
2771
2839
|
projectId;
|
|
2840
|
+
/** Internal mapped registry of event listeners. */
|
|
2772
2841
|
listeners;
|
|
2773
2842
|
logger;
|
|
2843
|
+
/** Whether the client should automatically fetch missing messages upon unexpected disconnects. */
|
|
2774
2844
|
recoverStateOnReconnect;
|
|
2845
|
+
/** True when the SDK is executed in a NodeJS environment constraint. */
|
|
2775
2846
|
node;
|
|
2847
|
+
/** Custom options passed during client initialization. */
|
|
2776
2848
|
options;
|
|
2777
2849
|
setUserPromise;
|
|
2850
|
+
/** Centralized global state orchestrating user and client metadata. */
|
|
2778
2851
|
state;
|
|
2779
2852
|
tokenManager;
|
|
2853
|
+
/** The globally authenticated current user object. */
|
|
2780
2854
|
user;
|
|
2781
2855
|
userAgent;
|
|
2856
|
+
/** The unique ID of the current authenticated user. */
|
|
2782
2857
|
userID;
|
|
2858
|
+
/** The configured WebSocket endpoint base URL for realtime subscriptions. */
|
|
2783
2859
|
wsBaseURL;
|
|
2860
|
+
/** The active WebSocket connection controller. */
|
|
2784
2861
|
wsConnection;
|
|
2785
2862
|
wsPromise;
|
|
2863
|
+
/** Tracks consecutive REST API failures for exponential backoff purposes. */
|
|
2786
2864
|
consecutiveFailures;
|
|
2787
2865
|
defaultWSTimeout;
|
|
2788
2866
|
eventSource = null;
|
|
2867
|
+
/**
|
|
2868
|
+
* Initializes a new Ermis Chat Client instance.
|
|
2869
|
+
*
|
|
2870
|
+
* @param apiKey - Your public Ermis Network API Key.
|
|
2871
|
+
* @param projectId - Your specific Project UUID pointing to the app config.
|
|
2872
|
+
* @param baseURL - The API base endpoint assigned to your project.
|
|
2873
|
+
* @param options - Additional connection rules and configuration options.
|
|
2874
|
+
*/
|
|
2789
2875
|
constructor(apiKey, projectId, baseURL, options) {
|
|
2790
2876
|
this.apiKey = apiKey;
|
|
2791
2877
|
this.projectId = projectId;
|
|
@@ -2819,6 +2905,16 @@ var ErmisChat = class _ErmisChat {
|
|
|
2819
2905
|
this.logger = isFunction(inputOptions.logger) ? inputOptions.logger : () => null;
|
|
2820
2906
|
this.recoverStateOnReconnect = this.options.recoverStateOnReconnect;
|
|
2821
2907
|
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Retrieves the globally registered Singleton instance of the ErmisChat client.
|
|
2910
|
+
* If the instance lacks existence, it initializes a new one.
|
|
2911
|
+
*
|
|
2912
|
+
* @param key - Your public Ermis Network API Key.
|
|
2913
|
+
* @param projectId - Your specific Project UUID.
|
|
2914
|
+
* @param baseURL - The API base endpoint.
|
|
2915
|
+
* @param options - Connection options.
|
|
2916
|
+
* @returns The shared ErmisChat client instance.
|
|
2917
|
+
*/
|
|
2822
2918
|
static getInstance(key, projectId, baseURL, options) {
|
|
2823
2919
|
if (!_ErmisChat._instance) {
|
|
2824
2920
|
_ErmisChat._instance = new _ErmisChat(key, projectId, baseURL, options);
|
|
@@ -2866,6 +2962,15 @@ var ErmisChat = class _ErmisChat {
|
|
|
2866
2962
|
}
|
|
2867
2963
|
return await response.json();
|
|
2868
2964
|
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Connects a user to the Ermis network and establishes the WebSocket connection.
|
|
2967
|
+
* This is the primary method to authenticate your client application.
|
|
2968
|
+
*
|
|
2969
|
+
* @param user - The User object containing `id`, `name`, and optional `avatar`.
|
|
2970
|
+
* @param userTokenOrProvider - The JWT token or an async token provider function.
|
|
2971
|
+
* @param extenal_auth - Set to `true` to use your custom backend external authentication flow.
|
|
2972
|
+
* @returns A promise resolving to the API connection response once authenticated.
|
|
2973
|
+
*/
|
|
2869
2974
|
connectUser = async (user, userTokenOrProvider, extenal_auth) => {
|
|
2870
2975
|
this.logger("info", "client:connectUser() - started", {
|
|
2871
2976
|
tags: ["connection", "client"]
|
|
@@ -2948,6 +3053,12 @@ var ErmisChat = class _ErmisChat {
|
|
|
2948
3053
|
return this.wsPromise;
|
|
2949
3054
|
};
|
|
2950
3055
|
_setupConnection = this.openConnection;
|
|
3056
|
+
/**
|
|
3057
|
+
* Gracefully disconnects the current user, terminates the WebSocket connection,
|
|
3058
|
+
* cleans up listeners, and resets the client's internal references.
|
|
3059
|
+
*
|
|
3060
|
+
* @param timeout - Optional timeout in milliseconds before forcing the disconnect.
|
|
3061
|
+
*/
|
|
2951
3062
|
disconnectUser = async (timeout) => {
|
|
2952
3063
|
this.logger("info", "client:disconnect() - Disconnecting the client", {
|
|
2953
3064
|
tags: ["connection", "client"]
|
|
@@ -3513,6 +3624,16 @@ var ErmisChat = class _ErmisChat {
|
|
|
3513
3624
|
this.state.updateUser(response);
|
|
3514
3625
|
return response;
|
|
3515
3626
|
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Queries the API for a list of channels based on provided search filters and sort conditions.
|
|
3629
|
+
* Also hydrates these channels into the local SDK state memory.
|
|
3630
|
+
*
|
|
3631
|
+
* @param filterConditions - Specific criteria to filter channels (e.g. `{ type: 'messaging', members: { $in: ['user1'] } }`).
|
|
3632
|
+
* @param sort - The sorting hierarchy applied to the channel results.
|
|
3633
|
+
* @param options - Pagination and message limit parameters.
|
|
3634
|
+
* @param stateOptions - Defines whether to skip state initialization or offline usage.
|
|
3635
|
+
* @returns An array of hydrated and locally manageable `Channel` objects.
|
|
3636
|
+
*/
|
|
3516
3637
|
async queryChannels(filterConditions, sort = [], options = {}, stateOptions = {}) {
|
|
3517
3638
|
await this.wsPromise;
|
|
3518
3639
|
let project_id = this.projectId;
|
|
@@ -3664,7 +3785,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3664
3785
|
return pinExpires;
|
|
3665
3786
|
}
|
|
3666
3787
|
getUserAgent() {
|
|
3667
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3788
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.4"}`;
|
|
3668
3789
|
}
|
|
3669
3790
|
setUserAgent(userAgent) {
|
|
3670
3791
|
this.userAgent = userAgent;
|
|
@@ -6374,21 +6495,38 @@ var ErmisCallNode = class {
|
|
|
6374
6495
|
const mediaConstraints = await this.getMediaConstraints();
|
|
6375
6496
|
try {
|
|
6376
6497
|
const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6498
|
+
return this.applyLocalStream(stream);
|
|
6499
|
+
} catch (error) {
|
|
6500
|
+
console.warn("Error getting user media:", error?.message);
|
|
6501
|
+
if (this.callType === "video" && mediaConstraints.video) {
|
|
6502
|
+
try {
|
|
6503
|
+
const audioOnlyStream = await navigator.mediaDevices.getUserMedia({
|
|
6504
|
+
audio: mediaConstraints.audio,
|
|
6505
|
+
video: false
|
|
6506
|
+
});
|
|
6507
|
+
this.setConnectionMessage("Camera not available, using audio only");
|
|
6508
|
+
return this.applyLocalStream(audioOnlyStream);
|
|
6509
|
+
} catch {
|
|
6510
|
+
}
|
|
6381
6511
|
}
|
|
6382
|
-
if (this.
|
|
6383
|
-
this.
|
|
6512
|
+
if (typeof this.onError === "function") {
|
|
6513
|
+
this.onError("No microphone or camera found. Please check your device.");
|
|
6384
6514
|
}
|
|
6385
|
-
this.localStream = stream;
|
|
6386
|
-
return stream;
|
|
6387
|
-
} catch (error) {
|
|
6388
|
-
console.error("Error getting user media:", error);
|
|
6389
6515
|
return null;
|
|
6390
6516
|
}
|
|
6391
6517
|
}
|
|
6518
|
+
applyLocalStream(stream) {
|
|
6519
|
+
if (this.callStatus === "ended" /* ENDED */) {
|
|
6520
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
6521
|
+
this.destroy();
|
|
6522
|
+
return;
|
|
6523
|
+
}
|
|
6524
|
+
if (this.onLocalStream) {
|
|
6525
|
+
this.onLocalStream(stream);
|
|
6526
|
+
}
|
|
6527
|
+
this.localStream = stream;
|
|
6528
|
+
return stream;
|
|
6529
|
+
}
|
|
6392
6530
|
setConnectionMessage(message) {
|
|
6393
6531
|
if (typeof this.onConnectionMessageChange === "function") {
|
|
6394
6532
|
this.onConnectionMessageChange(message);
|
|
@@ -6433,20 +6571,10 @@ var ErmisCallNode = class {
|
|
|
6433
6571
|
this.isDestroyed = false;
|
|
6434
6572
|
this.callStatus = "";
|
|
6435
6573
|
this.callType = is_video ? "video" : "audio";
|
|
6436
|
-
await this.startLocalStream();
|
|
6437
|
-
if (this.callStatus === "ended" /* ENDED */) return;
|
|
6438
6574
|
this.setUserInfo(cid, eventUserId);
|
|
6439
6575
|
this.setCallStatus("ringing" /* RINGING */);
|
|
6440
6576
|
this.cid = cid || "";
|
|
6441
6577
|
this.metadata = metadata || {};
|
|
6442
|
-
console.log("----metadata---", metadata);
|
|
6443
|
-
if (eventUserId !== this.userID) {
|
|
6444
|
-
await this.initialize();
|
|
6445
|
-
}
|
|
6446
|
-
if (this.localStream && this.mediaSender && this.mediaReceiver) {
|
|
6447
|
-
this.mediaSender?.initEncoders(this.localStream);
|
|
6448
|
-
this.mediaReceiver?.initDecoders(this.callType);
|
|
6449
|
-
}
|
|
6450
6578
|
if (typeof this.onCallEvent === "function") {
|
|
6451
6579
|
this.onCallEvent({
|
|
6452
6580
|
type: eventUserId !== this.userID ? "incoming" : "outgoing",
|
|
@@ -6457,6 +6585,15 @@ var ErmisCallNode = class {
|
|
|
6457
6585
|
metadata: this.metadata
|
|
6458
6586
|
});
|
|
6459
6587
|
}
|
|
6588
|
+
await this.startLocalStream();
|
|
6589
|
+
if (this.callStatus === "ended" /* ENDED */) return;
|
|
6590
|
+
if (eventUserId !== this.userID) {
|
|
6591
|
+
await this.initialize();
|
|
6592
|
+
}
|
|
6593
|
+
if (this.localStream && this.mediaSender && this.mediaReceiver) {
|
|
6594
|
+
this.mediaSender?.initEncoders(this.localStream);
|
|
6595
|
+
this.mediaReceiver?.initDecoders(this.callType);
|
|
6596
|
+
}
|
|
6460
6597
|
if (eventUserId === this.userID) {
|
|
6461
6598
|
if (this.missCallTimeout) clearTimeout(this.missCallTimeout);
|
|
6462
6599
|
this.missCallTimeout = setTimeout(async () => {
|
|
@@ -6717,21 +6854,30 @@ var ErmisCallNode = class {
|
|
|
6717
6854
|
}
|
|
6718
6855
|
async stopScreenShare() {
|
|
6719
6856
|
const mediaConstraints = await this.getMediaConstraints();
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6857
|
+
try {
|
|
6858
|
+
const cameraStream = await navigator.mediaDevices.getUserMedia({
|
|
6859
|
+
video: mediaConstraints.video,
|
|
6860
|
+
audio: false
|
|
6861
|
+
});
|
|
6862
|
+
const cameraTrack = cameraStream.getVideoTracks()[0];
|
|
6863
|
+
if (this.localStream) {
|
|
6864
|
+
this.localStream.getVideoTracks().forEach((track) => {
|
|
6865
|
+
track.stop();
|
|
6866
|
+
this.localStream?.removeTrack(track);
|
|
6867
|
+
});
|
|
6868
|
+
this.localStream.addTrack(cameraTrack);
|
|
6869
|
+
} else {
|
|
6870
|
+
this.localStream = cameraStream;
|
|
6871
|
+
}
|
|
6872
|
+
if (this.onLocalStream) {
|
|
6873
|
+
this.onLocalStream(this.localStream);
|
|
6874
|
+
this.mediaSender?.replaceVideoTrack(this.localStream.getVideoTracks()[0]);
|
|
6875
|
+
}
|
|
6876
|
+
if (typeof this.onScreenShareChange === "function") {
|
|
6877
|
+
this.onScreenShareChange(false);
|
|
6878
|
+
}
|
|
6879
|
+
} catch (error) {
|
|
6880
|
+
console.error("Error stopping screen share and reverting to camera:", error);
|
|
6735
6881
|
}
|
|
6736
6882
|
}
|
|
6737
6883
|
async toggleMic(enabled) {
|
|
@@ -6999,7 +7145,7 @@ var ErmisAuthProvider = class {
|
|
|
6999
7145
|
return data;
|
|
7000
7146
|
}
|
|
7001
7147
|
getUserAgent() {
|
|
7002
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7148
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.4"}`;
|
|
7003
7149
|
}
|
|
7004
7150
|
setUserAgent(userAgent) {
|
|
7005
7151
|
this.userAgent = userAgent;
|
|
@@ -7232,71 +7378,117 @@ function parseSystemMessage(value, userMap) {
|
|
|
7232
7378
|
}
|
|
7233
7379
|
|
|
7234
7380
|
// src/signal_message.ts
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7381
|
+
var CallType = {
|
|
7382
|
+
AUDIO: "audio",
|
|
7383
|
+
VIDEO: "video"
|
|
7384
|
+
};
|
|
7385
|
+
function formatDuration(durationMs) {
|
|
7386
|
+
if (!durationMs) return "";
|
|
7387
|
+
const ms = parseInt(durationMs, 10);
|
|
7388
|
+
if (isNaN(ms) || ms <= 0) return "";
|
|
7389
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
7390
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7391
|
+
const seconds = totalSeconds % 60;
|
|
7392
|
+
return `${minutes} min, ${seconds} sec`;
|
|
7244
7393
|
}
|
|
7245
|
-
function parseSignalMessage(value,
|
|
7246
|
-
if (!value || typeof value !== "string") return
|
|
7394
|
+
function parseSignalMessage(value, myUserId) {
|
|
7395
|
+
if (!value || typeof value !== "string") return null;
|
|
7247
7396
|
const trimmed = value.trim();
|
|
7248
|
-
if (!trimmed) return
|
|
7397
|
+
if (!trimmed) return null;
|
|
7249
7398
|
const parts = trimmed.split(" ");
|
|
7250
|
-
const
|
|
7251
|
-
const
|
|
7252
|
-
const
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
case
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
case
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7399
|
+
const number = parseInt(parts[0], 10);
|
|
7400
|
+
const callerId = parts[1] ?? "";
|
|
7401
|
+
const isMe = myUserId === callerId;
|
|
7402
|
+
let enderId = "";
|
|
7403
|
+
let duration = "";
|
|
7404
|
+
let callType = "";
|
|
7405
|
+
let color = "";
|
|
7406
|
+
if (number === 3 || number === 6) {
|
|
7407
|
+
enderId = parts[2] ?? "";
|
|
7408
|
+
duration = parts[3] === "0" ? "" : parts[3] ?? "";
|
|
7409
|
+
}
|
|
7410
|
+
let text;
|
|
7411
|
+
switch (number) {
|
|
7412
|
+
case 1:
|
|
7413
|
+
text = isMe ? "Calling..." : "Incoming audio call...";
|
|
7414
|
+
callType = CallType.AUDIO;
|
|
7415
|
+
color = "#54D62C";
|
|
7416
|
+
break;
|
|
7417
|
+
case 2:
|
|
7418
|
+
text = isMe ? "Outgoing audio call" : "You missed audio call";
|
|
7419
|
+
callType = CallType.AUDIO;
|
|
7420
|
+
color = "#FF4842";
|
|
7421
|
+
break;
|
|
7422
|
+
case 3:
|
|
7423
|
+
if (duration) {
|
|
7424
|
+
text = isMe ? "Outgoing audio call" : "Incoming audio call";
|
|
7425
|
+
color = "#54D62C";
|
|
7426
|
+
} else {
|
|
7427
|
+
if (enderId === myUserId) {
|
|
7428
|
+
text = "You cancel audio call";
|
|
7429
|
+
} else {
|
|
7430
|
+
text = "You missed audio call";
|
|
7431
|
+
}
|
|
7432
|
+
color = "#FF4842";
|
|
7433
|
+
}
|
|
7434
|
+
callType = CallType.AUDIO;
|
|
7435
|
+
break;
|
|
7436
|
+
case 4:
|
|
7437
|
+
text = isMe ? "Calling..." : "Incoming video call...";
|
|
7438
|
+
callType = CallType.VIDEO;
|
|
7439
|
+
color = "#54D62C";
|
|
7440
|
+
break;
|
|
7441
|
+
case 5:
|
|
7442
|
+
text = isMe ? "Outgoing video call" : "You missed video call";
|
|
7443
|
+
callType = CallType.VIDEO;
|
|
7444
|
+
color = "#FF4842";
|
|
7445
|
+
break;
|
|
7446
|
+
case 6:
|
|
7447
|
+
if (duration) {
|
|
7448
|
+
text = isMe ? "Outgoing video call" : "Incoming video call";
|
|
7449
|
+
color = "#54D62C";
|
|
7450
|
+
} else {
|
|
7451
|
+
if (enderId === myUserId) {
|
|
7452
|
+
text = "You cancel video call";
|
|
7453
|
+
} else {
|
|
7454
|
+
text = "You missed video call";
|
|
7455
|
+
}
|
|
7456
|
+
color = "#FF4842";
|
|
7457
|
+
}
|
|
7458
|
+
callType = CallType.VIDEO;
|
|
7459
|
+
break;
|
|
7460
|
+
case 7:
|
|
7461
|
+
text = isMe ? "Recipient rejected audio call" : "You rejected audio call";
|
|
7462
|
+
callType = CallType.AUDIO;
|
|
7463
|
+
color = "#FF4842";
|
|
7464
|
+
break;
|
|
7465
|
+
case 8:
|
|
7466
|
+
text = isMe ? "Recipient rejected video call" : "You rejected video call";
|
|
7467
|
+
callType = CallType.VIDEO;
|
|
7468
|
+
color = "#FF4842";
|
|
7469
|
+
break;
|
|
7470
|
+
case 9:
|
|
7471
|
+
text = isMe ? "Recipient was busy" : "You missed audio call";
|
|
7472
|
+
callType = CallType.AUDIO;
|
|
7473
|
+
color = "#FF4842";
|
|
7474
|
+
break;
|
|
7475
|
+
case 10:
|
|
7476
|
+
text = isMe ? "Recipient was busy" : "You missed video call";
|
|
7477
|
+
callType = CallType.VIDEO;
|
|
7478
|
+
color = "#FF4842";
|
|
7479
|
+
break;
|
|
7292
7480
|
default:
|
|
7293
|
-
|
|
7481
|
+
text = trimmed;
|
|
7482
|
+
callType = "";
|
|
7483
|
+
color = "";
|
|
7294
7484
|
}
|
|
7485
|
+
return { text, duration: formatDuration(duration), callType, color };
|
|
7295
7486
|
}
|
|
7296
7487
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7297
7488
|
0 && (module.exports = {
|
|
7298
7489
|
CallAction,
|
|
7299
7490
|
CallStatus,
|
|
7491
|
+
CallType,
|
|
7300
7492
|
Channel,
|
|
7301
7493
|
ChannelState,
|
|
7302
7494
|
ClientState,
|