@sendbird/uikit-react-native 3.1.1 → 3.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 (327) hide show
  1. package/README.md +67 -42
  2. package/lib/commonjs/components/ChannelInput/MessageToReplyPreview.js +145 -0
  3. package/lib/commonjs/components/ChannelInput/MessageToReplyPreview.js.map +1 -0
  4. package/lib/commonjs/components/ChannelInput/SendInput.js +147 -312
  5. package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
  6. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js +238 -0
  7. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js.map +1 -0
  8. package/lib/commonjs/components/ChannelInput/index.js +5 -1
  9. package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
  10. package/lib/commonjs/components/ChannelMessageList/index.js +1 -1
  11. package/lib/commonjs/components/ChannelMessageList/index.js.map +1 -1
  12. package/lib/commonjs/components/ChatFlatList/index.js +15 -3
  13. package/lib/commonjs/components/ChatFlatList/index.js.map +1 -1
  14. package/lib/commonjs/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js +24 -13
  15. package/lib/commonjs/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js.map +1 -1
  16. package/lib/commonjs/components/GroupChannelMessageRenderer/index.js +100 -5
  17. package/lib/commonjs/components/GroupChannelMessageRenderer/index.js.map +1 -1
  18. package/lib/commonjs/components/MessageSearchResultItem.js +1 -0
  19. package/lib/commonjs/components/MessageSearchResultItem.js.map +1 -1
  20. package/lib/commonjs/components/OpenChannelMessageRenderer/index.js +1 -0
  21. package/lib/commonjs/components/OpenChannelMessageRenderer/index.js.map +1 -1
  22. package/lib/commonjs/components/ReactionBottomSheets/ReactionUserListBottomSheet.js +2 -2
  23. package/lib/commonjs/components/ReactionBottomSheets/ReactionUserListBottomSheet.js.map +1 -1
  24. package/lib/commonjs/components/ReactionBottomSheets/index.js.map +1 -1
  25. package/lib/commonjs/components/StatusComposition.js.map +1 -1
  26. package/lib/commonjs/constants.js +5 -1
  27. package/lib/commonjs/constants.js.map +1 -1
  28. package/lib/commonjs/containers/GroupChannelPreviewContainer.js +1 -0
  29. package/lib/commonjs/containers/GroupChannelPreviewContainer.js.map +1 -1
  30. package/lib/commonjs/containers/InternalErrorBoundaryContainer.js.map +1 -1
  31. package/lib/commonjs/containers/SendbirdUIKitContainer.js +84 -34
  32. package/lib/commonjs/containers/SendbirdUIKitContainer.js.map +1 -1
  33. package/lib/commonjs/contexts/PlatformServiceCtx.js +16 -12
  34. package/lib/commonjs/contexts/PlatformServiceCtx.js.map +1 -1
  35. package/lib/commonjs/contexts/ReactionCtx.js +3 -2
  36. package/lib/commonjs/contexts/ReactionCtx.js.map +1 -1
  37. package/lib/commonjs/contexts/SendbirdChatCtx.js +2 -0
  38. package/lib/commonjs/contexts/SendbirdChatCtx.js.map +1 -1
  39. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +27 -42
  40. package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  41. package/lib/commonjs/domain/groupChannel/module/moduleContext.js +109 -5
  42. package/lib/commonjs/domain/groupChannel/module/moduleContext.js.map +1 -1
  43. package/lib/commonjs/domain/groupChannel/types.js.map +1 -1
  44. package/lib/commonjs/domain/userList/types.js.map +1 -1
  45. package/lib/commonjs/fragments/createGroupChannelFragment.js +30 -4
  46. package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
  47. package/lib/commonjs/fragments/createMessageSearchFragment.js +1 -1
  48. package/lib/commonjs/fragments/createMessageSearchFragment.js.map +1 -1
  49. package/lib/commonjs/hooks/useChannelInputItems.js +211 -0
  50. package/lib/commonjs/hooks/useChannelInputItems.js.map +1 -0
  51. package/lib/commonjs/hooks/useConnection.js +1 -1
  52. package/lib/commonjs/hooks/useConnection.js.map +1 -1
  53. package/lib/commonjs/hooks/useMentionSuggestion.js +1 -1
  54. package/lib/commonjs/hooks/useMentionSuggestion.js.map +1 -1
  55. package/lib/commonjs/hooks/useVoiceMessageInput.js +207 -0
  56. package/lib/commonjs/hooks/useVoiceMessageInput.js.map +1 -0
  57. package/lib/commonjs/index.js +32 -0
  58. package/lib/commonjs/index.js.map +1 -1
  59. package/lib/commonjs/libs/MentionManager.js.map +1 -1
  60. package/lib/commonjs/libs/SBUUtils.js +4 -0
  61. package/lib/commonjs/libs/SBUUtils.js.map +1 -1
  62. package/lib/commonjs/libs/VoiceMessageConfig.js +30 -0
  63. package/lib/commonjs/libs/VoiceMessageConfig.js.map +1 -0
  64. package/lib/commonjs/localization/StringSet.type.js.map +1 -1
  65. package/lib/commonjs/localization/createBaseStringSet.js +24 -9
  66. package/lib/commonjs/localization/createBaseStringSet.js.map +1 -1
  67. package/lib/commonjs/platform/createFileService.expo.js +10 -0
  68. package/lib/commonjs/platform/createFileService.expo.js.map +1 -1
  69. package/lib/commonjs/platform/createFileService.native.js +19 -0
  70. package/lib/commonjs/platform/createFileService.native.js.map +1 -1
  71. package/lib/commonjs/platform/createPlayerService.expo.js +137 -0
  72. package/lib/commonjs/platform/createPlayerService.expo.js.map +1 -0
  73. package/lib/commonjs/platform/createPlayerService.native.js +139 -0
  74. package/lib/commonjs/platform/createPlayerService.native.js.map +1 -0
  75. package/lib/commonjs/platform/createRecorderService.expo.js +158 -0
  76. package/lib/commonjs/platform/createRecorderService.expo.js.map +1 -0
  77. package/lib/commonjs/platform/createRecorderService.native.js +157 -0
  78. package/lib/commonjs/platform/createRecorderService.native.js.map +1 -0
  79. package/lib/commonjs/platform/types.js.map +1 -1
  80. package/lib/commonjs/types.js.map +1 -1
  81. package/lib/commonjs/version.js +1 -1
  82. package/lib/commonjs/version.js.map +1 -1
  83. package/lib/module/components/ChannelInput/MessageToReplyPreview.js +137 -0
  84. package/lib/module/components/ChannelInput/MessageToReplyPreview.js.map +1 -0
  85. package/lib/module/components/ChannelInput/SendInput.js +149 -314
  86. package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
  87. package/lib/module/components/ChannelInput/VoiceMessageInput.js +228 -0
  88. package/lib/module/components/ChannelInput/VoiceMessageInput.js.map +1 -0
  89. package/lib/module/components/ChannelInput/index.js +5 -1
  90. package/lib/module/components/ChannelInput/index.js.map +1 -1
  91. package/lib/module/components/ChannelMessageList/index.js +2 -2
  92. package/lib/module/components/ChannelMessageList/index.js.map +1 -1
  93. package/lib/module/components/ChatFlatList/index.js +15 -3
  94. package/lib/module/components/ChatFlatList/index.js.map +1 -1
  95. package/lib/module/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js +24 -13
  96. package/lib/module/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js.map +1 -1
  97. package/lib/module/components/GroupChannelMessageRenderer/index.js +99 -6
  98. package/lib/module/components/GroupChannelMessageRenderer/index.js.map +1 -1
  99. package/lib/module/components/MessageSearchResultItem.js +2 -1
  100. package/lib/module/components/MessageSearchResultItem.js.map +1 -1
  101. package/lib/module/components/OpenChannelMessageRenderer/index.js +1 -0
  102. package/lib/module/components/OpenChannelMessageRenderer/index.js.map +1 -1
  103. package/lib/module/components/ReactionBottomSheets/ReactionUserListBottomSheet.js +2 -2
  104. package/lib/module/components/ReactionBottomSheets/ReactionUserListBottomSheet.js.map +1 -1
  105. package/lib/module/components/ReactionBottomSheets/index.js.map +1 -1
  106. package/lib/module/components/StatusComposition.js.map +1 -1
  107. package/lib/module/constants.js +2 -0
  108. package/lib/module/constants.js.map +1 -1
  109. package/lib/module/containers/GroupChannelPreviewContainer.js +2 -1
  110. package/lib/module/containers/GroupChannelPreviewContainer.js.map +1 -1
  111. package/lib/module/containers/InternalErrorBoundaryContainer.js.map +1 -1
  112. package/lib/module/containers/SendbirdUIKitContainer.js +86 -36
  113. package/lib/module/containers/SendbirdUIKitContainer.js.map +1 -1
  114. package/lib/module/contexts/PlatformServiceCtx.js +14 -11
  115. package/lib/module/contexts/PlatformServiceCtx.js.map +1 -1
  116. package/lib/module/contexts/ReactionCtx.js +3 -2
  117. package/lib/module/contexts/ReactionCtx.js.map +1 -1
  118. package/lib/module/contexts/SendbirdChatCtx.js +2 -0
  119. package/lib/module/contexts/SendbirdChatCtx.js.map +1 -1
  120. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +28 -43
  121. package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
  122. package/lib/module/domain/groupChannel/module/moduleContext.js +111 -7
  123. package/lib/module/domain/groupChannel/module/moduleContext.js.map +1 -1
  124. package/lib/module/domain/groupChannel/types.js.map +1 -1
  125. package/lib/module/domain/userList/types.js.map +1 -1
  126. package/lib/module/fragments/createGroupChannelFragment.js +32 -6
  127. package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
  128. package/lib/module/fragments/createMessageSearchFragment.js +1 -1
  129. package/lib/module/fragments/createMessageSearchFragment.js.map +1 -1
  130. package/lib/module/hooks/useChannelInputItems.js +203 -0
  131. package/lib/module/hooks/useChannelInputItems.js.map +1 -0
  132. package/lib/module/hooks/useConnection.js +1 -1
  133. package/lib/module/hooks/useConnection.js.map +1 -1
  134. package/lib/module/hooks/useMentionSuggestion.js +1 -1
  135. package/lib/module/hooks/useMentionSuggestion.js.map +1 -1
  136. package/lib/module/hooks/useVoiceMessageInput.js +199 -0
  137. package/lib/module/hooks/useVoiceMessageInput.js.map +1 -0
  138. package/lib/module/index.js +4 -0
  139. package/lib/module/index.js.map +1 -1
  140. package/lib/module/libs/MentionManager.js.map +1 -1
  141. package/lib/module/libs/SBUUtils.js +4 -0
  142. package/lib/module/libs/SBUUtils.js.map +1 -1
  143. package/lib/module/libs/VoiceMessageConfig.js +23 -0
  144. package/lib/module/libs/VoiceMessageConfig.js.map +1 -0
  145. package/lib/module/localization/StringSet.type.js.map +1 -1
  146. package/lib/module/localization/createBaseStringSet.js +25 -10
  147. package/lib/module/localization/createBaseStringSet.js.map +1 -1
  148. package/lib/module/platform/createFileService.expo.js +10 -0
  149. package/lib/module/platform/createFileService.expo.js.map +1 -1
  150. package/lib/module/platform/createFileService.native.js +19 -0
  151. package/lib/module/platform/createFileService.native.js.map +1 -1
  152. package/lib/module/platform/createPlayerService.expo.js +129 -0
  153. package/lib/module/platform/createPlayerService.expo.js.map +1 -0
  154. package/lib/module/platform/createPlayerService.native.js +132 -0
  155. package/lib/module/platform/createPlayerService.native.js.map +1 -0
  156. package/lib/module/platform/createRecorderService.expo.js +150 -0
  157. package/lib/module/platform/createRecorderService.expo.js.map +1 -0
  158. package/lib/module/platform/createRecorderService.native.js +149 -0
  159. package/lib/module/platform/createRecorderService.native.js.map +1 -0
  160. package/lib/module/platform/types.js.map +1 -1
  161. package/lib/module/types.js.map +1 -1
  162. package/lib/module/version.js +1 -1
  163. package/lib/module/version.js.map +1 -1
  164. package/lib/typescript/src/components/ChannelCover.d.ts +2 -1
  165. package/lib/typescript/src/components/ChannelInput/AttachmentsButton.d.ts +2 -1
  166. package/lib/typescript/src/components/ChannelInput/MessageToReplyPreview.d.ts +7 -0
  167. package/lib/typescript/src/components/ChannelInput/VoiceMessageInput.d.ts +11 -0
  168. package/lib/typescript/src/components/ChannelInput/index.d.ts +7 -3
  169. package/lib/typescript/src/components/ChannelMessageList/index.d.ts +1 -1
  170. package/lib/typescript/src/components/FileViewer.d.ts +2 -1
  171. package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageDateSeparator.d.ts +2 -1
  172. package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageFocusAnimation.d.ts +1 -1
  173. package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageOutgoingStatus.d.ts +1 -1
  174. package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.d.ts +4 -2
  175. package/lib/typescript/src/components/NewMessagesButton.d.ts +1 -1
  176. package/lib/typescript/src/components/OpenChannelMessageRenderer/OpenChannelMessageDateSeparator.d.ts +2 -1
  177. package/lib/typescript/src/components/ProviderLayout.d.ts +1 -1
  178. package/lib/typescript/src/components/ReactionAddons/BottomSheetReactionAddon.d.ts +2 -1
  179. package/lib/typescript/src/components/ReactionAddons/MessageReactionAddon.d.ts +2 -1
  180. package/lib/typescript/src/components/ReactionAddons/ReactionRoundedButton.d.ts +3 -2
  181. package/lib/typescript/src/components/ReactionAddons/index.d.ts +3 -2
  182. package/lib/typescript/src/components/ReactionBottomSheets/ReactionListBottomSheet.d.ts +2 -1
  183. package/lib/typescript/src/components/ReactionBottomSheets/ReactionUserListBottomSheet.d.ts +2 -1
  184. package/lib/typescript/src/components/ReactionBottomSheets/index.d.ts +4 -4
  185. package/lib/typescript/src/components/ScrollToBottomButton.d.ts +1 -1
  186. package/lib/typescript/src/components/StatusComposition.d.ts +4 -4
  187. package/lib/typescript/src/components/TypedPlaceholder.d.ts +2 -1
  188. package/lib/typescript/src/components/UserActionBar.d.ts +2 -1
  189. package/lib/typescript/src/components/UserSelectableBar.d.ts +2 -1
  190. package/lib/typescript/src/constants.d.ts +2 -0
  191. package/lib/typescript/src/containers/GroupChannelPreviewContainer.d.ts +2 -1
  192. package/lib/typescript/src/containers/InternalErrorBoundaryContainer.d.ts +3 -3
  193. package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +18 -6
  194. package/lib/typescript/src/contexts/LocalizationCtx.d.ts +1 -1
  195. package/lib/typescript/src/contexts/PlatformServiceCtx.d.ts +8 -8
  196. package/lib/typescript/src/contexts/ReactionCtx.d.ts +5 -2
  197. package/lib/typescript/src/contexts/SendbirdChatCtx.d.ts +6 -3
  198. package/lib/typescript/src/contexts/UserProfileCtx.d.ts +1 -1
  199. package/lib/typescript/src/domain/groupChannel/component/GroupChannelHeader.d.ts +2 -1
  200. package/lib/typescript/src/domain/groupChannel/component/GroupChannelInput.d.ts +1 -1
  201. package/lib/typescript/src/domain/groupChannel/component/GroupChannelMessageList.d.ts +1 -1
  202. package/lib/typescript/src/domain/groupChannel/component/GroupChannelStatusEmpty.d.ts +2 -1
  203. package/lib/typescript/src/domain/groupChannel/component/GroupChannelStatusLoading.d.ts +2 -1
  204. package/lib/typescript/src/domain/groupChannel/component/GroupChannelSuggestedMentionList.d.ts +2 -1
  205. package/lib/typescript/src/domain/groupChannel/types.d.ts +45 -0
  206. package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersHeader.d.ts +2 -1
  207. package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersList.d.ts +2 -1
  208. package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersStatusEmpty.d.ts +2 -1
  209. package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersStatusLoading.d.ts +2 -1
  210. package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListHeader.d.ts +2 -1
  211. package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListList.d.ts +2 -1
  212. package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListStatusEmpty.d.ts +2 -1
  213. package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListStatusLoading.d.ts +2 -1
  214. package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListTypeSelector.d.ts +2 -1
  215. package/lib/typescript/src/domain/groupChannelModeration/component/GroupChannelModerationHeader.d.ts +2 -1
  216. package/lib/typescript/src/domain/groupChannelModeration/component/GroupChannelModerationMenu.d.ts +2 -1
  217. package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersHeader.d.ts +2 -1
  218. package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersList.d.ts +2 -1
  219. package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersStatusEmpty.d.ts +2 -1
  220. package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersStatusLoading.d.ts +2 -1
  221. package/lib/typescript/src/domain/groupChannelNotifications/component/GroupChannelNotificationsHeader.d.ts +2 -1
  222. package/lib/typescript/src/domain/groupChannelNotifications/component/GroupChannelNotificationsView.d.ts +2 -1
  223. package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsHeader.d.ts +2 -1
  224. package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsList.d.ts +2 -1
  225. package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsStatusEmpty.d.ts +2 -1
  226. package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsStatusLoading.d.ts +2 -1
  227. package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsHeader.d.ts +2 -1
  228. package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsInfo.d.ts +2 -1
  229. package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsMenu.d.ts +2 -1
  230. package/lib/typescript/src/domain/messageSearch/component/MessageSearchHeader.d.ts +2 -1
  231. package/lib/typescript/src/domain/messageSearch/component/MessageSearchList.d.ts +2 -1
  232. package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusEmpty.d.ts +2 -1
  233. package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusLoading.d.ts +2 -1
  234. package/lib/typescript/src/domain/openChannel/component/OpenChannelHeader.d.ts +2 -2
  235. package/lib/typescript/src/domain/openChannel/component/OpenChannelInput.d.ts +1 -1
  236. package/lib/typescript/src/domain/openChannel/component/OpenChannelMessageList.d.ts +1 -1
  237. package/lib/typescript/src/domain/openChannel/component/OpenChannelStatusEmpty.d.ts +2 -1
  238. package/lib/typescript/src/domain/openChannel/component/OpenChannelStatusLoading.d.ts +2 -1
  239. package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersHeader.d.ts +2 -1
  240. package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersList.d.ts +2 -1
  241. package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersStatusEmpty.d.ts +2 -1
  242. package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersStatusLoading.d.ts +2 -1
  243. package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateHeader.d.ts +2 -1
  244. package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateProfileInput.d.ts +2 -1
  245. package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateStatusLoading.d.ts +2 -1
  246. package/lib/typescript/src/domain/openChannelList/component/OpenChannelListHeader.d.ts +2 -1
  247. package/lib/typescript/src/domain/openChannelList/component/OpenChannelListList.d.ts +2 -1
  248. package/lib/typescript/src/domain/openChannelList/component/OpenChannelListStatusEmpty.d.ts +2 -1
  249. package/lib/typescript/src/domain/openChannelList/component/OpenChannelListStatusLoading.d.ts +2 -1
  250. package/lib/typescript/src/domain/openChannelModeration/component/OpenChannelModerationHeader.d.ts +2 -1
  251. package/lib/typescript/src/domain/openChannelModeration/component/OpenChannelModerationMenu.d.ts +2 -1
  252. package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsHeader.d.ts +2 -1
  253. package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsList.d.ts +2 -1
  254. package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsStatusEmpty.d.ts +2 -1
  255. package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsStatusLoading.d.ts +2 -1
  256. package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsHeader.d.ts +2 -1
  257. package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsList.d.ts +2 -1
  258. package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsStatusEmpty.d.ts +2 -1
  259. package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsStatusLoading.d.ts +2 -1
  260. package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsHeader.d.ts +2 -1
  261. package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsInfo.d.ts +2 -1
  262. package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsMenu.d.ts +2 -1
  263. package/lib/typescript/src/domain/userList/component/UserListHeader.d.ts +3 -3
  264. package/lib/typescript/src/domain/userList/component/UserListList.d.ts +1 -1
  265. package/lib/typescript/src/domain/userList/component/UserListStatusEmpty.d.ts +2 -1
  266. package/lib/typescript/src/domain/userList/component/UserListStatusLoading.d.ts +2 -1
  267. package/lib/typescript/src/domain/userList/types.d.ts +2 -2
  268. package/lib/typescript/src/hooks/useChannelInputItems.d.ts +10 -0
  269. package/lib/typescript/src/hooks/useVoiceMessageInput.d.ts +53 -0
  270. package/lib/typescript/src/index.d.ts +4 -0
  271. package/lib/typescript/src/libs/MentionManager.d.ts +2 -1
  272. package/lib/typescript/src/libs/SBUUtils.d.ts +1 -0
  273. package/lib/typescript/src/libs/VoiceMessageConfig.d.ts +25 -0
  274. package/lib/typescript/src/localization/StringSet.type.d.ts +7 -0
  275. package/lib/typescript/src/platform/createPlayerService.expo.d.ts +7 -0
  276. package/lib/typescript/src/platform/createPlayerService.native.d.ts +9 -0
  277. package/lib/typescript/src/platform/createRecorderService.expo.d.ts +7 -0
  278. package/lib/typescript/src/platform/createRecorderService.native.d.ts +9 -0
  279. package/lib/typescript/src/platform/types.d.ts +100 -1
  280. package/lib/typescript/src/types.d.ts +1 -1
  281. package/lib/typescript/src/version.d.ts +1 -1
  282. package/package.json +9 -11
  283. package/src/components/ChannelInput/MessageToReplyPreview.tsx +133 -0
  284. package/src/components/ChannelInput/SendInput.tsx +129 -320
  285. package/src/components/ChannelInput/VoiceMessageInput.tsx +206 -0
  286. package/src/components/ChannelInput/index.tsx +12 -4
  287. package/src/components/ChannelMessageList/index.tsx +3 -1
  288. package/src/components/ChatFlatList/index.tsx +14 -3
  289. package/src/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.tsx +24 -11
  290. package/src/components/GroupChannelMessageRenderer/index.tsx +80 -3
  291. package/src/components/MessageSearchResultItem.tsx +2 -1
  292. package/src/components/OpenChannelMessageRenderer/index.tsx +1 -0
  293. package/src/components/ReactionBottomSheets/ReactionUserListBottomSheet.tsx +2 -2
  294. package/src/components/ReactionBottomSheets/index.tsx +3 -2
  295. package/src/components/StatusComposition.tsx +3 -3
  296. package/src/constants.ts +2 -0
  297. package/src/containers/GroupChannelPreviewContainer.tsx +2 -0
  298. package/src/containers/InternalErrorBoundaryContainer.tsx +1 -1
  299. package/src/containers/SendbirdUIKitContainer.tsx +105 -54
  300. package/src/contexts/PlatformServiceCtx.tsx +22 -20
  301. package/src/contexts/ReactionCtx.tsx +7 -5
  302. package/src/contexts/SendbirdChatCtx.tsx +10 -2
  303. package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +29 -43
  304. package/src/domain/groupChannel/module/moduleContext.tsx +119 -7
  305. package/src/domain/groupChannel/types.ts +41 -0
  306. package/src/domain/userList/types.ts +2 -2
  307. package/src/fragments/createGroupChannelFragment.tsx +32 -5
  308. package/src/fragments/createMessageSearchFragment.tsx +1 -1
  309. package/src/hooks/useChannelInputItems.ts +215 -0
  310. package/src/hooks/useConnection.ts +1 -1
  311. package/src/hooks/useMentionSuggestion.ts +2 -1
  312. package/src/hooks/useVoiceMessageInput.ts +237 -0
  313. package/src/index.ts +4 -0
  314. package/src/libs/MentionManager.tsx +1 -1
  315. package/src/libs/SBUUtils.ts +5 -0
  316. package/src/libs/VoiceMessageConfig.ts +28 -0
  317. package/src/localization/StringSet.type.ts +8 -0
  318. package/src/localization/createBaseStringSet.ts +27 -11
  319. package/src/platform/createFileService.expo.ts +10 -0
  320. package/src/platform/createFileService.native.ts +19 -0
  321. package/src/platform/createPlayerService.expo.tsx +142 -0
  322. package/src/platform/createPlayerService.native.tsx +148 -0
  323. package/src/platform/createRecorderService.expo.tsx +160 -0
  324. package/src/platform/createRecorderService.native.tsx +170 -0
  325. package/src/platform/types.ts +114 -1
  326. package/src/types.ts +1 -1
  327. package/src/version.ts +1 -1
