@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.cjs
CHANGED
|
@@ -1066,6 +1066,44 @@ var Channel = class {
|
|
|
1066
1066
|
this.getClient().baseURL + `/messages/${this.type}/${this.id}/${messageID}/unpin`
|
|
1067
1067
|
);
|
|
1068
1068
|
}
|
|
1069
|
+
async pin() {
|
|
1070
|
+
if (this.data) this.data.is_pinned = true;
|
|
1071
|
+
this.getClient().dispatchEvent({
|
|
1072
|
+
type: "channel.pinned",
|
|
1073
|
+
cid: this.cid,
|
|
1074
|
+
channel: this.data
|
|
1075
|
+
});
|
|
1076
|
+
try {
|
|
1077
|
+
return await this.getClient().pinChannel(this.type, this.id);
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
if (this.data) this.data.is_pinned = false;
|
|
1080
|
+
this.getClient().dispatchEvent({
|
|
1081
|
+
type: "channel.unpinned",
|
|
1082
|
+
cid: this.cid,
|
|
1083
|
+
channel: this.data
|
|
1084
|
+
});
|
|
1085
|
+
throw e;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
async unpin() {
|
|
1089
|
+
if (this.data) this.data.is_pinned = false;
|
|
1090
|
+
this.getClient().dispatchEvent({
|
|
1091
|
+
type: "channel.unpinned",
|
|
1092
|
+
cid: this.cid,
|
|
1093
|
+
channel: this.data
|
|
1094
|
+
});
|
|
1095
|
+
try {
|
|
1096
|
+
return await this.getClient().unpinChannel(this.type, this.id);
|
|
1097
|
+
} catch (e) {
|
|
1098
|
+
if (this.data) this.data.is_pinned = true;
|
|
1099
|
+
this.getClient().dispatchEvent({
|
|
1100
|
+
type: "channel.pinned",
|
|
1101
|
+
cid: this.cid,
|
|
1102
|
+
channel: this.data
|
|
1103
|
+
});
|
|
1104
|
+
throw e;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1069
1107
|
async editMessage(oldMessageID, message) {
|
|
1070
1108
|
return await this.getClient().post(this.getClient().baseURL + `/messages/${this.type}/${this.id}/${oldMessageID}`, {
|
|
1071
1109
|
message
|
|
@@ -1971,6 +2009,18 @@ var Channel = class {
|
|
|
1971
2009
|
delete channelState.members[event.user.id];
|
|
1972
2010
|
}
|
|
1973
2011
|
break;
|
|
2012
|
+
case "channel.topic.enabled":
|
|
2013
|
+
if (channel.data) {
|
|
2014
|
+
channel.data.topics_enabled = true;
|
|
2015
|
+
}
|
|
2016
|
+
channelState.topics = channelState.topics || [];
|
|
2017
|
+
break;
|
|
2018
|
+
case "channel.topic.disabled":
|
|
2019
|
+
if (channel.data) {
|
|
2020
|
+
channel.data.topics_enabled = false;
|
|
2021
|
+
}
|
|
2022
|
+
channelState.topics = [];
|
|
2023
|
+
break;
|
|
1974
2024
|
case "channel.updated":
|
|
1975
2025
|
if (event.channel) {
|
|
1976
2026
|
channel.data = {
|
|
@@ -2109,7 +2159,12 @@ var Channel = class {
|
|
|
2109
2159
|
const topic = this.getClient().channel(event.channel_type || "", event.channel_id || "");
|
|
2110
2160
|
topic.data = event.channel;
|
|
2111
2161
|
topic._initializeState(topicState, "latest");
|
|
2112
|
-
channelState.topics
|
|
2162
|
+
if (!channelState.topics) {
|
|
2163
|
+
channelState.topics = [];
|
|
2164
|
+
}
|
|
2165
|
+
if (!channelState.topics.some((t) => t.cid === topic.cid)) {
|
|
2166
|
+
channelState.topics.push(topic);
|
|
2167
|
+
}
|
|
2113
2168
|
break;
|
|
2114
2169
|
case "channel.topic.closed":
|
|
2115
2170
|
if (channel.data) {
|
|
@@ -2928,7 +2983,6 @@ var ErmisChat = class _ErmisChat {
|
|
|
2928
2983
|
params.avatar = user.avatar;
|
|
2929
2984
|
}
|
|
2930
2985
|
const url = this.userBaseURL + "/get_token/external_auth";
|
|
2931
|
-
const query = new URLSearchParams(params).toString();
|
|
2932
2986
|
const headers = {
|
|
2933
2987
|
"Content-Type": "application/json"
|
|
2934
2988
|
};
|
|
@@ -2936,21 +2990,21 @@ var ErmisChat = class _ErmisChat {
|
|
|
2936
2990
|
const tokenStr = typeof token === "string" && token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
2937
2991
|
headers["Authorization"] = tokenStr;
|
|
2938
2992
|
}
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2993
|
+
try {
|
|
2994
|
+
const response = await this.axiosInstance.get(url, {
|
|
2995
|
+
params,
|
|
2996
|
+
headers
|
|
2997
|
+
});
|
|
2998
|
+
return response.data;
|
|
2999
|
+
} catch (error) {
|
|
3000
|
+
let errorMsg = "Failed to fetch external auth token";
|
|
3001
|
+
if (error.response && error.response.data) {
|
|
3002
|
+
errorMsg = error.response.data.message || JSON.stringify(error.response.data);
|
|
3003
|
+
} else if (error.message) {
|
|
3004
|
+
errorMsg = error.message;
|
|
2950
3005
|
}
|
|
2951
3006
|
throw new Error(errorMsg);
|
|
2952
3007
|
}
|
|
2953
|
-
return await response.json();
|
|
2954
3008
|
}
|
|
2955
3009
|
/**
|
|
2956
3010
|
* Connects a user to the Ermis network and establishes the WebSocket connection.
|
|
@@ -2961,19 +3015,24 @@ var ErmisChat = class _ErmisChat {
|
|
|
2961
3015
|
* @param extenal_auth - Set to `true` to use your custom backend external authentication flow.
|
|
2962
3016
|
* @returns A promise resolving to the API connection response once authenticated.
|
|
2963
3017
|
*/
|
|
2964
|
-
connectUser = async (user, userTokenOrProvider,
|
|
3018
|
+
connectUser = async (user, userTokenOrProvider, external_auth) => {
|
|
2965
3019
|
this.logger("info", "client:connectUser() - started", {
|
|
2966
3020
|
tags: ["connection", "client"]
|
|
2967
3021
|
});
|
|
2968
3022
|
if (!user.id) {
|
|
2969
3023
|
throw new Error('The "id" field on the user is missing');
|
|
2970
3024
|
}
|
|
2971
|
-
|
|
3025
|
+
let connectionUser = user;
|
|
3026
|
+
let connectionToken = userTokenOrProvider;
|
|
3027
|
+
if (external_auth) {
|
|
2972
3028
|
const external_auth_token = await this.getExternalAuthToken(user, userTokenOrProvider);
|
|
2973
|
-
|
|
2974
|
-
|
|
3029
|
+
connectionToken = external_auth_token.token;
|
|
3030
|
+
connectionUser = {
|
|
3031
|
+
...user,
|
|
3032
|
+
id: external_auth_token.user_id
|
|
3033
|
+
};
|
|
2975
3034
|
}
|
|
2976
|
-
if (this.userID ===
|
|
3035
|
+
if (this.userID === connectionUser.id && this.setUserPromise) {
|
|
2977
3036
|
console.warn(
|
|
2978
3037
|
"Consecutive calls to connectUser is detected, ideally you should only call this function once in your app."
|
|
2979
3038
|
);
|
|
@@ -2989,10 +3048,10 @@ var ErmisChat = class _ErmisChat {
|
|
|
2989
3048
|
'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.'
|
|
2990
3049
|
);
|
|
2991
3050
|
}
|
|
2992
|
-
this.userID =
|
|
2993
|
-
const setTokenPromise = this._setToken(
|
|
2994
|
-
this._setUser(
|
|
2995
|
-
this.state.updateUser({ id:
|
|
3051
|
+
this.userID = connectionUser.id;
|
|
3052
|
+
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
3053
|
+
this._setUser(connectionUser);
|
|
3054
|
+
this.state.updateUser({ id: connectionUser.id, name: connectionUser?.name || connectionUser.id, avatar: connectionUser?.avatar || "" });
|
|
2996
3055
|
const wsPromise = this.openConnection();
|
|
2997
3056
|
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(
|
|
2998
3057
|
(result) => result[1]
|
|
@@ -3198,6 +3257,20 @@ var ErmisChat = class _ErmisChat {
|
|
|
3198
3257
|
}
|
|
3199
3258
|
});
|
|
3200
3259
|
}
|
|
3260
|
+
/**
|
|
3261
|
+
* Downloads a media file as a Blob via the SDK's configured axiosInstance.
|
|
3262
|
+
* This avoids CORS issues that arise when using `fetch()` directly from the browser,
|
|
3263
|
+
* because axios is routed through the SDK's authenticated transport layer.
|
|
3264
|
+
*
|
|
3265
|
+
* @param url - The full URL of the media file to download.
|
|
3266
|
+
* @returns A Blob of the file content.
|
|
3267
|
+
*/
|
|
3268
|
+
async downloadMedia(url) {
|
|
3269
|
+
const response = await this.axiosInstance.get(url, {
|
|
3270
|
+
responseType: "blob"
|
|
3271
|
+
});
|
|
3272
|
+
return response.data;
|
|
3273
|
+
}
|
|
3201
3274
|
errorFromResponse(response) {
|
|
3202
3275
|
let err;
|
|
3203
3276
|
err = new ErrorFromResponse(`ErmisChat error HTTP code: ${response.status}`);
|
|
@@ -3218,7 +3291,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3218
3291
|
}
|
|
3219
3292
|
dispatchEvent = (event) => {
|
|
3220
3293
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3221
|
-
if (event.type === "channel.created") {
|
|
3294
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3222
3295
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3223
3296
|
this._afterDispatchEvent(event);
|
|
3224
3297
|
});
|
|
@@ -3352,6 +3425,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3352
3425
|
});
|
|
3353
3426
|
}
|
|
3354
3427
|
}
|
|
3428
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3429
|
+
postListenerCallbacks.push(() => {
|
|
3430
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3431
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3432
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3433
|
+
if (parentChannel.state.topics) {
|
|
3434
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3435
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3436
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3437
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3438
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3439
|
+
return bTime - aTime;
|
|
3440
|
+
});
|
|
3441
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
if (event.type === "channel.topic.updated") {
|
|
3447
|
+
postListenerCallbacks.push(() => {
|
|
3448
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3449
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3450
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3451
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3452
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3453
|
+
if (topicIndex !== -1) {
|
|
3454
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3455
|
+
if (t.data) {
|
|
3456
|
+
t.data = { ...t.data, ...event.channel };
|
|
3457
|
+
} else if (t.channel) {
|
|
3458
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3459
|
+
} else {
|
|
3460
|
+
Object.assign(t, event.channel);
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3467
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3468
|
+
if (event.channel) {
|
|
3469
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3470
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3476
|
+
postListenerCallbacks.push(() => {
|
|
3477
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3478
|
+
const parentCid = event.parent_cid;
|
|
3479
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3480
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3481
|
+
if (parentChannel.state?.topics) {
|
|
3482
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3483
|
+
if (topicIndex !== -1) {
|
|
3484
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3485
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3486
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3487
|
+
else t.is_closed_topic = isClosed;
|
|
3488
|
+
}
|
|
3489
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3493
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3494
|
+
if (topicChannel.data) {
|
|
3495
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3496
|
+
}
|
|
3497
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3498
|
+
}
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3355
3501
|
if (event.type === "connection.recovered") {
|
|
3356
3502
|
postListenerCallbacks.push(() => {
|
|
3357
3503
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3589,7 +3735,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3589
3735
|
_updateProjectID(project_id) {
|
|
3590
3736
|
this.projectId = project_id;
|
|
3591
3737
|
}
|
|
3592
|
-
|
|
3738
|
+
/**
|
|
3739
|
+
* Uploads a new avatar image for the current user.
|
|
3740
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3741
|
+
*
|
|
3742
|
+
* @param file - The image file to upload.
|
|
3743
|
+
* @returns The response containing the new avatar URL.
|
|
3744
|
+
*/
|
|
3745
|
+
async uploadAvatar(file) {
|
|
3593
3746
|
const formData = new FormData();
|
|
3594
3747
|
formData.append("avatar", file);
|
|
3595
3748
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3604,12 +3757,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3604
3757
|
}
|
|
3605
3758
|
return response;
|
|
3606
3759
|
}
|
|
3607
|
-
async updateProfile(
|
|
3608
|
-
let
|
|
3609
|
-
name,
|
|
3610
|
-
about_me
|
|
3611
|
-
};
|
|
3612
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3760
|
+
async updateProfile(updates) {
|
|
3761
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3613
3762
|
this.user = response;
|
|
3614
3763
|
this.state.updateUser(response);
|
|
3615
3764
|
return response;
|
|
@@ -3762,45 +3911,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3762
3911
|
return channel;
|
|
3763
3912
|
};
|
|
3764
3913
|
/**
|
|
3765
|
-
* Creates
|
|
3766
|
-
*
|
|
3914
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3915
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3916
|
+
* The creator is added as the first member automatically.
|
|
3767
3917
|
*
|
|
3768
|
-
* @param name -
|
|
3918
|
+
* @param name - An optional display name for the channel.
|
|
3769
3919
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3770
3920
|
*/
|
|
3771
|
-
async
|
|
3921
|
+
async createQuickChannel(name) {
|
|
3772
3922
|
if (!this.userID) {
|
|
3773
3923
|
throw Error("Call connectUser before creating a channel");
|
|
3774
3924
|
}
|
|
3925
|
+
const now = /* @__PURE__ */ new Date();
|
|
3926
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3927
|
+
month: "short",
|
|
3928
|
+
day: "2-digit",
|
|
3929
|
+
year: "numeric",
|
|
3930
|
+
hour: "2-digit",
|
|
3931
|
+
minute: "2-digit",
|
|
3932
|
+
hour12: false
|
|
3933
|
+
}).format(now);
|
|
3775
3934
|
const payload = {
|
|
3776
|
-
name: name || `
|
|
3935
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3777
3936
|
members: [this.userID],
|
|
3778
3937
|
public: true
|
|
3779
3938
|
};
|
|
3780
|
-
const
|
|
3781
|
-
await
|
|
3782
|
-
return
|
|
3939
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3940
|
+
await quickChannel.create();
|
|
3941
|
+
return quickChannel;
|
|
3783
3942
|
}
|
|
3784
3943
|
/**
|
|
3785
|
-
* Joins a
|
|
3786
|
-
*
|
|
3787
|
-
* If not, it
|
|
3944
|
+
* Joins a quick channel by its ID.
|
|
3945
|
+
* Automatically checks whether the caller is already a member.
|
|
3946
|
+
* If not, it joins the channel and synchronizes state.
|
|
3788
3947
|
*
|
|
3789
|
-
* @param channelId - The ID of the
|
|
3948
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3790
3949
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3791
3950
|
*/
|
|
3792
|
-
async
|
|
3951
|
+
async joinQuickChannel(channelId) {
|
|
3793
3952
|
if (!this.userID) {
|
|
3794
3953
|
throw Error("Call connectUser before joining a channel");
|
|
3795
3954
|
}
|
|
3796
|
-
const
|
|
3797
|
-
await
|
|
3798
|
-
const isMember =
|
|
3955
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3956
|
+
await quickChannel.watch();
|
|
3957
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3799
3958
|
if (!isMember) {
|
|
3800
|
-
await
|
|
3801
|
-
await
|
|
3959
|
+
await quickChannel.acceptInvite("join");
|
|
3960
|
+
await quickChannel.watch();
|
|
3802
3961
|
}
|
|
3803
|
-
return
|
|
3962
|
+
return quickChannel;
|
|
3804
3963
|
}
|
|
3805
3964
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3806
3965
|
let pinExpires = null;
|
|
@@ -3816,7 +3975,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3816
3975
|
return pinExpires;
|
|
3817
3976
|
}
|
|
3818
3977
|
getUserAgent() {
|
|
3819
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3978
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
3820
3979
|
}
|
|
3821
3980
|
setUserAgent(userAgent) {
|
|
3822
3981
|
this.userAgent = userAgent;
|
|
@@ -7175,7 +7334,7 @@ var ErmisAuthProvider = class {
|
|
|
7175
7334
|
return data;
|
|
7176
7335
|
}
|
|
7177
7336
|
getUserAgent() {
|
|
7178
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7337
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.8"}`;
|
|
7179
7338
|
}
|
|
7180
7339
|
setUserAgent(userAgent) {
|
|
7181
7340
|
this.userAgent = userAgent;
|