@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.cjs
CHANGED
|
@@ -1068,6 +1068,44 @@ var Channel = class {
|
|
|
1068
1068
|
this.getClient().baseURL + `/messages/${this.type}/${this.id}/${messageID}/unpin`
|
|
1069
1069
|
);
|
|
1070
1070
|
}
|
|
1071
|
+
async pin() {
|
|
1072
|
+
if (this.data) this.data.is_pinned = true;
|
|
1073
|
+
this.getClient().dispatchEvent({
|
|
1074
|
+
type: "channel.pinned",
|
|
1075
|
+
cid: this.cid,
|
|
1076
|
+
channel: this.data
|
|
1077
|
+
});
|
|
1078
|
+
try {
|
|
1079
|
+
return await this.getClient().pinChannel(this.type, this.id);
|
|
1080
|
+
} catch (e) {
|
|
1081
|
+
if (this.data) this.data.is_pinned = false;
|
|
1082
|
+
this.getClient().dispatchEvent({
|
|
1083
|
+
type: "channel.unpinned",
|
|
1084
|
+
cid: this.cid,
|
|
1085
|
+
channel: this.data
|
|
1086
|
+
});
|
|
1087
|
+
throw e;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
async unpin() {
|
|
1091
|
+
if (this.data) this.data.is_pinned = false;
|
|
1092
|
+
this.getClient().dispatchEvent({
|
|
1093
|
+
type: "channel.unpinned",
|
|
1094
|
+
cid: this.cid,
|
|
1095
|
+
channel: this.data
|
|
1096
|
+
});
|
|
1097
|
+
try {
|
|
1098
|
+
return await this.getClient().unpinChannel(this.type, this.id);
|
|
1099
|
+
} catch (e) {
|
|
1100
|
+
if (this.data) this.data.is_pinned = true;
|
|
1101
|
+
this.getClient().dispatchEvent({
|
|
1102
|
+
type: "channel.pinned",
|
|
1103
|
+
cid: this.cid,
|
|
1104
|
+
channel: this.data
|
|
1105
|
+
});
|
|
1106
|
+
throw e;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1071
1109
|
async editMessage(oldMessageID, message) {
|
|
1072
1110
|
return await this.getClient().post(this.getClient().baseURL + `/messages/${this.type}/${this.id}/${oldMessageID}`, {
|
|
1073
1111
|
message
|
|
@@ -1973,6 +2011,18 @@ var Channel = class {
|
|
|
1973
2011
|
delete channelState.members[event.user.id];
|
|
1974
2012
|
}
|
|
1975
2013
|
break;
|
|
2014
|
+
case "channel.topic.enabled":
|
|
2015
|
+
if (channel.data) {
|
|
2016
|
+
channel.data.topics_enabled = true;
|
|
2017
|
+
}
|
|
2018
|
+
channelState.topics = channelState.topics || [];
|
|
2019
|
+
break;
|
|
2020
|
+
case "channel.topic.disabled":
|
|
2021
|
+
if (channel.data) {
|
|
2022
|
+
channel.data.topics_enabled = false;
|
|
2023
|
+
}
|
|
2024
|
+
channelState.topics = [];
|
|
2025
|
+
break;
|
|
1976
2026
|
case "channel.updated":
|
|
1977
2027
|
if (event.channel) {
|
|
1978
2028
|
channel.data = {
|
|
@@ -2111,7 +2161,12 @@ var Channel = class {
|
|
|
2111
2161
|
const topic = this.getClient().channel(event.channel_type || "", event.channel_id || "");
|
|
2112
2162
|
topic.data = event.channel;
|
|
2113
2163
|
topic._initializeState(topicState, "latest");
|
|
2114
|
-
channelState.topics
|
|
2164
|
+
if (!channelState.topics) {
|
|
2165
|
+
channelState.topics = [];
|
|
2166
|
+
}
|
|
2167
|
+
if (!channelState.topics.some((t) => t.cid === topic.cid)) {
|
|
2168
|
+
channelState.topics.push(topic);
|
|
2169
|
+
}
|
|
2115
2170
|
break;
|
|
2116
2171
|
case "channel.topic.closed":
|
|
2117
2172
|
if (channel.data) {
|
|
@@ -2930,7 +2985,6 @@ var ErmisChat = class _ErmisChat {
|
|
|
2930
2985
|
params.avatar = user.avatar;
|
|
2931
2986
|
}
|
|
2932
2987
|
const url = this.userBaseURL + "/get_token/external_auth";
|
|
2933
|
-
const query = new URLSearchParams(params).toString();
|
|
2934
2988
|
const headers = {
|
|
2935
2989
|
"Content-Type": "application/json"
|
|
2936
2990
|
};
|
|
@@ -2938,21 +2992,21 @@ var ErmisChat = class _ErmisChat {
|
|
|
2938
2992
|
const tokenStr = typeof token === "string" && token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
2939
2993
|
headers["Authorization"] = tokenStr;
|
|
2940
2994
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2995
|
+
try {
|
|
2996
|
+
const response = await this.axiosInstance.get(url, {
|
|
2997
|
+
params,
|
|
2998
|
+
headers
|
|
2999
|
+
});
|
|
3000
|
+
return response.data;
|
|
3001
|
+
} catch (error) {
|
|
3002
|
+
let errorMsg = "Failed to fetch external auth token";
|
|
3003
|
+
if (error.response && error.response.data) {
|
|
3004
|
+
errorMsg = error.response.data.message || JSON.stringify(error.response.data);
|
|
3005
|
+
} else if (error.message) {
|
|
3006
|
+
errorMsg = error.message;
|
|
2952
3007
|
}
|
|
2953
3008
|
throw new Error(errorMsg);
|
|
2954
3009
|
}
|
|
2955
|
-
return await response.json();
|
|
2956
3010
|
}
|
|
2957
3011
|
/**
|
|
2958
3012
|
* Connects a user to the Ermis network and establishes the WebSocket connection.
|
|
@@ -2963,19 +3017,24 @@ var ErmisChat = class _ErmisChat {
|
|
|
2963
3017
|
* @param extenal_auth - Set to `true` to use your custom backend external authentication flow.
|
|
2964
3018
|
* @returns A promise resolving to the API connection response once authenticated.
|
|
2965
3019
|
*/
|
|
2966
|
-
connectUser = async (user, userTokenOrProvider,
|
|
3020
|
+
connectUser = async (user, userTokenOrProvider, external_auth) => {
|
|
2967
3021
|
this.logger("info", "client:connectUser() - started", {
|
|
2968
3022
|
tags: ["connection", "client"]
|
|
2969
3023
|
});
|
|
2970
3024
|
if (!user.id) {
|
|
2971
3025
|
throw new Error('The "id" field on the user is missing');
|
|
2972
3026
|
}
|
|
2973
|
-
|
|
3027
|
+
let connectionUser = user;
|
|
3028
|
+
let connectionToken = userTokenOrProvider;
|
|
3029
|
+
if (external_auth) {
|
|
2974
3030
|
const external_auth_token = await this.getExternalAuthToken(user, userTokenOrProvider);
|
|
2975
|
-
|
|
2976
|
-
|
|
3031
|
+
connectionToken = external_auth_token.token;
|
|
3032
|
+
connectionUser = {
|
|
3033
|
+
...user,
|
|
3034
|
+
id: external_auth_token.user_id
|
|
3035
|
+
};
|
|
2977
3036
|
}
|
|
2978
|
-
if (this.userID ===
|
|
3037
|
+
if (this.userID === connectionUser.id && this.setUserPromise) {
|
|
2979
3038
|
console.warn(
|
|
2980
3039
|
"Consecutive calls to connectUser is detected, ideally you should only call this function once in your app."
|
|
2981
3040
|
);
|
|
@@ -2991,10 +3050,10 @@ var ErmisChat = class _ErmisChat {
|
|
|
2991
3050
|
'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.'
|
|
2992
3051
|
);
|
|
2993
3052
|
}
|
|
2994
|
-
this.userID =
|
|
2995
|
-
const setTokenPromise = this._setToken(
|
|
2996
|
-
this._setUser(
|
|
2997
|
-
this.state.updateUser({ id:
|
|
3053
|
+
this.userID = connectionUser.id;
|
|
3054
|
+
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
3055
|
+
this._setUser(connectionUser);
|
|
3056
|
+
this.state.updateUser({ id: connectionUser.id, name: connectionUser?.name || connectionUser.id, avatar: connectionUser?.avatar || "" });
|
|
2998
3057
|
const wsPromise = this.openConnection();
|
|
2999
3058
|
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(
|
|
3000
3059
|
(result) => result[1]
|
|
@@ -3220,7 +3279,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3220
3279
|
}
|
|
3221
3280
|
dispatchEvent = (event) => {
|
|
3222
3281
|
if (!event.received_at) event.received_at = /* @__PURE__ */ new Date();
|
|
3223
|
-
if (event.type === "channel.created") {
|
|
3282
|
+
if (event.type === "channel.created" || event.type === "channel.topic.created") {
|
|
3224
3283
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
3225
3284
|
this._afterDispatchEvent(event);
|
|
3226
3285
|
});
|
|
@@ -3354,6 +3413,79 @@ var ErmisChat = class _ErmisChat {
|
|
|
3354
3413
|
});
|
|
3355
3414
|
}
|
|
3356
3415
|
}
|
|
3416
|
+
if (event.type === "message.new" && event.channel_type === "topic") {
|
|
3417
|
+
postListenerCallbacks.push(() => {
|
|
3418
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3419
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3420
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3421
|
+
if (parentChannel.state.topics) {
|
|
3422
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
3423
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
3424
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
3425
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
3426
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
3427
|
+
return bTime - aTime;
|
|
3428
|
+
});
|
|
3429
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
});
|
|
3433
|
+
}
|
|
3434
|
+
if (event.type === "channel.topic.updated") {
|
|
3435
|
+
postListenerCallbacks.push(() => {
|
|
3436
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
3437
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3438
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3439
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
3440
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3441
|
+
if (topicIndex !== -1) {
|
|
3442
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3443
|
+
if (t.data) {
|
|
3444
|
+
t.data = { ...t.data, ...event.channel };
|
|
3445
|
+
} else if (t.channel) {
|
|
3446
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
3447
|
+
} else {
|
|
3448
|
+
Object.assign(t, event.channel);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3455
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3456
|
+
if (event.channel) {
|
|
3457
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
3458
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
if (event.type === "channel.topic.closed" || event.type === "channel.topic.reopen") {
|
|
3464
|
+
postListenerCallbacks.push(() => {
|
|
3465
|
+
const isClosed = event.type === "channel.topic.closed";
|
|
3466
|
+
const parentCid = event.parent_cid;
|
|
3467
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
3468
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
3469
|
+
if (parentChannel.state?.topics) {
|
|
3470
|
+
const topicIndex = parentChannel.state.topics.findIndex((t) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
3471
|
+
if (topicIndex !== -1) {
|
|
3472
|
+
const t = parentChannel.state.topics[topicIndex];
|
|
3473
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
3474
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
3475
|
+
else t.is_closed_topic = isClosed;
|
|
3476
|
+
}
|
|
3477
|
+
parentChannel._callChannelListeners({ ...event, type: "channel.updated", channel: parentChannel.data });
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
3481
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
3482
|
+
if (topicChannel.data) {
|
|
3483
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
3484
|
+
}
|
|
3485
|
+
topicChannel._callChannelListeners({ ...event, type: "channel.updated", channel: topicChannel.data });
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3357
3489
|
if (event.type === "connection.recovered") {
|
|
3358
3490
|
postListenerCallbacks.push(() => {
|
|
3359
3491
|
Object.values(this.activeChannels).forEach((channel) => {
|
|
@@ -3591,7 +3723,14 @@ var ErmisChat = class _ErmisChat {
|
|
|
3591
3723
|
_updateProjectID(project_id) {
|
|
3592
3724
|
this.projectId = project_id;
|
|
3593
3725
|
}
|
|
3594
|
-
|
|
3726
|
+
/**
|
|
3727
|
+
* Uploads a new avatar image for the current user.
|
|
3728
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
3729
|
+
*
|
|
3730
|
+
* @param file - The image file to upload.
|
|
3731
|
+
* @returns The response containing the new avatar URL.
|
|
3732
|
+
*/
|
|
3733
|
+
async uploadAvatar(file) {
|
|
3595
3734
|
const formData = new FormData();
|
|
3596
3735
|
formData.append("avatar", file);
|
|
3597
3736
|
let response = await this.post(this.userBaseURL + "/users/upload", formData, {
|
|
@@ -3606,12 +3745,8 @@ var ErmisChat = class _ErmisChat {
|
|
|
3606
3745
|
}
|
|
3607
3746
|
return response;
|
|
3608
3747
|
}
|
|
3609
|
-
async updateProfile(
|
|
3610
|
-
let
|
|
3611
|
-
name,
|
|
3612
|
-
about_me
|
|
3613
|
-
};
|
|
3614
|
-
let response = await this.patch(this.userBaseURL + "/users/update", body);
|
|
3748
|
+
async updateProfile(updates) {
|
|
3749
|
+
let response = await this.patch(this.userBaseURL + "/users/update", updates);
|
|
3615
3750
|
this.user = response;
|
|
3616
3751
|
this.state.updateUser(response);
|
|
3617
3752
|
return response;
|
|
@@ -3764,45 +3899,55 @@ var ErmisChat = class _ErmisChat {
|
|
|
3764
3899
|
return channel;
|
|
3765
3900
|
};
|
|
3766
3901
|
/**
|
|
3767
|
-
* Creates
|
|
3768
|
-
*
|
|
3902
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
3903
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
3904
|
+
* The creator is added as the first member automatically.
|
|
3769
3905
|
*
|
|
3770
|
-
* @param name -
|
|
3906
|
+
* @param name - An optional display name for the channel.
|
|
3771
3907
|
* @returns A promise that resolves to the created `Channel` object.
|
|
3772
3908
|
*/
|
|
3773
|
-
async
|
|
3909
|
+
async createQuickChannel(name) {
|
|
3774
3910
|
if (!this.userID) {
|
|
3775
3911
|
throw Error("Call connectUser before creating a channel");
|
|
3776
3912
|
}
|
|
3913
|
+
const now = /* @__PURE__ */ new Date();
|
|
3914
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
3915
|
+
month: "short",
|
|
3916
|
+
day: "2-digit",
|
|
3917
|
+
year: "numeric",
|
|
3918
|
+
hour: "2-digit",
|
|
3919
|
+
minute: "2-digit",
|
|
3920
|
+
hour12: false
|
|
3921
|
+
}).format(now);
|
|
3777
3922
|
const payload = {
|
|
3778
|
-
name: name || `
|
|
3923
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
3779
3924
|
members: [this.userID],
|
|
3780
3925
|
public: true
|
|
3781
3926
|
};
|
|
3782
|
-
const
|
|
3783
|
-
await
|
|
3784
|
-
return
|
|
3927
|
+
const quickChannel = this.channel("meeting", payload);
|
|
3928
|
+
await quickChannel.create();
|
|
3929
|
+
return quickChannel;
|
|
3785
3930
|
}
|
|
3786
3931
|
/**
|
|
3787
|
-
* Joins a
|
|
3788
|
-
*
|
|
3789
|
-
* If not, it
|
|
3932
|
+
* Joins a quick channel by its ID.
|
|
3933
|
+
* Automatically checks whether the caller is already a member.
|
|
3934
|
+
* If not, it joins the channel and synchronizes state.
|
|
3790
3935
|
*
|
|
3791
|
-
* @param channelId - The ID of the
|
|
3936
|
+
* @param channelId - The ID of the quick channel to join.
|
|
3792
3937
|
* @returns A promise that resolves to the joined `Channel` object.
|
|
3793
3938
|
*/
|
|
3794
|
-
async
|
|
3939
|
+
async joinQuickChannel(channelId) {
|
|
3795
3940
|
if (!this.userID) {
|
|
3796
3941
|
throw Error("Call connectUser before joining a channel");
|
|
3797
3942
|
}
|
|
3798
|
-
const
|
|
3799
|
-
await
|
|
3800
|
-
const isMember =
|
|
3943
|
+
const quickChannel = this.channel("meeting", channelId);
|
|
3944
|
+
await quickChannel.watch();
|
|
3945
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
3801
3946
|
if (!isMember) {
|
|
3802
|
-
await
|
|
3803
|
-
await
|
|
3947
|
+
await quickChannel.acceptInvite("join");
|
|
3948
|
+
await quickChannel.watch();
|
|
3804
3949
|
}
|
|
3805
|
-
return
|
|
3950
|
+
return quickChannel;
|
|
3806
3951
|
}
|
|
3807
3952
|
_normalizeExpiration(timeoutOrExpirationDate) {
|
|
3808
3953
|
let pinExpires = null;
|
|
@@ -3818,7 +3963,7 @@ var ErmisChat = class _ErmisChat {
|
|
|
3818
3963
|
return pinExpires;
|
|
3819
3964
|
}
|
|
3820
3965
|
getUserAgent() {
|
|
3821
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
3966
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
3822
3967
|
}
|
|
3823
3968
|
setUserAgent(userAgent) {
|
|
3824
3969
|
this.userAgent = userAgent;
|
|
@@ -7176,7 +7321,7 @@ var ErmisAuthProvider = class {
|
|
|
7176
7321
|
return data;
|
|
7177
7322
|
}
|
|
7178
7323
|
getUserAgent() {
|
|
7179
|
-
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.
|
|
7324
|
+
return this.userAgent || `ermis-chat-sdk-javascript-client-${this.node ? "node" : "browser"}-${"1.0.7"}`;
|
|
7180
7325
|
}
|
|
7181
7326
|
setUserAgent(userAgent) {
|
|
7182
7327
|
this.userAgent = userAgent;
|