@@ -0,0 +1,237 @@
1
+ import { useRef, useState } from 'react';
2
+
3
+ import { useAlert } from '@sendbird/uikit-react-native-foundation';
4
+ import { Logger, getVoiceMessageFileObject, matchesOneOf } from '@sendbird/uikit-utils';
5
+
6
+ import SBUUtils from '../libs/SBUUtils';
7
+ import { FileType } from '../platform/types';
8
+ import { useLocalization, usePlatformService } from './useContext';
9
+
10
+ type State = {
11
+ /**
12
+ * Status
13
+ *
14
+ * idle:
15
+ * - cancel(): idle
16
+ * - startRecording(): recording
17
+ * recording:
18
+ * - cancel(): idle
19
+ * - stopRecording(): recording_completed
20
+ * - send(): recording_completed > idle
21
+ * recording_completed:
22
+ * - cancel(): idle
23
+ * - playPlayer(): playing
24
+ * - send(): idle
25
+ * playing:
26
+ * - cancel(): idle
27
+ * - pausePlayer(): playing_paused
28
+ * - send(): idle
29
+ * playing_paused:
30
+ * - cancel(): idle
31
+ * - playPlayer(): playing
32
+ * - send(): idle
33
+ * */
34
+ status: 'idle' | 'recording' | 'recording_completed' | 'playing' | 'playing_paused';
35
+ recordingTime: {
36
+ currentTime: number;
37
+ minDuration: number;
38
+ maxDuration: number;
39
+ };
40
+ playingTime: {
41
+ currentTime: number;
42
+ duration: number;
43
+ };
44
+ };
45
+
46
+ export interface VoiceMessageInputResult {
47
+ actions: {
48
+ cancel: () => Promise<void>;
49
+ startRecording: () => Promise<void>;
50
+ stopRecording: () => Promise<void>;
51
+ playPlayer: () => Promise<void>;
52
+ pausePlayer: () => Promise<void>;
53
+ send: () => Promise<void>;
54
+ };
55
+ state: State;
56
+ }
57
+
58
+ type Props = {
59
+ onClose: () => Promise<void>;
60
+ onSend: (voiceFile: FileType, duration: number) => void;
61
+ };
62
+
63
+ const useVoiceMessageInput = ({ onSend, onClose }: Props): VoiceMessageInputResult => {
64
+ const { alert } = useAlert();
65
+ const { STRINGS } = useLocalization();
66
+ const { recorderService, playerService, fileService } = usePlatformService();
67
+ const [status, setStatus] = useState<State['status']>('idle');
68
+
69
+ const [recordingTime, setRecordingTime] = useState({
70
+ currentTime: 0,
71
+ minDuration: recorderService.options.minDuration,
72
+ maxDuration: recorderService.options.maxDuration,
73
+ });
74
+ const [playingTime, setPlayingTime] = useState({
75
+ currentTime: 0,
76
+ duration: 0,
77
+ });
78
+
79
+ const recordingPath = useRef<{ recordFilePath: string; uri: string }>();
80
+ const getVoiceMessageRecordingPath = () => {
81
+ if (!recordingPath.current) throw new Error('No recording path');
82
+ return recordingPath.current;
83
+ };
84
+ const setVoiceMessageRecordingPath = (path: { recordFilePath: string; uri: string }) => {
85
+ recordingPath.current = path;
86
+ };
87
+
88
+ const clear = async () => {
89
+ recordingPath.current = undefined;
90
+ await playerService.reset();
91
+ await recorderService.reset();
92
+ setRecordingTime({
93
+ currentTime: 0,
94
+ minDuration: recorderService.options.minDuration,
95
+ maxDuration: recorderService.options.maxDuration,
96
+ });
97
+ setPlayingTime({
98
+ currentTime: 0,
99
+ duration: 0,
100
+ });
101
+ setStatus('idle');
102
+ };
103
+
104
+ return {
105
+ state: {
106
+ status,
107
+ recordingTime,
108
+ playingTime,
109
+ },
110
+ actions: {
111
+ async cancel() {
112
+ await clear();
113
+ },
114
+ async startRecording() {
115
+ const granted = await recorderService.requestPermission();
116
+ if (!granted) {
117
+ await onClose();
118
+ alert({
119
+ title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
120
+ message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(
121
+ STRINGS.LABELS.PERMISSION_MICROPHONE,
122
+ STRINGS.LABELS.PERMISSION_APP_NAME,
123
+ ),
124
+ buttons: [{ text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK, onPress: () => SBUUtils.openSettings() }],
125
+ });
126
+ Logger.error('Failed to request permission for recorder');
127
+ return;
128
+ }
129
+
130
+ if (matchesOneOf(status, ['idle'])) {
131
+ // Before start recording, if player is not idle, reset it.
132
+ if (playerService.state !== 'idle') {
133
+ await playerService.reset();
134
+ }
135
+
136
+ const unsubscribeRecording = recorderService.addRecordingListener(({ currentTime }) => {
137
+ setRecordingTime({
138
+ currentTime,
139
+ maxDuration: recorderService.options.maxDuration,
140
+ minDuration: recorderService.options.minDuration,
141
+ });
142
+ setPlayingTime((prev) => ({ ...prev, duration: currentTime }));
143
+ });
144
+
145
+ const unsubscribeState = recorderService.addStateListener((state) => {
146
+ switch (state) {
147
+ case 'recording':
148
+ setStatus('recording');
149
+ break;
150
+ case 'completed':
151
+ setStatus('recording_completed');
152
+ unsubscribeRecording();
153
+ unsubscribeState();
154
+ break;
155
+ }
156
+ });
157
+
158
+ if (SBUUtils.isExpo()) {
159
+ await recorderService.record();
160
+ if (recorderService.uri) {
161
+ setVoiceMessageRecordingPath({ recordFilePath: recorderService.uri, uri: recorderService.uri });
162
+ }
163
+ } else {
164
+ setVoiceMessageRecordingPath(fileService.createRecordFilePath(recorderService.options.extension));
165
+ await recorderService.record(getVoiceMessageRecordingPath().recordFilePath);
166
+ }
167
+ }
168
+ },
169
+ async stopRecording() {
170
+ if (matchesOneOf(status, ['recording'])) {
171
+ await recorderService.stop();
172
+ }
173
+ },
174
+ async playPlayer() {
175
+ const granted = await playerService.requestPermission();
176
+ if (!granted) {
177
+ alert({
178
+ title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
179
+ message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(
180
+ STRINGS.LABELS.PERMISSION_DEVICE_STORAGE,
181
+ STRINGS.LABELS.PERMISSION_APP_NAME,
182
+ ),
183
+ buttons: [{ text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK, onPress: () => SBUUtils.openSettings() }],
184
+ });
185
+ Logger.error('Failed to request permission for player');
186
+ return;
187
+ }
188
+
189
+ if (matchesOneOf(status, ['recording_completed', 'playing_paused'])) {
190
+ const unsubscribePlayback = playerService.addPlaybackListener(({ currentTime, duration }) => {
191
+ setPlayingTime({ currentTime, duration });
192
+ });
193
+
194
+ const unsubscribeState = playerService.addStateListener((state) => {
195
+ switch (state) {
196
+ case 'playing':
197
+ setStatus('playing');
198
+ break;
199
+ case 'paused': {
200
+ setStatus('playing_paused');
201
+ unsubscribeState();
202
+ unsubscribePlayback();
203
+ break;
204
+ }
205
+ case 'stopped': {
206
+ setStatus('playing_paused');
207
+ unsubscribeState();
208
+ unsubscribePlayback();
209
+ setPlayingTime((prev) => ({ ...prev, currentTime: 0 }));
210
+ break;
211
+ }
212
+ }
213
+ });
214
+
215
+ await playerService.play(getVoiceMessageRecordingPath().recordFilePath);
216
+ }
217
+ },
218
+ async pausePlayer() {
219
+ if (matchesOneOf(status, ['playing'])) {
220
+ await playerService.pause();
221
+ }
222
+ },
223
+ async send() {
224
+ if (
225
+ matchesOneOf(status, ['recording', 'recording_completed', 'playing', 'playing_paused']) &&
226
+ recordingPath.current
227
+ ) {
228
+ const voiceFile = getVoiceMessageFileObject(recordingPath.current.uri, recorderService.options.extension);
229
+ onSend(voiceFile, Math.floor(recordingTime.currentTime));
230
+ await clear();
231
+ }
232
+ },
233
+ },
234
+ };
235
+ };
236
+
237
+ export default useVoiceMessageInput;
package/src/index.ts CHANGED
@@ -69,10 +69,14 @@ export { default as createNativeFileService } from './platform/createFileService
69
69
  export { default as createNativeClipboardService } from './platform/createClipboardService.native';
