@sendbird/uikit-react-native 3.7.3 → 3.7.5

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 (143) hide show
  1. package/README.md +5 -4
  2. package/lib/commonjs/components/ChannelInput/SendInput.js +2 -1
  3. package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
  4. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js +33 -23
  5. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js.map +1 -1
  6. package/lib/commonjs/components/ChannelInput/index.js +11 -5
  7. package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
  8. package/lib/commonjs/components/ChannelMessageList/index.js +6 -1
  9. package/lib/commonjs/components/ChannelMessageList/index.js.map +1 -1
  10. package/lib/commonjs/components/ChannelThreadMessageList/index.js +6 -1
  11. package/lib/commonjs/components/ChannelThreadMessageList/index.js.map +1 -1
  12. package/lib/commonjs/components/FileViewer/FileViewerContent.js +1 -1
  13. package/lib/commonjs/components/FileViewer/FileViewerContent.js.map +1 -1
  14. package/lib/commonjs/components/FileViewer/FileViewerFooter.js +1 -0
  15. package/lib/commonjs/components/FileViewer/FileViewerFooter.js.map +1 -1
  16. package/lib/commonjs/components/FileViewer/FileViewerHeader.js +1 -0
  17. package/lib/commonjs/components/FileViewer/FileViewerHeader.js.map +1 -1
  18. package/lib/commonjs/components/GroupChannelMessageRenderer/index.js +1 -1
  19. package/lib/commonjs/components/GroupChannelMessageRenderer/index.js.map +1 -1
  20. package/lib/commonjs/components/ReactionAddons/BottomSheetReactionAddon.js +9 -3
  21. package/lib/commonjs/components/ReactionAddons/BottomSheetReactionAddon.js.map +1 -1
  22. package/lib/commonjs/components/ReactionBottomSheets/ReactionListBottomSheet.js +9 -3
  23. package/lib/commonjs/components/ReactionBottomSheets/ReactionListBottomSheet.js.map +1 -1
  24. package/lib/commonjs/components/ThreadParentMessageRenderer/index.js +1 -1
  25. package/lib/commonjs/components/ThreadParentMessageRenderer/index.js.map +1 -1
  26. package/lib/commonjs/containers/SendbirdUIKitContainer.js +0 -1
  27. package/lib/commonjs/containers/SendbirdUIKitContainer.js.map +1 -1
  28. package/lib/commonjs/contexts/PlatformServiceCtx.js +2 -1
  29. package/lib/commonjs/contexts/PlatformServiceCtx.js.map +1 -1
  30. package/lib/commonjs/contexts/SBUHandlersCtx.js.map +1 -1
  31. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +17 -3
  32. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  33. package/lib/commonjs/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js +8 -4
  34. package/lib/commonjs/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js.map +1 -1
  35. package/lib/commonjs/domain/groupChannelThread/component/GroupChannelThreadMessageList.js +16 -2
  36. package/lib/commonjs/domain/groupChannelThread/component/GroupChannelThreadMessageList.js.map +1 -1
  37. package/lib/commonjs/domain/groupChannelThread/component/GroupChannelThreadParentMessageInfo.js +8 -3
  38. package/lib/commonjs/domain/groupChannelThread/component/GroupChannelThreadParentMessageInfo.js.map +1 -1
  39. package/lib/commonjs/fragments/createGroupChannelFragment.js +3 -2
  40. package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
  41. package/lib/commonjs/fragments/createGroupChannelThreadFragment.js +3 -2
  42. package/lib/commonjs/fragments/createGroupChannelThreadFragment.js.map +1 -1
  43. package/lib/commonjs/hooks/useConnection.js +2 -2
  44. package/lib/commonjs/hooks/useConnection.js.map +1 -1
  45. package/lib/commonjs/hooks/usePushTokenRegistration.js +24 -6
  46. package/lib/commonjs/hooks/usePushTokenRegistration.js.map +1 -1
  47. package/lib/commonjs/platform/createFileService.native.js +2 -2
  48. package/lib/commonjs/platform/createFileService.native.js.map +1 -1
  49. package/lib/commonjs/platform/createPlayerService.expo.js +8 -2
  50. package/lib/commonjs/platform/createPlayerService.expo.js.map +1 -1
  51. package/lib/commonjs/platform/createPlayerService.native.js +12 -7
  52. package/lib/commonjs/platform/createPlayerService.native.js.map +1 -1
  53. package/lib/commonjs/platform/createRecorderService.native.js +8 -2
  54. package/lib/commonjs/platform/createRecorderService.native.js.map +1 -1
  55. package/lib/commonjs/version.js +1 -1
  56. package/lib/commonjs/version.js.map +1 -1
  57. package/lib/module/components/ChannelInput/SendInput.js +2 -1
  58. package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
  59. package/lib/module/components/ChannelInput/VoiceMessageInput.js +34 -24
  60. package/lib/module/components/ChannelInput/VoiceMessageInput.js.map +1 -1
  61. package/lib/module/components/ChannelInput/index.js +12 -6
  62. package/lib/module/components/ChannelInput/index.js.map +1 -1
  63. package/lib/module/components/ChannelMessageList/index.js +6 -1
  64. package/lib/module/components/ChannelMessageList/index.js.map +1 -1
  65. package/lib/module/components/ChannelThreadMessageList/index.js +6 -1
  66. package/lib/module/components/ChannelThreadMessageList/index.js.map +1 -1
  67. package/lib/module/components/FileViewer/FileViewerContent.js +1 -1
  68. package/lib/module/components/FileViewer/FileViewerContent.js.map +1 -1
  69. package/lib/module/components/FileViewer/FileViewerFooter.js +1 -0
  70. package/lib/module/components/FileViewer/FileViewerFooter.js.map +1 -1
  71. package/lib/module/components/FileViewer/FileViewerHeader.js +1 -0
  72. package/lib/module/components/FileViewer/FileViewerHeader.js.map +1 -1
  73. package/lib/module/components/GroupChannelMessageRenderer/index.js +1 -1
  74. package/lib/module/components/GroupChannelMessageRenderer/index.js.map +1 -1
  75. package/lib/module/components/ReactionAddons/BottomSheetReactionAddon.js +10 -4
  76. package/lib/module/components/ReactionAddons/BottomSheetReactionAddon.js.map +1 -1
  77. package/lib/module/components/ReactionBottomSheets/ReactionListBottomSheet.js +9 -3
  78. package/lib/module/components/ReactionBottomSheets/ReactionListBottomSheet.js.map +1 -1
  79. package/lib/module/components/ThreadParentMessageRenderer/index.js +1 -1
  80. package/lib/module/components/ThreadParentMessageRenderer/index.js.map +1 -1
  81. package/lib/module/containers/SendbirdUIKitContainer.js +0 -1
  82. package/lib/module/containers/SendbirdUIKitContainer.js.map +1 -1
  83. package/lib/module/contexts/PlatformServiceCtx.js +2 -1
  84. package/lib/module/contexts/PlatformServiceCtx.js.map +1 -1
  85. package/lib/module/contexts/SBUHandlersCtx.js.map +1 -1
  86. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +17 -3
  87. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  88. package/lib/module/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js +8 -4
  89. package/lib/module/domain/groupChannelSettings/component/GroupChannelSettingsMenu.js.map +1 -1
  90. package/lib/module/domain/groupChannelThread/component/GroupChannelThreadMessageList.js +16 -2
  91. package/lib/module/domain/groupChannelThread/component/GroupChannelThreadMessageList.js.map +1 -1
  92. package/lib/module/domain/groupChannelThread/component/GroupChannelThreadParentMessageInfo.js +8 -3
  93. package/lib/module/domain/groupChannelThread/component/GroupChannelThreadParentMessageInfo.js.map +1 -1
  94. package/lib/module/fragments/createGroupChannelFragment.js +3 -2
  95. package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
  96. package/lib/module/fragments/createGroupChannelThreadFragment.js +3 -2
  97. package/lib/module/fragments/createGroupChannelThreadFragment.js.map +1 -1
  98. package/lib/module/hooks/useConnection.js +2 -2
  99. package/lib/module/hooks/useConnection.js.map +1 -1
  100. package/lib/module/hooks/usePushTokenRegistration.js +24 -6
  101. package/lib/module/hooks/usePushTokenRegistration.js.map +1 -1
  102. package/lib/module/platform/createFileService.native.js +2 -2
  103. package/lib/module/platform/createFileService.native.js.map +1 -1
  104. package/lib/module/platform/createPlayerService.expo.js +9 -3
  105. package/lib/module/platform/createPlayerService.expo.js.map +1 -1
  106. package/lib/module/platform/createPlayerService.native.js +13 -8
  107. package/lib/module/platform/createPlayerService.native.js.map +1 -1
  108. package/lib/module/platform/createRecorderService.native.js +9 -3
  109. package/lib/module/platform/createRecorderService.native.js.map +1 -1
  110. package/lib/module/version.js +1 -1
  111. package/lib/module/version.js.map +1 -1
  112. package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
  113. package/lib/typescript/src/contexts/SBUHandlersCtx.d.ts +1 -1
  114. package/lib/typescript/src/version.d.ts +1 -1
  115. package/package.json +6 -6
  116. package/src/components/ChannelInput/SendInput.tsx +2 -1
  117. package/src/components/ChannelInput/VoiceMessageInput.tsx +40 -24
  118. package/src/components/ChannelInput/index.tsx +16 -6
  119. package/src/components/ChannelMessageList/index.tsx +5 -1
  120. package/src/components/ChannelThreadMessageList/index.tsx +5 -1
  121. package/src/components/FileViewer/FileViewerContent.tsx +1 -1
  122. package/src/components/FileViewer/FileViewerFooter.tsx +1 -0
  123. package/src/components/FileViewer/FileViewerHeader.tsx +1 -0
  124. package/src/components/GroupChannelMessageRenderer/index.tsx +1 -1
  125. package/src/components/ReactionAddons/BottomSheetReactionAddon.tsx +13 -5
  126. package/src/components/ReactionBottomSheets/ReactionListBottomSheet.tsx +11 -4
  127. package/src/components/ThreadParentMessageRenderer/index.tsx +1 -1
  128. package/src/containers/SendbirdUIKitContainer.tsx +0 -1
  129. package/src/contexts/PlatformServiceCtx.tsx +2 -1
  130. package/src/contexts/SBUHandlersCtx.tsx +1 -1
  131. package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +19 -3
  132. package/src/domain/groupChannelSettings/component/GroupChannelSettingsMenu.tsx +8 -4
  133. package/src/domain/groupChannelThread/component/GroupChannelThreadMessageList.tsx +17 -2
  134. package/src/domain/groupChannelThread/component/GroupChannelThreadParentMessageInfo.tsx +7 -3
  135. package/src/fragments/createGroupChannelFragment.tsx +3 -2
  136. package/src/fragments/createGroupChannelThreadFragment.tsx +3 -2
  137. package/src/hooks/useConnection.ts +8 -2
  138. package/src/hooks/usePushTokenRegistration.ts +24 -6
  139. package/src/platform/createFileService.native.ts +2 -6
  140. package/src/platform/createPlayerService.expo.tsx +9 -3
  141. package/src/platform/createPlayerService.native.tsx +15 -8
  142. package/src/platform/createRecorderService.native.tsx +9 -3
  143. package/src/version.ts +1 -1
