@sendbird/uikit-react-native 2.4.2 → 2.5.0-rc.1

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 (209) hide show
  1. package/lib/commonjs/components/ChannelMessageList/index.js +319 -0
  2. package/lib/commonjs/components/ChannelMessageList/index.js.map +1 -0
  3. package/lib/commonjs/components/ChatFlatList.js +30 -50
  4. package/lib/commonjs/components/ChatFlatList.js.map +1 -1
  5. package/lib/commonjs/components/MessageSearchResultItem.js +132 -0
  6. package/lib/commonjs/components/MessageSearchResultItem.js.map +1 -0
  7. package/lib/commonjs/components/ScrollToBottomButton.js +1 -1
  8. package/lib/commonjs/components/ScrollToBottomButton.js.map +1 -1
  9. package/lib/commonjs/constants.js +6 -2
  10. package/lib/commonjs/constants.js.map +1 -1
  11. package/lib/commonjs/containers/GroupChannelPreviewContainer.js +2 -2
  12. package/lib/commonjs/containers/GroupChannelPreviewContainer.js.map +1 -1
  13. package/lib/commonjs/containers/SendbirdUIKitContainer.js +4 -2
  14. package/lib/commonjs/containers/SendbirdUIKitContainer.js.map +1 -1
  15. package/lib/commonjs/contexts/SendbirdChatCtx.js +4 -2
  16. package/lib/commonjs/contexts/SendbirdChatCtx.js.map +1 -1
  17. package/lib/commonjs/domain/groupChannel/component/GroupChannelHeader.js +4 -2
  18. package/lib/commonjs/domain/groupChannel/component/GroupChannelHeader.js.map +1 -1
  19. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +84 -301
  20. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  21. package/lib/commonjs/domain/groupChannel/component/GroupChannelSuggestedMentionList.js +2 -0
  22. package/lib/commonjs/domain/groupChannel/component/GroupChannelSuggestedMentionList.js.map +1 -1
  23. package/lib/commonjs/domain/groupChannel/module/moduleContext.js +9 -2
  24. package/lib/commonjs/domain/groupChannel/module/moduleContext.js.map +1 -1
  25. package/lib/commonjs/domain/groupChannel/types.js.map +1 -1
  26. package/lib/commonjs/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js +18 -4
  27. package/lib/commonjs/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js.map +1 -1
  28. package/lib/commonjs/domain/groupChannelSettings/types.js.map +1 -1
  29. package/lib/commonjs/domain/messageSearch/component/MessageSearchHeader.js +105 -0
  30. package/lib/commonjs/domain/messageSearch/component/MessageSearchHeader.js.map +1 -0
  31. package/lib/commonjs/domain/messageSearch/component/MessageSearchList.js +40 -0
  32. package/lib/commonjs/domain/messageSearch/component/MessageSearchList.js.map +1 -0
  33. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusEmpty.js +22 -0
  34. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusEmpty.js.map +1 -0
  35. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusError.js +26 -0
  36. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusError.js.map +1 -0
  37. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusLoading.js +22 -0
  38. package/lib/commonjs/domain/messageSearch/component/MessageSearchStatusLoading.js.map +1 -0
  39. package/lib/commonjs/domain/messageSearch/index.js +62 -0
  40. package/lib/commonjs/domain/messageSearch/index.js.map +1 -0
  41. package/lib/commonjs/domain/messageSearch/module/createMessageSearchModule.js +36 -0
  42. package/lib/commonjs/domain/messageSearch/module/createMessageSearchModule.js.map +1 -0
  43. package/lib/commonjs/domain/messageSearch/module/moduleContext.js +25 -0
  44. package/lib/commonjs/domain/messageSearch/module/moduleContext.js.map +1 -0
  45. package/lib/commonjs/domain/messageSearch/types.js +6 -0
  46. package/lib/commonjs/domain/messageSearch/types.js.map +1 -0
  47. package/lib/commonjs/domain/openChannel/component/OpenChannelMessageList.js +38 -279
  48. package/lib/commonjs/domain/openChannel/component/OpenChannelMessageList.js.map +1 -1
  49. package/lib/commonjs/domain/openChannel/module/moduleContext.js +9 -2
  50. package/lib/commonjs/domain/openChannel/module/moduleContext.js.map +1 -1
  51. package/lib/commonjs/domain/openChannel/types.js.map +1 -1
  52. package/lib/commonjs/fragments/createGroupChannelFragment.js +107 -15
  53. package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
  54. package/lib/commonjs/fragments/createGroupChannelSettingsFragment.js +2 -0
  55. package/lib/commonjs/fragments/createGroupChannelSettingsFragment.js.map +1 -1
  56. package/lib/commonjs/fragments/createMessageSearchFragment.js +145 -0
  57. package/lib/commonjs/fragments/createMessageSearchFragment.js.map +1 -0
  58. package/lib/commonjs/fragments/createOpenChannelFragment.js +40 -8
  59. package/lib/commonjs/fragments/createOpenChannelFragment.js.map +1 -1
  60. package/lib/commonjs/hooks/useMentionSuggestion.js +17 -0
  61. package/lib/commonjs/hooks/useMentionSuggestion.js.map +1 -1
  62. package/lib/commonjs/index.js +60 -40
  63. package/lib/commonjs/index.js.map +1 -1
  64. package/lib/commonjs/localization/StringSet.type.js.map +1 -1
  65. package/lib/commonjs/localization/createBaseStringSet.js +33 -20
  66. package/lib/commonjs/localization/createBaseStringSet.js.map +1 -1
  67. package/lib/commonjs/utils/pubsub.js +21 -0
  68. package/lib/commonjs/utils/pubsub.js.map +1 -0
  69. package/lib/commonjs/version.js +1 -1
  70. package/lib/commonjs/version.js.map +1 -1
  71. package/lib/module/components/ChannelMessageList/index.js +311 -0
  72. package/lib/module/components/ChannelMessageList/index.js.map +1 -0
  73. package/lib/module/components/ChatFlatList.js +32 -52
  74. package/lib/module/components/ChatFlatList.js.map +1 -1
  75. package/lib/module/components/MessageSearchResultItem.js +124 -0
  76. package/lib/module/components/MessageSearchResultItem.js.map +1 -0
  77. package/lib/module/components/ScrollToBottomButton.js +1 -1
  78. package/lib/module/components/ScrollToBottomButton.js.map +1 -1
  79. package/lib/module/constants.js +3 -1
  80. package/lib/module/constants.js.map +1 -1
  81. package/lib/module/containers/GroupChannelPreviewContainer.js +2 -2
  82. package/lib/module/containers/GroupChannelPreviewContainer.js.map +1 -1
  83. package/lib/module/containers/SendbirdUIKitContainer.js +4 -2
  84. package/lib/module/containers/SendbirdUIKitContainer.js.map +1 -1
  85. package/lib/module/contexts/SendbirdChatCtx.js +4 -2
  86. package/lib/module/contexts/SendbirdChatCtx.js.map +1 -1
  87. package/lib/module/domain/groupChannel/component/GroupChannelHeader.js +4 -2
  88. package/lib/module/domain/groupChannel/component/GroupChannelHeader.js.map +1 -1
  89. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +88 -305
  90. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  91. package/lib/module/domain/groupChannel/component/GroupChannelSuggestedMentionList.js +2 -0
  92. package/lib/module/domain/groupChannel/component/GroupChannelSuggestedMentionList.js.map +1 -1
  93. package/lib/module/domain/groupChannel/module/moduleContext.js +9 -2
  94. package/lib/module/domain/groupChannel/module/moduleContext.js.map +1 -1
  95. package/lib/module/domain/groupChannel/types.js.map +1 -1
  96. package/lib/module/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js +18 -4
  97. package/lib/module/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js.map +1 -1
  98. package/lib/module/domain/groupChannelSettings/types.js.map +1 -1
  99. package/lib/module/domain/messageSearch/component/MessageSearchHeader.js +96 -0
  100. package/lib/module/domain/messageSearch/component/MessageSearchHeader.js.map +1 -0
  101. package/lib/module/domain/messageSearch/component/MessageSearchList.js +32 -0
  102. package/lib/module/domain/messageSearch/component/MessageSearchList.js.map +1 -0
  103. package/lib/module/domain/messageSearch/component/MessageSearchStatusEmpty.js +14 -0
  104. package/lib/module/domain/messageSearch/component/MessageSearchStatusEmpty.js.map +1 -0
  105. package/lib/module/domain/messageSearch/component/MessageSearchStatusError.js +18 -0
  106. package/lib/module/domain/messageSearch/component/MessageSearchStatusError.js.map +1 -0
  107. package/lib/module/domain/messageSearch/component/MessageSearchStatusLoading.js +14 -0
  108. package/lib/module/domain/messageSearch/component/MessageSearchStatusLoading.js.map +1 -0
  109. package/lib/module/domain/messageSearch/index.js +8 -0
  110. package/lib/module/domain/messageSearch/index.js.map +1 -0
  111. package/lib/module/domain/messageSearch/module/createMessageSearchModule.js +28 -0
  112. package/lib/module/domain/messageSearch/module/createMessageSearchModule.js.map +1 -0
  113. package/lib/module/domain/messageSearch/module/moduleContext.js +14 -0
  114. package/lib/module/domain/messageSearch/module/moduleContext.js.map +1 -0
  115. package/lib/module/domain/messageSearch/types.js +2 -0
  116. package/lib/module/domain/messageSearch/types.js.map +1 -0
  117. package/lib/module/domain/openChannel/component/OpenChannelMessageList.js +40 -281
  118. package/lib/module/domain/openChannel/component/OpenChannelMessageList.js.map +1 -1
  119. package/lib/module/domain/openChannel/module/moduleContext.js +9 -2
  120. package/lib/module/domain/openChannel/module/moduleContext.js.map +1 -1
  121. package/lib/module/domain/openChannel/types.js.map +1 -1
  122. package/lib/module/fragments/createGroupChannelFragment.js +109 -17
  123. package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
  124. package/lib/module/fragments/createGroupChannelSettingsFragment.js +2 -0
  125. package/lib/module/fragments/createGroupChannelSettingsFragment.js.map +1 -1
  126. package/lib/module/fragments/createMessageSearchFragment.js +135 -0
  127. package/lib/module/fragments/createMessageSearchFragment.js.map +1 -0
  128. package/lib/module/fragments/createOpenChannelFragment.js +41 -9
  129. package/lib/module/fragments/createOpenChannelFragment.js.map +1 -1
  130. package/lib/module/hooks/useMentionSuggestion.js +18 -1
  131. package/lib/module/hooks/useMentionSuggestion.js.map +1 -1
  132. package/lib/module/index.js +2 -0
  133. package/lib/module/index.js.map +1 -1
  134. package/lib/module/localization/StringSet.type.js.map +1 -1
  135. package/lib/module/localization/createBaseStringSet.js +34 -21
  136. package/lib/module/localization/createBaseStringSet.js.map +1 -1
  137. package/lib/module/utils/pubsub.js +14 -0
  138. package/lib/module/utils/pubsub.js.map +1 -0
  139. package/lib/module/version.js +1 -1
  140. package/lib/module/version.js.map +1 -1
  141. package/lib/typescript/src/components/ChannelMessageList/index.d.ts +55 -0
  142. package/lib/typescript/src/components/ChatFlatList.d.ts +7 -8
  143. package/lib/typescript/src/components/MessageRenderer/index.d.ts +4 -0
  144. package/lib/typescript/src/components/MessageSearchResultItem.d.ts +2 -0
  145. package/lib/typescript/src/components/OpenChannelMessageRenderer/index.d.ts +1 -0
  146. package/lib/typescript/src/components/ScrollToBottomButton.d.ts +1 -1
  147. package/lib/typescript/src/constants.d.ts +3 -1
  148. package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +2 -1
  149. package/lib/typescript/src/contexts/SendbirdChatCtx.d.ts +3 -1
  150. package/lib/typescript/src/domain/groupChannel/component/GroupChannelHeader.d.ts +1 -1
  151. package/lib/typescript/src/domain/groupChannel/component/GroupChannelMessageList.d.ts +3 -32
  152. package/lib/typescript/src/domain/groupChannel/types.d.ts +23 -35
  153. package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsMenu.d.ts +1 -1
  154. package/lib/typescript/src/domain/groupChannelSettings/types.d.ts +2 -0
  155. package/lib/typescript/src/domain/messageSearch/component/MessageSearchHeader.d.ts +3 -0
  156. package/lib/typescript/src/domain/messageSearch/component/MessageSearchList.d.ts +3 -0
  157. package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusEmpty.d.ts +2 -0
  158. package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusError.d.ts +3 -0
  159. package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusLoading.d.ts +2 -0
  160. package/lib/typescript/src/domain/messageSearch/index.d.ts +7 -0
  161. package/lib/typescript/src/domain/messageSearch/module/createMessageSearchModule.d.ts +3 -0
  162. package/lib/typescript/src/domain/messageSearch/module/moduleContext.d.ts +3 -0
  163. package/lib/typescript/src/domain/messageSearch/types.d.ts +53 -0
  164. package/lib/typescript/src/domain/openChannel/component/OpenChannelMessageList.d.ts +1 -37
  165. package/lib/typescript/src/domain/openChannel/types.d.ts +17 -36
  166. package/lib/typescript/src/fragments/createMessageSearchFragment.d.ts +3 -0
  167. package/lib/typescript/src/hooks/useMentionSuggestion.d.ts +3 -2
  168. package/lib/typescript/src/index.d.ts +2 -0
  169. package/lib/typescript/src/localization/StringSet.type.d.ts +17 -3
  170. package/lib/typescript/src/utils/pubsub.d.ts +6 -0
  171. package/lib/typescript/src/version.d.ts +1 -1
  172. package/package.json +8 -7
  173. package/src/components/ChannelMessageList/index.tsx +392 -0
  174. package/src/components/ChatFlatList.tsx +33 -51
  175. package/src/components/MessageSearchResultItem.tsx +125 -0
  176. package/src/components/ScrollToBottomButton.tsx +3 -4
  177. package/src/constants.ts +3 -1
  178. package/src/containers/GroupChannelPreviewContainer.tsx +2 -2
  179. package/src/containers/SendbirdUIKitContainer.tsx +2 -0
  180. package/src/contexts/SendbirdChatCtx.tsx +7 -1
  181. package/src/domain/groupChannel/component/GroupChannelHeader.tsx +9 -3
  182. package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +73 -316
  183. package/src/domain/groupChannel/component/GroupChannelSuggestedMentionList.tsx +2 -1
  184. package/src/domain/groupChannel/module/moduleContext.tsx +10 -2
  185. package/src/domain/groupChannel/types.ts +49 -38
  186. package/src/domain/groupChannelSettings/component/GroupChannelSettingsMenu.tsx +29 -13
  187. package/src/domain/groupChannelSettings/types.ts +2 -0
  188. package/src/domain/messageSearch/component/MessageSearchHeader.tsx +98 -0
  189. package/src/domain/messageSearch/component/MessageSearchList.tsx +26 -0
  190. package/src/domain/messageSearch/component/MessageSearchStatusEmpty.tsx +15 -0
  191. package/src/domain/messageSearch/component/MessageSearchStatusError.tsx +16 -0
  192. package/src/domain/messageSearch/component/MessageSearchStatusLoading.tsx +15 -0
  193. package/src/domain/messageSearch/index.ts +7 -0
  194. package/src/domain/messageSearch/module/createMessageSearchModule.tsx +21 -0
  195. package/src/domain/messageSearch/module/moduleContext.tsx +16 -0
  196. package/src/domain/messageSearch/types.ts +55 -0
  197. package/src/domain/openChannel/component/OpenChannelMessageList.tsx +35 -303
  198. package/src/domain/openChannel/module/moduleContext.tsx +8 -2
  199. package/src/domain/openChannel/types.ts +40 -38
  200. package/src/fragments/createGroupChannelFragment.tsx +114 -17
  201. package/src/fragments/createGroupChannelSettingsFragment.tsx +2 -0
  202. package/src/fragments/createMessageSearchFragment.tsx +159 -0
  203. package/src/fragments/createOpenChannelFragment.tsx +48 -12
  204. package/src/hooks/useMentionSuggestion.ts +23 -3
  205. package/src/index.ts +3 -0
  206. package/src/localization/StringSet.type.ts +20 -2
  207. package/src/localization/createBaseStringSet.ts +22 -2
  208. package/src/utils/pubsub.ts +20 -0
  209. package/src/version.ts +0 -2
