@ermis-network/ermis-chat-sdk 1.0.1 → 1.0.3
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 +174 -95
- 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 +173 -95
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +174 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +29 -9
- package/dist/index.d.ts +29 -9
- package/dist/index.mjs +173 -95
- 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/client.ts +17 -6
- package/src/ermis_call_node.ts +77 -47
- package/src/index.ts +2 -1
- package/src/signal_message.ts +121 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ermis-network/ermis-chat-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Ermis Chat SDK",
|
|
5
5
|
"author": "Ermis",
|
|
6
6
|
"homepage": "https://ermis.network/",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"require": "./dist/index.cjs"
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"ermis-init-call": "./bin/init-call.js"
|
|
31
|
+
},
|
|
29
32
|
"jsdelivr": "./dist/index.browser.full-bundle.min.js",
|
|
30
33
|
"license": "SEE LICENSE IN LICENSE",
|
|
31
34
|
"keywords": [
|
|
@@ -40,7 +43,9 @@
|
|
|
40
43
|
"/dist",
|
|
41
44
|
"/src",
|
|
42
45
|
"readme.md",
|
|
43
|
-
"license"
|
|
46
|
+
"license",
|
|
47
|
+
"/bin",
|
|
48
|
+
"/public"
|
|
44
49
|
],
|
|
45
50
|
"dependencies": {
|
|
46
51
|
"@types/event-source-polyfill": "^1.0.5",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/client.ts
CHANGED
|
@@ -1192,11 +1192,13 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1192
1192
|
return await this.post<APIResponse>(this.baseURL + `/channels/${channelType}/${channelId}/unpin`);
|
|
1193
1193
|
}
|
|
1194
1194
|
|
|
1195
|
+
channel(type: string, custom?: ChannelData<ErmisChatGenerics>): Channel<ErmisChatGenerics>;
|
|
1196
|
+
channel(type: string, id: string, custom?: ChannelData<ErmisChatGenerics>): Channel<ErmisChatGenerics>;
|
|
1195
1197
|
channel(
|
|
1196
1198
|
channelType: string,
|
|
1197
|
-
|
|
1198
|
-
custom
|
|
1199
|
-
) {
|
|
1199
|
+
channelIDOrCustom?: string | ChannelData<ErmisChatGenerics>,
|
|
1200
|
+
custom?: ChannelData<ErmisChatGenerics>,
|
|
1201
|
+
): Channel<ErmisChatGenerics> {
|
|
1200
1202
|
if (!this.userID) {
|
|
1201
1203
|
throw Error('Call connectUser before creating a channel');
|
|
1202
1204
|
}
|
|
@@ -1205,11 +1207,20 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1205
1207
|
throw Error(`Invalid channel group ${channelType}, can't contain the : character`);
|
|
1206
1208
|
}
|
|
1207
1209
|
|
|
1208
|
-
|
|
1210
|
+
let channelID: string | undefined = undefined;
|
|
1211
|
+
let customData = custom || ({} as ChannelData<ErmisChatGenerics>);
|
|
1212
|
+
|
|
1213
|
+
if (typeof channelIDOrCustom === 'string') {
|
|
1214
|
+
channelID = channelIDOrCustom;
|
|
1215
|
+
} else if (typeof channelIDOrCustom === 'object' && channelIDOrCustom !== null) {
|
|
1216
|
+
customData = channelIDOrCustom as ChannelData<ErmisChatGenerics>;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
return this.getChannelById(channelType, channelID, customData);
|
|
1209
1220
|
}
|
|
1210
1221
|
|
|
1211
|
-
getChannelById = (channelType: string, channelID: string, custom: ChannelData<ErmisChatGenerics>) => {
|
|
1212
|
-
const cid = `${channelType}:${channelID}`;
|
|
1222
|
+
getChannelById = (channelType: string, channelID: string | undefined, custom: ChannelData<ErmisChatGenerics>) => {
|
|
1223
|
+
const cid = `${channelType}:${channelID || ''}`;
|
|
1213
1224
|
if (cid in this.activeChannels && !this.activeChannels[cid].disconnected) {
|
|
1214
1225
|
const channel = this.activeChannels[cid];
|
|
1215
1226
|
if (Object.keys(custom).length > 0) {
|
package/src/ermis_call_node.ts
CHANGED
|
@@ -335,28 +335,46 @@ export class ErmisCallNode<ErmisChatGenerics extends ExtendableGenerics = Defaul
|
|
|
335
335
|
const mediaConstraints = await this.getMediaConstraints();
|
|
336
336
|
|
|
337
337
|
try {
|
|
338
|
-
// Request the media stream with the determined constraints
|
|
339
338
|
const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
339
|
+
return this.applyLocalStream(stream);
|
|
340
|
+
} catch (error: any) {
|
|
341
|
+
console.warn('Error getting user media:', error?.message);
|
|
342
|
+
|
|
343
|
+
// Video call: try fallback to audio-only (camera not available)
|
|
344
|
+
if (this.callType === 'video' && mediaConstraints.video) {
|
|
345
|
+
try {
|
|
346
|
+
const audioOnlyStream = await navigator.mediaDevices.getUserMedia({
|
|
347
|
+
audio: mediaConstraints.audio,
|
|
348
|
+
video: false,
|
|
349
|
+
});
|
|
350
|
+
this.setConnectionMessage('Camera not available, using audio only');
|
|
351
|
+
return this.applyLocalStream(audioOnlyStream);
|
|
352
|
+
} catch {
|
|
353
|
+
// Audio fallback also failed
|
|
354
|
+
}
|
|
345
355
|
}
|
|
346
|
-
|
|
347
|
-
|
|
356
|
+
|
|
357
|
+
// No device found at all — report error
|
|
358
|
+
if (typeof this.onError === 'function') {
|
|
359
|
+
this.onError('No microphone or camera found. Please check your device.');
|
|
348
360
|
}
|
|
349
|
-
this.localStream = stream;
|
|
350
|
-
return stream;
|
|
351
|
-
} catch (error) {
|
|
352
|
-
console.error('Error getting user media:', error);
|
|
353
|
-
// if (typeof this.onError === 'function') {
|
|
354
|
-
// this.onError('Unable to access microphone/camera');
|
|
355
|
-
// }
|
|
356
361
|
return null;
|
|
357
362
|
}
|
|
358
363
|
}
|
|
359
364
|
|
|
365
|
+
private applyLocalStream(stream: MediaStream) {
|
|
366
|
+
if (this.callStatus === CallStatus.ENDED) {
|
|
367
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
368
|
+
this.destroy();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (this.onLocalStream) {
|
|
372
|
+
this.onLocalStream(stream);
|
|
373
|
+
}
|
|
374
|
+
this.localStream = stream;
|
|
375
|
+
return stream;
|
|
376
|
+
}
|
|
377
|
+
|
|
360
378
|
private setConnectionMessage(message: string | null) {
|
|
361
379
|
if (typeof this.onConnectionMessageChange === 'function') {
|
|
362
380
|
this.onConnectionMessageChange(message);
|
|
@@ -415,23 +433,12 @@ export class ErmisCallNode<ErmisChatGenerics extends ExtendableGenerics = Defaul
|
|
|
415
433
|
this.isDestroyed = false;
|
|
416
434
|
this.callStatus = '';
|
|
417
435
|
this.callType = is_video ? 'video' : 'audio';
|
|
418
|
-
|
|
419
|
-
if (this.callStatus === CallStatus.ENDED) return;
|
|
436
|
+
|
|
420
437
|
this.setUserInfo(cid, eventUserId);
|
|
421
438
|
this.setCallStatus(CallStatus.RINGING);
|
|
422
439
|
this.cid = cid || '';
|
|
423
440
|
this.metadata = metadata || {};
|
|
424
441
|
|
|
425
|
-
console.log('----metadata---', metadata);
|
|
426
|
-
if (eventUserId !== this.userID) {
|
|
427
|
-
await this.initialize();
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (this.localStream && this.mediaSender && this.mediaReceiver) {
|
|
431
|
-
this.mediaSender?.initEncoders(this.localStream);
|
|
432
|
-
this.mediaReceiver?.initDecoders(this.callType);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
442
|
if (typeof this.onCallEvent === 'function') {
|
|
436
443
|
this.onCallEvent({
|
|
437
444
|
type: eventUserId !== this.userID ? 'incoming' : 'outgoing',
|
|
@@ -443,6 +450,18 @@ export class ErmisCallNode<ErmisChatGenerics extends ExtendableGenerics = Defaul
|
|
|
443
450
|
});
|
|
444
451
|
}
|
|
445
452
|
|
|
453
|
+
await this.startLocalStream();
|
|
454
|
+
if (this.callStatus === CallStatus.ENDED) return;
|
|
455
|
+
|
|
456
|
+
if (eventUserId !== this.userID) {
|
|
457
|
+
await this.initialize();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (this.localStream && this.mediaSender && this.mediaReceiver) {
|
|
461
|
+
this.mediaSender?.initEncoders(this.localStream);
|
|
462
|
+
this.mediaReceiver?.initDecoders(this.callType);
|
|
463
|
+
}
|
|
464
|
+
|
|
446
465
|
if (eventUserId === this.userID) {
|
|
447
466
|
// Set missCall timeout if no connection after 60s
|
|
448
467
|
if (this.missCallTimeout) clearTimeout(this.missCallTimeout);
|
|
@@ -782,29 +801,40 @@ export class ErmisCallNode<ErmisChatGenerics extends ExtendableGenerics = Defaul
|
|
|
782
801
|
public async stopScreenShare() {
|
|
783
802
|
const mediaConstraints = await this.getMediaConstraints();
|
|
784
803
|
|
|
785
|
-
|
|
786
|
-
|
|
804
|
+
try {
|
|
805
|
+
// Only request video; we already have an active audio track in localStream
|
|
806
|
+
const cameraStream = await navigator.mediaDevices.getUserMedia({
|
|
807
|
+
video: mediaConstraints.video,
|
|
808
|
+
audio: false,
|
|
809
|
+
});
|
|
810
|
+
const cameraTrack = cameraStream.getVideoTracks()[0];
|
|
787
811
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
812
|
+
// Replace video track in localStream
|
|
813
|
+
if (this.localStream) {
|
|
814
|
+
// Stop old screen tracks
|
|
815
|
+
this.localStream.getVideoTracks().forEach((track) => {
|
|
816
|
+
track.stop();
|
|
817
|
+
this.localStream?.removeTrack(track);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// Add new camera track
|
|
821
|
+
this.localStream.addTrack(cameraTrack);
|
|
822
|
+
} else {
|
|
823
|
+
this.localStream = cameraStream;
|
|
824
|
+
}
|
|
798
825
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
826
|
+
// Call callback if UI needs to update
|
|
827
|
+
if (this.onLocalStream) {
|
|
828
|
+
this.onLocalStream(this.localStream);
|
|
829
|
+
this.mediaSender?.replaceVideoTrack(this.localStream.getVideoTracks()[0]);
|
|
830
|
+
}
|
|
804
831
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
832
|
+
// Call callback when screen sharing stops
|
|
833
|
+
if (typeof this.onScreenShareChange === 'function') {
|
|
834
|
+
this.onScreenShareChange(false);
|
|
835
|
+
}
|
|
836
|
+
} catch (error) {
|
|
837
|
+
console.error('Error stopping screen share and reverting to camera:', error);
|
|
808
838
|
}
|
|
809
839
|
}
|
|
810
840
|
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './ermis_call_node';
|
|
|
10
10
|
export * from './auth';
|
|
11
11
|
export { chatCodes, logChatPromiseExecution, formatMessage, createForwardMessagePayload } from './utils';
|
|
12
12
|
export { parseSystemMessage } from './system_message';
|
|
13
|
-
export { parseSignalMessage } from './signal_message';
|
|
13
|
+
export { parseSignalMessage, CallType } from './signal_message';
|
|
14
|
+
export type { SignalMessageResult, CallTypeValue } from './signal_message';
|
|
14
15
|
export { normalizeFileName, getAttachmentCategory, isVideoFile, isHeicFile, buildAttachmentPayload } from './attachment_utils';
|
|
15
16
|
export type { VoiceRecordingMeta } from './attachment_utils';
|
package/src/signal_message.ts
CHANGED
|
@@ -1,96 +1,146 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Call type constants for signal messages.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export const CallType = {
|
|
5
|
+
AUDIO: 'audio',
|
|
6
|
+
VIDEO: 'video',
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type CallTypeValue = (typeof CallType)[keyof typeof CallType];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Result of parsing a signal message.
|
|
13
|
+
*/
|
|
14
|
+
export interface SignalMessageResult {
|
|
15
|
+
text: string;
|
|
16
|
+
duration: string;
|
|
17
|
+
callType: CallTypeValue | '';
|
|
18
|
+
color: string;
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
14
|
-
* Falls back to the raw userId if no entry is found.
|
|
22
|
+
* Format duration from milliseconds to "X min, Y sec" format.
|
|
15
23
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
24
|
+
function formatDuration(durationMs: string): string {
|
|
25
|
+
if (!durationMs) return '';
|
|
26
|
+
const ms = parseInt(durationMs, 10);
|
|
27
|
+
if (isNaN(ms) || ms <= 0) return '';
|
|
28
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
29
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
30
|
+
const seconds = totalSeconds % 60;
|
|
31
|
+
return `${minutes} min, ${seconds} sec`;
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
/**
|
|
21
|
-
* Parse a raw signal message string into a
|
|
35
|
+
* Parse a raw signal message string into a structured object
|
|
36
|
+
* containing text, duration, call type, and color.
|
|
22
37
|
*
|
|
23
38
|
* Signal messages represent call events. The raw format is:
|
|
24
|
-
* `"<formatId> <
|
|
39
|
+
* `"<formatId> <callerId> [<enderId> <duration>]"`
|
|
25
40
|
*
|
|
26
|
-
* @param value
|
|
27
|
-
* @param
|
|
28
|
-
* @returns
|
|
41
|
+
* @param value - Raw signal message string from the server
|
|
42
|
+
* @param myUserId - The current user's ID (from client.userID)
|
|
43
|
+
* @returns Parsed signal message object, or null if input is empty
|
|
29
44
|
*/
|
|
30
45
|
export function parseSignalMessage(
|
|
31
46
|
value: string,
|
|
32
|
-
|
|
33
|
-
):
|
|
34
|
-
if (!value || typeof value !== 'string') return
|
|
47
|
+
myUserId: string,
|
|
48
|
+
): SignalMessageResult | null {
|
|
49
|
+
if (!value || typeof value !== 'string') return null;
|
|
35
50
|
|
|
36
51
|
const trimmed = value.trim();
|
|
37
|
-
if (!trimmed) return
|
|
52
|
+
if (!trimmed) return null;
|
|
38
53
|
|
|
39
54
|
const parts = trimmed.split(' ');
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
switch (formatId) {
|
|
45
|
-
// 1: Audio call started
|
|
46
|
-
case '1':
|
|
47
|
-
return `📞 ${userName} started an audio call.`;
|
|
48
|
-
|
|
49
|
-
// 2: Audio call missed
|
|
50
|
-
case '2':
|
|
51
|
-
return `📞 Missed audio call from ${userName}.`;
|
|
52
|
-
|
|
53
|
-
// 3: Audio call ended (caller_id ender_id duration)
|
|
54
|
-
case '3': {
|
|
55
|
-
const enderId = parts[2] ?? '';
|
|
56
|
-
const duration = parts[3] ?? '0';
|
|
57
|
-
const enderName = enderId ? resolveUser(enderId, userMap) : 'User';
|
|
58
|
-
return `📞 Audio call by ${userName}, ended by ${enderName}. Duration: ${formatDuration(duration)}.`;
|
|
59
|
-
}
|
|
55
|
+
const number = parseInt(parts[0], 10);
|
|
56
|
+
const callerId = parts[1] ?? '';
|
|
57
|
+
const isMe = myUserId === callerId;
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
let enderId = '';
|
|
60
|
+
let duration = '';
|
|
61
|
+
let callType: CallTypeValue | '' = '';
|
|
62
|
+
let color = '';
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// 6: Video call ended (caller_id ender_id duration)
|
|
70
|
-
case '6': {
|
|
71
|
-
const enderId = parts[2] ?? '';
|
|
72
|
-
const duration = parts[3] ?? '0';
|
|
73
|
-
const enderName = enderId ? resolveUser(enderId, userMap) : 'User';
|
|
74
|
-
return `📹 Video call by ${userName}, ended by ${enderName}. Duration: ${formatDuration(duration)}.`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 7: Audio call rejected
|
|
78
|
-
case '7':
|
|
79
|
-
return `📞 Audio call from ${userName} was rejected.`;
|
|
80
|
-
|
|
81
|
-
// 8: Video call rejected
|
|
82
|
-
case '8':
|
|
83
|
-
return `📹 Video call from ${userName} was rejected.`;
|
|
84
|
-
|
|
85
|
-
// 9: Audio call busy
|
|
86
|
-
case '9':
|
|
87
|
-
return `📞 Audio call from ${userName} — recipient was busy.`;
|
|
88
|
-
|
|
89
|
-
// 10: Video call busy
|
|
90
|
-
case '10':
|
|
91
|
-
return `📹 Video call from ${userName} — recipient was busy.`;
|
|
64
|
+
if (number === 3 || number === 6) {
|
|
65
|
+
enderId = parts[2] ?? '';
|
|
66
|
+
duration = parts[3] === '0' ? '' : (parts[3] ?? '');
|
|
67
|
+
}
|
|
92
68
|
|
|
69
|
+
let text: string;
|
|
70
|
+
switch (number) {
|
|
71
|
+
case 1: // AudioCallStarted
|
|
72
|
+
text = isMe ? 'Calling...' : 'Incoming audio call...';
|
|
73
|
+
callType = CallType.AUDIO;
|
|
74
|
+
color = '#54D62C';
|
|
75
|
+
break;
|
|
76
|
+
case 2: // AudioCallMissed
|
|
77
|
+
text = isMe ? 'Outgoing audio call' : 'You missed audio call';
|
|
78
|
+
callType = CallType.AUDIO;
|
|
79
|
+
color = '#FF4842';
|
|
80
|
+
break;
|
|
81
|
+
case 3: // AudioCallEnded
|
|
82
|
+
if (duration) {
|
|
83
|
+
text = isMe ? 'Outgoing audio call' : 'Incoming audio call';
|
|
84
|
+
color = '#54D62C';
|
|
85
|
+
} else {
|
|
86
|
+
if (enderId === myUserId) {
|
|
87
|
+
text = 'You cancel audio call';
|
|
88
|
+
} else {
|
|
89
|
+
text = 'You missed audio call';
|
|
90
|
+
}
|
|
91
|
+
color = '#FF4842';
|
|
92
|
+
}
|
|
93
|
+
callType = CallType.AUDIO;
|
|
94
|
+
break;
|
|
95
|
+
case 4: // VideoCallStarted
|
|
96
|
+
text = isMe ? 'Calling...' : 'Incoming video call...';
|
|
97
|
+
callType = CallType.VIDEO;
|
|
98
|
+
color = '#54D62C';
|
|
99
|
+
break;
|
|
100
|
+
case 5: // VideoCallMissed
|
|
101
|
+
text = isMe ? 'Outgoing video call' : 'You missed video call';
|
|
102
|
+
callType = CallType.VIDEO;
|
|
103
|
+
color = '#FF4842';
|
|
104
|
+
break;
|
|
105
|
+
case 6: // VideoCallEnded
|
|
106
|
+
if (duration) {
|
|
107
|
+
text = isMe ? 'Outgoing video call' : 'Incoming video call';
|
|
108
|
+
color = '#54D62C';
|
|
109
|
+
} else {
|
|
110
|
+
if (enderId === myUserId) {
|
|
111
|
+
text = 'You cancel video call';
|
|
112
|
+
} else {
|
|
113
|
+
text = 'You missed video call';
|
|
114
|
+
}
|
|
115
|
+
color = '#FF4842';
|
|
116
|
+
}
|
|
117
|
+
callType = CallType.VIDEO;
|
|
118
|
+
break;
|
|
119
|
+
case 7: // AudioCallRejected
|
|
120
|
+
text = isMe ? 'Recipient rejected audio call' : 'You rejected audio call';
|
|
121
|
+
callType = CallType.AUDIO;
|
|
122
|
+
color = '#FF4842';
|
|
123
|
+
break;
|
|
124
|
+
case 8: // VideoCallRejected
|
|
125
|
+
text = isMe ? 'Recipient rejected video call' : 'You rejected video call';
|
|
126
|
+
callType = CallType.VIDEO;
|
|
127
|
+
color = '#FF4842';
|
|
128
|
+
break;
|
|
129
|
+
case 9: // AudioCallBusy
|
|
130
|
+
text = isMe ? 'Recipient was busy' : 'You missed audio call';
|
|
131
|
+
callType = CallType.AUDIO;
|
|
132
|
+
color = '#FF4842';
|
|
133
|
+
break;
|
|
134
|
+
case 10: // VideoCallBusy
|
|
135
|
+
text = isMe ? 'Recipient was busy' : 'You missed video call';
|
|
136
|
+
callType = CallType.VIDEO;
|
|
137
|
+
color = '#FF4842';
|
|
138
|
+
break;
|
|
93
139
|
default:
|
|
94
|
-
|
|
140
|
+
text = trimmed;
|
|
141
|
+
callType = '';
|
|
142
|
+
color = '';
|
|
95
143
|
}
|
|
144
|
+
|
|
145
|
+
return { text, duration: formatDuration(duration), callType, color };
|
|
96
146
|
}
|