@@ -37,6 +37,7 @@ import {
37
37
  useSendbirdChat,
38
38
  useUserProfile,
39
39
  } from '../../hooks/useContext';
40
+ import SBUUtils from '../../libs/SBUUtils';
40
41
  import { ReactionAddons } from '../ReactionAddons';
41
42
  import ThreadChatFlatList from '../ThreadChatFlatList';
42
43
 
@@ -249,8 +250,11 @@ const useCreateMessagePressActions = <T extends SendbirdGroupChannel | SendbirdO
249
250
  const fileType = getFileType(message.type || getFileExtension(message.name));
250
251
  if (['image', 'video', 'audio'].includes(fileType)) {
251
252
  onPressMediaMessage?.(message, () => onDeleteMessage(message), getAvailableUriFromFileMessage(message));
253
+ handlers.onOpenFileURL?.(message.url);
254
+ } else {
255
+ const openFile = handlers.onOpenFileURL ?? SBUUtils.openURL;
256
+ openFile(message.url);
252
257
  }
253
- handlers.onOpenFileURL(message.url);
254
258
  }
255
259
  };
256
260
 
@@ -131,7 +131,7 @@ const ZoomableImageView = ({
131
131
 
132
132
  const styles = createStyleSheet({
133
133
  container: {
134
- zIndex: -1,
134
+ zIndex: 0,
135
135
  flex: 1,
136
136
  alignItems: 'center',
137
137
  justifyContent: 'center',
@@ -48,6 +48,7 @@ const FileViewerFooter = ({ bottomInset, deleteShown, onPressDelete, onPressDown
48
48
 
49
49
  const styles = createStyleSheet({
50
50
  container: {
51
+ zIndex: 1,
51
52
  position: 'absolute',
52
53
  left: 0,
53
54
  right: 0,
@@ -58,6 +58,7 @@ const FileViewerHeader = ({ headerShown = true, topInset, onClose, subtitle, tit
58
58
 
59
59
  const styles = createStyleSheet({
60
60
  container: {
61
+ zIndex: 1,
61
62
  top: 0,
62
63
  left: 0,
63
64
  right: 0,
@@ -279,7 +279,7 @@ const GroupChannelMessageRenderer: GroupChannelProps['Fragment']['renderMessage'
279
279
  onUnsubscribeStatus={voiceMessageStatusManager.unsubscribe}
280
280
  onUnmount={() => {
281
281
  if (isVoiceMessage(message) && playerService.uri === message.url) {
282
- resetPlayer();
282
+ resetPlayer().catch((_) => {});
283
283
  }
284
284
  }}
285
285
  {...messageProps}
@@ -5,7 +5,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import type { BaseMessage } from '@sendbird/chat/message';
6
6
  import { useChannelHandler } from '@sendbird/uikit-chat-hooks';
7
7
  import { Icon, Image, createStyleSheet, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';
8
- import { SendbirdBaseChannel, SendbirdBaseMessage, useUniqHandlerId } from '@sendbird/uikit-utils';
8
+ import { Logger, SendbirdBaseChannel, SendbirdBaseMessage, useUniqHandlerId } from '@sendbird/uikit-utils';
9
9
 
10
10
  import { UNKNOWN_USER_ID } from '../../constants';
11
11
  import { useReaction, useSendbirdChat } from '../../hooks/useContext';
@@ -46,10 +46,18 @@ const BottomSheetReactionAddon = ({ onClose, message, channel }: Props) => {
46
46
  const currentUserIdx = reactionUserIds.indexOf(currentUser?.userId ?? UNKNOWN_USER_ID);
47
47
  const reacted = currentUserIdx > -1;
48
48
 
49
- const onPress = () => {
50
- if (reacted) channel.deleteReaction(message, key);
51
- else channel.addReaction(message, key);
52
- onClose();
49
+ const onPress = async () => {
50
+ const action = (message: BaseMessage, key: string) => {
51
+ return reacted ? channel.deleteReaction(message, key) : channel.addReaction(message, key);
52
+ };
53
+
54
+ await action(message, key)
55
+ .catch((error) => {
56
+ Logger.warn('Failed to reaction', error);
57
+ })
58
+ .finally(() => {
59
+ onClose();
60
+ });
53
61
  };
54
62
 
55
63
  return (
@@ -2,7 +2,9 @@ import React from 'react';
2
2
  import { FlatList, Pressable, View, useWindowDimensions } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
 
5
+ import type { BaseMessage } from '@sendbird/chat/message';
5
6
  import { Image, Modal, createStyleSheet, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';
7
+ import { Logger } from '@sendbird/uikit-utils';
6
8
 
7
9
  import { UNKNOWN_USER_ID } from '../../constants';
8
10
  import type { ReactionBottomSheetProps } from './index';
@@ -53,12 +55,17 @@ const ReactionListBottomSheet = ({ visible, onClose, onDismiss, reactionCtx, cha
53
55
  <View style={styles.emojiItem}>
54
56
  <Pressable
55
57
  key={key}
56
- onPress={() => {
58
+ onPress={async () => {
57
59
  if (message && channel) {
58
- if (reacted) channel.deleteReaction(message, key);
59
- else channel.addReaction(message, key);
60
+ const action = (message: BaseMessage, key: string) => {
61
+ return reacted ? channel.deleteReaction(message, key) : channel.addReaction(message, key);
62
+ };
63
+
64
+ action(message, key).catch((error) => {
65
+ Logger.warn('Failed to reaction', error);
66
+ });
60
67
  }
61
- onClose();
68
+ await onClose();
62
69
  }}
63
70
  style={({ pressed }) => [
64
71
  styles.button,
@@ -178,7 +178,7 @@ const ThreadParentMessageRenderer = (props: ThreadParentMessageRendererProps) =>
178
178
  durationMetaArrayKey={VOICE_MESSAGE_META_ARRAY_DURATION_KEY}
179
179
  onUnmount={() => {
180
180
  if (isVoiceMessage(parentMessage) && playerService.uri === parentMessage.url) {
181
- resetPlayer();
181
+ resetPlayer().catch((_) => {});
182
182
  }
183
183
  }}
184
184
  {...messageProps}
@@ -219,7 +219,6 @@ const SendbirdUIKitContainer = (props: SendbirdUIKitContainerProps) => {
219
219
 
220
220
  const sbuHandlers: SBUHandlers = {
221
221
  onOpenURL: SBUUtils.openURL,
222
- onOpenFileURL: SBUUtils.openURL,
223
222
  ...handlers,
224
223
  };
225
224
 
@@ -31,7 +31,8 @@ export const PlatformServiceProvider = ({ children, voiceMessageConfig, ...servi
31
31
 
32
32
  useAppState('change', (state) => {
33
33
  if (state !== 'active') {
34
- Promise.allSettled([services.playerService.reset(), services.recorderService.reset()]);
34
+ services.playerService.reset().catch(() => {});
35
+ services.recorderService.reset().catch(() => {});
35
36
  }
36
37
  });
37
38
 
@@ -14,7 +14,7 @@ export interface SBUHandlers {
14
14
  * Note that this function is also called redundantly
15
15
  * when `onPressMediaMessage` handler is triggered by clicking on media messages containing images, videos, or audio.
16
16
  */
17
- onOpenFileURL: (url: string) => void;
17
+ onOpenFileURL?: (url: string) => void;
18
18
  }
19
19
 
20
20
  type Props = React.PropsWithChildren<SBUHandlers>;
@@ -40,7 +40,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
40
40
  if (focusAnimated) {
41
41
  props.onUpdateSearchItem({ startingPoint: createdAt });
42
42
  }
43
- props.onResetMessageListWithStartingPoint(createdAt);
43
+ props.onResetMessageListWithStartingPoint(createdAt).catch((_) => {});
44
44
  } else {
45
45
  return false;
46
46
  }
@@ -54,7 +54,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
54
54
  props.onUpdateSearchItem(undefined);
55
55
  props.onScrolledAwayFromBottom(false);
56
56
 
57
- await props.onResetMessageList();
57
+ await props.onResetMessageList().catch((_) => {});
58
58
  props.onScrolledAwayFromBottom(false);
59
59
  lazyScrollToBottom({ animated });
60
60
  } else {
@@ -75,7 +75,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
75
75
  });
76
76
 
77
77
  useEffect(() => {
78
- return subscribe(({ type }) => {
78
+ return subscribe(({ type, data }) => {
79
79
  switch (type) {
80
80
  case 'TYPING_BUBBLE_RENDERED':
81
81
  case 'MESSAGES_RECEIVED': {
@@ -84,6 +84,22 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
84
84
  }
85
85
  break;
86
86
  }
87
+ case 'MESSAGES_UPDATED': {
88
+ const lastMessage = props.channel.lastMessage;
89
+ const [updatedMessage] = data.messages;
90
+
91
+ const lastMessageUpdated =
92
+ updatedMessage && lastMessage && lastMessage.messageId === updatedMessage.messageId;
93
+
94
+ const isMaybeStreaming = props.channel.hasAiBot && lastMessageUpdated;
95
+
96
+ if (isMaybeStreaming) {
97
+ scrollToBottom(false);
98
+ } else if (!props.scrolledAwayFromBottom && lastMessageUpdated) {
99
+ scrollToBottom(true);
100
+ }
101
+ break;
102
+ }
87
103
  case 'MESSAGE_SENT_SUCCESS':
88
104
  case 'MESSAGE_SENT_PENDING': {
89
105
  scrollToBottom(false);
@@ -47,10 +47,14 @@ const GroupChannelSettingsMenu = ({
47
47
  }
48
48
 
49
49
  const toggleNotification = async () => {
50
- if (channel.myPushTriggerOption === 'off') {
51
- await channel.setMyPushTriggerOption(PushTriggerOption.DEFAULT);
52
- } else {
53
- await channel.setMyPushTriggerOption(PushTriggerOption.OFF);
50
+ try {
51
+ if (channel.myPushTriggerOption === 'off') {
52
+ await channel.setMyPushTriggerOption(PushTriggerOption.DEFAULT);
53
+ } else {
54
+ await channel.setMyPushTriggerOption(PushTriggerOption.OFF);
55
+ }
56
+ } catch (error) {
57
+ Logger.warn('Failed to toggle notification', error);
54
58
  }
55
59
  };
56
60
 
@@ -33,7 +33,7 @@ const GroupChannelThreadMessageList = (props: GroupChannelThreadProps['MessageLi
33
33
  if (props.hasNext()) {
34
34
  props.onScrolledAwayFromBottom(false);
35
35
 
36
- await props.onResetMessageList();
36
+ await props.onResetMessageList().catch((_) => {});
37
37
  props.onScrolledAwayFromBottom(false);
38
38
  lazyScrollToBottom({ animated });
39
39
  } else {
@@ -69,7 +69,7 @@ const GroupChannelThreadMessageList = (props: GroupChannelThreadProps['MessageLi
69
69
  });
70
70
 
71
71
  useEffect(() => {
72
- return subscribe(({ type }) => {
72
+ return subscribe(({ type, data }) => {
73
73
  switch (type) {
74
74
  case 'TYPING_BUBBLE_RENDERED':
75
75
  case 'MESSAGES_RECEIVED': {
@@ -78,6 +78,21 @@ const GroupChannelThreadMessageList = (props: GroupChannelThreadProps['MessageLi
78
78
  }
79
79
  break;
80
80
  }
81
+ case 'MESSAGES_UPDATED': {
82
+ const lastMessage = props.channel.lastMessage;
83
+ const [updatedMessage] = data.messages;
84
+
85
+ const lastMessageUpdated =
86
+ updatedMessage && lastMessage && lastMessage.messageId === updatedMessage.messageId;
87
+ const isMaybeStreaming = props.channel.hasAiBot && lastMessageUpdated;
88
+
89
+ if (isMaybeStreaming) {
90
+ scrollToBottom(false);
91
+ } else if (!props.scrolledAwayFromBottom && lastMessageUpdated) {
92
+ scrollToBottom(true);
93
+ }
94
+ break;
95
+ }
81
96
  case 'MESSAGE_SENT_SUCCESS':
82
97
  case 'MESSAGE_SENT_PENDING': {
83
98
  scrollToBottom(false);
@@ -31,6 +31,7 @@ import ThreadParentMessageRenderer, {
31
31
  ThreadParentMessageRendererProps,
32
32
  } from '../../../components/ThreadParentMessageRenderer';
33
33
  import { useLocalization, usePlatformService, useSBUHandlers, useSendbirdChat } from '../../../hooks/useContext';
34
+ import SBUUtils from '../../../libs/SBUUtils';
34
35
  import { GroupChannelThreadContexts } from '../module/moduleContext';
35
36
  import type { GroupChannelThreadProps } from '../types';
36
37
  import { ReactionAddons } from './../../../components/ReactionAddons';
@@ -224,9 +225,12 @@ const useCreateMessagePressActions = ({
224
225
  if (message.isFileMessage()) {
225
226
  const fileType = getFileType(message.type || getFileExtension(message.name));
226
227
  if (['image', 'video', 'audio'].includes(fileType)) {
227
- onPressMediaMessage?.(message, () => onDeleteMessage?.(message), getAvailableUriFromFileMessage(message));
228
+ onPressMediaMessage?.(message, () => onDeleteMessage(message), getAvailableUriFromFileMessage(message));
229
+ handlers.onOpenFileURL?.(message.url);
230
+ } else {
231
+ const openFile = handlers.onOpenFileURL ?? SBUUtils.openURL;
232
+ openFile(message.url);
228
233
  }
229
- handlers.onOpenFileURL(message.url);
230
234
  }
231
235
  };
232
236
 
@@ -239,7 +243,7 @@ const useCreateMessagePressActions = ({
239
243
  text: STRINGS.LABELS.CHANNEL_MESSAGE_DELETE_CONFIRM_OK,
240
244
  style: 'destructive',
241
245
  onPress: () => {
242
- onDeleteMessage?.(message).catch(onDeleteFailure);
246
+ onDeleteMessage(message).catch(onDeleteFailure);
243
247
  },
244
248
  },
245
249
  ],
@@ -116,8 +116,9 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
116
116
  startingPoint: internalSearchItem?.startingPoint,
117
117
  });
118
118
 
119
- const onBlurFragment = () => {
120
- return Promise.allSettled([playerService.reset(), recorderService.reset()]);
119
+ const onBlurFragment = async () => {
120
+ await playerService.reset().catch(() => {});
121
+ await recorderService.reset().catch(() => {});
121
122
  };
122
123
  const _onPressHeaderLeft = useFreshCallback(async () => {
123
124
  voiceMessageStatusManager.clear();
@@ -103,8 +103,9 @@ const createGroupChannelThreadFragment = (
103
103
  startingPoint,
104
104
  });
105
105
 
106
- const onBlurFragment = () => {
107
- return Promise.allSettled([playerService.reset(), recorderService.reset()]);
106
+ const onBlurFragment = async () => {
107
+ await playerService.reset().catch(() => {});
108
+ await recorderService.reset().catch(() => {});
108
109
  };
109
110
  const _onPressHeaderLeft = useFreshCallback(async () => {
110
111
  await onBlurFragment();
@@ -52,7 +52,10 @@ const useConnection = () => {
52
52
  });
53
53
  }
54
54
 
55
- await Promise.allSettled([initEmoji(sdk, emojiManager), initDashboardConfigs(sdk)]);
55
+ await Promise.all([
56
+ initEmoji(sdk, emojiManager).catch(() => Logger.info('[useConnection]', 'initEmoji failure')),
57
+ initDashboardConfigs(sdk).catch(() => Logger.info('[useConnection]', 'initDashboardConfigs failure')),
58
+ ]);
56
59
 
57
60
  Logger.debug('[useConnection]', 'connected! (online)');
58
61
  setCurrentUser(user);
@@ -67,7 +70,10 @@ const useConnection = () => {
67
70
  Logger.warn('[useConnection]', 'clear cached-data');
68
71
  await sdk.clearCachedData().catch((e) => Logger.warn('[useConnection]', 'clear cached-data failure', e));
69
72
  } else if (sdk.currentUser) {
70
- await Promise.allSettled([initEmoji(sdk, emojiManager), initDashboardConfigs(sdk)]);
73
+ await Promise.all([
74
+ initEmoji(sdk, emojiManager).catch(() => Logger.info('[useConnection]', 'initEmoji failure')),
75
+ initDashboardConfigs(sdk).catch(() => Logger.info('[useConnection]', 'initDashboardConfigs failure')),
76
+ ]);
71
77
 
72
78
  Logger.debug('[useConnection]', 'connected! (offline)');
73
79
  setCurrentUser(sdk.currentUser);
@@ -21,8 +21,8 @@ const usePushTokenRegistration = () => {
21
21
  default: (token: string) => sdk.unregisterFCMPushTokenForCurrentUser(token),
22
22
  }),
23
23
  Platform.select({
24
- ios: notificationService.getAPNSToken,
25
- default: notificationService.getFCMToken,
24
+ ios: () => notificationService.getAPNSToken(),
25
+ default: () => notificationService.getFCMToken(),
26
26
  }),
27
27
  ];
28
28
  });
@@ -44,9 +44,23 @@ const usePushTokenRegistration = () => {
44
44
  // Register token
45
45
  const token = await getToken();
46
46
  if (token) {
47
- Logger.log('[usePushTokenRegistration]', 'registered token:', token);
48
- await registerToken(token);
47
+ try {
48
+ await registerToken(token);
49
+ Logger.log('[usePushTokenRegistration]', 'registered token:', token);
50
+ } catch (error) {
51
+ Logger.error('[usePushTokenRegistration]', 'failed to register token:', error);
52
+ }
49
53
  }
54
+
55
+ // Remove listener
56
+ refreshListener.current = notificationService.onTokenRefresh(async (token) => {
57
+ try {
58
+ await registerToken(token);
59
+ Logger.log('[usePushTokenRegistration]', 'registered token:', token);
60
+ } catch (error) {
61
+ Logger.error('[usePushTokenRegistration]', 'failed to register token:', error);
62
+ }
63
+ });
50
64
  });
51
65
 
52
66
  const unregisterPushTokenForCurrentUser = useFreshCallback(async () => {
@@ -56,8 +70,12 @@ const usePushTokenRegistration = () => {
56
70
  // Unregister token
57
71
  const token = await getToken();
58
72
  if (token) {
59
- await unregisterToken(token);
60
- Logger.log('[usePushTokenRegistration]', 'unregistered token:', token);
73
+ try {
74
+ await unregisterToken(token);
75
+ Logger.log('[usePushTokenRegistration]', 'unregistered token:', token);
76
+ } catch (error) {
77
+ Logger.error('[usePushTokenRegistration]', 'failed to unregister token:', error);
78
+ }
61
79
  }
62
80
  });
63
81
 
@@ -31,11 +31,7 @@ function getAndroidStoragePermissionsByAPILevel(permissionModule: typeof Permiss
31
31
  if (Platform.OS !== 'android') return [];
32
32
 
33
33
  if (Platform.Version > 32) {
34
- return [
35
- permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_AUDIO,
36
- permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_IMAGES,
37
- permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_VIDEO,
38
- ];
34
+ return [];
39
35
  }
40
36
 
41
37
  if (Platform.Version > 28) {
@@ -63,7 +59,7 @@ const createNativeFileService = ({
63
59
  }): FileServiceInterface => {
64
60
  const cameraPermissions: Permission[] = Platform.select({
65
61
  ios: [permissionModule.PERMISSIONS.IOS.CAMERA, permissionModule.PERMISSIONS.IOS.MICROPHONE],
66
- android: [permissionModule.PERMISSIONS.ANDROID.CAMERA],
62
+ android: [],
67
63
  default: [],
68
64
  });
69
65
  const mediaLibraryPermissions: Permission[] = Platform.select({
@@ -1,6 +1,6 @@
1
1
  import type * as ExpoAV from 'expo-av';
2
2
 
3
- import { matchesOneOf } from '@sendbird/uikit-utils';
3
+ import { Logger, matchesOneOf } from '@sendbird/uikit-utils';
4
4
 
5
5
  import expoPermissionGranted from '../utils/expoPermissionGranted';
6
6
  import type { PlayerServiceInterface, Unsubscribe } from './types';
@@ -28,10 +28,16 @@ const createExpoPlayerService = ({ avModule }: Modules): PlayerServiceInterface
28
28
  };
29
29
 
30
30
  private setListener = () => {
31
- sound.setProgressUpdateIntervalAsync(100);
31
+ sound.setProgressUpdateIntervalAsync(100).catch((error) => {
32
+ Logger.warn('[PlayerService.Expo] Failed to set progress update interval', error);
33
+ });
32
34
  sound.setOnPlaybackStatusUpdate((status) => {
33
35
  if (status.isLoaded) {
34
- if (status.didJustFinish) this.stop();
36
+ if (status.didJustFinish) {
37
+ this.stop().catch((error) => {
38
+ Logger.warn('[PlayerService.Expo] Failed to stop in OnPlaybackStatusUpdate', error);
39
+ });
40
+ }
35
41
  if (status.isPlaying) {
36
42
  this.playbackSubscribers.forEach((callback) => {
37
43
  callback({
@@ -2,7 +2,7 @@ import { Platform } from 'react-native';
2
2
  import type * as RNAudioRecorder from 'react-native-audio-recorder-player';
3
3
  import * as Permissions from 'react-native-permissions';
4
4
 
5
- import { matchesOneOf, sleep } from '@sendbird/uikit-utils';
5
+ import { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';
6
6
 
7
7
  import type { PlayerServiceInterface, Unsubscribe } from './types';
8
8
 
@@ -23,7 +23,9 @@ const createNativePlayerService = ({ audioRecorderModule, permissionModule }: Mo
23
23
  private readonly stateSubscribers = new Set<StateListener>();
24
24
 
25
25
  constructor() {
26
- module.setSubscriptionDuration(0.1);
26
+ module.setSubscriptionDuration(0.1).catch((error) => {
27
+ Logger.warn('[PlayerService.Native] Failed to set subscription duration', error);
28
+ });
27
29
  }
28
30
 
29
31
  private setState = (state: PlayerServiceInterface['state']) => {
@@ -34,10 +36,14 @@ const createNativePlayerService = ({ audioRecorderModule, permissionModule }: Mo
34
36
  };
35
37
 
36
38
  private setListener = () => {
37
- module.addPlayBackListener((data) => {
39
+ module.addPlayBackListener(async (data) => {
38
40
  const stopped = data.currentPosition >= data.duration;
39
41
 
40
- if (stopped) this.stop();
42
+ if (stopped) {
43
+ this.stop().catch((error) => {
44
+ Logger.warn('[PlayerService.Native] Failed to stop in PlayBackListener', error);
45
+ });
46
+ }
41
47
  if (this.state === 'playing') {
42
48
  this.playbackSubscribers.forEach((callback) => {
43
49
  callback({ currentTime: data.currentPosition, duration: data.duration, stopped });
@@ -52,14 +58,15 @@ const createNativePlayerService = ({ audioRecorderModule, permissionModule }: Mo
52
58
 
53
59
  public requestPermission = async (): Promise<boolean> => {
54
60
  if (Platform.OS === 'android') {
55
- const { READ_MEDIA_AUDIO, READ_EXTERNAL_STORAGE } = permissionModule.PERMISSIONS.ANDROID;
56
- const permission = Platform.Version > 32 ? READ_MEDIA_AUDIO : READ_EXTERNAL_STORAGE;
61
+ if (Platform.Version > 32) return true;
62
+
63
+ const { READ_EXTERNAL_STORAGE } = permissionModule.PERMISSIONS.ANDROID;
57
64
 
58
- const status = await permissionModule.check(permission);
65
+ const status = await permissionModule.check(READ_EXTERNAL_STORAGE);
59
66
  if (status === 'granted') {
60
67
  return true;
61
68
  } else {
62
- const status = await permissionModule.request(permission);
69
+ const status = await permissionModule.request(READ_EXTERNAL_STORAGE);
63
70
  return status === 'granted';
64
71
  }
65
72
  } else {
@@ -3,7 +3,7 @@ import * as RNAudioRecorder from 'react-native-audio-recorder-player';
3
3
  import * as Permissions from 'react-native-permissions';
4
4
  import { Permission } from 'react-native-permissions/src/types';
5
5
 
6
- import { matchesOneOf, sleep } from '@sendbird/uikit-utils';
6
+ import { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';
7
7
 
8
8
  import VoiceMessageConfig from '../libs/VoiceMessageConfig';
9
9
  import nativePermissionGranted from '../utils/nativePermissionGranted';
@@ -64,11 +64,17 @@ const createNativeRecorderService = ({ audioRecorderModule, permissionModule }:
64
64
  });
65
65
 
66
66
  constructor() {
67
- module.setSubscriptionDuration(0.1);
67
+ module.setSubscriptionDuration(0.1).catch((error) => {
68
+ Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);
69
+ });
68
70
  module.addRecordBackListener((data) => {
69
71
  const completed = data.currentPosition >= this.options.maxDuration;
70
72
 
71
- if (completed) this.stop();
73
+ if (completed) {
74
+ this.stop().catch((error) => {
75
+ Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);
76
+ });
77
+ }
72
78
  if (this.state === 'recording') {
73
79
  this.recordingSubscribers.forEach((callback) => {
74
80
  callback({ currentTime: data.currentPosition, completed });
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- const VERSION = '3.7.3';
1
+ const VERSION = '3.7.5';
2
2
  export default VERSION;