@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.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]
|
|
@@ -3218,7 +3277,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3218
3277
|
}
|
|
3219
3278
|
dispatchEvent = (event) => {
|
|
3220
3279
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3221
|
-
if (event.type === "channel.created") {
|
|
3280
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3222
3281
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3223
3282
|
this._afterDispatchEvent(event);
|
|
3224
3283
|
});
|
|
@@ -3352,6 +3411,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3352
3411
|
});
|
|
3353
3412
|
}
|
|
3354
3413
|
}
|
|
3414
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3415
|
+
postListenerCallbacks.push(() => {
|
|
3416
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3417
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3418
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3419
|
+
if (parentChannel.state.topics) {
|
|
3420
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3421
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3422
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3423
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3424
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3425
|
+
return bTime - aTime;
|
|
3426
|
+
});
|
|
3427
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
if (event.type === "channel.topic.updated") {
|
|
3433
|
+
postListenerCallbacks.push(() => {
|
|
3434
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3435
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3436
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3437
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3438
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3439
|
+
if (topicIndex !== -1) {
|
|
3440
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3441
|
+
if (t.data) {
|
|
3442
|
+
t.data = { ...t.data, ...event.channel };
|
|
3443
|
+
} else if (t.channel) {
|
|
3444
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3445
|
+
} else {
|
|
3446
|
+
Object.assign(t, event.channel);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3453
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3454
|
+
if (event.channel) {
|
|
3455
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3456
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
});
|
|
3460
|
+
}
|
|
3461
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3462
|
+
postListenerCallbacks.push(() => {
|
|
3463
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3464
|
+
const parentCid = event.parent_cid;
|
|
3465
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3466
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3467
|
+
if (parentChannel.state?.topics) {
|
|
3468
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3469
|
+
if (topicIndex !== -1) {
|
|
3470
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3471
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3472
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3473
|
+
else t.is_closed_topic = isClosed;
|
|
3474
|
+
}
|
|
3475
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3479
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3480
|
+
if (topicChannel.data) {
|
|
3481
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3482
|
+
}
|
|
3483
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3484
|
+
}
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3355
3487
|
if (event.type === "connection.recovered") {
|
|
3356
3488
|
postListenerCallbacks.push(() => {
|
|
3357
3489
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3589,7 +3721,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3589
3721
|
_updateProjectID(project_id) {
|
|
3590
3722
|
this.projectId = project_id;
|
|
3591
3723
|
}
|
|
3592
|
-
|
|
3724
|
+
/**
|
|
3725
|
+
* Uploads a new avatar image for the current user.
|
|
3726
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3727
|
+
*
|
|
3728
|
+
* @param file - The image file to upload.
|
|
3729
|
+
* @returns The response containing the new avatar URL.
|
|
3730
|
+
*/
|
|
3731
|
+
async uploadAvatar(file) {
|
|
3593
3732
|
const formData = new FormData();
|
|
3594
3733
|
formData.append("avatar", file);
|
|
3595
3734
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3604,12 +3743,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3604
3743
|
}
|
|
3605
3744
|
return response;
|
|
3606
3745
|
}
|
|
3607
|
-
async updateProfile(
|
|
3608
|
-
let
|
|
3609
|
-
name,
|
|
3610
|
-
about_me
|
|
3611
|
-
};
|
|
3612
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3746
|
+
async updateProfile(updates) {
|
|
3747
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3613
3748
|
this.user = response;
|
|
3614
3749
|
this.state.updateUser(response);
|
|
3615
3750
|
return response;
|
|
@@ -3762,45 +3897,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3762
3897
|
return channel;
|
|
3763
3898
|
};
|
|
3764
3899
|
/**
|
|
3765
|
-
* Creates
|
|
3766
|
-
*
|
|
3900
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3901
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3902
|
+
* The creator is added as the first member automatically.
|
|
3767
3903
|
*
|
|
3768
|
-
* @param name -
|
|
3904
|
+
* @param name - An optional display name for the channel.
|
|
3769
3905
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3770
3906
|
*/
|
|
3771
|
-
async
|
|
3907
|
+
async createQuickChannel(name) {
|
|
3772
3908
|
if (!this.userID) {
|
|
3773
3909
|
throw Error("Call connectUser before creating a channel");
|
|
3774
3910
|
}
|
|
3911
|
+
const now = /* @__PURE__ */ new Date();
|
|
3912
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3913
|
+
month: "short",
|
|
3914
|
+
day: "2-digit",
|
|
3915
|
+
year: "numeric",
|
|
3916
|
+
hour: "2-digit",
|
|
3917
|
+
minute: "2-digit",
|
|
3918
|
+
hour12: false
|
|
3919
|
+
}).format(now);
|
|
3775
3920
|
const payload = {
|
|
3776
|
-
name: name || `
|
|
3921
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3777
3922
|
members: [this.userID],
|
|
3778
3923
|
public: true
|
|
3779
3924
|
};
|
|
3780
|
-
const
|
|
3781
|
-
await
|
|
3782
|
-
return
|
|
3925
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3926
|
+
await quickChannel.create();
|
|
3927
|
+
return quickChannel;
|
|
3783
3928
|
}
|
|
3784
3929
|
/**
|
|
3785
|
-
* Joins a
|
|
3786
|
-
*
|
|
3787
|
-
* If not, it
|
|
3930
|
+
* Joins a quick channel by its ID.
|
|
3931
|
+
* Automatically checks whether the caller is already a member.
|
|
3932
|
+
* If not, it joins the channel and synchronizes state.
|
|
3788
3933
|
*
|
|
3789
|
-
* @param channelId - The ID of the
|
|
3934
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3790
3935
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3791
3936
|
*/
|
|
3792
|
-
async
|
|
3937
|
+
async joinQuickChannel(channelId) {
|
|
3793
3938
|
if (!this.userID) {
|
|
3794
3939
|
throw Error("Call connectUser before joining a channel");
|
|
3795
3940
|
}
|
|
3796
|
-
const
|
|
3797
|
-
await
|
|
3798
|
-
const isMember =
|
|
3941
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3942
|
+
await quickChannel.watch();
|
|
3943
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3799
3944
|
if (!isMember) {
|
|
3800
|
-
await
|
|
3801
|
-
await
|
|
3945
|
+
await quickChannel.acceptInvite("join");
|
|
3946
|
+
await quickChannel.watch();
|
|
3802
3947
|
}
|
|
3803
|
-
return
|
|
3948
|
+
return quickChannel;
|
|
3804
3949
|
}
|
|
3805
3950
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3806
3951
|
let pinExpires = null;
|
|
@@ -3816,7 +3961,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3816
3961
|
return pinExpires;
|
|
3817
3962
|
}
|
|
3818
3963
|
getUserAgent() {
|
|
3819
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3964
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
3820
3965
|
}
|
|
3821
3966
|
setUserAgent(userAgent) {
|
|
3822
3967
|
this.userAgent = userAgent;
|
|
@@ -7175,7 +7320,7 @@ var ErmisAuthProvider = class {
|
|
|
7175
7320
|
return data;
|
|
7176
7321
|
}
|
|
7177
7322
|
getUserAgent() {
|
|
7178
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7323
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
7179
7324
|
}
|
|
7180
7325
|
setUserAgent(userAgent) {
|
|
7181
7326
|
this.userAgent = userAgent;
|