@sendbird/uikit-chat-hooks 2.0.3 → 2.2.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.
Files changed (63) hide show
  1. package/lib/commonjs/channel/useGroupChannel.js +33 -0
  2. package/lib/commonjs/channel/useGroupChannel.js.map +1 -0
  3. package/lib/commonjs/channel/useGroupChannelMessages/reducer.js +3 -3
  4. package/lib/commonjs/channel/useGroupChannelMessages/reducer.js.map +1 -1
  5. package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js +75 -79
  6. package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js.map +1 -1
  7. package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js +30 -41
  8. package/lib/commonjs/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js.map +1 -1
  9. package/lib/commonjs/common/useAppFeatures.js +4 -4
  10. package/lib/commonjs/common/useAppFeatures.js.map +1 -1
  11. package/lib/commonjs/common/useMessageOutgoingStatus.js +1 -0
  12. package/lib/commonjs/common/useMessageOutgoingStatus.js.map +1 -1
  13. package/lib/commonjs/common/useTotalUnreadChannelCount.js +1 -1
  14. package/lib/commonjs/common/useTotalUnreadChannelCount.js.map +1 -1
  15. package/lib/commonjs/common/useTotalUnreadMessageCount.js +1 -1
  16. package/lib/commonjs/common/useTotalUnreadMessageCount.js.map +1 -1
  17. package/lib/commonjs/common/useUserList.js +33 -15
  18. package/lib/commonjs/common/useUserList.js.map +1 -1
  19. package/lib/commonjs/index.js +8 -0
  20. package/lib/commonjs/index.js.map +1 -1
  21. package/lib/commonjs/types.js.map +1 -1
  22. package/lib/module/channel/useGroupChannel.js +22 -0
  23. package/lib/module/channel/useGroupChannel.js.map +1 -0
  24. package/lib/module/channel/useGroupChannelMessages/reducer.js +3 -3
  25. package/lib/module/channel/useGroupChannelMessages/reducer.js.map +1 -1
  26. package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js +76 -79
  27. package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.js.map +1 -1
  28. package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js +30 -40
  29. package/lib/module/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.js.map +1 -1
  30. package/lib/module/common/useAppFeatures.js +4 -4
  31. package/lib/module/common/useAppFeatures.js.map +1 -1
  32. package/lib/module/common/useMessageOutgoingStatus.js +1 -0
  33. package/lib/module/common/useMessageOutgoingStatus.js.map +1 -1
  34. package/lib/module/common/useTotalUnreadChannelCount.js +2 -2
  35. package/lib/module/common/useTotalUnreadChannelCount.js.map +1 -1
  36. package/lib/module/common/useTotalUnreadMessageCount.js +2 -2
  37. package/lib/module/common/useTotalUnreadMessageCount.js.map +1 -1
  38. package/lib/module/common/useUserList.js +35 -17
  39. package/lib/module/common/useUserList.js.map +1 -1
  40. package/lib/module/index.js +1 -0
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/types.js.map +1 -1
  43. package/lib/typescript/src/channel/useGroupChannel.d.ts +6 -0
  44. package/lib/typescript/src/channel/useGroupChannelList/reducer.d.ts +1 -1
  45. package/lib/typescript/src/common/useMessageOutgoingStatus.d.ts +1 -1
  46. package/lib/typescript/src/common/useTotalUnreadChannelCount.d.ts +1 -1
  47. package/lib/typescript/src/common/useTotalUnreadMessageCount.d.ts +1 -1
  48. package/lib/typescript/src/common/useUserList.d.ts +2 -2
  49. package/lib/typescript/src/index.d.ts +2 -1
  50. package/lib/typescript/src/model/CustomQuery.d.ts +1 -1
  51. package/lib/typescript/src/types.d.ts +13 -9
  52. package/package.json +4 -4
  53. package/src/channel/useGroupChannel.ts +21 -0
  54. package/src/channel/useGroupChannelMessages/reducer.ts +4 -4
  55. package/src/channel/useGroupChannelMessages/useGroupChannelMessagesWithCollection.ts +92 -86
  56. package/src/channel/useGroupChannelMessages/useGroupChannelMessagesWithQuery.ts +33 -37
  57. package/src/common/useAppFeatures.ts +4 -4
  58. package/src/common/useMessageOutgoingStatus.ts +2 -0
  59. package/src/common/useTotalUnreadChannelCount.ts +2 -2
  60. package/src/common/useTotalUnreadMessageCount.ts +2 -2
  61. package/src/common/useUserList.ts +46 -20
  62. package/src/index.ts +2 -1
  63. package/src/types.ts +13 -7
