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