@ermis-network/ermis-chat-sdk 1.0.6 → 1.0.8
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 +211 -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 +211 -52
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +211 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +47 -16
- package/dist/index.d.ts +47 -16
- package/dist/index.mjs +211 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/channel.ts +65 -3
- package/src/client.ts +159 -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]
|
|
@@ -3140,6 +3199,20 @@ var ErmisChat = class _ErmisChat {
|
|
|
3140
3199
|
}
|
|
3141
3200
|
});
|
|
3142
3201
|
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Downloads a media file as a Blob via the SDK's configured axiosInstance.
|
|
3204
|
+
* This avoids CORS issues that arise when using `fetch()` directly from the browser,
|
|
3205
|
+
* because axios is routed through the SDK's authenticated transport layer.
|
|
3206
|
+
*
|
|
3207
|
+
* @param url - The full URL of the media file to download.
|
|
3208
|
+
* @returns A Blob of the file content.
|
|
3209
|
+
*/
|
|
3210
|
+
async downloadMedia(url) {
|
|
3211
|
+
const response = await this.axiosInstance.get(url, {
|
|
3212
|
+
responseType: "blob"
|
|
3213
|
+
});
|
|
3214
|
+
return response.data;
|
|
3215
|
+
}
|
|
3143
3216
|
errorFromResponse(response) {
|
|
3144
3217
|
let err;
|
|
3145
3218
|
err = new ErrorFromResponse(`ErmisChat error HTTP code: ${response.status}`);
|
|
@@ -3160,7 +3233,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3160
3233
|
}
|
|
3161
3234
|
dispatchEvent = (event) => {
|
|
3162
3235
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3163
|
-
if (event.type === "channel.created") {
|
|
3236
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3164
3237
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3165
3238
|
this._afterDispatchEvent(event);
|
|
3166
3239
|
});
|
|
@@ -3294,6 +3367,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3294
3367
|
});
|
|
3295
3368
|
}
|
|
3296
3369
|
}
|
|
3370
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3371
|
+
postListenerCallbacks.push(() => {
|
|
3372
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3373
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3374
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3375
|
+
if (parentChannel.state.topics) {
|
|
3376
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3377
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3378
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3379
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3380
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3381
|
+
return bTime - aTime;
|
|
3382
|
+
});
|
|
3383
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
if (event.type === "channel.topic.updated") {
|
|
3389
|
+
postListenerCallbacks.push(() => {
|
|
3390
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3391
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3392
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3393
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3394
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3395
|
+
if (topicIndex !== -1) {
|
|
3396
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3397
|
+
if (t.data) {
|
|
3398
|
+
t.data = { ...t.data, ...event.channel };
|
|
3399
|
+
} else if (t.channel) {
|
|
3400
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3401
|
+
} else {
|
|
3402
|
+
Object.assign(t, event.channel);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3409
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3410
|
+
if (event.channel) {
|
|
3411
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3412
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3418
|
+
postListenerCallbacks.push(() => {
|
|
3419
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3420
|
+
const parentCid = event.parent_cid;
|
|
3421
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3422
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3423
|
+
if (parentChannel.state?.topics) {
|
|
3424
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3425
|
+
if (topicIndex !== -1) {
|
|
3426
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3427
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3428
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3429
|
+
else t.is_closed_topic = isClosed;
|
|
3430
|
+
}
|
|
3431
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3435
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3436
|
+
if (topicChannel.data) {
|
|
3437
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3438
|
+
}
|
|
3439
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3440
|
+
}
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3297
3443
|
if (event.type === "connection.recovered") {
|
|
3298
3444
|
postListenerCallbacks.push(() => {
|
|
3299
3445
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3531,7 +3677,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3531
3677
|
_updateProjectID(project_id) {
|
|
3532
3678
|
this.projectId = project_id;
|
|
3533
3679
|
}
|
|
3534
|
-
|
|
3680
|
+
/**
|
|
3681
|
+
* Uploads a new avatar image for the current user.
|
|
3682
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3683
|
+
*
|
|
3684
|
+
* @param file - The image file to upload.
|
|
3685
|
+
* @returns The response containing the new avatar URL.
|
|
3686
|
+
*/
|
|
3687
|
+
async uploadAvatar(file) {
|
|
3535
3688
|
const formData = new FormData();
|
|
3536
3689
|
formData.append("avatar", file);
|
|
3537
3690
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3546,12 +3699,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3546
3699
|
}
|
|
3547
3700
|
return response;
|
|
3548
3701
|
}
|
|
3549
|
-
async updateProfile(
|
|
3550
|
-
let
|
|
3551
|
-
name,
|
|
3552
|
-
about_me
|
|
3553
|
-
};
|
|
3554
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3702
|
+
async updateProfile(updates) {
|
|
3703
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3555
3704
|
this.user = response;
|
|
3556
3705
|
this.state.updateUser(response);
|
|
3557
3706
|
return response;
|
|
@@ -3704,45 +3853,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3704
3853
|
return channel;
|
|
3705
3854
|
};
|
|
3706
3855
|
/**
|
|
3707
|
-
* Creates
|
|
3708
|
-
*
|
|
3856
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3857
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3858
|
+
* The creator is added as the first member automatically.
|
|
3709
3859
|
*
|
|
3710
|
-
* @param name -
|
|
3860
|
+
* @param name - An optional display name for the channel.
|
|
3711
3861
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3712
3862
|
*/
|
|
3713
|
-
async
|
|
3863
|
+
async createQuickChannel(name) {
|
|
3714
3864
|
if (!this.userID) {
|
|
3715
3865
|
throw Error("Call connectUser before creating a channel");
|
|
3716
3866
|
}
|
|
3867
|
+
const now = /* @__PURE__ */ new Date();
|
|
3868
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3869
|
+
month: "short",
|
|
3870
|
+
day: "2-digit",
|
|
3871
|
+
year: "numeric",
|
|
3872
|
+
hour: "2-digit",
|
|
3873
|
+
minute: "2-digit",
|
|
3874
|
+
hour12: false
|
|
3875
|
+
}).format(now);
|
|
3717
3876
|
const payload = {
|
|
3718
|
-
name: name || `
|
|
3877
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3719
3878
|
members: [this.userID],
|
|
3720
3879
|
public: true
|
|
3721
3880
|
};
|
|
3722
|
-
const
|
|
3723
|
-
await
|
|
3724
|
-
return
|
|
3881
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3882
|
+
await quickChannel.create();
|
|
3883
|
+
return quickChannel;
|
|
3725
3884
|
}
|
|
3726
3885
|
/**
|
|
3727
|
-
* Joins a
|
|
3728
|
-
*
|
|
3729
|
-
* If not, it
|
|
3886
|
+
* Joins a quick channel by its ID.
|
|
3887
|
+
* Automatically checks whether the caller is already a member.
|
|
3888
|
+
* If not, it joins the channel and synchronizes state.
|
|
3730
3889
|
*
|
|
3731
|
-
* @param channelId - The ID of the
|
|
3890
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3732
3891
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3733
3892
|
*/
|
|
3734
|
-
async
|
|
3893
|
+
async joinQuickChannel(channelId) {
|
|
3735
3894
|
if (!this.userID) {
|
|
3736
3895
|
throw Error("Call connectUser before joining a channel");
|
|
3737
3896
|
}
|
|
3738
|
-
const
|
|
3739
|
-
await
|
|
3740
|
-
const isMember =
|
|
3897
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3898
|
+
await quickChannel.watch();
|
|
3899
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3741
3900
|
if (!isMember) {
|
|
3742
|
-
await
|
|
3743
|
-
await
|
|
3901
|
+
await quickChannel.acceptInvite("join");
|
|
3902
|
+
await quickChannel.watch();
|
|
3744
3903
|
}
|
|
3745
|
-
return
|
|
3904
|
+
return quickChannel;
|
|
3746
3905
|
}
|
|
3747
3906
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3748
3907
|
let pinExpires = null;
|
|
@@ -3758,7 +3917,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3758
3917
|
return pinExpires;
|
|
3759
3918
|
}
|
|
3760
3919
|
getUserAgent() {
|
|
3761
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3920
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
3762
3921
|
}
|
|
3763
3922
|
setUserAgent(userAgent) {
|
|
3764
3923
|
this.userAgent = userAgent;
|
|
@@ -7116,7 +7275,7 @@ var ErmisAuthProvider = class {
|
|
|
7116
7275
|
return data;
|
|
7117
7276
|
}
|
|
7118
7277
|
getUserAgent() {
|
|
7119
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7278
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
7120
7279
|
}
|
|
7121
7280
|
setUserAgent(userAgent) {
|
|
7122
7281
|
this.userAgent = userAgent;
|