@sendbird/uikit-chat-hooks 2.0.3 → 2.1.0
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/lib/commonjs/channel/useGroupChannel.js +33 -0
- package/lib/commonjs/channel/useGroupChannel.js.map +1 -0
- package/lib/commonjs/channel/useGroupChannelMessages/reducer.js +3 -3
- package/lib/commonjs/channel/useGroupChannelMessages/reducer.js.map +1 -1
- package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js +56 -68
- package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js.map +1 -1
- package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js +26 -35
- package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js.map +1 -1
- package/lib/commonjs/common/useAppFeatures.js +4 -4
- package/lib/commonjs/common/useAppFeatures.js.map +1 -1
- package/lib/commonjs/common/useMessageOutgoingStatus.js +1 -0
- package/lib/commonjs/common/useMessageOutgoingStatus.js.map +1 -1
- package/lib/commonjs/common/useUserList.js +25 -7
- package/lib/commonjs/common/useUserList.js.map +1 -1
- package/lib/commonjs/index.js +8 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/channel/useGroupChannel.js +22 -0
- package/lib/module/channel/useGroupChannel.js.map +1 -0
- package/lib/module/channel/useGroupChannelMessages/reducer.js +3 -3
- package/lib/module/channel/useGroupChannelMessages/reducer.js.map +1 -1
- package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js +57 -68
- package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js.map +1 -1
- package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js +26 -34
- package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js.map +1 -1
- package/lib/module/common/useAppFeatures.js +4 -4
- package/lib/module/common/useAppFeatures.js.map +1 -1
- package/lib/module/common/useMessageOutgoingStatus.js +1 -0
- package/lib/module/common/useMessageOutgoingStatus.js.map +1 -1
- package/lib/module/common/useUserList.js +27 -9
- package/lib/module/common/useUserList.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/channel/useGroupChannel.d.ts +6 -0
- package/lib/typescript/src/common/useUserList.d.ts +2 -2
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/types.d.ts +13 -6
- package/package.json +3 -3
- package/src/channel/useGroupChannel.ts +21 -0
- package/src/channel/useGroupChannelMessages/reducer.ts +4 -4
- package/src/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.ts +70 -76
- package/src/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.ts +29 -31
- package/src/common/useAppFeatures.ts +4 -4
- package/src/common/useMessageOutgoingStatus.ts +2 -0
- package/src/common/useUserList.ts +38 -12
- package/src/index.ts +3 -1
- package/src/types.ts +16 -7
|
@@ -2,16 +2,21 @@ import { useCallback, useEffect, useRef } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { MessageCollectionInitPolicy, MessageEventSource, MessageFilter } from '@sendbird/chat/groupChannel';
|
|
4
4
|
import type {
|
|
5
|
-
|
|
5
|
+
SendbirdBaseMessage,
|
|
6
6
|
SendbirdFileMessage,
|
|
7
7
|
SendbirdGroupChannel,
|
|
8
8
|
SendbirdMessageCollection,
|
|
9
9
|
} from '@sendbird/uikit-utils';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Logger,
|
|
12
|
+
confirmAndMarkAsRead,
|
|
13
|
+
isDifferentChannel,
|
|
14
|
+
useForceUpdate,
|
|
15
|
+
useFreshCallback,
|
|
16
|
+
} from '@sendbird/uikit-utils';
|
|
11
17
|
|
|
12
18
|
import { useChannelHandler } from '../../handler/useChannelHandler';
|
|
13
19
|
import type { UseGroupChannelMessages, UseGroupChannelMessagesOptions } from '../../types';
|
|
14
|
-
import { useActiveGroupChannel } from '../useActiveGroupChannel';
|
|
15
20
|
import { useGroupChannelMessagesReducer } from './reducer';
|
|
16
21
|
|
|
17
22
|
const createMessageCollection = (
|
|
@@ -26,11 +31,8 @@ const createMessageCollection = (
|
|
|
26
31
|
const HOOK_NAME = 'useGroupChannelMessagesWithCollection';
|
|
27
32
|
|
|
28
33
|
export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (sdk, channel, userId, options) => {
|
|
29
|
-
const collectionRef = useRef<SendbirdMessageCollection>();
|
|
30
|
-
|
|
31
|
-
// NOTE: We cannot determine the channel object of Sendbird SDK is stale or not, so force update af
|
|
32
|
-
const { activeChannel, setActiveChannel } = useActiveGroupChannel(sdk, channel);
|
|
33
34
|
const forceUpdate = useForceUpdate();
|
|
35
|
+
const collectionRef = useRef<SendbirdMessageCollection>();
|
|
34
36
|
|
|
35
37
|
const {
|
|
36
38
|
loading,
|
|
@@ -48,24 +50,18 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
48
50
|
|
|
49
51
|
const channelMarkAs = async () => {
|
|
50
52
|
try {
|
|
51
|
-
await confirmAndMarkAsRead([
|
|
53
|
+
await confirmAndMarkAsRead([channel]);
|
|
52
54
|
} catch (e) {
|
|
53
55
|
Logger.warn(`[${HOOK_NAME}/channelMarkAs/Read]`, e);
|
|
54
56
|
}
|
|
55
57
|
};
|
|
56
58
|
|
|
57
|
-
const updateChannel = (channel: SendbirdChannel) => {
|
|
58
|
-
if (channel.isGroupChannel() && !isDifferentChannel(channel, activeChannel)) {
|
|
59
|
-
forceUpdate();
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
59
|
const init = useCallback(
|
|
64
60
|
async (uid?: string, callback?: () => void) => {
|
|
65
61
|
if (collectionRef.current) collectionRef.current?.dispose();
|
|
66
62
|
|
|
67
63
|
if (uid) {
|
|
68
|
-
collectionRef.current = createMessageCollection(
|
|
64
|
+
collectionRef.current = createMessageCollection(channel, options?.collectionCreator);
|
|
69
65
|
updateNextMessages([], true, sdk.currentUser.userId);
|
|
70
66
|
channelMarkAs();
|
|
71
67
|
|
|
@@ -79,22 +75,25 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
79
75
|
break;
|
|
80
76
|
}
|
|
81
77
|
updateNextMessages(messages, false, sdk.currentUser.userId);
|
|
82
|
-
updateChannel(channel);
|
|
83
78
|
},
|
|
84
|
-
onMessagesUpdated: (_,
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
onMessagesUpdated: (_, __, messages) => {
|
|
80
|
+
// NOTE: v4 MESSAGE_RECEIVED is called twice from onMessagesAdded and onMessagesUpdated when receiving new message.
|
|
81
|
+
// This is not intended behavior but not bugs.
|
|
82
|
+
if (_.source !== MessageEventSource.EVENT_MESSAGE_RECEIVED) {
|
|
83
|
+
updateMessages(messages, false, sdk.currentUser.userId);
|
|
84
|
+
}
|
|
87
85
|
},
|
|
88
|
-
onMessagesDeleted: (_,
|
|
86
|
+
onMessagesDeleted: (_, __, messageIds) => {
|
|
89
87
|
deleteMessages(messageIds, []);
|
|
90
88
|
deleteNextMessages(messageIds, []);
|
|
91
|
-
updateChannel(channel);
|
|
92
89
|
},
|
|
93
90
|
onChannelDeleted: () => {
|
|
94
91
|
options?.onChannelDeleted?.();
|
|
95
92
|
},
|
|
96
|
-
onChannelUpdated: (_,
|
|
97
|
-
|
|
93
|
+
onChannelUpdated: (_, eventChannel) => {
|
|
94
|
+
if (eventChannel.isGroupChannel() && !isDifferentChannel(eventChannel, channel)) {
|
|
95
|
+
forceUpdate();
|
|
96
|
+
}
|
|
98
97
|
},
|
|
99
98
|
onHugeGapDetected: () => {
|
|
100
99
|
init(uid);
|
|
@@ -107,16 +106,18 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
107
106
|
if (err) sdk.isCacheEnabled && Logger.error(`[${HOOK_NAME}/onCacheResult]`, err);
|
|
108
107
|
else {
|
|
109
108
|
Logger.debug(`[${HOOK_NAME}/onCacheResult]`, 'message length:', messages.length);
|
|
109
|
+
|
|
110
110
|
updateMessages(messages, true, sdk.currentUser.userId);
|
|
111
111
|
updateMessages(collectionRef.current?.pendingMessages ?? [], false, sdk.currentUser.userId);
|
|
112
112
|
updateMessages(collectionRef.current?.failedMessages ?? [], false, sdk.currentUser.userId);
|
|
113
|
-
if (messages?.length) callback?.();
|
|
114
113
|
}
|
|
114
|
+
callback?.();
|
|
115
115
|
})
|
|
116
116
|
.onApiResult((err, messages) => {
|
|
117
117
|
if (err) Logger.warn(`[${HOOK_NAME}/onApiResult]`, err);
|
|
118
118
|
else {
|
|
119
119
|
Logger.debug(`[${HOOK_NAME}/onApiResult]`, 'message length:', messages.length);
|
|
120
|
+
|
|
120
121
|
updateMessages(messages, true, sdk.currentUser.userId);
|
|
121
122
|
if (sdk.isCacheEnabled) {
|
|
122
123
|
updateMessages(collectionRef.current?.pendingMessages ?? [], false, sdk.currentUser.userId);
|
|
@@ -127,16 +128,15 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
127
128
|
});
|
|
128
129
|
}
|
|
129
130
|
},
|
|
130
|
-
[sdk,
|
|
131
|
+
[sdk, channel.url, options?.collectionCreator],
|
|
131
132
|
);
|
|
132
133
|
|
|
133
134
|
useChannelHandler(sdk, HOOK_NAME, {
|
|
134
135
|
onUserBanned(channel, bannedUser) {
|
|
135
|
-
if (channel.isGroupChannel() && !isDifferentChannel(channel,
|
|
136
|
+
if (channel.isGroupChannel() && !isDifferentChannel(channel, channel)) {
|
|
136
137
|
if (bannedUser.userId === sdk.currentUser.userId) {
|
|
137
138
|
options?.onChannelDeleted?.();
|
|
138
139
|
} else {
|
|
139
|
-
setActiveChannel(channel);
|
|
140
140
|
forceUpdate();
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -144,7 +144,7 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
useEffect(() => {
|
|
147
|
-
// NOTE: Cache read is heavy
|
|
147
|
+
// NOTE: Cache read is heavy task, and it prevents smooth ui transition
|
|
148
148
|
setTimeout(async () => {
|
|
149
149
|
updateLoading(true);
|
|
150
150
|
init(userId, () => updateLoading(false));
|
|
@@ -157,41 +157,44 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
157
157
|
};
|
|
158
158
|
}, []);
|
|
159
159
|
|
|
160
|
-
const refresh: ReturnType<UseGroupChannelMessages>['refresh'] =
|
|
160
|
+
const refresh: ReturnType<UseGroupChannelMessages>['refresh'] = useFreshCallback(async () => {
|
|
161
161
|
updateRefreshing(true);
|
|
162
162
|
init(userId, () => updateRefreshing(false));
|
|
163
|
-
}
|
|
163
|
+
});
|
|
164
164
|
|
|
165
|
-
const prev: ReturnType<UseGroupChannelMessages>['prev'] =
|
|
165
|
+
const prev: ReturnType<UseGroupChannelMessages>['prev'] = useFreshCallback(async () => {
|
|
166
166
|
if (collectionRef.current && collectionRef.current?.hasPrevious) {
|
|
167
167
|
try {
|
|
168
168
|
const list = await collectionRef.current?.loadPrevious();
|
|
169
169
|
updateMessages(list, false, sdk.currentUser.userId);
|
|
170
170
|
} catch {}
|
|
171
171
|
}
|
|
172
|
-
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const next: ReturnType<UseGroupChannelMessages>['next'] = useFreshCallback(async () => {
|
|
175
|
+
const messageCandidates: SendbirdBaseMessage[] = [];
|
|
173
176
|
|
|
174
|
-
const next: ReturnType<UseGroupChannelMessages>['next'] = useCallback(async () => {
|
|
175
|
-
const list = [];
|
|
176
177
|
if (collectionRef.current && collectionRef.current?.hasNext) {
|
|
177
178
|
try {
|
|
178
179
|
const fetchedList = await collectionRef.current?.loadNext();
|
|
179
|
-
|
|
180
|
+
messageCandidates.push(...fetchedList);
|
|
180
181
|
} catch {}
|
|
181
182
|
}
|
|
183
|
+
|
|
182
184
|
if (nextMessages.length > 0) {
|
|
183
|
-
|
|
185
|
+
messageCandidates.push(...nextMessages);
|
|
184
186
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
|
|
188
|
+
if (messageCandidates.length > 0) {
|
|
189
|
+
updateMessages(messageCandidates, false, sdk.currentUser.userId);
|
|
187
190
|
updateNextMessages([], true, sdk.currentUser.userId);
|
|
188
191
|
}
|
|
189
|
-
}
|
|
192
|
+
});
|
|
190
193
|
|
|
191
|
-
const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] =
|
|
194
|
+
const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] = useFreshCallback(
|
|
192
195
|
(params, onPending) => {
|
|
193
196
|
return new Promise((resolve, reject) => {
|
|
194
|
-
|
|
197
|
+
channel
|
|
195
198
|
.sendUserMessage(params)
|
|
196
199
|
.onPending((pendingMessage) => {
|
|
197
200
|
if (pendingMessage.isUserMessage()) {
|
|
@@ -205,17 +208,17 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
205
208
|
resolve(sentMessage);
|
|
206
209
|
}
|
|
207
210
|
})
|
|
208
|
-
.onFailed((err) => {
|
|
211
|
+
.onFailed((err, _failedMessage) => {
|
|
212
|
+
// updateMessages([failedMessage], false, sdk.currentUser.userId);
|
|
209
213
|
reject(err);
|
|
210
214
|
});
|
|
211
215
|
});
|
|
212
216
|
},
|
|
213
|
-
[activeChannel],
|
|
214
217
|
);
|
|
215
|
-
const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] =
|
|
218
|
+
const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] = useFreshCallback(
|
|
216
219
|
(params, onPending) => {
|
|
217
220
|
return new Promise((resolve, reject) => {
|
|
218
|
-
|
|
221
|
+
channel
|
|
219
222
|
.sendFileMessage(params)
|
|
220
223
|
.onPending((pendingMessage) => {
|
|
221
224
|
if (pendingMessage.isFileMessage()) {
|
|
@@ -229,58 +232,50 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
229
232
|
resolve(sentMessage as SendbirdFileMessage);
|
|
230
233
|
}
|
|
231
234
|
})
|
|
232
|
-
.onFailed((err) => {
|
|
235
|
+
.onFailed((err, _failedMessage) => {
|
|
236
|
+
// updateMessages([failedMessage], false, sdk.currentUser.userId);
|
|
233
237
|
reject(err);
|
|
234
238
|
});
|
|
235
239
|
});
|
|
236
240
|
},
|
|
237
|
-
[activeChannel],
|
|
238
241
|
);
|
|
239
|
-
const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] =
|
|
242
|
+
const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] = useFreshCallback(
|
|
240
243
|
async (messageId, params) => {
|
|
241
|
-
const updatedMessage = await
|
|
244
|
+
const updatedMessage = await channel.updateUserMessage(messageId, params);
|
|
242
245
|
updateMessages([updatedMessage], false, sdk.currentUser.userId);
|
|
243
246
|
return updatedMessage;
|
|
244
247
|
},
|
|
245
|
-
[activeChannel],
|
|
246
248
|
);
|
|
247
|
-
const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] =
|
|
249
|
+
const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] = useFreshCallback(
|
|
248
250
|
async (messageId, params) => {
|
|
249
|
-
const updatedMessage = await
|
|
251
|
+
const updatedMessage = await channel.updateFileMessage(messageId, params);
|
|
250
252
|
updateMessages([updatedMessage], false, sdk.currentUser.userId);
|
|
251
253
|
return updatedMessage;
|
|
252
254
|
},
|
|
253
|
-
[activeChannel],
|
|
254
255
|
);
|
|
255
|
-
const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] =
|
|
256
|
+
const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] = useFreshCallback(
|
|
256
257
|
async (failedMessage) => {
|
|
257
|
-
const
|
|
258
|
-
if (failedMessage.isUserMessage()) return
|
|
259
|
-
|
|
260
|
-
// @ts-ignore
|
|
261
|
-
if (failedMessage.isFileMessage()) return activeChannel.resendFileMessage(failedMessage);
|
|
258
|
+
const resentMessage = await (() => {
|
|
259
|
+
if (failedMessage.isUserMessage()) return channel.resendUserMessage(failedMessage);
|
|
260
|
+
if (failedMessage.isFileMessage()) return channel.resendFileMessage(failedMessage);
|
|
262
261
|
return null;
|
|
263
262
|
})();
|
|
264
263
|
|
|
265
|
-
if (
|
|
264
|
+
if (resentMessage) updateMessages([resentMessage], false, sdk.currentUser.userId);
|
|
266
265
|
},
|
|
267
|
-
[activeChannel],
|
|
268
266
|
);
|
|
269
|
-
const deleteMessage: ReturnType<UseGroupChannelMessages>['deleteMessage'] =
|
|
270
|
-
|
|
271
|
-
if (message.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
deleteMessages([message.messageId], [message.reqId]);
|
|
279
|
-
}
|
|
267
|
+
const deleteMessage: ReturnType<UseGroupChannelMessages>['deleteMessage'] = useFreshCallback(async (message) => {
|
|
268
|
+
if (message.sendingStatus === 'succeeded') {
|
|
269
|
+
if (message.isUserMessage()) await channel.deleteMessage(message);
|
|
270
|
+
if (message.isFileMessage()) await channel.deleteMessage(message);
|
|
271
|
+
} else {
|
|
272
|
+
try {
|
|
273
|
+
await collectionRef.current?.removeFailedMessage(message.reqId);
|
|
274
|
+
} finally {
|
|
275
|
+
deleteMessages([message.messageId], [message.reqId]);
|
|
280
276
|
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
284
279
|
|
|
285
280
|
return {
|
|
286
281
|
loading,
|
|
@@ -297,6 +292,5 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
|
|
|
297
292
|
updateFileMessage,
|
|
298
293
|
resendMessage,
|
|
299
294
|
deleteMessage,
|
|
300
|
-
activeChannel,
|
|
301
295
|
};
|
|
302
296
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { useCallback, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
SendbirdBaseChannel,
|
|
5
|
+
SendbirdGroupChannel,
|
|
6
|
+
SendbirdPreviousMessageListQuery,
|
|
7
|
+
} from '@sendbird/uikit-utils';
|
|
4
8
|
import {
|
|
5
9
|
Logger,
|
|
6
10
|
confirmAndMarkAsRead,
|
|
@@ -8,11 +12,9 @@ import {
|
|
|
8
12
|
useAsyncEffect,
|
|
9
13
|
useForceUpdate,
|
|
10
14
|
} from '@sendbird/uikit-utils';
|
|
11
|
-
import type { SendbirdPreviousMessageListQuery } from '@sendbird/uikit-utils';
|
|
12
15
|
|
|
13
16
|
import { useChannelHandler } from '../../handler/useChannelHandler';
|
|
14
17
|
import type { UseGroupChannelMessages, UseGroupChannelMessagesOptions } from '../../types';
|
|
15
|
-
import { useActiveGroupChannel } from '../useActiveGroupChannel';
|
|
16
18
|
import { useGroupChannelMessagesReducer } from './reducer';
|
|
17
19
|
|
|
18
20
|
const createMessageQuery = (
|
|
@@ -30,8 +32,6 @@ const HOOK_NAME = 'useGroupChannelMessagesWithQuery';
|
|
|
30
32
|
export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, channel, userId, options) => {
|
|
31
33
|
const queryRef = useRef<SendbirdPreviousMessageListQuery>();
|
|
32
34
|
|
|
33
|
-
// NOTE: We cannot determine the channel object of Sendbird SDK is stale or not, so force update after setActiveChannel
|
|
34
|
-
const { activeChannel, setActiveChannel } = useActiveGroupChannel(sdk, channel);
|
|
35
35
|
const forceUpdate = useForceUpdate();
|
|
36
36
|
|
|
37
37
|
const {
|
|
@@ -59,7 +59,7 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
59
59
|
const init = useCallback(
|
|
60
60
|
async (uid?: string) => {
|
|
61
61
|
if (uid) {
|
|
62
|
-
queryRef.current = createMessageQuery(
|
|
62
|
+
queryRef.current = createMessageQuery(channel, options?.queryCreator);
|
|
63
63
|
channelMarkAs().catch();
|
|
64
64
|
if (queryRef.current?.hasNext) {
|
|
65
65
|
const list = await queryRef.current?.load();
|
|
@@ -68,12 +68,11 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
68
68
|
updateNextMessages([], true, sdk.currentUser.userId);
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
[sdk,
|
|
71
|
+
[sdk, channel.url, options?.queryCreator],
|
|
72
72
|
);
|
|
73
73
|
|
|
74
74
|
const channelUpdater = (channel: SendbirdBaseChannel) => {
|
|
75
|
-
if (channel.isGroupChannel() && !isDifferentChannel(channel,
|
|
76
|
-
setActiveChannel(channel);
|
|
75
|
+
if (channel.isGroupChannel() && !isDifferentChannel(channel, channel)) {
|
|
77
76
|
forceUpdate();
|
|
78
77
|
}
|
|
79
78
|
};
|
|
@@ -81,16 +80,16 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
81
80
|
useChannelHandler(sdk, HOOK_NAME, {
|
|
82
81
|
// Messages
|
|
83
82
|
onMessageReceived(eventChannel, message) {
|
|
84
|
-
if (isDifferentChannel(
|
|
83
|
+
if (isDifferentChannel(channel, eventChannel)) return;
|
|
85
84
|
channelMarkAs();
|
|
86
85
|
updateNextMessages([message], false, sdk.currentUser.userId);
|
|
87
86
|
},
|
|
88
87
|
onMessageUpdated(eventChannel, message) {
|
|
89
|
-
if (isDifferentChannel(
|
|
88
|
+
if (isDifferentChannel(channel, eventChannel)) return;
|
|
90
89
|
updateMessages([message], false, sdk.currentUser.userId);
|
|
91
90
|
},
|
|
92
91
|
onMessageDeleted(eventChannel, messageId) {
|
|
93
|
-
if (isDifferentChannel(
|
|
92
|
+
if (isDifferentChannel(channel, eventChannel)) return;
|
|
94
93
|
deleteMessages([messageId], []);
|
|
95
94
|
deleteNextMessages([messageId], []);
|
|
96
95
|
},
|
|
@@ -100,11 +99,11 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
100
99
|
onChannelUnfrozen: channelUpdater,
|
|
101
100
|
onChannelHidden: channelUpdater,
|
|
102
101
|
onChannelMemberCountChanged(channels) {
|
|
103
|
-
const
|
|
104
|
-
if (
|
|
102
|
+
const foundChannel = channels.find((c) => !isDifferentChannel(c, channel));
|
|
103
|
+
if (foundChannel) channelUpdater(foundChannel);
|
|
105
104
|
},
|
|
106
105
|
onChannelDeleted(channelUrl: string) {
|
|
107
|
-
if (
|
|
106
|
+
if (channel.url === channelUrl) options?.onChannelDeleted?.();
|
|
108
107
|
},
|
|
109
108
|
// Users
|
|
110
109
|
onOperatorUpdated: channelUpdater,
|
|
@@ -116,7 +115,7 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
116
115
|
onUserMuted: channelUpdater,
|
|
117
116
|
onUserUnmuted: channelUpdater,
|
|
118
117
|
onUserBanned(eventChannel, bannedUser) {
|
|
119
|
-
if (isDifferentChannel(
|
|
118
|
+
if (isDifferentChannel(channel, eventChannel)) return;
|
|
120
119
|
|
|
121
120
|
if (bannedUser.userId === sdk.currentUser.userId) {
|
|
122
121
|
options?.onChannelDeleted?.();
|
|
@@ -155,7 +154,7 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
155
154
|
const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] = useCallback(
|
|
156
155
|
(params, onPending) => {
|
|
157
156
|
return new Promise((resolve, reject) => {
|
|
158
|
-
|
|
157
|
+
channel
|
|
159
158
|
.sendUserMessage(params)
|
|
160
159
|
.onPending((pendingMessage) => {
|
|
161
160
|
if (pendingMessage.isUserMessage()) {
|
|
@@ -174,12 +173,12 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
174
173
|
});
|
|
175
174
|
});
|
|
176
175
|
},
|
|
177
|
-
[
|
|
176
|
+
[channel],
|
|
178
177
|
);
|
|
179
178
|
const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] = useCallback(
|
|
180
179
|
(params, onPending) => {
|
|
181
180
|
return new Promise((resolve, reject) => {
|
|
182
|
-
|
|
181
|
+
channel
|
|
183
182
|
.sendFileMessage(params)
|
|
184
183
|
.onPending((pendingMessage) => {
|
|
185
184
|
if (pendingMessage.isFileMessage()) {
|
|
@@ -198,48 +197,48 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
198
197
|
});
|
|
199
198
|
});
|
|
200
199
|
},
|
|
201
|
-
[
|
|
200
|
+
[channel],
|
|
202
201
|
);
|
|
203
202
|
const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] = useCallback(
|
|
204
203
|
async (messageId, params) => {
|
|
205
|
-
const updatedMessage = await
|
|
204
|
+
const updatedMessage = await channel.updateUserMessage(messageId, params);
|
|
206
205
|
updateMessages([updatedMessage], false, sdk.currentUser.userId);
|
|
207
206
|
return updatedMessage;
|
|
208
207
|
},
|
|
209
|
-
[
|
|
208
|
+
[channel],
|
|
210
209
|
);
|
|
211
210
|
const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] = useCallback(
|
|
212
211
|
async (messageId, params) => {
|
|
213
|
-
const updatedMessage = await
|
|
212
|
+
const updatedMessage = await channel.updateFileMessage(messageId, params);
|
|
214
213
|
updateMessages([updatedMessage], false, sdk.currentUser.userId);
|
|
215
214
|
return updatedMessage;
|
|
216
215
|
},
|
|
217
|
-
[
|
|
216
|
+
[channel],
|
|
218
217
|
);
|
|
219
218
|
const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] = useCallback(
|
|
220
219
|
async (failedMessage) => {
|
|
221
220
|
const message = await (() => {
|
|
222
|
-
if (failedMessage.isUserMessage()) return
|
|
221
|
+
if (failedMessage.isUserMessage()) return channel.resendUserMessage(failedMessage);
|
|
223
222
|
// FIXME: v4 bugs
|
|
224
223
|
// @ts-ignore
|
|
225
|
-
if (failedMessage.isFileMessage()) return
|
|
224
|
+
if (failedMessage.isFileMessage()) return channel.resendFileMessage(failedMessage);
|
|
226
225
|
return null;
|
|
227
226
|
})();
|
|
228
227
|
|
|
229
228
|
if (message) updateNextMessages([message], false, sdk.currentUser.userId);
|
|
230
229
|
},
|
|
231
|
-
[
|
|
230
|
+
[channel],
|
|
232
231
|
);
|
|
233
232
|
const deleteMessage: ReturnType<UseGroupChannelMessages>['deleteMessage'] = useCallback(
|
|
234
233
|
async (message) => {
|
|
235
234
|
if (message.sendingStatus === 'succeeded') {
|
|
236
|
-
if (message.isUserMessage()) await
|
|
237
|
-
if (message.isFileMessage()) await
|
|
235
|
+
if (message.isUserMessage()) await channel.deleteMessage(message);
|
|
236
|
+
if (message.isFileMessage()) await channel.deleteMessage(message);
|
|
238
237
|
} else {
|
|
239
238
|
deleteMessages([message.messageId], [message.reqId]);
|
|
240
239
|
}
|
|
241
240
|
},
|
|
242
|
-
[
|
|
241
|
+
[channel],
|
|
243
242
|
);
|
|
244
243
|
|
|
245
244
|
return {
|
|
@@ -257,6 +256,5 @@ export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, c
|
|
|
257
256
|
updateFileMessage,
|
|
258
257
|
resendMessage,
|
|
259
258
|
deleteMessage,
|
|
260
|
-
activeChannel,
|
|
261
259
|
};
|
|
262
260
|
};
|
|
@@ -3,9 +3,9 @@ import { ApplicationAttributes, PremiumFeatures, SendbirdChatSDK } from '@sendbi
|
|
|
3
3
|
export const useAppFeatures = (sdk: SendbirdChatSDK) => {
|
|
4
4
|
const { premiumFeatureList = [], applicationAttributes = [] } = sdk.appInfo ?? {};
|
|
5
5
|
return {
|
|
6
|
-
deliveryReceiptEnabled:
|
|
7
|
-
broadcastChannelEnabled:
|
|
8
|
-
superGroupChannelEnabled:
|
|
9
|
-
reactionEnabled:
|
|
6
|
+
deliveryReceiptEnabled: premiumFeatureList.includes(PremiumFeatures.delivery_receipt),
|
|
7
|
+
broadcastChannelEnabled: applicationAttributes.includes(ApplicationAttributes.allow_broadcast_channel),
|
|
8
|
+
superGroupChannelEnabled: applicationAttributes.includes(ApplicationAttributes.allow_super_group_channel),
|
|
9
|
+
reactionEnabled: applicationAttributes.includes(ApplicationAttributes.reactions),
|
|
10
10
|
};
|
|
11
11
|
};
|
|
@@ -40,6 +40,8 @@ export const useMessageOutgoingStatus = (
|
|
|
40
40
|
if (message.sendingStatus === 'failed') return 'FAILED';
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
if (channel.isBroadcast || channel.isSuper) return 'NONE';
|
|
44
|
+
|
|
43
45
|
if (channel.getUnreadMemberCount(message) === 0) return 'READ';
|
|
44
46
|
|
|
45
47
|
if (features.deliveryReceiptEnabled) {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { Optional, SendbirdChatSDK, SendbirdUser } from '@sendbird/uikit-utils';
|
|
4
|
-
import { Logger, SBErrorCode, SBErrorMessage, useAsyncEffect } from '@sendbird/uikit-utils';
|
|
4
|
+
import { Logger, SBErrorCode, SBErrorMessage, useAsyncEffect, useFreshCallback } from '@sendbird/uikit-utils';
|
|
5
5
|
|
|
6
|
-
import type { CustomQueryInterface, UseUserListOptions, UseUserListReturn } from '../types';
|
|
6
|
+
import type { CustomQueryInterface, UseUserListOptions, UseUserListReturn, UserStruct } from '../types';
|
|
7
7
|
|
|
8
|
-
const createUserQuery = <User
|
|
8
|
+
const createUserQuery = <User extends UserStruct>(
|
|
9
|
+
sdk: SendbirdChatSDK,
|
|
10
|
+
queryCreator?: UseUserListOptions<User>['queryCreator'],
|
|
11
|
+
) => {
|
|
9
12
|
if (queryCreator) return queryCreator();
|
|
10
13
|
// In order to use the API, the option must be turned on in the dashboard.
|
|
11
14
|
return sdk.createApplicationUserListQuery() as unknown as CustomQueryInterface<User>;
|
|
@@ -34,7 +37,11 @@ const createUserQuery = <User>(sdk: SendbirdChatSDK, queryCreator?: UseUserListO
|
|
|
34
37
|
* */
|
|
35
38
|
export const useUserList = <
|
|
36
39
|
Options extends UseUserListOptions<QueriedUser>,
|
|
37
|
-
QueriedUser = Options['queryCreator'] extends Optional<
|
|
40
|
+
QueriedUser extends UserStruct = Options['queryCreator'] extends Optional<
|
|
41
|
+
() => CustomQueryInterface<infer User extends UserStruct>
|
|
42
|
+
>
|
|
43
|
+
? User
|
|
44
|
+
: SendbirdUser,
|
|
38
45
|
>(
|
|
39
46
|
sdk: SendbirdChatSDK,
|
|
40
47
|
options?: Options,
|
|
@@ -51,12 +58,29 @@ export const useUserList = <
|
|
|
51
58
|
return users;
|
|
52
59
|
}, [users, options?.sortComparator]);
|
|
53
60
|
|
|
61
|
+
const upsertUser = useFreshCallback((user: QueriedUser) => {
|
|
62
|
+
setUsers(([...draft]) => {
|
|
63
|
+
const userIdx = draft.findIndex((it) => it.userId === user.userId);
|
|
64
|
+
if (userIdx > -1) draft[userIdx] = user;
|
|
65
|
+
else draft.push(user);
|
|
66
|
+
return draft;
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const deleteUser = useFreshCallback((userId: QueriedUser['userId']) => {
|
|
71
|
+
setUsers(([...draft]) => {
|
|
72
|
+
const userIdx = draft.findIndex((it) => it.userId === userId);
|
|
73
|
+
if (userIdx > -1) draft.splice(userIdx, 1);
|
|
74
|
+
return draft;
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
54
78
|
const updateUsers = (users: QueriedUser[], clearPrev: boolean) => {
|
|
55
79
|
if (clearPrev) setUsers(users);
|
|
56
80
|
else setUsers((prev) => prev.concat(users));
|
|
57
81
|
};
|
|
58
82
|
|
|
59
|
-
const init =
|
|
83
|
+
const init = useFreshCallback(async () => {
|
|
60
84
|
query.current = createUserQuery<QueriedUser>(sdk, options?.queryCreator);
|
|
61
85
|
if (query.current?.hasNext) {
|
|
62
86
|
const users = await query.current?.next().catch((err) => {
|
|
@@ -66,7 +90,7 @@ export const useUserList = <
|
|
|
66
90
|
});
|
|
67
91
|
updateUsers(users, true);
|
|
68
92
|
}
|
|
69
|
-
}
|
|
93
|
+
});
|
|
70
94
|
|
|
71
95
|
useAsyncEffect(async () => {
|
|
72
96
|
setLoading(true);
|
|
@@ -78,9 +102,9 @@ export const useUserList = <
|
|
|
78
102
|
} finally {
|
|
79
103
|
setLoading(false);
|
|
80
104
|
}
|
|
81
|
-
}, [
|
|
105
|
+
}, []);
|
|
82
106
|
|
|
83
|
-
const refresh =
|
|
107
|
+
const refresh = useFreshCallback(async () => {
|
|
84
108
|
setRefreshing(true);
|
|
85
109
|
setError(null);
|
|
86
110
|
try {
|
|
@@ -90,9 +114,9 @@ export const useUserList = <
|
|
|
90
114
|
} finally {
|
|
91
115
|
setRefreshing(false);
|
|
92
116
|
}
|
|
93
|
-
}
|
|
117
|
+
});
|
|
94
118
|
|
|
95
|
-
const next =
|
|
119
|
+
const next = useFreshCallback(async () => {
|
|
96
120
|
if (query.current && query.current?.hasNext) {
|
|
97
121
|
const nextUsers = await query.current.next().catch((err) => {
|
|
98
122
|
Logger.error(error);
|
|
@@ -101,12 +125,14 @@ export const useUserList = <
|
|
|
101
125
|
});
|
|
102
126
|
updateUsers(nextUsers, false);
|
|
103
127
|
}
|
|
104
|
-
}
|
|
128
|
+
});
|
|
105
129
|
|
|
106
130
|
return {
|
|
107
131
|
loading,
|
|
108
132
|
error,
|
|
109
133
|
users: sortedUsers,
|
|
134
|
+
upsertUser,
|
|
135
|
+
deleteUser,
|
|
110
136
|
next,
|
|
111
137
|
refreshing,
|
|
112
138
|
refresh,
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { useGroupChannelList } from './channel/useGroupChannelList';
|
|
2
2
|
export { useGroupChannelMessages } from './channel/useGroupChannelMessages';
|
|
3
3
|
export { useActiveGroupChannel } from './channel/useActiveGroupChannel';
|
|
4
|
+
export { useGroupChannel } from './channel/useGroupChannel';
|
|
4
5
|
|
|
5
6
|
export { useAppFeatures } from './common/useAppFeatures';
|
|
6
7
|
export { useMessageOutgoingStatus } from './common/useMessageOutgoingStatus';
|
|
@@ -19,8 +20,9 @@ export type {
|
|
|
19
20
|
UseGroupChannelList,
|
|
20
21
|
UseGroupChannelListOptions,
|
|
21
22
|
UseGroupChannelMessages,
|
|
22
|
-
UseUserListOptions,
|
|
23
23
|
UseGroupChannelMessagesOptions,
|
|
24
|
+
UserStruct,
|
|
25
|
+
UseUserListOptions,
|
|
24
26
|
UseUserListReturn,
|
|
25
27
|
CustomQueryInterface,
|
|
26
28
|
CustomBidirectionalQueryInterface,
|