@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.mjs
CHANGED
|
@@ -1006,6 +1006,44 @@ var Channel = class {
|
|
|
1006
1006
|
this.getClient().baseURL + `/messages/${this.type}/${this.id}/${messageID}/unpin`
|
|
1007
1007
|
);
|
|
1008
1008
|
}
|
|
1009
|
+
async pin() {
|
|
1010
|
+
if (this.data) this.data.is_pinned = true;
|
|
1011
|
+
this.getClient().dispatchEvent({
|
|
1012
|
+
type: "channel.pinned",
|
|
1013
|
+
cid: this.cid,
|
|
1014
|
+
channel: this.data
|
|
1015
|
+
});
|
|
1016
|
+
try {
|
|
1017
|
+
return await this.getClient().pinChannel(this.type, this.id);
|
|
1018
|
+
} catch (e) {
|
|
1019
|
+
if (this.data) this.data.is_pinned = false;
|
|
1020
|
+
this.getClient().dispatchEvent({
|
|
1021
|
+
type: "channel.unpinned",
|
|
1022
|
+
cid: this.cid,
|
|
1023
|
+
channel: this.data
|
|
1024
|
+
});
|
|
1025
|
+
throw e;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
async unpin() {
|
|
1029
|
+
if (this.data) this.data.is_pinned = false;
|
|
1030
|
+
this.getClient().dispatchEvent({
|
|
1031
|
+
type: "channel.unpinned",
|
|
1032
|
+
cid: this.cid,
|
|
1033
|
+
channel: this.data
|
|
1034
|
+
});
|
|
1035
|
+
try {
|
|
1036
|
+
return await this.getClient().unpinChannel(this.type, this.id);
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
if (this.data) this.data.is_pinned = true;
|
|
1039
|
+
this.getClient().dispatchEvent({
|
|
1040
|
+
type: "channel.pinned",
|
|
1041
|
+
cid: this.cid,
|
|
1042
|
+
channel: this.data
|
|
1043
|
+
});
|
|
1044
|
+
throw e;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1009
1047
|
async editMessage(oldMessageID, message) {
|
|
1010
1048
|
return await this.getClient().post(this.getClient().baseURL + `/messages/${this.type}/${this.id}/${oldMessageID}`, {
|
|
1011
1049
|
message
|
|
@@ -1911,6 +1949,18 @@ var Channel = class {
|
|
|
1911
1949
|
delete channelState.members[event.user.id];
|
|
1912
1950
|
}
|
|
1913
1951
|
break;
|
|
1952
|
+
case "channel.topic.enabled":
|
|
1953
|
+
if (channel.data) {
|
|
1954
|
+
channel.data.topics_enabled = true;
|
|
1955
|
+
}
|
|
1956
|
+
channelState.topics = channelState.topics || [];
|
|
1957
|
+
break;
|
|
1958
|
+
case "channel.topic.disabled":
|
|
1959
|
+
if (channel.data) {
|
|
1960
|
+
channel.data.topics_enabled = false;
|
|
1961
|
+
}
|
|
1962
|
+
channelState.topics = [];
|
|
1963
|
+
break;
|
|
1914
1964
|
case "channel.updated":
|
|
1915
1965
|
if (event.channel) {
|
|
1916
1966
|
channel.data = {
|
|
@@ -2049,7 +2099,12 @@ var Channel = class {
|
|
|
2049
2099
|
const topic = this.getClient().channel(event.channel_type || "", event.channel_id || "");
|
|
2050
2100
|
topic.data = event.channel;
|
|
2051
2101
|
topic._initializeState(topicState, "latest");
|
|
2052
|
-
channelState.topics
|
|
2102
|
+
if (!channelState.topics) {
|
|
2103
|
+
channelState.topics = [];
|
|
2104
|
+
}
|
|
2105
|
+
if (!channelState.topics.some((t) => t.cid === topic.cid)) {
|
|
2106
|
+
channelState.topics.push(topic);
|
|
2107
|
+
}
|
|
2053
2108
|
break;
|
|
2054
2109
|
case "channel.topic.closed":
|
|
2055
2110
|
if (channel.data) {
|
|
@@ -2868,7 +2923,6 @@ var ErmisChat = class _ErmisChat {
|
|
|
2868
2923
|
params.avatar = user.avatar;
|
|
2869
2924
|
}
|
|
2870
2925
|
const url = this.userBaseURL + "/get_token/external_auth";
|
|
2871
|
-
const query = new URLSearchParams(params).toString();
|
|
2872
2926
|
const headers = {
|
|
2873
2927
|
"Content-Type": "application/json"
|
|
2874
2928
|
};
|
|
@@ -2876,21 +2930,21 @@ var ErmisChat = class _ErmisChat {
|
|
|
2876
2930
|
const tokenStr = typeof token === "string" && token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
2877
2931
|
headers["Authorization"] = tokenStr;
|
|
2878
2932
|
}
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2933
|
+
try {
|
|
2934
|
+
const response = await this.axiosInstance.get(url, {
|
|
2935
|
+
params,
|
|
2936
|
+
headers
|
|
2937
|
+
});
|
|
2938
|
+
return response.data;
|
|
2939
|
+
} catch (error) {
|
|
2940
|
+
let errorMsg = "Failed to fetch external auth token";
|
|
2941
|
+
if (error.response && error.response.data) {
|
|
2942
|
+
errorMsg = error.response.data.message || JSON.stringify(error.response.data);
|
|
2943
|
+
} else if (error.message) {
|
|
2944
|
+
errorMsg = error.message;
|
|
2890
2945
|
}
|
|
2891
2946
|
throw new Error(errorMsg);
|
|
2892
2947
|
}
|
|
2893
|
-
return await response.json();
|
|
2894
2948
|
}
|
|
2895
2949
|
/**
|
|
2896
2950
|
* Connects a user to the Ermis network and establishes the WebSocket connection.
|
|
@@ -2901,19 +2955,24 @@ var ErmisChat = class _ErmisChat {
|
|
|
2901
2955
|
* @param extenal_auth - Set to `true` to use your custom backend external authentication flow.
|
|
2902
2956
|
* @returns A promise resolving to the API connection response once authenticated.
|
|
2903
2957
|
*/
|
|
2904
|
-
connectUser = async (user, userTokenOrProvider,
|
|
2958
|
+
connectUser = async (user, userTokenOrProvider, external_auth) => {
|
|
2905
2959
|
this.logger("info", "client:connectUser() - started", {
|
|
2906
2960
|
tags: ["connection", "client"]
|
|
2907
2961
|
});
|
|
2908
2962
|
if (!user.id) {
|
|
2909
2963
|
throw new Error('The "id" field on the user is missing');
|
|
2910
2964
|
}
|
|
2911
|
-
|
|
2965
|
+
let connectionUser = user;
|
|
2966
|
+
let connectionToken = userTokenOrProvider;
|
|
2967
|
+
if (external_auth) {
|
|
2912
2968
|
const external_auth_token = await this.getExternalAuthToken(user, userTokenOrProvider);
|
|
2913
|
-
|
|
2914
|
-
|
|
2969
|
+
connectionToken = external_auth_token.token;
|
|
2970
|
+
connectionUser = {
|
|
2971
|
+
...user,
|
|
2972
|
+
id: external_auth_token.user_id
|
|
2973
|
+
};
|
|
2915
2974
|
}
|
|
2916
|
-
if (this.userID ===
|
|
2975
|
+
if (this.userID === connectionUser.id && this.setUserPromise) {
|
|
2917
2976
|
console.warn(
|
|
2918
2977
|
"Consecutive calls to connectUser is detected, ideally you should only call this function once in your app."
|
|
2919
2978
|
);
|
|
@@ -2929,10 +2988,10 @@ var ErmisChat = class _ErmisChat {
|
|
|
2929
2988
|
'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.'
|
|
2930
2989
|
);
|
|
2931
2990
|
}
|
|
2932
|
-
this.userID =
|
|
2933
|
-
const setTokenPromise = this._setToken(
|
|
2934
|
-
this._setUser(
|
|
2935
|
-
this.state.updateUser({ id:
|
|
2991
|
+
this.userID = connectionUser.id;
|
|
2992
|
+
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
2993
|
+
this._setUser(connectionUser);
|
|
2994
|
+
this.state.updateUser({ id: connectionUser.id, name: connectionUser?.name || connectionUser.id, avatar: connectionUser?.avatar || "" });
|
|
2936
2995
|
const wsPromise = this.openConnection();
|
|
2937
2996
|
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(
|
|
2938
2997
|
(result) => result[1]
|
|
@@ -3138,6 +3197,20 @@ var ErmisChat = class _ErmisChat {
|
|
|
3138
3197
|
}
|
|
3139
3198
|
});
|
|
3140
3199
|
}
|
|
3200
|
+
/**
|
|
3201
|
+
* Downloads a media file as a Blob via the SDK's configured axiosInstance.
|
|
3202
|
+
* This avoids CORS issues that arise when using `fetch()` directly from the browser,
|
|
3203
|
+
* because axios is routed through the SDK's authenticated transport layer.
|
|
3204
|
+
*
|
|
3205
|
+
* @param url - The full URL of the media file to download.
|
|
3206
|
+
* @returns A Blob of the file content.
|
|
3207
|
+
*/
|
|
3208
|
+
async downloadMedia(url) {
|
|
3209
|
+
const response = await this.axiosInstance.get(url, {
|
|
3210
|
+
responseType: "blob"
|
|
3211
|
+
});
|
|
3212
|
+
return response.data;
|
|
3213
|
+
}
|
|
3141
3214
|
errorFromResponse(response) {
|
|
3142
3215
|
let err;
|
|
3143
3216
|
err = new ErrorFromResponse(`ErmisChat error HTTP code: ${response.status}`);
|
|
@@ -3158,7 +3231,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3158
3231
|
}
|
|
3159
3232
|
dispatchEvent = (event) => {
|
|
3160
3233
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3161
|
-
if (event.type === "channel.created") {
|
|
3234
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3162
3235
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3163
3236
|
this._afterDispatchEvent(event);
|
|
3164
3237
|
});
|
|
@@ -3292,6 +3365,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3292
3365
|
});
|
|
3293
3366
|
}
|
|
3294
3367
|
}
|
|
3368
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3369
|
+
postListenerCallbacks.push(() => {
|
|
3370
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3371
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3372
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3373
|
+
if (parentChannel.state.topics) {
|
|
3374
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3375
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3376
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3377
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3378
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3379
|
+
return bTime - aTime;
|
|
3380
|
+
});
|
|
3381
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3386
|
+
if (event.type === "channel.topic.updated") {
|
|
3387
|
+
postListenerCallbacks.push(() => {
|
|
3388
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3389
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3390
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3391
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3392
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3393
|
+
if (topicIndex !== -1) {
|
|
3394
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3395
|
+
if (t.data) {
|
|
3396
|
+
t.data = { ...t.data, ...event.channel };
|
|
3397
|
+
} else if (t.channel) {
|
|
3398
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3399
|
+
} else {
|
|
3400
|
+
Object.assign(t, event.channel);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3407
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3408
|
+
if (event.channel) {
|
|
3409
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3410
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3416
|
+
postListenerCallbacks.push(() => {
|
|
3417
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3418
|
+
const parentCid = event.parent_cid;
|
|
3419
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3420
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3421
|
+
if (parentChannel.state?.topics) {
|
|
3422
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3423
|
+
if (topicIndex !== -1) {
|
|
3424
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3425
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3426
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3427
|
+
else t.is_closed_topic = isClosed;
|
|
3428
|
+
}
|
|
3429
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3433
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3434
|
+
if (topicChannel.data) {
|
|
3435
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3436
|
+
}
|
|
3437
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3438
|
+
}
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3295
3441
|
if (event.type === "connection.recovered") {
|
|
3296
3442
|
postListenerCallbacks.push(() => {
|
|
3297
3443
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3529,7 +3675,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3529
3675
|
_updateProjectID(project_id) {
|
|
3530
3676
|
this.projectId = project_id;
|
|
3531
3677
|
}
|
|
3532
|
-
|
|
3678
|
+
/**
|
|
3679
|
+
* Uploads a new avatar image for the current user.
|
|
3680
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3681
|
+
*
|
|
3682
|
+
* @param file - The image file to upload.
|
|
3683
|
+
* @returns The response containing the new avatar URL.
|
|
3684
|
+
*/
|
|
3685
|
+
async uploadAvatar(file) {
|
|
3533
3686
|
const formData = new FormData();
|
|
3534
3687
|
formData.append("avatar", file);
|
|
3535
3688
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3544,12 +3697,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3544
3697
|
}
|
|
3545
3698
|
return response;
|
|
3546
3699
|
}
|
|
3547
|
-
async updateProfile(
|
|
3548
|
-
let
|
|
3549
|
-
name,
|
|
3550
|
-
about_me
|
|
3551
|
-
};
|
|
3552
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3700
|
+
async updateProfile(updates) {
|
|
3701
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3553
3702
|
this.user = response;
|
|
3554
3703
|
this.state.updateUser(response);
|
|
3555
3704
|
return response;
|
|
@@ -3702,45 +3851,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3702
3851
|
return channel;
|
|
3703
3852
|
};
|
|
3704
3853
|
/**
|
|
3705
|
-
* Creates
|
|
3706
|
-
*
|
|
3854
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3855
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3856
|
+
* The creator is added as the first member automatically.
|
|
3707
3857
|
*
|
|
3708
|
-
* @param name -
|
|
3858
|
+
* @param name - An optional display name for the channel.
|
|
3709
3859
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3710
3860
|
*/
|
|
3711
|
-
async
|
|
3861
|
+
async createQuickChannel(name) {
|
|
3712
3862
|
if (!this.userID) {
|
|
3713
3863
|
throw Error("Call connectUser before creating a channel");
|
|
3714
3864
|
}
|
|
3865
|
+
const now = /* @__PURE__ */ new Date();
|
|
3866
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3867
|
+
month: "short",
|
|
3868
|
+
day: "2-digit",
|
|
3869
|
+
year: "numeric",
|
|
3870
|
+
hour: "2-digit",
|
|
3871
|
+
minute: "2-digit",
|
|
3872
|
+
hour12: false
|
|
3873
|
+
}).format(now);
|
|
3715
3874
|
const payload = {
|
|
3716
|
-
name: name || `
|
|
3875
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3717
3876
|
members: [this.userID],
|
|
3718
3877
|
public: true
|
|
3719
3878
|
};
|
|
3720
|
-
const
|
|
3721
|
-
await
|
|
3722
|
-
return
|
|
3879
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3880
|
+
await quickChannel.create();
|
|
3881
|
+
return quickChannel;
|
|
3723
3882
|
}
|
|
3724
3883
|
/**
|
|
3725
|
-
* Joins a
|
|
3726
|
-
*
|
|
3727
|
-
* If not, it
|
|
3884
|
+
* Joins a quick channel by its ID.
|
|
3885
|
+
* Automatically checks whether the caller is already a member.
|
|
3886
|
+
* If not, it joins the channel and synchronizes state.
|
|
3728
3887
|
*
|
|
3729
|
-
* @param channelId - The ID of the
|
|
3888
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3730
3889
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3731
3890
|
*/
|
|
3732
|
-
async
|
|
3891
|
+
async joinQuickChannel(channelId) {
|
|
3733
3892
|
if (!this.userID) {
|
|
3734
3893
|
throw Error("Call connectUser before joining a channel");
|
|
3735
3894
|
}
|
|
3736
|
-
const
|
|
3737
|
-
await
|
|
3738
|
-
const isMember =
|
|
3895
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3896
|
+
await quickChannel.watch();
|
|
3897
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3739
3898
|
if (!isMember) {
|
|
3740
|
-
await
|
|
3741
|
-
await
|
|
3899
|
+
await quickChannel.acceptInvite("join");
|
|
3900
|
+
await quickChannel.watch();
|
|
3742
3901
|
}
|
|
3743
|
-
return
|
|
3902
|
+
return quickChannel;
|
|
3744
3903
|
}
|
|
3745
3904
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3746
3905
|
let pinExpires = null;
|
|
@@ -3756,7 +3915,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3756
3915
|
return pinExpires;
|
|
3757
3916
|
}
|
|
3758
3917
|
getUserAgent() {
|
|
3759
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3918
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
3760
3919
|
}
|
|
3761
3920
|
setUserAgent(userAgent) {
|
|
3762
3921
|
this.userAgent = userAgent;
|
|
@@ -7115,7 +7274,7 @@ var ErmisAuthProvider = class {
|
|
|
7115
7274
|
return data;
|
|
7116
7275
|
}
|
|
7117
7276
|
getUserAgent() {
|
|
7118
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7277
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
7119
7278
|
}
|
|
7120
7279
|
setUserAgent(userAgent) {
|
|
7121
7280
|
this.userAgent = userAgent;
|