@ermis-network/ermis-chat-sdk 1.0.6 → 1.0.7
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/dist/index.browser.cjs +197 -52
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.full-bundle.min.js +2 -2
- package/dist/index.browser.full-bundle.min.js.map +1 -1
- package/dist/index.browser.mjs +197 -52
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +197 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +38 -16
- package/dist/index.d.ts +38 -16
- package/dist/index.mjs +197 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/channel.ts +65 -3
- package/src/client.ts +144 -50
- package/src/types.ts +14 -0
package/dist/index.browser.mjs
CHANGED
|
@@ -1008,6 +1008,44 @@ var Channel = class {
|
|
|
1008
1008
|
this.getClient().baseURL + `/messages/${this.type}/${this.id}/${messageID}/unpin`
|
|
1009
1009
|
);
|
|
1010
1010
|
}
|
|
1011
|
+
async pin() {
|
|
1012
|
+
if (this.data) this.data.is_pinned = true;
|
|
1013
|
+
this.getClient().dispatchEvent({
|
|
1014
|
+
type: "channel.pinned",
|
|
1015
|
+
cid: this.cid,
|
|
1016
|
+
channel: this.data
|
|
1017
|
+
});
|
|
1018
|
+
try {
|
|
1019
|
+
return await this.getClient().pinChannel(this.type, this.id);
|
|
1020
|
+
} catch (e) {
|
|
1021
|
+
if (this.data) this.data.is_pinned = false;
|
|
1022
|
+
this.getClient().dispatchEvent({
|
|
1023
|
+
type: "channel.unpinned",
|
|
1024
|
+
cid: this.cid,
|
|
1025
|
+
channel: this.data
|
|
1026
|
+
});
|
|
1027
|
+
throw e;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
async unpin() {
|
|
1031
|
+
if (this.data) this.data.is_pinned = false;
|
|
1032
|
+
this.getClient().dispatchEvent({
|
|
1033
|
+
type: "channel.unpinned",
|
|
1034
|
+
cid: this.cid,
|
|
1035
|
+
channel: this.data
|
|
1036
|
+
});
|
|
1037
|
+
try {
|
|
1038
|
+
return await this.getClient().unpinChannel(this.type, this.id);
|
|
1039
|
+
} catch (e) {
|
|
1040
|
+
if (this.data) this.data.is_pinned = true;
|
|
1041
|
+
this.getClient().dispatchEvent({
|
|
1042
|
+
type: "channel.pinned",
|
|
1043
|
+
cid: this.cid,
|
|
1044
|
+
channel: this.data
|
|
1045
|
+
});
|
|
1046
|
+
throw e;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1011
1049
|
async editMessage(oldMessageID, message) {
|
|
1012
1050
|
return await this.getClient().post(this.getClient().baseURL + `/messages/${this.type}/${this.id}/${oldMessageID}`, {
|
|
1013
1051
|
message
|
|
@@ -1913,6 +1951,18 @@ var Channel = class {
|
|
|
1913
1951
|
delete channelState.members[event.user.id];
|
|
1914
1952
|
}
|
|
1915
1953
|
break;
|
|
1954
|
+
case "channel.topic.enabled":
|
|
1955
|
+
if (channel.data) {
|
|
1956
|
+
channel.data.topics_enabled = true;
|
|
1957
|
+
}
|
|
1958
|
+
channelState.topics = channelState.topics || [];
|
|
1959
|
+
break;
|
|
1960
|
+
case "channel.topic.disabled":
|
|
1961
|
+
if (channel.data) {
|
|
1962
|
+
channel.data.topics_enabled = false;
|
|
1963
|
+
}
|
|
1964
|
+
channelState.topics = [];
|
|
1965
|
+
break;
|
|
1916
1966
|
case "channel.updated":
|
|
1917
1967
|
if (event.channel) {
|
|
1918
1968
|
channel.data = {
|
|
@@ -2051,7 +2101,12 @@ var Channel = class {
|
|
|
2051
2101
|
const topic = this.getClient().channel(event.channel_type || "", event.channel_id || "");
|
|
2052
2102
|
topic.data = event.channel;
|
|
2053
2103
|
topic._initializeState(topicState, "latest");
|
|
2054
|
-
channelState.topics
|
|
2104
|
+
if (!channelState.topics) {
|
|
2105
|
+
channelState.topics = [];
|
|
2106
|
+
}
|
|
2107
|
+
if (!channelState.topics.some((t) => t.cid === topic.cid)) {
|
|
2108
|
+
channelState.topics.push(topic);
|
|
2109
|
+
}
|
|
2055
2110
|
break;
|
|
2056
2111
|
case "channel.topic.closed":
|
|
2057
2112
|
if (channel.data) {
|
|
@@ -2870,7 +2925,6 @@ var ErmisChat = class _ErmisChat {
|
|
|
2870
2925
|
params.avatar = user.avatar;
|
|
2871
2926
|
}
|
|
2872
2927
|
const url = this.userBaseURL + "/get_token/external_auth";
|
|
2873
|
-
const query = new URLSearchParams(params).toString();
|
|
2874
2928
|
const headers = {
|
|
2875
2929
|
"Content-Type": "application/json"
|
|
2876
2930
|
};
|
|
@@ -2878,21 +2932,21 @@ var ErmisChat = class _ErmisChat {
|
|
|
2878
2932
|
const tokenStr = typeof token === "string" && token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
2879
2933
|
headers["Authorization"] = tokenStr;
|
|
2880
2934
|
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2935
|
+
try {
|
|
2936
|
+
const response = await this.axiosInstance.get(url, {
|
|
2937
|
+
params,
|
|
2938
|
+
headers
|
|
2939
|
+
});
|
|
2940
|
+
return response.data;
|
|
2941
|
+
} catch (error) {
|
|
2942
|
+
let errorMsg = "Failed to fetch external auth token";
|
|
2943
|
+
if (error.response && error.response.data) {
|
|
2944
|
+
errorMsg = error.response.data.message || JSON.stringify(error.response.data);
|
|
2945
|
+
} else if (error.message) {
|
|
2946
|
+
errorMsg = error.message;
|
|
2892
2947
|
}
|
|
2893
2948
|
throw new Error(errorMsg);
|
|
2894
2949
|
}
|
|
2895
|
-
return await response.json();
|
|
2896
2950
|
}
|
|
2897
2951
|
/**
|
|
2898
2952
|
* Connects a user to the Ermis network and establishes the WebSocket connection.
|
|
@@ -2903,19 +2957,24 @@ var ErmisChat = class _ErmisChat {
|
|
|
2903
2957
|
* @param extenal_auth - Set to `true` to use your custom backend external authentication flow.
|
|
2904
2958
|
* @returns A promise resolving to the API connection response once authenticated.
|
|
2905
2959
|
*/
|
|
2906
|
-
connectUser = async (user, userTokenOrProvider,
|
|
2960
|
+
connectUser = async (user, userTokenOrProvider, external_auth) => {
|
|
2907
2961
|
this.logger("info", "client:connectUser() - started", {
|
|
2908
2962
|
tags: ["connection", "client"]
|
|
2909
2963
|
});
|
|
2910
2964
|
if (!user.id) {
|
|
2911
2965
|
throw new Error('The "id" field on the user is missing');
|
|
2912
2966
|
}
|
|
2913
|
-
|
|
2967
|
+
let connectionUser = user;
|
|
2968
|
+
let connectionToken = userTokenOrProvider;
|
|
2969
|
+
if (external_auth) {
|
|
2914
2970
|
const external_auth_token = await this.getExternalAuthToken(user, userTokenOrProvider);
|
|
2915
|
-
|
|
2916
|
-
|
|
2971
|
+
connectionToken = external_auth_token.token;
|
|
2972
|
+
connectionUser = {
|
|
2973
|
+
...user,
|
|
2974
|
+
id: external_auth_token.user_id
|
|
2975
|
+
};
|
|
2917
2976
|
}
|
|
2918
|
-
if (this.userID ===
|
|
2977
|
+
if (this.userID === connectionUser.id && this.setUserPromise) {
|
|
2919
2978
|
console.warn(
|
|
2920
2979
|
"Consecutive calls to connectUser is detected, ideally you should only call this function once in your app."
|
|
2921
2980
|
);
|
|
@@ -2931,10 +2990,10 @@ var ErmisChat = class _ErmisChat {
|
|
|
2931
2990
|
'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.'
|
|
2932
2991
|
);
|
|
2933
2992
|
}
|
|
2934
|
-
this.userID =
|
|
2935
|
-
const setTokenPromise = this._setToken(
|
|
2936
|
-
this._setUser(
|
|
2937
|
-
this.state.updateUser({ id:
|
|
2993
|
+
this.userID = connectionUser.id;
|
|
2994
|
+
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
2995
|
+
this._setUser(connectionUser);
|
|
2996
|
+
this.state.updateUser({ id: connectionUser.id, name: connectionUser?.name || connectionUser.id, avatar: connectionUser?.avatar || "" });
|
|
2938
2997
|
const wsPromise = this.openConnection();
|
|
2939
2998
|
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(
|
|
2940
2999
|
(result) => result[1]
|
|
@@ -3160,7 +3219,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3160
3219
|
}
|
|
3161
3220
|
dispatchEvent = (event) => {
|
|
3162
3221
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3163
|
-
if (event.type === "channel.created") {
|
|
3222
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3164
3223
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3165
3224
|
this._afterDispatchEvent(event);
|
|
3166
3225
|
});
|
|
@@ -3294,6 +3353,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3294
3353
|
});
|
|
3295
3354
|
}
|
|
3296
3355
|
}
|
|
3356
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3357
|
+
postListenerCallbacks.push(() => {
|
|
3358
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3359
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3360
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3361
|
+
if (parentChannel.state.topics) {
|
|
3362
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3363
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3364
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3365
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3366
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3367
|
+
return bTime - aTime;
|
|
3368
|
+
});
|
|
3369
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
if (event.type === "channel.topic.updated") {
|
|
3375
|
+
postListenerCallbacks.push(() => {
|
|
3376
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3377
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3378
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3379
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3380
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3381
|
+
if (topicIndex !== -1) {
|
|
3382
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3383
|
+
if (t.data) {
|
|
3384
|
+
t.data = { ...t.data, ...event.channel };
|
|
3385
|
+
} else if (t.channel) {
|
|
3386
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3387
|
+
} else {
|
|
3388
|
+
Object.assign(t, event.channel);
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3395
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3396
|
+
if (event.channel) {
|
|
3397
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3398
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3404
|
+
postListenerCallbacks.push(() => {
|
|
3405
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3406
|
+
const parentCid = event.parent_cid;
|
|
3407
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3408
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3409
|
+
if (parentChannel.state?.topics) {
|
|
3410
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3411
|
+
if (topicIndex !== -1) {
|
|
3412
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3413
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3414
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3415
|
+
else t.is_closed_topic = isClosed;
|
|
3416
|
+
}
|
|
3417
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3421
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3422
|
+
if (topicChannel.data) {
|
|
3423
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3424
|
+
}
|
|
3425
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3297
3429
|
if (event.type === "connection.recovered") {
|
|
3298
3430
|
postListenerCallbacks.push(() => {
|
|
3299
3431
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3531,7 +3663,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3531
3663
|
_updateProjectID(project_id) {
|
|
3532
3664
|
this.projectId = project_id;
|
|
3533
3665
|
}
|
|
3534
|
-
|
|
3666
|
+
/**
|
|
3667
|
+
* Uploads a new avatar image for the current user.
|
|
3668
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3669
|
+
*
|
|
3670
|
+
* @param file - The image file to upload.
|
|
3671
|
+
* @returns The response containing the new avatar URL.
|
|
3672
|
+
*/
|
|
3673
|
+
async uploadAvatar(file) {
|
|
3535
3674
|
const formData = new FormData();
|
|
3536
3675
|
formData.append("avatar", file);
|
|
3537
3676
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3546,12 +3685,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3546
3685
|
}
|
|
3547
3686
|
return response;
|
|
3548
3687
|
}
|
|
3549
|
-
async updateProfile(
|
|
3550
|
-
let
|
|
3551
|
-
name,
|
|
3552
|
-
about_me
|
|
3553
|
-
};
|
|
3554
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3688
|
+
async updateProfile(updates) {
|
|
3689
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3555
3690
|
this.user = response;
|
|
3556
3691
|
this.state.updateUser(response);
|
|
3557
3692
|
return response;
|
|
@@ -3704,45 +3839,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3704
3839
|
return channel;
|
|
3705
3840
|
};
|
|
3706
3841
|
/**
|
|
3707
|
-
* Creates
|
|
3708
|
-
*
|
|
3842
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3843
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3844
|
+
* The creator is added as the first member automatically.
|
|
3709
3845
|
*
|
|
3710
|
-
* @param name -
|
|
3846
|
+
* @param name - An optional display name for the channel.
|
|
3711
3847
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3712
3848
|
*/
|
|
3713
|
-
async
|
|
3849
|
+
async createQuickChannel(name) {
|
|
3714
3850
|
if (!this.userID) {
|
|
3715
3851
|
throw Error("Call connectUser before creating a channel");
|
|
3716
3852
|
}
|
|
3853
|
+
const now = /* @__PURE__ */ new Date();
|
|
3854
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3855
|
+
month: "short",
|
|
3856
|
+
day: "2-digit",
|
|
3857
|
+
year: "numeric",
|
|
3858
|
+
hour: "2-digit",
|
|
3859
|
+
minute: "2-digit",
|
|
3860
|
+
hour12: false
|
|
3861
|
+
}).format(now);
|
|
3717
3862
|
const payload = {
|
|
3718
|
-
name: name || `
|
|
3863
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3719
3864
|
members: [this.userID],
|
|
3720
3865
|
public: true
|
|
3721
3866
|
};
|
|
3722
|
-
const
|
|
3723
|
-
await
|
|
3724
|
-
return
|
|
3867
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3868
|
+
await quickChannel.create();
|
|
3869
|
+
return quickChannel;
|
|
3725
3870
|
}
|
|
3726
3871
|
/**
|
|
3727
|
-
* Joins a
|
|
3728
|
-
*
|
|
3729
|
-
* If not, it
|
|
3872
|
+
* Joins a quick channel by its ID.
|
|
3873
|
+
* Automatically checks whether the caller is already a member.
|
|
3874
|
+
* If not, it joins the channel and synchronizes state.
|
|
3730
3875
|
*
|
|
3731
|
-
* @param channelId - The ID of the
|
|
3876
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3732
3877
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3733
3878
|
*/
|
|
3734
|
-
async
|
|
3879
|
+
async joinQuickChannel(channelId) {
|
|
3735
3880
|
if (!this.userID) {
|
|
3736
3881
|
throw Error("Call connectUser before joining a channel");
|
|
3737
3882
|
}
|
|
3738
|
-
const
|
|
3739
|
-
await
|
|
3740
|
-
const isMember =
|
|
3883
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3884
|
+
await quickChannel.watch();
|
|
3885
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3741
3886
|
if (!isMember) {
|
|
3742
|
-
await
|
|
3743
|
-
await
|
|
3887
|
+
await quickChannel.acceptInvite("join");
|
|
3888
|
+
await quickChannel.watch();
|
|
3744
3889
|
}
|
|
3745
|
-
return
|
|
3890
|
+
return quickChannel;
|
|
3746
3891
|
}
|
|
3747
3892
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3748
3893
|
let pinExpires = null;
|
|
@@ -3758,7 +3903,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3758
3903
|
return pinExpires;
|
|
3759
3904
|
}
|
|
3760
3905
|
getUserAgent() {
|
|
3761
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3906
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
3762
3907
|
}
|
|
3763
3908
|
setUserAgent(userAgent) {
|
|
3764
3909
|
this.userAgent = userAgent;
|
|
@@ -7116,7 +7261,7 @@ var ErmisAuthProvider = class {
|
|
|
7116
7261
|
return data;
|
|
7117
7262
|
}
|
|
7118
7263
|
getUserAgent() {
|
|
7119
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7264
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
7120
7265
|
}
|
|
7121
7266
|
setUserAgent(userAgent) {
|
|
7122
7267
|
this.userAgent = userAgent;
|