70
70
  export { default as createNativeNotificationService } from './platform/createNotificationService.native';
71
71
  export { default as createNativeMediaService } from './platform/createMediaService.native';
72
+ export { default as createNativePlayerService } from './platform/createPlayerService.native';
73
+ export { default as createNativeRecorderService } from './platform/createRecorderService.native';
72
74
  export { default as createExpoFileService } from './platform/createFileService.expo';
73
75
  export { default as createExpoClipboardService } from './platform/createClipboardService.expo';
74
76
  export { default as createExpoNotificationService } from './platform/createNotificationService.expo';
75
77
  export { default as createExpoMediaService } from './platform/createMediaService.expo';
78
+ export { default as createExpoPlayerService } from './platform/createPlayerService.expo';
79
+ export { default as createExpoRecorderService } from './platform/createRecorderService.expo';
76
80
  export * from './platform/types';
77
81
 
78
82
  /** Feature - shared **/
@@ -142,7 +142,7 @@ class MentionManager {
142
142
  ],
143
143
  };
144
144
  },
145
- { leftText: text, components: [] as (string | JSX.Element)[] },
145
+ { leftText: text, components: [] as (string | React.ReactNode)[] },
146
146
  );
147
147
 
148
148
  return [leftText, ...components];
@@ -37,4 +37,9 @@ export default class SBUUtils {
37
37
  await callback();
38
38
  } catch (e) {}
