@ermis-network/ermis-chat-sdk 1.0.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/init-call.js +9 -0
- package/dist/index.browser.cjs +778 -1628
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.full-bundle.min.js +16 -18
- package/dist/index.browser.full-bundle.min.js.map +1 -1
- package/dist/index.browser.mjs +780 -1630
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +778 -1628
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +173 -40
- package/dist/index.d.ts +173 -40
- package/dist/index.mjs +780 -1630
- package/dist/index.mjs.map +1 -1
- package/dist/wasm_worker.worker.mjs +1596 -0
- package/dist/wasm_worker.worker.mjs.map +1 -0
- package/package.json +2 -2
- package/public/ermis_call_node_wasm_bg.wasm +0 -0
- package/src/channel.ts +117 -44
- package/src/channel_state.ts +6 -1
- package/src/client.ts +198 -56
- package/src/ermis_call_node.ts +123 -55
- package/src/index.ts +2 -1
- package/src/media_stream_receiver.ts +103 -35
- package/src/media_stream_sender.ts +72 -7
- package/src/signal_message.ts +48 -23
- package/src/system_message.ts +169 -27
- package/src/types.ts +13 -0
- package/src/utils.ts +22 -3
- package/src/wasm/ermis_call_node_wasm.d.ts +80 -78
- package/src/wasm/ermis_call_node_wasm.js +1427 -1357
- package/src/wasm_worker.ts +219 -0
- package/src/wasm_worker_proxy.ts +244 -0
package/src/client.ts
CHANGED
|
@@ -292,7 +292,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
292
292
|
|
|
293
293
|
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
294
294
|
this._setUser(connectionUser);
|
|
295
|
-
this.state.updateUser({
|
|
295
|
+
this.state.updateUser({
|
|
296
|
+
id: connectionUser.id,
|
|
297
|
+
name: connectionUser?.name || connectionUser.id,
|
|
298
|
+
avatar: connectionUser?.avatar || '',
|
|
299
|
+
});
|
|
296
300
|
|
|
297
301
|
const wsPromise = this.openConnection();
|
|
298
302
|
|
|
@@ -304,6 +308,21 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
304
308
|
const result = await this.setUserPromise;
|
|
305
309
|
// Call SSE after successful connect
|
|
306
310
|
await this.connectToSSE();
|
|
311
|
+
|
|
312
|
+
// Automatically fetch full profile asynchronously and dispatch event
|
|
313
|
+
this.queryUser(connectionUser.id)
|
|
314
|
+
.then((fullProfile) => {
|
|
315
|
+
this.user = { ...this.user, ...fullProfile };
|
|
316
|
+
this.state.updateUser(this.user);
|
|
317
|
+
this.dispatchEvent({
|
|
318
|
+
type: 'user.updated',
|
|
319
|
+
me: this.user,
|
|
320
|
+
} as unknown as Event<ErmisChatGenerics>);
|
|
321
|
+
})
|
|
322
|
+
.catch((err) => {
|
|
323
|
+
this.logger('error', 'client:connectUser() - failed to fetch full user profile', { err });
|
|
324
|
+
});
|
|
325
|
+
|
|
307
326
|
return result;
|
|
308
327
|
} catch (err) {
|
|
309
328
|
this.disconnectUser();
|
|
@@ -600,10 +619,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
600
619
|
* @returns A Blob of the file content.
|
|
601
620
|
*/
|
|
602
621
|
async downloadMedia(url: string): Promise<Blob> {
|
|
603
|
-
const response = await
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
622
|
+
const response = await fetch(url, { cache: 'no-store' });
|
|
623
|
+
if (!response.ok) {
|
|
624
|
+
throw new Error(`Failed to download media: ${response.statusText}`);
|
|
625
|
+
}
|
|
626
|
+
return await response.blob();
|
|
607
627
|
}
|
|
608
628
|
|
|
609
629
|
errorFromResponse(response: AxiosResponse<APIErrorResponse>): ErrorFromResponse<APIErrorResponse> {
|
|
@@ -709,21 +729,76 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
709
729
|
};
|
|
710
730
|
|
|
711
731
|
_updateMemberWatcherReferences = (user: UserResponse<ErmisChatGenerics>) => {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const channel = this.activeChannels[channelID];
|
|
732
|
+
// Iterate through all active channels to ensure we update members even if they haven't sent messages yet
|
|
733
|
+
Object.values(this.activeChannels).forEach((channel) => {
|
|
715
734
|
if (channel?.state) {
|
|
735
|
+
let hasChange = false;
|
|
716
736
|
if (channel.state.members[user.id]) {
|
|
717
|
-
channel.state.members
|
|
737
|
+
channel.state.members = {
|
|
738
|
+
...channel.state.members,
|
|
739
|
+
[user.id]: {
|
|
740
|
+
...channel.state.members[user.id],
|
|
741
|
+
user,
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
hasChange = true;
|
|
745
|
+
|
|
746
|
+
// Update display name/image for 1-1 Messaging Channels
|
|
747
|
+
if (channel.data?.type === 'messaging') {
|
|
748
|
+
const members = Object.values(channel.state.members);
|
|
749
|
+
if (members.length === 2) {
|
|
750
|
+
const otherMember = members.find((m) => m.user?.id !== this.userID);
|
|
751
|
+
if (otherMember && otherMember.user?.id === user.id) {
|
|
752
|
+
channel.data.name = user.name || user.id;
|
|
753
|
+
channel.data.image = user.avatar || '';
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
718
757
|
}
|
|
719
758
|
if (channel.state.watchers[user.id]) {
|
|
720
|
-
channel.state.watchers
|
|
759
|
+
channel.state.watchers = {
|
|
760
|
+
...channel.state.watchers,
|
|
761
|
+
[user.id]: user,
|
|
762
|
+
};
|
|
763
|
+
hasChange = true;
|
|
721
764
|
}
|
|
722
765
|
if (channel.state.read[user.id]) {
|
|
723
|
-
channel.state.read
|
|
766
|
+
channel.state.read = {
|
|
767
|
+
...channel.state.read,
|
|
768
|
+
[user.id]: {
|
|
769
|
+
...channel.state.read[user.id],
|
|
770
|
+
user,
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
hasChange = true;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (hasChange) {
|
|
777
|
+
// Trigger channel update for Sidebar and Header
|
|
778
|
+
channel._callChannelListeners({
|
|
779
|
+
type: 'channel.updated',
|
|
780
|
+
channel: channel.data,
|
|
781
|
+
cid: channel.cid,
|
|
782
|
+
} as any);
|
|
783
|
+
|
|
784
|
+
// Trigger member update specifically for Member List components
|
|
785
|
+
if (channel.state.members[user.id]) {
|
|
786
|
+
channel._callChannelListeners({
|
|
787
|
+
type: 'member.updated',
|
|
788
|
+
member: channel.state.members[user.id],
|
|
789
|
+
cid: channel.cid,
|
|
790
|
+
} as any);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Trigger general user update at channel level
|
|
794
|
+
channel._callChannelListeners({
|
|
795
|
+
type: 'user.updated',
|
|
796
|
+
user: user,
|
|
797
|
+
cid: channel.cid,
|
|
798
|
+
} as any);
|
|
724
799
|
}
|
|
725
800
|
}
|
|
726
|
-
}
|
|
801
|
+
});
|
|
727
802
|
};
|
|
728
803
|
|
|
729
804
|
_updateUserReferences = this._updateMemberWatcherReferences;
|
|
@@ -733,13 +808,29 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
733
808
|
|
|
734
809
|
for (const channelID in refMap) {
|
|
735
810
|
const channel = this.activeChannels[channelID];
|
|
736
|
-
|
|
737
811
|
if (!channel) continue;
|
|
738
812
|
|
|
739
813
|
const state = channel.state;
|
|
740
814
|
|
|
741
|
-
/**
|
|
815
|
+
/** Update the message objects from this user in the state. */
|
|
742
816
|
state?.updateUserMessages(user);
|
|
817
|
+
|
|
818
|
+
// Trigger re-render for message list components
|
|
819
|
+
channel._callChannelListeners({
|
|
820
|
+
type: 'channel.updated',
|
|
821
|
+
channel: channel.data,
|
|
822
|
+
cid: channel.cid,
|
|
823
|
+
} as any);
|
|
824
|
+
|
|
825
|
+
// Force MessageList refresh by dispatching an update for the last message
|
|
826
|
+
const lastMessage = state?.messages[state.messages.length - 1];
|
|
827
|
+
if (lastMessage) {
|
|
828
|
+
channel._callChannelListeners({
|
|
829
|
+
type: 'message.updated',
|
|
830
|
+
message: lastMessage,
|
|
831
|
+
cid: channel.cid,
|
|
832
|
+
} as any);
|
|
833
|
+
}
|
|
743
834
|
}
|
|
744
835
|
};
|
|
745
836
|
|
|
@@ -822,7 +913,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
822
913
|
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
823
914
|
return bTime - aTime;
|
|
824
915
|
});
|
|
825
|
-
parentChannel._callChannelListeners({
|
|
916
|
+
parentChannel._callChannelListeners({
|
|
917
|
+
...event,
|
|
918
|
+
type: 'channel.updated',
|
|
919
|
+
channel: parentChannel.data,
|
|
920
|
+
} as any);
|
|
826
921
|
}
|
|
827
922
|
}
|
|
828
923
|
});
|
|
@@ -833,7 +928,9 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
833
928
|
if (parentCid && this.activeChannels[parentCid]) {
|
|
834
929
|
const parentChannel = this.activeChannels[parentCid];
|
|
835
930
|
if (parentChannel.state?.topics && event.channel) {
|
|
836
|
-
const topicIndex = parentChannel.state.topics.findIndex(
|
|
931
|
+
const topicIndex = parentChannel.state.topics.findIndex(
|
|
932
|
+
(t: any) => t.cid === event.cid || t.channel?.cid === event.cid,
|
|
933
|
+
);
|
|
837
934
|
if (topicIndex !== -1) {
|
|
838
935
|
const t = parentChannel.state.topics[topicIndex] as any;
|
|
839
936
|
if (t.data) {
|
|
@@ -844,7 +941,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
844
941
|
Object.assign(t, event.channel);
|
|
845
942
|
}
|
|
846
943
|
}
|
|
847
|
-
parentChannel._callChannelListeners({
|
|
944
|
+
parentChannel._callChannelListeners({
|
|
945
|
+
...event,
|
|
946
|
+
type: 'channel.updated',
|
|
947
|
+
channel: parentChannel.data,
|
|
948
|
+
} as any);
|
|
848
949
|
}
|
|
849
950
|
}
|
|
850
951
|
|
|
@@ -852,7 +953,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
852
953
|
const topicChannel = this.activeChannels[event.cid];
|
|
853
954
|
if (event.channel) {
|
|
854
955
|
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
855
|
-
topicChannel._callChannelListeners({
|
|
956
|
+
topicChannel._callChannelListeners({
|
|
957
|
+
...event,
|
|
958
|
+
type: 'channel.updated',
|
|
959
|
+
channel: topicChannel.data,
|
|
960
|
+
} as any);
|
|
856
961
|
}
|
|
857
962
|
}
|
|
858
963
|
});
|
|
@@ -865,14 +970,20 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
865
970
|
if (parentCid && this.activeChannels[parentCid]) {
|
|
866
971
|
const parentChannel = this.activeChannels[parentCid];
|
|
867
972
|
if (parentChannel.state?.topics) {
|
|
868
|
-
const topicIndex = parentChannel.state.topics.findIndex(
|
|
973
|
+
const topicIndex = parentChannel.state.topics.findIndex(
|
|
974
|
+
(t: any) => t.cid === event.cid || t.channel?.cid === event.cid,
|
|
975
|
+
);
|
|
869
976
|
if (topicIndex !== -1) {
|
|
870
977
|
const t = parentChannel.state.topics[topicIndex] as any;
|
|
871
978
|
if (t.data) t.data.is_closed_topic = isClosed;
|
|
872
979
|
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
873
980
|
else t.is_closed_topic = isClosed;
|
|
874
981
|
}
|
|
875
|
-
parentChannel._callChannelListeners({
|
|
982
|
+
parentChannel._callChannelListeners({
|
|
983
|
+
...event,
|
|
984
|
+
type: 'channel.updated',
|
|
985
|
+
channel: parentChannel.data,
|
|
986
|
+
} as any);
|
|
876
987
|
}
|
|
877
988
|
}
|
|
878
989
|
|
|
@@ -886,8 +997,6 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
886
997
|
});
|
|
887
998
|
}
|
|
888
999
|
|
|
889
|
-
|
|
890
|
-
|
|
891
1000
|
if (event.type === 'connection.recovered') {
|
|
892
1001
|
postListenerCallbacks.push(() => {
|
|
893
1002
|
// Auto-resend offline failed messages
|
|
@@ -944,14 +1053,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
944
1053
|
tags: ['connection', 'client'],
|
|
945
1054
|
});
|
|
946
1055
|
|
|
947
|
-
const
|
|
948
|
-
type: ['messaging', 'team'],
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
message_limit: 25,
|
|
953
|
-
};
|
|
954
|
-
|
|
1056
|
+
const {
|
|
1057
|
+
filter = { type: ['messaging', 'team', 'meeting'] } as ChannelFilters,
|
|
1058
|
+
sort = [],
|
|
1059
|
+
options = { message_limit: 1 },
|
|
1060
|
+
} = this.options.recoveryConfig || {};
|
|
955
1061
|
await this.queryChannels(filter, sort, options);
|
|
956
1062
|
|
|
957
1063
|
this.logger('info', 'client:recoverState() - Querying channels finished', { tags: ['connection', 'client'] });
|
|
@@ -1024,45 +1130,42 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1024
1130
|
const data = JSON.parse(event.data);
|
|
1025
1131
|
|
|
1026
1132
|
this.logger('info', `client:connectToSSE() - SSE message received event : ${JSON.stringify(data)}`, { event });
|
|
1027
|
-
|
|
1028
1133
|
if (data.type === 'AccountUserChainProjects') {
|
|
1029
|
-
|
|
1030
|
-
name: data.name,
|
|
1134
|
+
const userInfo: UserResponse<ErmisChatGenerics> = {
|
|
1031
1135
|
id: data.id,
|
|
1136
|
+
name: data.name,
|
|
1032
1137
|
avatar: data.avatar,
|
|
1033
1138
|
about_me: data.about_me,
|
|
1034
1139
|
project_id: data.project_id,
|
|
1035
1140
|
};
|
|
1036
1141
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1142
|
+
// 1. Update current user info if ID matches
|
|
1143
|
+
if (this.user?.id === userInfo.id) {
|
|
1144
|
+
this.user = { ...this.user, ...userInfo };
|
|
1039
1145
|
}
|
|
1040
1146
|
|
|
1041
|
-
|
|
1147
|
+
// 2. Update Client State
|
|
1148
|
+
this.state.updateUser(userInfo);
|
|
1042
1149
|
|
|
1043
|
-
const
|
|
1044
|
-
id:
|
|
1045
|
-
name:
|
|
1046
|
-
avatar:
|
|
1150
|
+
const minimalUserInfo = {
|
|
1151
|
+
id: userInfo.id,
|
|
1152
|
+
name: userInfo.name || userInfo.id,
|
|
1153
|
+
avatar: userInfo.avatar || '',
|
|
1047
1154
|
};
|
|
1048
1155
|
|
|
1049
|
-
|
|
1050
|
-
this.
|
|
1051
|
-
|
|
1052
|
-
Object.values(this.activeChannels).forEach((channel) => {
|
|
1053
|
-
if (channel.data?.type === 'messaging' && Object.keys(channel.state.members).length === 2) {
|
|
1054
|
-
const otherMember = Object.values(channel.state.members).find((member) => member.user?.id !== this.userID);
|
|
1055
|
-
if (otherMember && otherMember.user?.id === user.id) {
|
|
1056
|
-
// Cập nhật tên và avatar channel theo user vừa đổi thông tin
|
|
1057
|
-
channel.data.name = user.name || user.id;
|
|
1058
|
-
channel.data.image = user.avatar || '';
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1156
|
+
// 3. Update references and trigger re-renders
|
|
1157
|
+
this._updateMemberWatcherReferences(minimalUserInfo);
|
|
1158
|
+
this._updateUserMessageReferences(minimalUserInfo);
|
|
1062
1159
|
|
|
1063
1160
|
if (onCallBack) {
|
|
1064
1161
|
onCallBack(data);
|
|
1065
1162
|
}
|
|
1163
|
+
|
|
1164
|
+
this.dispatchEvent({
|
|
1165
|
+
type: 'user.updated',
|
|
1166
|
+
user: userInfo,
|
|
1167
|
+
me: this.user?.id === userInfo.id ? this.user : undefined,
|
|
1168
|
+
} as any);
|
|
1066
1169
|
}
|
|
1067
1170
|
};
|
|
1068
1171
|
this.eventSource.onerror = (event: any) => {
|
|
@@ -1204,6 +1307,20 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1204
1307
|
this.user.avatar = response.avatar;
|
|
1205
1308
|
const new_user = { ...this.user, avatar: response.avatar };
|
|
1206
1309
|
this.state.updateUser(new_user);
|
|
1310
|
+
|
|
1311
|
+
const userInfo = {
|
|
1312
|
+
id: this.user.id,
|
|
1313
|
+
name: this.user.name ? this.user.name : this.user.id,
|
|
1314
|
+
avatar: this.user?.avatar || '',
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
this._updateMemberWatcherReferences(userInfo);
|
|
1318
|
+
this._updateUserMessageReferences(userInfo);
|
|
1319
|
+
|
|
1320
|
+
this.dispatchEvent({
|
|
1321
|
+
type: 'user.updated',
|
|
1322
|
+
me: this.user,
|
|
1323
|
+
} as unknown as Event<ErmisChatGenerics>);
|
|
1207
1324
|
}
|
|
1208
1325
|
|
|
1209
1326
|
return response;
|
|
@@ -1212,6 +1329,23 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1212
1329
|
let response = await this.patch<UserResponse<ErmisChatGenerics>>(this.userBaseURL + '/users/update', updates);
|
|
1213
1330
|
this.user = response;
|
|
1214
1331
|
this.state.updateUser(response);
|
|
1332
|
+
|
|
1333
|
+
if (this.user) {
|
|
1334
|
+
const userInfo = {
|
|
1335
|
+
id: this.user.id,
|
|
1336
|
+
name: this.user.name ? this.user.name : this.user.id,
|
|
1337
|
+
avatar: this.user?.avatar || '',
|
|
1338
|
+
};
|
|
1339
|
+
|
|
1340
|
+
this._updateMemberWatcherReferences(userInfo);
|
|
1341
|
+
this._updateUserMessageReferences(userInfo);
|
|
1342
|
+
|
|
1343
|
+
this.dispatchEvent({
|
|
1344
|
+
type: 'user.updated',
|
|
1345
|
+
me: this.user,
|
|
1346
|
+
} as unknown as Event<ErmisChatGenerics>);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1215
1349
|
return response;
|
|
1216
1350
|
}
|
|
1217
1351
|
|
|
@@ -1309,6 +1443,10 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1309
1443
|
|
|
1310
1444
|
console.log('---channels---', channels);
|
|
1311
1445
|
|
|
1446
|
+
this.dispatchEvent({
|
|
1447
|
+
type: 'channels.queried',
|
|
1448
|
+
} as unknown as Event<ErmisChatGenerics>);
|
|
1449
|
+
|
|
1312
1450
|
return channels;
|
|
1313
1451
|
}
|
|
1314
1452
|
|
|
@@ -1475,8 +1613,12 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1475
1613
|
|
|
1476
1614
|
const now = new Date();
|
|
1477
1615
|
const formattedDate = new Intl.DateTimeFormat('en-US', {
|
|
1478
|
-
month: 'short',
|
|
1479
|
-
|
|
1616
|
+
month: 'short',
|
|
1617
|
+
day: '2-digit',
|
|
1618
|
+
year: 'numeric',
|
|
1619
|
+
hour: '2-digit',
|
|
1620
|
+
minute: '2-digit',
|
|
1621
|
+
hour12: false,
|
|
1480
1622
|
}).format(now);
|
|
1481
1623
|
|
|
1482
1624
|
const payload = {
|
|
@@ -1487,7 +1629,7 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1487
1629
|
|
|
1488
1630
|
const quickChannel = this.channel('meeting', payload);
|
|
1489
1631
|
await quickChannel.create();
|
|
1490
|
-
|
|
1632
|
+
|
|
1491
1633
|
return quickChannel;
|
|
1492
1634
|
}
|
|
1493
1635
|
|