@ermis-network/ermis-chat-sdk 1.0.5 → 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 +229 -53
- 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 +229 -53
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +229 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +46 -7
- package/dist/index.d.ts +46 -7
- package/dist/index.mjs +229 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/channel.ts +85 -25
- package/src/client.ts +175 -31
- package/src/types.ts +14 -0
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
PollMessage,
|
|
37
37
|
EditMessage,
|
|
38
38
|
ForwardMessage,
|
|
39
|
+
CreateTopicData,
|
|
40
|
+
EditTopicData,
|
|
39
41
|
} from './types';
|
|
40
42
|
/**
|
|
41
43
|
* Represents a Channel in the Ermis Network.
|
|
@@ -132,13 +134,16 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
132
134
|
|
|
133
135
|
// 3. Call API — don't update status on success (WS message.new will handle it)
|
|
134
136
|
try {
|
|
135
|
-
return await this.getClient().post<SendMessageAPIResponse<ErmisChatGenerics>>(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
137
|
+
return await this.getClient().post<SendMessageAPIResponse<ErmisChatGenerics>>(this._channelURL() + '/message', {
|
|
138
|
+
message: { ...message },
|
|
139
|
+
});
|
|
139
140
|
} catch (error: any) {
|
|
140
141
|
// 4. On error: check if it's an offline/network error
|
|
141
|
-
const isOfflineError =
|
|
142
|
+
const isOfflineError =
|
|
143
|
+
!error.response ||
|
|
144
|
+
error.code === 'ERR_NETWORK' ||
|
|
145
|
+
error.isWSFailure ||
|
|
146
|
+
!this.getClient().wsConnection?.isHealthy;
|
|
142
147
|
const statusToSet = isOfflineError ? 'failed_offline' : 'error';
|
|
143
148
|
this.state.updateMessageStatus(messageId, statusToSet);
|
|
144
149
|
throw error;
|
|
@@ -166,12 +171,15 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
try {
|
|
169
|
-
return await this.getClient().post<SendMessageAPIResponse<ErmisChatGenerics>>(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
);
|
|
174
|
+
return await this.getClient().post<SendMessageAPIResponse<ErmisChatGenerics>>(this._channelURL() + '/message', {
|
|
175
|
+
message: messagePayload,
|
|
176
|
+
});
|
|
173
177
|
} catch (error: any) {
|
|
174
|
-
const isOfflineError =
|
|
178
|
+
const isOfflineError =
|
|
179
|
+
!error.response ||
|
|
180
|
+
error.code === 'ERR_NETWORK' ||
|
|
181
|
+
error.isWSFailure ||
|
|
182
|
+
!this.getClient().wsConnection?.isHealthy;
|
|
175
183
|
this.state.updateMessageStatus(messageId, isOfflineError ? 'failed_offline' : 'error');
|
|
176
184
|
throw error;
|
|
177
185
|
}
|
|
@@ -218,6 +226,48 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
218
226
|
);
|
|
219
227
|
}
|
|
220
228
|
|
|
229
|
+
async pin() {
|
|
230
|
+
if (this.data) this.data.is_pinned = true;
|
|
231
|
+
this.getClient().dispatchEvent({
|
|
232
|
+
type: 'channel.pinned',
|
|
233
|
+
cid: this.cid,
|
|
234
|
+
channel: this.data,
|
|
235
|
+
} as Event<ErmisChatGenerics>);
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
return await this.getClient().pinChannel(this.type, this.id as string);
|
|
239
|
+
} catch (e) {
|
|
240
|
+
if (this.data) this.data.is_pinned = false;
|
|
241
|
+
this.getClient().dispatchEvent({
|
|
242
|
+
type: 'channel.unpinned',
|
|
243
|
+
cid: this.cid,
|
|
244
|
+
channel: this.data,
|
|
245
|
+
} as Event<ErmisChatGenerics>);
|
|
246
|
+
throw e;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async unpin() {
|
|
251
|
+
if (this.data) this.data.is_pinned = false;
|
|
252
|
+
this.getClient().dispatchEvent({
|
|
253
|
+
type: 'channel.unpinned',
|
|
254
|
+
cid: this.cid,
|
|
255
|
+
channel: this.data,
|
|
256
|
+
} as Event<ErmisChatGenerics>);
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
return await this.getClient().unpinChannel(this.type, this.id as string);
|
|
260
|
+
} catch (e) {
|
|
261
|
+
if (this.data) this.data.is_pinned = true;
|
|
262
|
+
this.getClient().dispatchEvent({
|
|
263
|
+
type: 'channel.pinned',
|
|
264
|
+
cid: this.cid,
|
|
265
|
+
channel: this.data,
|
|
266
|
+
} as Event<ErmisChatGenerics>);
|
|
267
|
+
throw e;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
221
271
|
async editMessage(oldMessageID: string, message: EditMessage) {
|
|
222
272
|
return await this.getClient().post(this.getClient().baseURL + `/messages/${this.type}/${this.id}/${oldMessageID}`, {
|
|
223
273
|
message,
|
|
@@ -263,9 +313,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
263
313
|
|
|
264
314
|
// 2. Upload all files in parallel
|
|
265
315
|
const uploadResults = await Promise.allSettled(
|
|
266
|
-
processedFiles.map((file) =>
|
|
267
|
-
this.sendFile(file, file.name, file.type),
|
|
268
|
-
),
|
|
316
|
+
processedFiles.map((file) => this.sendFile(file, file.name, file.type)),
|
|
269
317
|
);
|
|
270
318
|
|
|
271
319
|
// 3. For successful video uploads, generate and upload thumbnails
|
|
@@ -280,11 +328,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
280
328
|
try {
|
|
281
329
|
const thumbBlob = await this.getThumbBlobVideo(files[i]);
|
|
282
330
|
if (thumbBlob) {
|
|
283
|
-
const thumbFile = new File(
|
|
284
|
-
[thumbBlob],
|
|
285
|
-
`thumb_${processedFiles[i].name}.jpg`,
|
|
286
|
-
{ type: 'image/jpeg' },
|
|
287
|
-
);
|
|
331
|
+
const thumbFile = new File([thumbBlob], `thumb_${processedFiles[i].name}.jpg`, { type: 'image/jpeg' });
|
|
288
332
|
const thumbResp = await this.sendFile(thumbFile, thumbFile.name, 'image/jpeg');
|
|
289
333
|
thumbUrls.set(i, thumbResp.file);
|
|
290
334
|
}
|
|
@@ -306,9 +350,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
306
350
|
const uploadedUrl = result.value.file;
|
|
307
351
|
const thumbUrl = thumbUrls.get(i);
|
|
308
352
|
const voiceMeta = options?.voiceMetadata?.get(i);
|
|
309
|
-
attachments.push(
|
|
310
|
-
buildAttachmentPayload(processedFiles[i], uploadedUrl, thumbUrl, voiceMeta),
|
|
311
|
-
);
|
|
353
|
+
attachments.push(buildAttachmentPayload(processedFiles[i], uploadedUrl, thumbUrl, voiceMeta));
|
|
312
354
|
} else {
|
|
313
355
|
failedFiles.push({
|
|
314
356
|
file: files[i],
|
|
@@ -728,7 +770,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
728
770
|
}
|
|
729
771
|
};
|
|
730
772
|
|
|
731
|
-
async createTopic(data:
|
|
773
|
+
async createTopic(data: CreateTopicData) {
|
|
732
774
|
const project_id = this._client.projectId;
|
|
733
775
|
const uuid = randomId();
|
|
734
776
|
const topicID = `${project_id}:${uuid}`;
|
|
@@ -756,7 +798,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
756
798
|
if (this.id) {
|
|
757
799
|
queryURL += `/${this.id}`;
|
|
758
800
|
} else {
|
|
759
|
-
if (this.type === 'team') {
|
|
801
|
+
if (this.type === 'team' || this.type === 'meeting') {
|
|
760
802
|
const uuid = randomId();
|
|
761
803
|
this.id = `${project_id}:${uuid}`;
|
|
762
804
|
queryURL += `/${this.id}`;
|
|
@@ -1081,7 +1123,7 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
1081
1123
|
});
|
|
1082
1124
|
}
|
|
1083
1125
|
|
|
1084
|
-
async editTopic(topicCID: string, data:
|
|
1126
|
+
async editTopic(topicCID: string, data: EditTopicData) {
|
|
1085
1127
|
const response: any = await this.getClient().post(
|
|
1086
1128
|
this.getClient().baseURL + `/channels/${this.type}/${this.id}/topics`,
|
|
1087
1129
|
{
|
|
@@ -1392,6 +1434,18 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
1392
1434
|
delete channelState.members[event.user.id];
|
|
1393
1435
|
}
|
|
1394
1436
|
break;
|
|
1437
|
+
case 'channel.topic.enabled':
|
|
1438
|
+
if (channel.data) {
|
|
1439
|
+
channel.data.topics_enabled = true;
|
|
1440
|
+
}
|
|
1441
|
+
channelState.topics = channelState.topics || [];
|
|
1442
|
+
break;
|
|
1443
|
+
case 'channel.topic.disabled':
|
|
1444
|
+
if (channel.data) {
|
|
1445
|
+
channel.data.topics_enabled = false;
|
|
1446
|
+
}
|
|
1447
|
+
channelState.topics = [];
|
|
1448
|
+
break;
|
|
1395
1449
|
case 'channel.updated':
|
|
1396
1450
|
if (event.channel) {
|
|
1397
1451
|
channel.data = {
|
|
@@ -1547,7 +1601,13 @@ export class Channel<ErmisChatGenerics extends ExtendableGenerics = DefaultGener
|
|
|
1547
1601
|
const topic = this.getClient().channel(event.channel_type || '', event.channel_id || '');
|
|
1548
1602
|
topic.data = event.channel;
|
|
1549
1603
|
topic._initializeState(topicState, 'latest');
|
|
1550
|
-
|
|
1604
|
+
|
|
1605
|
+
if (!channelState.topics) {
|
|
1606
|
+
channelState.topics = [];
|
|
1607
|
+
}
|
|
1608
|
+
if (!channelState.topics.some((t) => t.cid === topic.cid)) {
|
|
1609
|
+
channelState.topics.push(topic);
|
|
1610
|
+
}
|
|
1551
1611
|
break;
|
|
1552
1612
|
case 'channel.topic.closed':
|
|
1553
1613
|
if (channel.data) {
|
package/src/client.ts
CHANGED
|
@@ -205,7 +205,6 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
205
205
|
params.avatar = user.avatar;
|
|
206
206
|
}
|
|
207
207
|
const url = this.userBaseURL + '/get_token/external_auth';
|
|
208
|
-
const query = new URLSearchParams(params).toString();
|
|
209
208
|
const headers: Record<string, string> = {
|
|
210
209
|
'Content-Type': 'application/json',
|
|
211
210
|
};
|
|
@@ -213,21 +212,21 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
213
212
|
const tokenStr = typeof token === 'string' && token.startsWith('Bearer ') ? token : `Bearer ${token}`;
|
|
214
213
|
headers['Authorization'] = tokenStr;
|
|
215
214
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
215
|
+
try {
|
|
216
|
+
const response = await this.axiosInstance.get(url, {
|
|
217
|
+
params,
|
|
218
|
+
headers,
|
|
219
|
+
});
|
|
220
|
+
return response.data;
|
|
221
|
+
} catch (error: any) {
|
|
222
|
+
let errorMsg = 'Failed to fetch external auth token';
|
|
223
|
+
if (error.response && error.response.data) {
|
|
224
|
+
errorMsg = error.response.data.message || JSON.stringify(error.response.data);
|
|
225
|
+
} else if (error.message) {
|
|
226
|
+
errorMsg = error.message;
|
|
227
227
|
}
|
|
228
228
|
throw new Error(errorMsg);
|
|
229
229
|
}
|
|
230
|
-
return await response.json();
|
|
231
230
|
}
|
|
232
231
|
|
|
233
232
|
/**
|
|
@@ -242,7 +241,7 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
242
241
|
connectUser = async (
|
|
243
242
|
user: UserResponse<ErmisChatGenerics>,
|
|
244
243
|
userTokenOrProvider: string | null,
|
|
245
|
-
|
|
244
|
+
external_auth?: boolean, // pass true if you are using external auth
|
|
246
245
|
) => {
|
|
247
246
|
this.logger('info', 'client:connectUser() - started', {
|
|
248
247
|
tags: ['connection', 'client'],
|
|
@@ -251,19 +250,25 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
251
250
|
throw new Error('The "id" field on the user is missing');
|
|
252
251
|
}
|
|
253
252
|
|
|
253
|
+
let connectionUser = user;
|
|
254
|
+
let connectionToken = userTokenOrProvider;
|
|
255
|
+
|
|
254
256
|
// If external auth is enabled, get the token from the server
|
|
255
|
-
if (
|
|
257
|
+
if (external_auth) {
|
|
256
258
|
const external_auth_token = await this.getExternalAuthToken(user, userTokenOrProvider);
|
|
257
259
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
connectionToken = external_auth_token.token;
|
|
261
|
+
connectionUser = {
|
|
262
|
+
...user,
|
|
263
|
+
id: external_auth_token.user_id,
|
|
264
|
+
};
|
|
260
265
|
}
|
|
261
266
|
|
|
262
267
|
/**
|
|
263
268
|
* Calling connectUser multiple times is potentially the result of a bad integration, however,
|
|
264
269
|
* If the user id remains the same we don't throw error
|
|
265
270
|
*/
|
|
266
|
-
if (this.userID ===
|
|
271
|
+
if (this.userID === connectionUser.id && this.setUserPromise) {
|
|
267
272
|
console.warn(
|
|
268
273
|
'Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.',
|
|
269
274
|
);
|
|
@@ -283,11 +288,11 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
283
288
|
}
|
|
284
289
|
|
|
285
290
|
// we generate the client id client side
|
|
286
|
-
this.userID =
|
|
291
|
+
this.userID = connectionUser.id;
|
|
287
292
|
|
|
288
|
-
const setTokenPromise = this._setToken(
|
|
289
|
-
this._setUser(
|
|
290
|
-
this.state.updateUser({ id:
|
|
293
|
+
const setTokenPromise = this._setToken(connectionUser, connectionToken);
|
|
294
|
+
this._setUser(connectionUser);
|
|
295
|
+
this.state.updateUser({ id: connectionUser.id, name: connectionUser?.name || connectionUser.id, avatar: connectionUser?.avatar || '' });
|
|
291
296
|
|
|
292
297
|
const wsPromise = this.openConnection();
|
|
293
298
|
|
|
@@ -609,8 +614,8 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
609
614
|
dispatchEvent = (event: Event<ErmisChatGenerics>) => {
|
|
610
615
|
if (!event.received_at) event.received_at = new Date();
|
|
611
616
|
|
|
612
|
-
// If the event is channel.created, handle it asynchronously
|
|
613
|
-
if (event.type === 'channel.created') {
|
|
617
|
+
// If the event is channel.created or channel.topic.created, handle it asynchronously
|
|
618
|
+
if (event.type === 'channel.created' || event.type === 'channel.topic.created') {
|
|
614
619
|
this._handleChannelCreatedEvent(event).then(() => {
|
|
615
620
|
this._afterDispatchEvent(event);
|
|
616
621
|
});
|
|
@@ -789,6 +794,85 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
789
794
|
}
|
|
790
795
|
}
|
|
791
796
|
|
|
797
|
+
if (event.type === 'message.new' && event.channel_type === 'topic') {
|
|
798
|
+
postListenerCallbacks.push(() => {
|
|
799
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
800
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
801
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
802
|
+
if (parentChannel.state.topics) {
|
|
803
|
+
parentChannel.state.topics.sort((a, b) => {
|
|
804
|
+
const aLatest = a.state?.latestMessages?.[a.state.latestMessages.length - 1]?.created_at;
|
|
805
|
+
const bLatest = b.state?.latestMessages?.[b.state.latestMessages.length - 1]?.created_at;
|
|
806
|
+
const aTime = aLatest ? new Date(aLatest).getTime() : 0;
|
|
807
|
+
const bTime = bLatest ? new Date(bLatest).getTime() : 0;
|
|
808
|
+
return bTime - aTime;
|
|
809
|
+
});
|
|
810
|
+
parentChannel._callChannelListeners({ ...event, type: 'channel.updated', channel: parentChannel.data } as any);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (event.type === 'channel.topic.updated') {
|
|
816
|
+
postListenerCallbacks.push(() => {
|
|
817
|
+
const parentCid = event.parent_cid || event.channel?.parent_cid;
|
|
818
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
819
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
820
|
+
if (parentChannel.state?.topics && event.channel) {
|
|
821
|
+
const topicIndex = parentChannel.state.topics.findIndex((t: any) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
822
|
+
if (topicIndex !== -1) {
|
|
823
|
+
const t = parentChannel.state.topics[topicIndex] as any;
|
|
824
|
+
if (t.data) {
|
|
825
|
+
t.data = { ...t.data, ...event.channel };
|
|
826
|
+
} else if (t.channel) {
|
|
827
|
+
t.channel = { ...t.channel, ...event.channel };
|
|
828
|
+
} else {
|
|
829
|
+
Object.assign(t, event.channel);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
parentChannel._callChannelListeners({ ...event, type: 'channel.updated', channel: parentChannel.data } as any);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
837
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
838
|
+
if (event.channel) {
|
|
839
|
+
topicChannel.data = { ...topicChannel.data, ...event.channel };
|
|
840
|
+
topicChannel._callChannelListeners({ ...event, type: 'channel.updated', channel: topicChannel.data } as any);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (event.type === 'channel.topic.closed' || event.type === 'channel.topic.reopen') {
|
|
847
|
+
postListenerCallbacks.push(() => {
|
|
848
|
+
const isClosed = event.type === 'channel.topic.closed';
|
|
849
|
+
const parentCid = event.parent_cid;
|
|
850
|
+
if (parentCid && this.activeChannels[parentCid]) {
|
|
851
|
+
const parentChannel = this.activeChannels[parentCid];
|
|
852
|
+
if (parentChannel.state?.topics) {
|
|
853
|
+
const topicIndex = parentChannel.state.topics.findIndex((t: any) => t.cid === event.cid || t.channel?.cid === event.cid);
|
|
854
|
+
if (topicIndex !== -1) {
|
|
855
|
+
const t = parentChannel.state.topics[topicIndex] as any;
|
|
856
|
+
if (t.data) t.data.is_closed_topic = isClosed;
|
|
857
|
+
else if (t.channel) t.channel.is_closed_topic = isClosed;
|
|
858
|
+
else t.is_closed_topic = isClosed;
|
|
859
|
+
}
|
|
860
|
+
parentChannel._callChannelListeners({ ...event, type: 'channel.updated', channel: parentChannel.data } as any);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (event.cid && this.activeChannels[event.cid]) {
|
|
865
|
+
const topicChannel = this.activeChannels[event.cid];
|
|
866
|
+
if (topicChannel.data) {
|
|
867
|
+
topicChannel.data.is_closed_topic = isClosed;
|
|
868
|
+
}
|
|
869
|
+
topicChannel._callChannelListeners({ ...event, type: 'channel.updated', channel: topicChannel.data } as any);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
|
|
792
876
|
if (event.type === 'connection.recovered') {
|
|
793
877
|
postListenerCallbacks.push(() => {
|
|
794
878
|
// Auto-resend offline failed messages
|
|
@@ -1086,7 +1170,14 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1086
1170
|
this.projectId = project_id;
|
|
1087
1171
|
}
|
|
1088
1172
|
|
|
1089
|
-
|
|
1173
|
+
/**
|
|
1174
|
+
* Uploads a new avatar image for the current user.
|
|
1175
|
+
* The user's avatar URL is automatically updated in both the client and the local state.
|
|
1176
|
+
*
|
|
1177
|
+
* @param file - The image file to upload.
|
|
1178
|
+
* @returns The response containing the new avatar URL.
|
|
1179
|
+
*/
|
|
1180
|
+
async uploadAvatar(file: File) {
|
|
1090
1181
|
const formData = new FormData();
|
|
1091
1182
|
formData.append('avatar', file);
|
|
1092
1183
|
let response = await this.post<{ avatar: string }>(this.userBaseURL + '/users/upload', formData, {
|
|
@@ -1102,12 +1193,8 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1102
1193
|
|
|
1103
1194
|
return response;
|
|
1104
1195
|
}
|
|
1105
|
-
async updateProfile(
|
|
1106
|
-
let
|
|
1107
|
-
name,
|
|
1108
|
-
about_me,
|
|
1109
|
-
};
|
|
1110
|
-
let response = await this.patch<UserResponse<ErmisChatGenerics>>(this.userBaseURL + '/users/update', body);
|
|
1196
|
+
async updateProfile(updates: Partial<UserResponse<ErmisChatGenerics>>) {
|
|
1197
|
+
let response = await this.patch<UserResponse<ErmisChatGenerics>>(this.userBaseURL + '/users/update', updates);
|
|
1111
1198
|
this.user = response;
|
|
1112
1199
|
this.state.updateUser(response);
|
|
1113
1200
|
return response;
|
|
@@ -1358,6 +1445,63 @@ export class ErmisChat<ErmisChatGenerics extends ExtendableGenerics = DefaultGen
|
|
|
1358
1445
|
return channel;
|
|
1359
1446
|
};
|
|
1360
1447
|
|
|
1448
|
+
/**
|
|
1449
|
+
* Creates a quick channel and immediately registers it on the server.
|
|
1450
|
+
* Quick channels are public group channels that anyone can join without an invitation.
|
|
1451
|
+
* The creator is added as the first member automatically.
|
|
1452
|
+
*
|
|
1453
|
+
* @param name - An optional display name for the channel.
|
|
1454
|
+
* @returns A promise that resolves to the created `Channel` object.
|
|
1455
|
+
*/
|
|
1456
|
+
async createQuickChannel(name?: string): Promise<Channel<ErmisChatGenerics>> {
|
|
1457
|
+
if (!this.userID) {
|
|
1458
|
+
throw Error('Call connectUser before creating a channel');
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
const now = new Date();
|
|
1462
|
+
const formattedDate = new Intl.DateTimeFormat('en-US', {
|
|
1463
|
+
month: 'short', day: '2-digit', year: 'numeric',
|
|
1464
|
+
hour: '2-digit', minute: '2-digit', hour12: false,
|
|
1465
|
+
}).format(now);
|
|
1466
|
+
|
|
1467
|
+
const payload = {
|
|
1468
|
+
name: name || `Quick Channel - ${formattedDate}`,
|
|
1469
|
+
members: [this.userID],
|
|
1470
|
+
public: true,
|
|
1471
|
+
} as unknown as ChannelData<ErmisChatGenerics>;
|
|
1472
|
+
|
|
1473
|
+
const quickChannel = this.channel('meeting', payload);
|
|
1474
|
+
await quickChannel.create();
|
|
1475
|
+
|
|
1476
|
+
return quickChannel;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Joins a quick channel by its ID.
|
|
1481
|
+
* Automatically checks whether the caller is already a member.
|
|
1482
|
+
* If not, it joins the channel and synchronizes state.
|
|
1483
|
+
*
|
|
1484
|
+
* @param channelId - The ID of the quick channel to join.
|
|
1485
|
+
* @returns A promise that resolves to the joined `Channel` object.
|
|
1486
|
+
*/
|
|
1487
|
+
async joinQuickChannel(channelId: string): Promise<Channel<ErmisChatGenerics>> {
|
|
1488
|
+
if (!this.userID) {
|
|
1489
|
+
throw Error('Call connectUser before joining a channel');
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
const quickChannel = this.channel('meeting', channelId);
|
|
1493
|
+
await quickChannel.watch();
|
|
1494
|
+
|
|
1495
|
+
const isMember = quickChannel.state.members && quickChannel.state.members[this.userID];
|
|
1496
|
+
|
|
1497
|
+
if (!isMember) {
|
|
1498
|
+
await quickChannel.acceptInvite('join');
|
|
1499
|
+
await quickChannel.watch();
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
return quickChannel;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1361
1505
|
_normalizeExpiration(timeoutOrExpirationDate?: null | number | string | Date) {
|
|
1362
1506
|
let pinExpires: null | string = null;
|
|
1363
1507
|
if (typeof timeoutOrExpirationDate === 'number') {
|
package/src/types.ts
CHANGED
|
@@ -58,6 +58,7 @@ export type ChannelResponse<ErmisChatGenerics extends ExtendableGenerics = Defau
|
|
|
58
58
|
member_capabilities?: string[];
|
|
59
59
|
is_pinned?: boolean;
|
|
60
60
|
topics_enabled?: boolean;
|
|
61
|
+
parent_cid?: string;
|
|
61
62
|
is_closed_topic?: boolean;
|
|
62
63
|
};
|
|
63
64
|
|
|
@@ -265,6 +266,7 @@ export type Event<ErmisChatGenerics extends ExtendableGenerics = DefaultGenerics
|
|
|
265
266
|
message?: MessageResponse<ErmisChatGenerics>;
|
|
266
267
|
online?: boolean;
|
|
267
268
|
parent_id?: string;
|
|
269
|
+
parent_cid?: string;
|
|
268
270
|
reaction?: ReactionResponse<ErmisChatGenerics>;
|
|
269
271
|
received_at?: string | Date;
|
|
270
272
|
unread_messages?: number;
|
|
@@ -300,6 +302,18 @@ export type ChannelFilters = {
|
|
|
300
302
|
include_parent?: boolean;
|
|
301
303
|
};
|
|
302
304
|
|
|
305
|
+
export type CreateTopicData = {
|
|
306
|
+
name: string;
|
|
307
|
+
image?: string;
|
|
308
|
+
[key: string]: any;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export type EditTopicData = {
|
|
312
|
+
name?: string;
|
|
313
|
+
image?: string;
|
|
314
|
+
description?: string;
|
|
315
|
+
};
|
|
316
|
+
|
|
303
317
|
export type ChannelSort = {
|
|
304
318
|
field: string;
|
|
305
319
|
direction: -1 | 1;
|