@@ -60,6 +60,7 @@ export const SendbirdUIKit = Object.freeze({
60
60
  USE_USER_ID_FOR_NICKNAME: false,
61
61
  USER_MENTION: false,
62
62
  IMAGE_COMPRESSION: true,
63
+ MESSAGE_SEARCH: false,
63
64
  },
64
65
  });
65
66
 
@@ -206,6 +207,7 @@ const SendbirdUIKitContainer = ({
206
207
  }
207
208
  enableUserMention={chatOptions?.enableUserMention ?? SendbirdUIKit.DEFAULT.USER_MENTION}
208
209
  enableImageCompression={chatOptions?.enableImageCompression ?? SendbirdUIKit.DEFAULT.IMAGE_COMPRESSION}
210
+ enableMessageSearch={chatOptions?.enableMessageSearch ?? SendbirdUIKit.DEFAULT.MESSAGE_SEARCH}
209
211
  >
210
212
  <LocalizationProvider stringSet={defaultStringSet}>
211
213
  <PlatformServiceProvider
@@ -21,6 +21,7 @@ export interface UIKitFeaturesInSendbirdChatContext {
21
21
  enableUseUserIdForNickname: boolean;
22
22
  enableUserMention: boolean;
23
23
  enableImageCompression: boolean;
24
+ enableMessageSearch: boolean;
24
25
  }
25
26
 
26
27
  interface Props extends UIKitFeaturesInSendbirdChatContext, React.PropsWithChildren {
@@ -43,13 +44,16 @@ export type SendbirdChatContextType = {
43
44
  markAsDeliveredWithChannel: (channel: SendbirdGroupChannel) => void;
44
45
 
45
46
  features: {
46
- // UIKit features
47
+ // RN UIKit features
47
48
  autoPushTokenRegistrationEnabled: boolean;
49
+
50
+ // UIKit features
48
51
  channelListTypingIndicatorEnabled: boolean;
49
52
  channelListMessageReceiptStatusEnabled: boolean;
50
53
  useUserIdForNicknameEnabled: boolean;
51
54
  userMentionEnabled: boolean;
52
55
  imageCompressionEnabled: boolean;
56
+ messageSearchEnabled: boolean;
53
57
 
54
58
  // Sendbird application features
55
59
  deliveryReceiptEnabled: boolean;
@@ -72,6 +76,7 @@ export const SendbirdChatProvider = ({
72
76
  enableUseUserIdForNickname,
73
77
  enableUserMention,
74
78
  enableImageCompression,
79
+ enableMessageSearch,
75
80
  }: Props) => {
76
81
  const [currentUser, _setCurrentUser] = useState<SendbirdUser>();
77
82
  const forceUpdate = useForceUpdate();
@@ -147,6 +152,7 @@ export const SendbirdChatProvider = ({
147
152
  useUserIdForNicknameEnabled: enableUseUserIdForNickname,
148
153
  userMentionEnabled: enableUserMention,
149
154
  imageCompressionEnabled: enableImageCompression,
155
+ messageSearchEnabled: enableMessageSearch,
150
156
  },
151
157
  };
152
158
 
@@ -8,13 +8,19 @@ import { useLocalization } from '../../../hooks/useContext';
8
8
  import { GroupChannelContexts } from '../module/moduleContext';
9
9
  import type { GroupChannelProps } from '../types';
10
10
 
11
- const GroupChannelHeader = ({ onPressHeaderLeft, onPressHeaderRight }: GroupChannelProps['Header']) => {
11
+ const GroupChannelHeader = ({
12
+ shouldHideRight,
13
+ onPressHeaderLeft,
14
+ onPressHeaderRight,
15
+ }: GroupChannelProps['Header']) => {
12
16
  const { headerTitle, channel } = useContext(GroupChannelContexts.Fragment);
13
17
  const { typingUsers } = useContext(GroupChannelContexts.TypingIndicator);
14
18
  const { STRINGS } = useLocalization();
15
19
  const { HeaderComponent } = useHeaderStyle();
16
20
  const subtitle = STRINGS.LABELS.TYPING_INDICATOR_TYPINGS(typingUsers);
17
21
 
22
+ const isHidden = shouldHideRight();
23
+
18
24
  return (
19
25
  <HeaderComponent
20
26
  clearTitleMargin
@@ -29,8 +35,8 @@ const GroupChannelHeader = ({ onPressHeaderLeft, onPressHeaderRight }: GroupChan
29
35
  }
30
36
  left={<Icon icon={'arrow-left'} />}
31
37
  onPressLeft={onPressHeaderLeft}
32
- right={<Icon icon={'info'} />}
33
- onPressRight={onPressHeaderRight}
38
+ right={isHidden ? null : <Icon icon={'info'} />}
39
+ onPressRight={isHidden ? undefined : onPressHeaderRight}
34
40
  />
35
41
  );
36
42
  };
@@ -1,342 +1,99 @@
1
- import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
2
- import { ListRenderItem, Platform, View } from 'react-native';
3
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
1
+ import React, { useContext, useEffect, useRef } from 'react';
2
+ import type { FlatList } from 'react-native';
4
3
 
5
- import type { BottomSheetItem } from '@sendbird/uikit-react-native-foundation';
6
- import {
7
- ChannelFrozenBanner,
8
- createStyleSheet,
9
- useAlert,
10
- useBottomSheet,
11
- useToast,
12
- useUIKitTheme,
13
- } from '@sendbird/uikit-react-native-foundation';
14
- import type { SendbirdFileMessage, SendbirdMessage, SendbirdUserMessage } from '@sendbird/uikit-utils';
15
- import {
16
- Logger,
17
- getAvailableUriFromFileMessage,
18
- getFileExtension,
19
- getFileType,
20
- isMyMessage,
21
- messageKeyExtractor,
22
- shouldRenderReaction,
23
- toMegabyte,
24
- useFreshCallback,
25
- } from '@sendbird/uikit-utils';
4
+ import { useChannelHandler } from '@sendbird/uikit-chat-hooks';
5
+ import type { SendbirdMessage } from '@sendbird/uikit-utils';
6
+ import { isDifferentChannel, useFreshCallback, useUniqHandlerId } from '@sendbird/uikit-utils';
26
7
 
27
- import type { ChatFlatListRef } from '../../../components/ChatFlatList';
28
- import ChatFlatList from '../../../components/ChatFlatList';
29
- import { ReactionAddons } from '../../../components/ReactionAddons';
30
- import { DEPRECATION_WARNING } from '../../../constants';
31
- import { useLocalization, usePlatformService, useSendbirdChat } from '../../../hooks/useContext';
32
- import SBUUtils from '../../../libs/SBUUtils';
8
+ import ChannelMessageList from '../../../components/ChannelMessageList';
9
+ import { MESSAGE_SEARCH_SAFE_SCROLL_DELAY } from '../../../constants';
10
+ import { useSendbirdChat } from '../../../hooks/useContext';
33
11
  import { GroupChannelContexts } from '../module/moduleContext';
34
12
  import type { GroupChannelProps } from '../types';
35
13
 
36
- const HANDLE_NEXT_MSG_SEPARATELY = Platform.select({ default: true });
37
-
38
- const GroupChannelMessageList = ({
39
- currentUserId,
40
- channel,
41
- messages,
42
- renderMessage,
43
- nextMessages,
44
- newMessagesFromMembers,
45
- onBottomReached,
46
- onTopReached,
47
- renderNewMessagesButton,
48
- renderScrollToBottomButton,
49
- onResendFailedMessage,
50
- onDeleteMessage,
51
- onPressImageMessage,
52
- onPressMediaMessage,
53
- flatListProps,
54
- enableMessageGrouping,
55
- }: GroupChannelProps['MessageList']) => {
56
- const { STRINGS } = useLocalization();
57
- const { colors } = useUIKitTheme();
58
- const { left, right } = useSafeAreaInsets();
59
- const [scrollLeaveBottom, setScrollLeaveBottom] = useState(false);
60
- const scrollRef = useRef<ChatFlatListRef>(null);
61
- const [newMessagesInternalBuffer, setNewMessagesInternalBuffer] = useState(() => newMessagesFromMembers);
62
- const getMessagePressActions = useGetMessagePressActions({
63
- channel,
64
- currentUserId,
65
- onDeleteMessage,
66
- onResendFailedMessage,
67
- onPressImageMessage,
68
- onPressMediaMessage,
69
- });
70
-
71
- const safeAreaLayout = { paddingLeft: left, paddingRight: right };
72
-
73
- const renderItem: ListRenderItem<SendbirdMessage> = useFreshCallback(({ item, index }) => {
74
- const { onPress, onLongPress } = getMessagePressActions(item);
75
- return renderMessage({
76
- message: item,
77
- prevMessage: messages[index + 1],
78
- nextMessage: messages[index - 1],
79
- onPress,
80
- onLongPress,
81
- enableMessageGrouping,
82
- channel,
83
- currentUserId,
84
- });
85
- });
86
-
87
- if (!HANDLE_NEXT_MSG_SEPARATELY) {
88
- useEffect(() => {
89
- if (newMessagesInternalBuffer.length !== 0) {
90
- setNewMessagesInternalBuffer((prev) => prev.concat(newMessagesFromMembers));
91
- }
92
- onBottomReached();
93
- }, [newMessagesFromMembers]);
94
- }
95
-
96
- const onLeaveScrollBottom = useCallback((val: boolean) => {
97
- if (!HANDLE_NEXT_MSG_SEPARATELY) setNewMessagesInternalBuffer([]);
98
- setScrollLeaveBottom(val);
99
- }, []);
100
-
101
- return (
102
- <View style={[{ flex: 1, backgroundColor: colors.background }, safeAreaLayout]}>
103
- {channel.isFrozen && (
104
- <ChannelFrozenBanner style={styles.frozenBanner} text={STRINGS.GROUP_CHANNEL.LIST_BANNER_FROZEN} />
105
- )}
106
- <ChatFlatList
107
- nextMessages={nextMessages}
108
- onBottomReached={onBottomReached}
109
- onTopReached={onTopReached}
110
- onLeaveScrollBottom={onLeaveScrollBottom}
111
- currentUserId={currentUserId}
112
- {...flatListProps}
113
- ref={scrollRef}
114
- data={messages}
115
- renderItem={renderItem}
116
- keyExtractor={messageKeyExtractor}
117
- contentContainerStyle={[
118
- // { minHeight: '100%', justifyContent: 'flex-end' },
119
- channel.isFrozen && styles.frozenListPadding,
120
- flatListProps?.contentContainerStyle,
121
- ]}
122
- />
123
- {renderNewMessagesButton && (
124
- <View style={[styles.newMsgButton, safeAreaLayout]}>
125
- {renderNewMessagesButton({
126
- visible: scrollLeaveBottom,
127
- onPress: () => scrollRef.current?.scrollToBottom(false),
128
- newMessages: !HANDLE_NEXT_MSG_SEPARATELY ? newMessagesInternalBuffer : newMessagesFromMembers,
129
- })}
130
- </View>
131
- )}
132
- {renderScrollToBottomButton && (
133
- <View pointerEvents={scrollLeaveBottom ? 'auto' : 'none'} style={[styles.scrollButton, safeAreaLayout]}>
134
- {renderScrollToBottomButton({
135
- visible: scrollLeaveBottom,
136
- onPress: () => scrollRef.current?.scrollToBottom(false),
137
- })}
138
- </View>
139
- )}
140
- </View>
141
- );
142
- };
143
-
144
- type HandleableMessage = SendbirdUserMessage | SendbirdFileMessage;
145
- const useGetMessagePressActions = ({
146
- channel,
147
- currentUserId,
148
- onResendFailedMessage,
149
- onDeleteMessage,
150
- onPressImageMessage,
151
- onPressMediaMessage,
152
- }: Pick<
153
- GroupChannelProps['MessageList'],
154
- | 'channel'
155
- | 'currentUserId'
156
- | 'onResendFailedMessage'
157
- | 'onDeleteMessage'
158
- | 'onPressImageMessage'
159
- | 'onPressMediaMessage'
160
- >) => {
161
- const { colors } = useUIKitTheme();
162
- const { STRINGS } = useLocalization();
163
- const toast = useToast();
164
- const { openSheet } = useBottomSheet();
165
- const { alert } = useAlert();
166
- const { clipboardService, fileService } = usePlatformService();
167
- const { features } = useSendbirdChat();
14
+ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
15
+ const { sdk } = useSendbirdChat();
168
16
  const { setMessageToEdit } = useContext(GroupChannelContexts.Fragment);
17
+ const { subscribe } = useContext(GroupChannelContexts.PubSub);
169
18
 
170
- const handleFailedMessage = (message: HandleableMessage) => {
171
- openSheet({
172
- sheetItems: [
173
- {
174
- title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_RETRY,
175
- onPress: () =>
176
- onResendFailedMessage(message).catch(() => toast.show(STRINGS.TOAST.RESEND_MSG_ERROR, 'error')),
177
- },
178
- {
179
- title: STRINGS.LABELS.CHANNEL_MESSAGE_FAILED_REMOVE,
180
- titleColor: colors.ui.dialog.default.none.destructive,
181
- onPress: () => confirmDelete(message),
182
- },
183
- ],
184
- });
185
- };
186
- const confirmDelete = (message: HandleableMessage) => {
187
- alert({
188
- title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_TITLE,
189
- buttons: [
190
- {
191
- text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_CANCEL,
192
- },
193
- {
194
- text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_OK,
195
- style: 'destructive',
196
- onPress: () => onDeleteMessage(message).catch(() => toast.show(STRINGS.TOAST.DELETE_MSG_ERROR, 'error')),
197
- },
198
- ],
199
- });
200
- };
201
-
202
- return (msg: SendbirdMessage) => {
203
- if (!msg.isUserMessage() && !msg.isFileMessage()) {
204
- return { onPress: undefined, onLongPress: undefined };
205
- }
19
+ const id = useUniqHandlerId('GroupChannelMessageList');
20
+ const ref = useRef<FlatList<SendbirdMessage>>(null);
206
21
 
207
- const sheetItems: BottomSheetItem['sheetItems'] = [];
208
- const response: { onPress?: () => void; onLongPress?: () => void } = {
209
- onPress: undefined,
210
- onLongPress: undefined,
211
- };
22
+ // FIXME: Workaround, should run after data has been applied to UI.
23
+ const lazyScrollToBottom = (animated = false, timeout = 0) => {
24
+ setTimeout(() => {
25
+ ref.current?.scrollToOffset({ offset: 0, animated });
26
+ }, timeout);
27
+ };
212
28
 
213
- if (msg.isUserMessage()) {
214
- sheetItems.push({
215
- icon: 'copy',
216
- title: STRINGS.LABELS.CHANNEL_MESSAGE_COPY,
217
- onPress: () => {
218
- clipboardService.setString(msg.message || '');
219
- toast.show(STRINGS.TOAST.COPY_OK, 'success');
220
- },
221
- });
29
+ // FIXME: Workaround, should run after data has been applied to UI.
30
+ const lazyScrollToIndex = (index = 0, animated = false, timeout = 0) => {
31
+ setTimeout(() => {
32
+ ref.current?.scrollToIndex({ index, animated, viewPosition: 0.5 });
33
+ }, timeout);
34
+ };
222
35
 
223
- if (isMyMessage(msg, currentUserId) && msg.sendingStatus === 'succeeded') {
224
- sheetItems.push(
225
- {
226
- icon: 'edit',
227
- title: STRINGS.LABELS.CHANNEL_MESSAGE_EDIT,
228
- onPress: () => setMessageToEdit(msg),
229
- },
230
- {
231
- icon: 'delete',
232
- title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE,
233
- onPress: () => confirmDelete(msg),
234
- },
235
- );
36
+ useEffect(() => {
37
+ if (props.searchItem) {
38
+ const createdAt = props.searchItem.startingPoint;
39
+ const foundMessageIndex = props.messages.findIndex((it) => it.createdAt === createdAt);
40
+ const isIncludedInList = foundMessageIndex > -1;
41
+ if (isIncludedInList) {
42
+ lazyScrollToIndex(foundMessageIndex, true, MESSAGE_SEARCH_SAFE_SCROLL_DELAY);
236
43
  }
237
44
  }
45
+ }, [props.searchItem]);
238
46
 
239
- if (msg.isFileMessage()) {
240
- sheetItems.push({
241
- icon: 'download',
242
- title: STRINGS.LABELS.CHANNEL_MESSAGE_SAVE,
243
- onPress: async () => {
244
- if (toMegabyte(msg.size) > 4) {
245
- toast.show(STRINGS.TOAST.DOWNLOAD_START, 'success');
246
- }
247
-
248
- fileService
249
- .save({ fileUrl: msg.url, fileName: msg.name, fileType: msg.type })
250
- .then((response) => {
251
- toast.show(STRINGS.TOAST.DOWNLOAD_OK, 'success');
252
- Logger.log('File saved to', response);
253
- })
254
- .catch((err) => {
255
- toast.show(STRINGS.TOAST.DOWNLOAD_ERROR, 'error');
256
- Logger.log('File save failure', err);
257
- });
258
- },
47
+ const scrollToBottom = useFreshCallback((animated = false) => {
48
+ if (props.hasNext()) {
49
+ props.onResetMessageList(() => {
50
+ lazyScrollToBottom(animated);
51
+ props.onScrolledAwayFromBottom(false);
259
52
  });
53
+ } else {
54
+ lazyScrollToBottom(animated);
55
+ }
56
+ });
260
57
 
261
- if (isMyMessage(msg, currentUserId) && msg.sendingStatus === 'succeeded') {
262
- sheetItems.push({
263
- icon: 'delete',
264
- title: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE,
265
- onPress: () => confirmDelete(msg),
266
- });
58
+ useChannelHandler(sdk, id, {
59
+ onReactionUpdated(channel, event) {
60
+ if (isDifferentChannel(channel, props.channel)) return;
61
+ const recentMessage = props.messages[0];
62
+ const isRecentMessage = recentMessage && recentMessage.messageId === event.messageId;
63
+ const scrollReachedBottomAndCanScroll = !props.scrolledAwayFromBottom && !props.hasNext();
64
+ if (isRecentMessage && scrollReachedBottomAndCanScroll) {
65
+ lazyScrollToBottom(true, 250);
267
66
  }
67
+ },
68
+ });
268
69
 
269
- const fileType = getFileType(msg.type || getFileExtension(msg.name));
270
- switch (fileType) {
271
- case 'image':
272
- case 'video':
273
- case 'audio': {
274
- response.onPress = () => {
275
- if (onPressImageMessage && fileType === 'image') {
276
- Logger.warn(DEPRECATION_WARNING.GROUP_CHANNEL.ON_PRESS_IMAGE_MESSAGE);
277
- onPressImageMessage(msg, getAvailableUriFromFileMessage(msg));
278
- }
279
- onPressMediaMessage?.(msg, () => onDeleteMessage(msg), getAvailableUriFromFileMessage(msg));
280
- };
70
+ useEffect(() => {
71
+ return subscribe(({ type }) => {
72
+ switch (type) {
73
+ case 'MESSAGES_RECEIVED': {
74
+ if (!props.scrolledAwayFromBottom) {
75
+ scrollToBottom(true);
76
+ }
281
77
  break;
282
78
  }
283
- default: {
284
- response.onPress = () => SBUUtils.openURL(msg.url);
79
+ case 'MESSAGE_SENT_SUCCESS':
80
+ case 'MESSAGE_SENT_PENDING': {
81
+ scrollToBottom(false);
285
82
  break;
286
83
  }
287
84
  }
288
- }
289
-
290
- if (sheetItems.length > 0) {
291
- response.onLongPress = () => {
292
- openSheet({
293
- sheetItems,
294
- HeaderComponent: shouldRenderReaction(channel, features.reactionEnabled)
295
- ? ({ onClose }) => <ReactionAddons.BottomSheet message={msg} channel={channel} onClose={onClose} />
296
- : undefined,
297
- });
298
- };
299
- }
300
-
301
- if (msg.sendingStatus === 'failed') {
302
- response.onLongPress = () => handleFailedMessage(msg);
303
- response.onPress = () => {
304
- onResendFailedMessage(msg).catch(() => toast.show(STRINGS.TOAST.RESEND_MSG_ERROR, 'error'));
305
- };
306
- }
307
-
308
- if (msg.sendingStatus === 'pending') {
309
- response.onLongPress = undefined;
310
- response.onPress = undefined;
311
- }
85
+ });
86
+ }, [props.scrolledAwayFromBottom]);
312
87
 
313
- return response;
314
- };
88
+ return (
89
+ <ChannelMessageList
90
+ {...props}
91
+ ref={ref}
92
+ onEditMessage={setMessageToEdit}
93
+ onPressNewMessagesButton={scrollToBottom}
94
+ onPressScrollToBottomButton={scrollToBottom}
95
+ />
96
+ );
315
97
  };
316
98
 
317
- const styles = createStyleSheet({
318
- frozenBanner: {
319
- position: 'absolute',
320
- zIndex: 999,
321
- top: 8,
322
- left: 8,
323
- right: 8,
324
- },
325
- frozenListPadding: {
326
- paddingBottom: 32,
327
- },
328
- newMsgButton: {
329
- position: 'absolute',
330
- zIndex: 999,
331
- bottom: 10,
332
- alignSelf: 'center',
333
- },
334
- scrollButton: {
335
- position: 'absolute',
336
- zIndex: 998,
337
- bottom: 10,
338
- right: 16,
339
- },
340
- });
341
-
342
99
  export default React.memo(GroupChannelMessageList);
@@ -29,7 +29,7 @@ const GroupChannelSuggestedMentionList = ({
29
29
  }: GroupChannelProps['SuggestedMentionList']) => {
30
30
  const { width: screenWidth, height: screenHeight } = useWindowDimensions();
31
31
  const { channel } = useContext(GroupChannelContexts.Fragment);
32
- const { mentionManager } = useSendbirdChat();
32
+ const { sdk, mentionManager } = useSendbirdChat();
33
33
  const { STRINGS } = useLocalization();
34
34
  const { colors } = useUIKitTheme();
35
35
  const { topInset } = useHeaderStyle();
@@ -38,6 +38,7 @@ const GroupChannelSuggestedMentionList = ({
38
38
  const keyboard = useKeyboardStatus();
39
39
 
40
40
  const { members, reset, searchStringRange, searchLimited } = useMentionSuggestion({
41
+ sdk,
41
42
  text,
42
43
  selection,
43
44
  channel,
@@ -13,7 +13,8 @@ import {
13
13
 
14
14
  import ProviderLayout from '../../../components/ProviderLayout';
15
15
  import { useLocalization, useSendbirdChat } from '../../../hooks/useContext';
16
- import type { GroupChannelContextsType, GroupChannelModule } from '../types';
16
+ import type { PubSub } from '../../../utils/pubsub';
17
+ import type { GroupChannelContextsType, GroupChannelModule, GroupChannelPubSubContextPayload } from '../types';
17
18
 
18
19
  export const GroupChannelContexts: GroupChannelContextsType = {
19
20
  Fragment: createContext({
@@ -24,6 +25,10 @@ export const GroupChannelContexts: GroupChannelContextsType = {
24
25
  TypingIndicator: createContext({
25
26
  typingUsers: [] as SendbirdUser[],
26
27
  }),
28
+ PubSub: createContext({
29
+ publish: NOOP,
30
+ subscribe: () => NOOP,
31
+ } as PubSub<GroupChannelPubSubContextPayload>),
27
32
  };
28
33
 
29
34
  export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
@@ -31,6 +36,7 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
31
36
  channel,
32
37
  enableTypingIndicator,
33
38
  keyboardAvoidOffset = 0,
39
+ groupChannelPubSub,
34
40
  }) => {
35
41
  if (!channel) throw new Error('GroupChannel is not provided to GroupChannelModule');
36
42
 
@@ -61,7 +67,9 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
61
67
  }}
62
68
  >
63
69
  <GroupChannelContexts.TypingIndicator.Provider value={{ typingUsers }}>
64
- {children}
70
+ <GroupChannelContexts.PubSub.Provider value={groupChannelPubSub}>
71
+ {children}
72
+ </GroupChannelContexts.PubSub.Provider>
65
73
  </GroupChannelContexts.TypingIndicator.Provider>
66
74
  </GroupChannelContexts.Fragment.Provider>
67
75
  </ProviderLayout>