@@ -0,0 +1,6 @@
1
+ import { SendbirdChatSDK } from '@sendbird/uikit-utils';
2
+ export declare const useGroupChannel: (sdk: SendbirdChatSDK, channelUrl: string) => {
3
+ channel: import("@sendbird/chat/groupChannel").GroupChannel | undefined;
4
+ loading: boolean;
5
+ error: unknown;
6
+ };
@@ -1,5 +1,5 @@
1
1
  import type { SendbirdChannel } from '@sendbird/uikit-utils';
2
- declare type Order = 'latest_last_message' | 'chronological' | 'channel_name_alphabetical' | 'metadata_value_alphabetical';
2
+ type Order = 'latest_last_message' | 'chronological' | 'channel_name_alphabetical' | 'metadata_value_alphabetical';
3
3
  export declare const useGroupChannelListReducer: (order?: Order) => {
4
4
  updateLoading: (status: boolean) => void;
5
5
  updateRefreshing: (status: boolean) => void;
@@ -1,3 +1,3 @@
1
1
  import type { SendbirdChatSDK, SendbirdGroupChannel, SendbirdMessage } from '@sendbird/uikit-utils';
2
- export declare type SBUOutgoingStatus = 'NONE' | 'PENDING' | 'FAILED' | 'UNDELIVERED' | 'DELIVERED' | 'UNREAD' | 'READ';
2
+ export type SBUOutgoingStatus = 'NONE' | 'PENDING' | 'FAILED' | 'UNDELIVERED' | 'DELIVERED' | 'UNREAD' | 'READ';
3
3
  export declare const useMessageOutgoingStatus: (sdk: SendbirdChatSDK, channel: SendbirdGroupChannel, message?: SendbirdMessage | null) => SBUOutgoingStatus;
@@ -1,5 +1,5 @@
1
1
  import type { SendbirdChatSDK } from '@sendbird/uikit-utils';
2
- declare type Params = {
2
+ type Params = {
3
3
  maxCount?: number;
4
4
  };
5
5
  export declare const useTotalUnreadChannelCount: (sdk: SendbirdChatSDK, params?: Params) => string;
@@ -1,6 +1,6 @@
1
1
  import { TotalUnreadMessageCountParams } from '@sendbird/chat/groupChannel';
2
2
  import type { SendbirdChatSDK } from '@sendbird/uikit-utils';
3
- declare type Options = {
3
+ type Options = {
4
4
  params?: TotalUnreadMessageCountParams;
5
5
  maxCount?: number;
6
6
  };
@@ -1,4 +1,4 @@
1
- import type { Optional, SendbirdChatSDK } from '@sendbird/uikit-utils';
1
+ import type { Optional, SendbirdChatSDK, UserStruct } from '@sendbird/uikit-utils';
2
2
  import type { CustomQueryInterface, UseUserListOptions, UseUserListReturn } from '../types';
3
3
  /**
4
4
  * Get user list from query.
@@ -21,4 +21,4 @@ import type { CustomQueryInterface, UseUserListOptions, UseUserListReturn } from
21
21
  * })
22
22
  * ```
23
23
  * */
24
- export declare const useUserList: <Options extends UseUserListOptions<QueriedUser>, QueriedUser = Options["queryCreator"] extends Optional<() => CustomQueryInterface<infer User>> ? User : import("@sendbird/chat").User>(sdk: SendbirdChatSDK, options?: Options | undefined) => UseUserListReturn<QueriedUser>;
24
+ export declare const useUserList: <Options extends UseUserListOptions<QueriedUser>, QueriedUser extends UserStruct = Options["queryCreator"] extends Optional<() => CustomQueryInterface<infer User extends UserStruct>> ? User : import("@sendbird/chat").User>(sdk: SendbirdChatSDK, options?: Options | undefined) => UseUserListReturn<QueriedUser>;
@@ -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
  export { useAppFeatures } from './common/useAppFeatures';
5
6
  export { useMessageOutgoingStatus } from './common/useMessageOutgoingStatus';
6
7
  export { usePushTrigger } from './common/usePushTrigger';
@@ -11,4 +12,4 @@ export { useChannelHandler } from './handler/useChannelHandler';
11
12
  export { useConnectionHandler } from './handler/useConnectionHandler';
12
13
  export { useUserEventHandler } from './handler/useUserEventHandler';
13
14
  export { CustomQuery } from './model/CustomQuery';
14
- export type { UseGroupChannelList, UseGroupChannelListOptions, UseGroupChannelMessages, UseUserListOptions, UseGroupChannelMessagesOptions, UseUserListReturn, CustomQueryInterface, CustomBidirectionalQueryInterface, } from './types';
15
+ export type { UseGroupChannelList, UseGroupChannelListOptions, UseGroupChannelMessages, UseGroupChannelMessagesOptions, UseUserListOptions, UseUserListReturn, CustomQueryInterface, CustomBidirectionalQueryInterface, } from './types';
@@ -1,5 +1,5 @@
1
1
  import type { CustomQueryInterface } from '../types';
2
- declare type ConstructorParams<T> = {
2
+ type ConstructorParams<T> = {
3
3
  next: () => Promise<T[]>;
4
4
  isLoading: () => boolean;
5
5
  hasNext: () => boolean;
@@ -1,4 +1,4 @@
1
- import type { SendbirdChatSDK, SendbirdError, SendbirdFileMessage, SendbirdFileMessageCreateParams, SendbirdGroupChannel, SendbirdGroupChannelCollection, SendbirdGroupChannelListQuery, SendbirdMessage, SendbirdMessageCollection, SendbirdPreviousMessageListQuery, SendbirdUserMessage, SendbirdUserMessageCreateParams } from '@sendbird/uikit-utils';
1
+ import type { SendbirdChatSDK, SendbirdError, SendbirdFileMessage, SendbirdFileMessageCreateParams, SendbirdGroupChannel, SendbirdGroupChannelCollection, SendbirdGroupChannelListQuery, SendbirdMessage, SendbirdMessageCollection, SendbirdPreviousMessageListQuery, SendbirdUserMessage, SendbirdUserMessageCreateParams, UserStruct } from '@sendbird/uikit-utils';
2
2
  export interface CustomQueryInterface<Data> {
3
3
  isLoading: boolean;
4
4
  next: () => Promise<Data[]>;
@@ -41,7 +41,7 @@ export interface UseGroupChannelList {
41
41
  next: () => Promise<void>;
42
42
  };
43
43
  }
44
- export declare type UseGroupChannelListOptions = {
44
+ export type UseGroupChannelListOptions = {
45
45
  queryCreator?: () => SendbirdGroupChannelListQuery;
46
46
  collectionCreator?: () => SendbirdGroupChannelCollection;
47
47
  enableCollectionWithoutLocalCache?: boolean;
@@ -139,13 +139,9 @@ export interface UseGroupChannelMessages {
139
139
  * @return {Promise<void>}
140
140
  * */
141
141
  deleteMessage: (message: SendbirdFileMessage | SendbirdUserMessage) => Promise<void>;
142
- /**
143
- * Activated channel
144
- * */
145
- activeChannel: SendbirdGroupChannel;
146
142
  };
147
143
  }
148
- export declare type UseGroupChannelMessagesOptions = {
144
+ export type UseGroupChannelMessagesOptions = {
149
145
  sortComparator?: (a: SendbirdMessage, b: SendbirdMessage) => number;
150
146
  queryCreator?: () => SendbirdPreviousMessageListQuery;
151
147
  collectionCreator?: () => SendbirdMessageCollection;
@@ -156,7 +152,7 @@ export declare type UseGroupChannelMessagesOptions = {
156
152
  * @interface UseUserListReturn
157
153
  * @description interface for user list hook return value
158
154
  * */
159
- export interface UseUserListReturn<User> {
155
+ export interface UseUserListReturn<User extends UserStruct> {
160
156
  /**
161
157
  * Loading state, only available on first render
162
158
  * */
@@ -178,13 +174,21 @@ export interface UseUserListReturn<User> {
178
174
  * Get users state
179
175
  * */
180
176
  users: User[];
177
+ /**
178
+ * Update or Insert user to list
179
+ * */
180
+ upsertUser: (user: User) => void;
181
+ /**
182
+ * Delete user from list
183
+ * */
184
+ deleteUser: (userId: User['userId']) => void;
181
185
  /**
182
186
  * Fetch next users to state
183
187
  * @return {Promise<void>}
184
188
  * */
185
189
  next: () => Promise<void>;
186
190
  }
187
- export declare type UseUserListOptions<User> = {
191
+ export type UseUserListOptions<User extends UserStruct> = {
188
192
  sortComparator?: (a: User, b: User) => number;
189
193
  queryCreator?: () => CustomQueryInterface<User>;
190
194
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendbird/uikit-chat-hooks",
3
- "version": "2.0.3",
3
+ "version": "2.2.0",
4
4
  "description": "React hooks that built with SendbirdChat SDK",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -38,13 +38,13 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@sendbird/uikit-utils": "2.0.3"
41
+ "@sendbird/uikit-utils": "2.2.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "*",
45
45
  "react": "^16.13.1",
46
46
  "react-native-builder-bob": "^0.18.0",
47
- "typescript": "^4.1.3"
47
+ "typescript": "4.9.4"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@sendbird/chat": "^4.0.13",
@@ -69,5 +69,5 @@
69
69
  "readmeFile": "./README.md",
70
70
  "displayName": "@sendbird/uikit-chat-hooks"
71
71
  },
72
- "gitHead": "e3a34ae962741ffd2b647a466cf97e38f5006379"
72
+ "gitHead": "bd7ab217a07e91ba644c0cb69694a92b123326bf"
73
73
  }
@@ -0,0 +1,21 @@
1
+ import { useState } from 'react';
2
+
3
+ import { SendbirdChatSDK, SendbirdGroupChannel, useAsyncEffect } from '@sendbird/uikit-utils';
4
+
5
+ export const useGroupChannel = (sdk: SendbirdChatSDK, channelUrl: string) => {
6
+ const [channel, setChannel] = useState<SendbirdGroupChannel>();
7
+ const [loading, setLoading] = useState(true);
8
+ const [error, setError] = useState<unknown>();
9
+
10
+ useAsyncEffect(async () => {
11
+ try {
12
+ setChannel(await sdk.groupChannel.getChannel(channelUrl));
13
+ } catch (e) {
14
+ setError(e);
15
+ } finally {
16
+ setLoading(false);
17
+ }
18
+ }, []);
19
+
20
+ return { channel, loading, error };
21
+ };
@@ -37,12 +37,12 @@ const defaultReducer = ({ ...draft }: State, action: Action) => {
37
37
  }
38
38
  case 'update_messages':
39
39
  case 'update_next_messages': {
40
- const key = action.type === 'update_messages' ? 'messageMap' : ('nextMessageMap' as const);
41
- const messageMap = arrayToMapWithGetter(action.value.messages, getMessageUniqId);
40
+ const key = action.type === 'update_messages' ? 'messageMap' : 'nextMessageMap';
41
+ const receivedMessagesAsMap = arrayToMapWithGetter(action.value.messages, getMessageUniqId);
42
42
  if (action.value.clearPrev) {
43
- draft[key] = messageMap;
43
+ draft[key] = receivedMessagesAsMap;
44
44
  } else {
45
- draft[key] = { ...draft[key], ...messageMap };
45
+ draft[key] = { ...draft[key], ...receivedMessagesAsMap };
46
46
 
47
47
  // NOTE: Replace pending message to succeeded message
48
48
  action.value.messages
@@ -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
- SendbirdChannel,
5
+ SendbirdBaseMessage,
6
6
  SendbirdFileMessage,
7
7
  SendbirdGroupChannel,
8
8
  SendbirdMessageCollection,
9
9
  } from '@sendbird/uikit-utils';
10
- import { Logger, confirmAndMarkAsRead, isDifferentChannel, useForceUpdate } from '@sendbird/uikit-utils';
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,
@@ -46,17 +48,18 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
46
48
  updateRefreshing,
47
49
  } = useGroupChannelMessagesReducer(userId, options?.sortComparator);
48
50
 
49
- const channelMarkAs = async () => {
51
+ const channelMarkAsRead = async (source?: MessageEventSource) => {
50
52
  try {
51
- await confirmAndMarkAsRead([activeChannel]);
53
+ switch (source) {
54
+ case MessageEventSource.EVENT_MESSAGE_RECEIVED:
55
+ case MessageEventSource.EVENT_MESSAGE_SENT_SUCCESS:
56
+ case MessageEventSource.SYNC_MESSAGE_FILL:
57
+ case undefined:
58
+ await confirmAndMarkAsRead([channel]);
59
+ break;
60
+ }
52
61
  } catch (e) {
53
- Logger.warn(`[${HOOK_NAME}/channelMarkAs/Read]`, e);
54
- }
55
- };
56
-
57
- const updateChannel = (channel: SendbirdChannel) => {
58
- if (channel.isGroupChannel() && !isDifferentChannel(channel, activeChannel)) {
59
- forceUpdate();
62
+ Logger.warn(`[${HOOK_NAME}/channelMarkAsRead]`, e);
60
63
  }
61
64
  };
62
65
 
@@ -65,36 +68,44 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
65
68
  if (collectionRef.current) collectionRef.current?.dispose();
66
69
 
67
70
  if (uid) {
68
- collectionRef.current = createMessageCollection(activeChannel, options?.collectionCreator);
71
+ collectionRef.current = createMessageCollection(channel, options?.collectionCreator);
69
72
  updateNextMessages([], true, sdk.currentUser.userId);
70
- channelMarkAs();
73
+ channelMarkAsRead();
71
74
 
72
75
  collectionRef.current?.setMessageCollectionHandler({
73
76
  onMessagesAdded: (_, __, messages) => {
74
- switch (_.source) {
75
- case MessageEventSource.EVENT_MESSAGE_RECEIVED:
76
- case MessageEventSource.EVENT_MESSAGE_SENT_SUCCESS:
77
- case MessageEventSource.SYNC_MESSAGE_FILL:
78
- channelMarkAs();
79
- break;
80
- }
77
+ channelMarkAsRead(_.source);
81
78
  updateNextMessages(messages, false, sdk.currentUser.userId);
82
- updateChannel(channel);
83
79
  },
84
- onMessagesUpdated: (_, channel, messages) => {
85
- updateMessages(messages, false, sdk.currentUser.userId);
86
- updateChannel(channel);
80
+ onMessagesUpdated: (_, __, messages) => {
81
+ channelMarkAsRead(_.source);
82
+
83
+ // NOTE: admin message is not added via onMessagesAdded handler, not checked yet is this a bug.
84
+ if (_.source === MessageEventSource.EVENT_MESSAGE_RECEIVED) {
85
+ const nextMessageIds = nextMessages.map((it) => it.messageId);
86
+ const nonAddedMessagesFromReceivedEvent = messages.filter(
87
+ (it) => nextMessageIds.indexOf(it.messageId) === -1,
88
+ );
89
+ updateNextMessages(nonAddedMessagesFromReceivedEvent, false, sdk.currentUser.userId);
90
+ }
91
+
92
+ // NOTE: v4 MESSAGE_RECEIVED is called twice from onMessagesAdded and onMessagesUpdated when receiving new message.
93
+ // This is not intended behavior but not bugs.
94
+ if (_.source !== MessageEventSource.EVENT_MESSAGE_RECEIVED) {
95
+ updateMessages(messages, false, sdk.currentUser.userId);
96
+ }
87
97
  },
88
- onMessagesDeleted: (_, channel, messageIds) => {
98
+ onMessagesDeleted: (_, __, messageIds) => {
89
99
  deleteMessages(messageIds, []);
90
100
  deleteNextMessages(messageIds, []);
91
- updateChannel(channel);
92
101
  },
93
102
  onChannelDeleted: () => {
94
103
  options?.onChannelDeleted?.();
95
104
  },
96
- onChannelUpdated: (_, channel) => {
97
- updateChannel(channel);
105
+ onChannelUpdated: (_, eventChannel) => {
106
+ if (eventChannel.isGroupChannel() && !isDifferentChannel(eventChannel, channel)) {
107
+ forceUpdate();
108
+ }
98
109
  },
99
110
  onHugeGapDetected: () => {
100
111
  init(uid);
@@ -107,16 +118,18 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
107
118
  if (err) sdk.isCacheEnabled && Logger.error(`[${HOOK_NAME}/onCacheResult]`, err);
108
119
  else {
109
120
  Logger.debug(`[${HOOK_NAME}/onCacheResult]`, 'message length:', messages.length);
121
+
110
122
  updateMessages(messages, true, sdk.currentUser.userId);
111
123
  updateMessages(collectionRef.current?.pendingMessages ?? [], false, sdk.currentUser.userId);
112
124
  updateMessages(collectionRef.current?.failedMessages ?? [], false, sdk.currentUser.userId);
113
- if (messages?.length) callback?.();
114
125
  }
126
+ callback?.();
115
127
  })
116
128
  .onApiResult((err, messages) => {
117
129
  if (err) Logger.warn(`[${HOOK_NAME}/onApiResult]`, err);
118
130
  else {
119
131
  Logger.debug(`[${HOOK_NAME}/onApiResult]`, 'message length:', messages.length);
132
+
120
133
  updateMessages(messages, true, sdk.currentUser.userId);
121
134
  if (sdk.isCacheEnabled) {
122
135
  updateMessages(collectionRef.current?.pendingMessages ?? [], false, sdk.currentUser.userId);
@@ -127,16 +140,15 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
127
140
  });
128
141
  }
129
142
  },
130
- [sdk, activeChannel.url, options?.collectionCreator],
143
+ [sdk, channel.url, options?.collectionCreator],
131
144
  );
132
145
 
133
146
  useChannelHandler(sdk, HOOK_NAME, {
134
147
  onUserBanned(channel, bannedUser) {
135
- if (channel.isGroupChannel() && !isDifferentChannel(channel, activeChannel)) {
148
+ if (channel.isGroupChannel() && !isDifferentChannel(channel, channel)) {
136
149
  if (bannedUser.userId === sdk.currentUser.userId) {
137
150
  options?.onChannelDeleted?.();
138
151
  } else {
139
- setActiveChannel(channel);
140
152
  forceUpdate();
141
153
  }
142
154
  }
@@ -144,7 +156,7 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
144
156
  });
145
157
 
146
158
  useEffect(() => {
147
- // NOTE: Cache read is heavy synchronous task, and it prevents smooth ui transition
159
+ // NOTE: Cache read is heavy task, and it prevents smooth ui transition
148
160
  setTimeout(async () => {
149
161
  updateLoading(true);
150
162
  init(userId, () => updateLoading(false));
@@ -157,41 +169,44 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
157
169
  };
158
170
  }, []);
159
171
 
160
- const refresh: ReturnType<UseGroupChannelMessages>['refresh'] = useCallback(async () => {
172
+ const refresh: ReturnType<UseGroupChannelMessages>['refresh'] = useFreshCallback(async () => {
161
173
  updateRefreshing(true);
162
174
  init(userId, () => updateRefreshing(false));
163
- }, [init, userId]);
175
+ });
164
176
 
165
- const prev: ReturnType<UseGroupChannelMessages>['prev'] = useCallback(async () => {
177
+ const prev: ReturnType<UseGroupChannelMessages>['prev'] = useFreshCallback(async () => {
166
178
  if (collectionRef.current && collectionRef.current?.hasPrevious) {
167
179
  try {
168
180
  const list = await collectionRef.current?.loadPrevious();
169
181
  updateMessages(list, false, sdk.currentUser.userId);
170
182
  } catch {}
171
183
  }
172
- }, []);
184
+ });
185
+
186
+ const next: ReturnType<UseGroupChannelMessages>['next'] = useFreshCallback(async () => {
187
+ const messageCandidates: SendbirdBaseMessage[] = [];
173
188
 
174
- const next: ReturnType<UseGroupChannelMessages>['next'] = useCallback(async () => {
175
- const list = [];
176
189
  if (collectionRef.current && collectionRef.current?.hasNext) {
177
190
  try {
178
191
  const fetchedList = await collectionRef.current?.loadNext();
179
- list.push(...fetchedList);
192
+ messageCandidates.push(...fetchedList);
180
193
  } catch {}
181
194
  }
195
+
182
196
  if (nextMessages.length > 0) {
183
- list.push(...nextMessages);
197
+ messageCandidates.push(...nextMessages);
184
198
  }
185
- if (list.length > 0) {
186
- updateMessages(list, false, sdk.currentUser.userId);
199
+
200
+ if (messageCandidates.length > 0) {
201
+ updateMessages(messageCandidates, false, sdk.currentUser.userId);
187
202
  updateNextMessages([], true, sdk.currentUser.userId);
188
203
  }
189
- }, [nextMessages.length]);
204
+ });
190
205
 
191
- const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] = useCallback(
206
+ const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] = useFreshCallback(
192
207
  (params, onPending) => {
193
208
  return new Promise((resolve, reject) => {
194
- activeChannel
209
+ channel
195
210
  .sendUserMessage(params)
196
211
  .onPending((pendingMessage) => {
197
212
  if (pendingMessage.isUserMessage()) {
@@ -205,17 +220,17 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
205
220
  resolve(sentMessage);
206
221
  }
207
222
  })
208
- .onFailed((err) => {
223
+ .onFailed((err, _failedMessage) => {
224
+ // updateMessages([failedMessage], false, sdk.currentUser.userId);
209
225
  reject(err);
210
226
  });
211
227
  });
212
228
  },
213
- [activeChannel],
214
229
  );
215
- const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] = useCallback(
230
+ const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] = useFreshCallback(
216
231
  (params, onPending) => {
217
232
  return new Promise((resolve, reject) => {
218
- activeChannel
233
+ channel
219
234
  .sendFileMessage(params)
220
235
  .onPending((pendingMessage) => {
221
236
  if (pendingMessage.isFileMessage()) {
@@ -229,58 +244,50 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
229
244
  resolve(sentMessage as SendbirdFileMessage);
230
245
  }
231
246
  })
232
- .onFailed((err) => {
247
+ .onFailed((err, _failedMessage) => {
248
+ // updateMessages([failedMessage], false, sdk.currentUser.userId);
233
249
  reject(err);
234
250
  });
235
251
  });
236
252
  },
237
- [activeChannel],
238
253
  );
239
- const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] = useCallback(
254
+ const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] = useFreshCallback(
240
255
  async (messageId, params) => {
241
- const updatedMessage = await activeChannel.updateUserMessage(messageId, params);
256
+ const updatedMessage = await channel.updateUserMessage(messageId, params);
242
257
  updateMessages([updatedMessage], false, sdk.currentUser.userId);
243
258
  return updatedMessage;
244
259
  },
245
- [activeChannel],
246
260
  );
247
- const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] = useCallback(
261
+ const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] = useFreshCallback(
248
262
  async (messageId, params) => {
249
- const updatedMessage = await activeChannel.updateFileMessage(messageId, params);
263
+ const updatedMessage = await channel.updateFileMessage(messageId, params);
250
264
  updateMessages([updatedMessage], false, sdk.currentUser.userId);
251
265
  return updatedMessage;
252
266
  },
253
- [activeChannel],
254
267
  );
255
- const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] = useCallback(
268
+ const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] = useFreshCallback(
256
269
  async (failedMessage) => {
257
- const message = await (() => {
258
- if (failedMessage.isUserMessage()) return activeChannel.resendUserMessage(failedMessage);
259
- // FIXME: v4 bugs
260
- // @ts-ignore
261
- if (failedMessage.isFileMessage()) return activeChannel.resendFileMessage(failedMessage);
270
+ const resentMessage = await (() => {
271
+ if (failedMessage.isUserMessage()) return channel.resendUserMessage(failedMessage);
272
+ if (failedMessage.isFileMessage()) return channel.resendFileMessage(failedMessage);
262
273
  return null;
263
274
  })();
264
275
 
265
- if (message) updateMessages([message], false, sdk.currentUser.userId);
276
+ if (resentMessage) updateMessages([resentMessage], false, sdk.currentUser.userId);
266
277
  },
267
- [activeChannel],
268
278
  );
269
- const deleteMessage: ReturnType<UseGroupChannelMessages>['deleteMessage'] = useCallback(
270
- async (message) => {
271
- if (message.sendingStatus === 'succeeded') {
272
- if (message.isUserMessage()) await activeChannel.deleteMessage(message);
273
- if (message.isFileMessage()) await activeChannel.deleteMessage(message);
274
- } else {
275
- try {
276
- await collectionRef.current?.removeFailedMessage(message.reqId);
277
- } finally {
278
- deleteMessages([message.messageId], [message.reqId]);
279
- }
279
+ const deleteMessage: ReturnType<UseGroupChannelMessages>['deleteMessage'] = useFreshCallback(async (message) => {
280
+ if (message.sendingStatus === 'succeeded') {
281
+ if (message.isUserMessage()) await channel.deleteMessage(message);
282
+ if (message.isFileMessage()) await channel.deleteMessage(message);
283
+ } else {
284
+ try {
285
+ await collectionRef.current?.removeFailedMessage(message.reqId);
286
+ } finally {
287
+ deleteMessages([message.messageId], [message.reqId]);
280
288
  }
281
- },
282
- [activeChannel],
283
- );
289
+ }
290
+ });
284
291
 
285
292
  return {
286
293
  loading,
@@ -297,6 +304,5 @@ export const useGroupChannelMessagesWithCollection: UseGroupChannelMessages = (s
297
304
  updateFileMessage,
298
305
  resendMessage,
299
306
  deleteMessage,
300
- activeChannel,
301
307
  };
302
308
  };