39
39
  }
40
+
41
+ static isExpo() {
42
+ const _g = global ?? window;
43
+ return typeof _g === 'object' && 'expo' in _g;
44
+ }
40
45
  }
@@ -0,0 +1,28 @@
1
+ export interface VoiceMessageConfigInterface {
2
+ recorder: {
3
+ minDuration: number;
4
+ maxDuration: number;
5
+ };
6
+ }
7
+
8
+ class VoiceMessageConfig {
9
+ static DEFAULT = {
10
+ RECORDER: {
11
+ MIN_DURATION: 1000,
12
+ MAX_DURATION: 600 * 1000,
13
+ EXTENSION: 'm4a',
14
+
15
+ BIT_RATE: 12000,
16
+ SAMPLE_RATE: 11025,
17
+ CHANNELS: 1,
18
+ },
19
+ };
20
+
21
+ constructor(private _config: VoiceMessageConfigInterface) {}
22
+
23
+ get recorder() {
24
+ return this._config.recorder;
25
+ }
26
+ }
27
+
28
+ export default VoiceMessageConfig;
@@ -240,6 +240,7 @@ export interface StringSet {
240
240
  PERMISSION_APP_NAME: string;
241
241
  PERMISSION_CAMERA: string;
242
242
  PERMISSION_DEVICE_STORAGE: string;
243
+ PERMISSION_MICROPHONE: string;
243
244
 
244
245
  USER_NO_NAME: string;
245
246
  CHANNEL_NO_MEMBERS: string;
@@ -249,6 +250,7 @@ export interface StringSet {
249
250
  parentMessage: SendbirdUserMessage | SendbirdFileMessage,
250
251
  currentUserId?: string,
251
252
  ) => string;
253
+ MESSAGE_UNAVAILABLE: string;
252
254
 
253
255
  USER_BAR_ME_POSTFIX: string;
254
256
  USER_BAR_OPERATOR: string;
@@ -291,6 +293,10 @@ export interface StringSet {
291
293
  /** Channel > Message > Failed **/
292
294
  CHANNEL_MESSAGE_FAILED_RETRY: string;
293
295
  CHANNEL_MESSAGE_FAILED_REMOVE: string;
296
+
297
+ /** Voice message **/
298
+ VOICE_MESSAGE: string;
299
+ VOICE_MESSAGE_INPUT_CANCEL: string;
294
300
  };
295
301
  FILE_VIEWER: {
296
302
  TITLE: (message: SendbirdFileMessage) => string;
@@ -331,6 +337,8 @@ export interface StringSet {
331
337
  RESEND_MSG_ERROR: string;
332
338
  DELETE_MSG_ERROR: string;
333
339
  SEND_MSG_ERROR: string;
340
+ USER_MUTED_ERROR: string;
341
+ CHANNEL_FROZEN_ERROR: string;
334
342
  UPDATE_MSG_ERROR: string;
335
343
  TURN_ON_NOTIFICATIONS_ERROR: string;
336
344
  TURN_OFF_NOTIFICATIONS_ERROR: string;
@@ -3,16 +3,16 @@ import type { Locale } from 'date-fns';
3
3
  import type { PartialDeep } from '@sendbird/uikit-utils';
4
4
  import {
5
5
  getDateSeparatorFormat,
6
- getFileTypeFromMessage,
7
- getGroupChannelLastMessage,
8
6
  getGroupChannelPreviewTime,
9
7
  getGroupChannelTitle,
10
8
  getMessagePreviewBody,
11
9
  getMessagePreviewTime,
12
10
  getMessagePreviewTitle,
13
11
  getMessageTimeFormat,
12
+ getMessageType,
14
13
  getOpenChannelParticipants,
15
14
  getOpenChannelTitle,
15
+ isVoiceMessage,
16
16
  } from '@sendbird/uikit-utils';
17
17
 
18
18
  import { UNKNOWN_USER_ID } from '../constants';
@@ -33,6 +33,7 @@ type StringSetCreateOptions = {
33
33
  export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOptions): StringSet => {
34
34
  const USER_NO_NAME = overrides?.LABELS?.USER_NO_NAME ?? '(No name)';
35
35
  const CHANNEL_NO_MEMBERS = overrides?.LABELS?.CHANNEL_NO_MEMBERS ?? '(No members)';
36
+
36
37
  return {
37
38
  OPEN_CHANNEL: {
38
39
  HEADER_TITLE: (channel) => getOpenChannelTitle(channel),
@@ -191,7 +192,11 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
191
192
  CHANNEL_PREVIEW_TITLE: (currentUserId, channel) =>
192
193
  getGroupChannelTitle(currentUserId, channel, USER_NO_NAME, CHANNEL_NO_MEMBERS),
193
194
  CHANNEL_PREVIEW_TITLE_CAPTION: (channel, locale) => getGroupChannelPreviewTime(channel, locale ?? dateLocale),
194
- CHANNEL_PREVIEW_BODY: (channel) => getGroupChannelLastMessage(channel),
195
+ CHANNEL_PREVIEW_BODY: (channel) => {
196
+ if (!channel.lastMessage) return '';
197
+ if (isVoiceMessage(channel.lastMessage)) return 'Voice message';
198
+ return getMessagePreviewBody(channel.lastMessage);
199
+ },
195
200
  TYPE_SELECTOR_HEADER_TITLE: 'Channel type',
196
201
  TYPE_SELECTOR_GROUP: 'Group',
197
202
  TYPE_SELECTOR_SUPER_GROUP: 'Super group',
@@ -232,7 +237,10 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
232
237
  HEADER_INPUT_PLACEHOLDER: 'Search',
233
238
  HEADER_RIGHT: 'Search',
234
239
  SEARCH_RESULT_ITEM_TITLE: (message) => getMessagePreviewTitle(message),
235
- SEARCH_RESULT_ITEM_BODY: (message) => getMessagePreviewBody(message),
240
+ SEARCH_RESULT_ITEM_BODY: (message) => {
241
+ if (isVoiceMessage(message)) return 'Voice message';
242
+ return getMessagePreviewBody(message);
243
+ },
236
244
  SEARCH_RESULT_ITEM_TITLE_CAPTION: (message, locale) => {
237
245
  return getMessagePreviewTime(message.createdAt, locale ?? dateLocale);
238
246
  },
@@ -241,6 +249,7 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
241
249
  PERMISSION_APP_NAME: 'Application',
242
250
  PERMISSION_CAMERA: 'camera',
243
251
  PERMISSION_DEVICE_STORAGE: 'device storage',
252
+ PERMISSION_MICROPHONE: 'microphone',
244
253
  USER_NO_NAME,
245
254
  CHANNEL_NO_MEMBERS,
246
255
  TYPING_INDICATOR_TYPINGS: (users, NO_NAME = USER_NO_NAME) => {
@@ -255,6 +264,7 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
255
264
  const receiverNickname = parent.sender.nickname || USER_NO_NAME;
256
265
  return `${reply.sender.userId !== currentUserId ? senderNickname : 'You'} replied to ${receiverNickname}`;
257
266
  },
267
+ MESSAGE_UNAVAILABLE: 'Message unavailable',
258
268
 
259
269
  USER_BAR_ME_POSTFIX: ' (You)',
260
270
  USER_BAR_OPERATOR: 'Operator',
@@ -288,22 +298,26 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
288
298
  CHANNEL_INPUT_REPLY_PREVIEW_TITLE: (user) => `Reply to ${user.nickname || USER_NO_NAME}`,
289
299
  CHANNEL_INPUT_REPLY_PREVIEW_BODY: (message) => {
290
300
  if (message.isFileMessage()) {
291
- const fileType = getFileTypeFromMessage(message);
292
- switch (fileType) {
293
- case 'image':
301
+ const messageType = getMessageType(message);
302
+ switch (messageType) {
303
+ case 'file.image':
294
304
  return message.type.toLowerCase().includes('gif') ? 'GIF' : 'Photo';
295
- case 'video':
305
+ case 'file.video':
296
306
  return 'Video';
297
- case 'audio':
307
+ case 'file.audio':
298
308
  return 'Audio';
309
+ case 'file.voice':
310
+ return 'Voice message';
299
311
  default:
300
312
  return message.name;
301
313
  }
302
314
  } else if (message.isUserMessage()) {
303
315
  return message.message;
304
316
  }
305
- return 'Unknown message type.';
317
+ return 'Unknown message';
306
318
  },
319
+ VOICE_MESSAGE: 'Voice message',
320
+ VOICE_MESSAGE_INPUT_CANCEL: 'Cancel',
307
321
  ...overrides?.LABELS,
308
322
  },
309
323
  FILE_VIEWER: {
@@ -329,7 +343,7 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
329
343
  ALERT_DEFAULT_OK: 'OK',
330
344
  ALERT_PERMISSIONS_TITLE: 'Allow access?',
331
345
  ALERT_PERMISSIONS_MESSAGE: (permission, appName = 'Application') => {
332
- return `${appName} need permission to access your ${permission}.`;
346
+ return `${appName} needs permission to access your ${permission}.`;
333
347
  },
334
348
  ALERT_PERMISSIONS_OK: 'Go to settings',
335
349
  PROMPT_DEFAULT_OK: 'Submit',
@@ -348,6 +362,8 @@ export const createBaseStringSet = ({ dateLocale, overrides }: StringSetCreateOp
348
362
  DELETE_MSG_ERROR: "Couldn't delete message.",
349
363
  RESEND_MSG_ERROR: "Couldn't send message.",
350
364
  SEND_MSG_ERROR: "Couldn't send message.",
365
+ USER_MUTED_ERROR: "You're muted by the operator.",
366
+ CHANNEL_FROZEN_ERROR: 'Channel is frozen.',
351
367
  UPDATE_MSG_ERROR: "Couldn't edit message.",
352
368
  TURN_ON_NOTIFICATIONS_ERROR: "Couldn't turn on notifications.",
353
369
  TURN_OFF_NOTIFICATIONS_ERROR: "Couldn't turn off notifications.",
@@ -142,6 +142,16 @@ const createExpoFileService = ({
142
142
  }
143
143
  return response.uri;
144
144
  }
145
+ createRecordFilePath(customExtension = 'm4a'): { recordFilePath: string; uri: string } {
146
+ const basePath = fsModule.cacheDirectory;
147
+ if (!basePath) throw new Error('Cannot determine directory');
148
+
149
+ const filename = `record-${Date.now()}.${customExtension}`;
150
+ return {
151
+ uri: `${basePath}/${filename}`,
152
+ recordFilePath: `${basePath}/${filename}`,
153
+ };
154
+ }
145
155
  }
146
156
 
147
157
  return new ExpoFileServiceInterface();
@@ -241,6 +241,25 @@ const createNativeFileService = ({
241
241
  } as const,
242
242
  };
243
243
  };
244
+
245
+ createRecordFilePath(customExtension = 'm4a'): { recordFilePath: string; uri: string } {
246
+ const filename = `record-${Date.now()}.${customExtension}`;
247
+ const path = `${fsModule.Dirs.CacheDir}/${filename}`;
248
+ return Platform.select({
249
+ ios: {
250
+ uri: path,
251
+ recordFilePath: filename,
252
+ },
253
+ android: {
254
+ uri: path.startsWith('file://') ? path : 'file://' + path,
255
+ recordFilePath: path,
256
+ },
257
+ default: {
258
+ uri: path,
259
+ recordFilePath: path,
260
+ },
261
+ });
262
+ }
244
263
  }
245
264
 
246
265
  return new NativeFileService();
@@ -0,0 +1,142 @@
1
+ import type * as ExpoAV from 'expo-av';
2
+
3
+ import { matchesOneOf } from '@sendbird/uikit-utils';
4
+
5
+ import expoPermissionGranted from '../utils/expoPermissionGranted';
6
+ import type { PlayerServiceInterface, Unsubscribe } from './types';
7
+
8
+ type Modules = {
9
+ avModule: typeof ExpoAV;
10
+ };
11
+ type PlaybackListener = Parameters<PlayerServiceInterface['addPlaybackListener']>[number];
12
+ type StateListener = Parameters<PlayerServiceInterface['addStateListener']>[number];
13
+ const createExpoPlayerService = ({ avModule }: Modules): PlayerServiceInterface => {
14
+ const sound = new avModule.Audio.Sound();
15
+
16
+ class VoicePlayer implements PlayerServiceInterface {
17
+ uri?: string;
18
+ state: PlayerServiceInterface['state'] = 'idle';
19
+
20
+ private readonly playbackSubscribers = new Set<PlaybackListener>();
21
+ private readonly stateSubscribers = new Set<StateListener>();
22
+
23
+ private setState = (state: PlayerServiceInterface['state']) => {
24
+ this.state = state;
25
+ this.stateSubscribers.forEach((callback) => {
26
+ callback(state);
27
+ });
28
+ };
29
+
30
+ private setListener = () => {
31
+ sound.setProgressUpdateIntervalAsync(100);
32
+ sound.setOnPlaybackStatusUpdate((status) => {
33
+ if (status.isLoaded) {
34
+ if (status.didJustFinish) this.stop();
35
+ if (status.isPlaying) {
36
+ this.playbackSubscribers.forEach((callback) => {
37
+ callback({
38
+ currentTime: status.positionMillis,
39
+ duration: status.durationMillis ?? 0,
40
+ stopped: status.didJustFinish,
41
+ });
42
+ });
43
+ }
44
+ }
45
+ });
46
+ };
47
+
48
+ private removeListener = () => {
49
+ sound.setOnPlaybackStatusUpdate(null);
50
+ };
51
+
52
+ public requestPermission = async (): Promise<boolean> => {
53
+ const status = await avModule.Audio.getPermissionsAsync();
54
+ if (expoPermissionGranted([status])) {
55
+ return true;
56
+ } else {
57
+ const status = await avModule.Audio.requestPermissionsAsync();
58
+ return expoPermissionGranted([status]);
59
+ }
60
+ };
61
+
62
+ public addPlaybackListener = (callback: PlaybackListener): Unsubscribe => {
63
+ this.playbackSubscribers.add(callback);
64
+ return () => {
65
+ this.playbackSubscribers.delete(callback);
66
+ };
67
+ };
68
+
69
+ public addStateListener = (callback: (state: PlayerServiceInterface['state']) => void): Unsubscribe => {
70
+ this.stateSubscribers.add(callback);
71
+ return () => {
72
+ this.stateSubscribers.delete(callback);
73
+ };
74
+ };
75
+
76
+ private prepare = async (uri: string) => {
77
+ this.setState('preparing');
78
+ await sound.loadAsync({ uri }, { shouldPlay: false }, true);
79
+ this.uri = uri;
80
+ };
81
+
82
+ public play = async (uri: string): Promise<void> => {
83
+ if (matchesOneOf(this.state, ['idle', 'stopped'])) {
84
+ try {
85
+ await this.prepare(uri);
86
+ this.setListener();
87
+ await sound.playAsync();
88
+ this.setState('playing');
89
+ } catch (e) {
90
+ this.setState('idle');
91
+ this.uri = undefined;
92
+ this.removeListener();
93
+ throw e;
94
+ }
95
+ } else if (matchesOneOf(this.state, ['paused']) && this.uri === uri) {
96
+ try {
97
+ this.setListener();
98
+ await sound.playAsync();
99
+ this.setState('playing');
100
+ } catch (e) {
101
+ this.removeListener();
102
+ throw e;
103
+ }
104
+ }
105
+ };
106
+
107
+ public pause = async (): Promise<void> => {
108
+ if (matchesOneOf(this.state, ['playing'])) {
109
+ await sound.pauseAsync();
110
+ this.removeListener();
111
+ this.setState('paused');
112
+ }
113
+ };
114
+
115
+ public stop = async (): Promise<void> => {
116
+ if (matchesOneOf(this.state, ['playing', 'paused'])) {
117
+ await sound.stopAsync();
118
+ await sound.unloadAsync();
119
+ this.removeListener();
120
+ this.setState('stopped');
121
+ }
122
+ };
123
+
124
+ public reset = async (): Promise<void> => {
125
+ await this.stop();
126
+ this.setState('idle');
127
+ this.uri = undefined;
128
+ this.playbackSubscribers.clear();
129
+ this.stateSubscribers.clear();
130
+ };
131
+
132
+ public seek = async (time: number): Promise<void> => {
133
+ if (matchesOneOf(this.state, ['playing', 'paused'])) {
134
+ await sound.playFromPositionAsync(time);
135
+ }
136
+ };
137
+ }
138
+
139
+ return new VoicePlayer();
140
+ };
141
+
142
+ export default createExpoPlayerService;