@sendbird/uikit-react-native 3.1.2 → 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.
- package/README.md +67 -42
- package/lib/commonjs/components/ChannelInput/MessageToReplyPreview.js +145 -0
- package/lib/commonjs/components/ChannelInput/MessageToReplyPreview.js.map +1 -0
- package/lib/commonjs/components/ChannelInput/SendInput.js +147 -312
- package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js +238 -0
- package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js.map +1 -0
- package/lib/commonjs/components/ChannelInput/index.js +5 -1
- package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
- package/lib/commonjs/components/ChannelMessageList/index.js +1 -1
- package/lib/commonjs/components/ChannelMessageList/index.js.map +1 -1
- package/lib/commonjs/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js +24 -13
- package/lib/commonjs/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js.map +1 -1
- package/lib/commonjs/components/GroupChannelMessageRenderer/index.js +100 -5
- package/lib/commonjs/components/GroupChannelMessageRenderer/index.js.map +1 -1
- package/lib/commonjs/components/MessageSearchResultItem.js +1 -0
- package/lib/commonjs/components/MessageSearchResultItem.js.map +1 -1
- package/lib/commonjs/components/OpenChannelMessageRenderer/index.js +1 -0
- package/lib/commonjs/components/OpenChannelMessageRenderer/index.js.map +1 -1
- package/lib/commonjs/components/ReactionBottomSheets/ReactionUserListBottomSheet.js +2 -2
- package/lib/commonjs/components/ReactionBottomSheets/ReactionUserListBottomSheet.js.map +1 -1
- package/lib/commonjs/components/ReactionBottomSheets/index.js.map +1 -1
- package/lib/commonjs/components/StatusComposition.js.map +1 -1
- package/lib/commonjs/constants.js +5 -1
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/containers/GroupChannelPreviewContainer.js +1 -0
- package/lib/commonjs/containers/GroupChannelPreviewContainer.js.map +1 -1
- package/lib/commonjs/containers/InternalErrorBoundaryContainer.js.map +1 -1
- package/lib/commonjs/containers/SendbirdUIKitContainer.js +72 -34
- package/lib/commonjs/containers/SendbirdUIKitContainer.js.map +1 -1
- package/lib/commonjs/contexts/PlatformServiceCtx.js +16 -12
- package/lib/commonjs/contexts/PlatformServiceCtx.js.map +1 -1
- package/lib/commonjs/contexts/ReactionCtx.js +3 -2
- package/lib/commonjs/contexts/ReactionCtx.js.map +1 -1
- package/lib/commonjs/contexts/SendbirdChatCtx.js +2 -0
- package/lib/commonjs/contexts/SendbirdChatCtx.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +27 -42
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/module/moduleContext.js +109 -5
- package/lib/commonjs/domain/groupChannel/module/moduleContext.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/types.js.map +1 -1
- package/lib/commonjs/domain/userList/types.js.map +1 -1
- package/lib/commonjs/fragments/createGroupChannelFragment.js +30 -4
- package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/commonjs/fragments/createMessageSearchFragment.js +1 -1
- package/lib/commonjs/fragments/createMessageSearchFragment.js.map +1 -1
- package/lib/commonjs/hooks/useChannelInputItems.js +211 -0
- package/lib/commonjs/hooks/useChannelInputItems.js.map +1 -0
- package/lib/commonjs/hooks/useConnection.js +1 -1
- package/lib/commonjs/hooks/useConnection.js.map +1 -1
- package/lib/commonjs/hooks/useVoiceMessageInput.js +207 -0
- package/lib/commonjs/hooks/useVoiceMessageInput.js.map +1 -0
- package/lib/commonjs/index.js +32 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/libs/MentionManager.js.map +1 -1
- package/lib/commonjs/libs/SBUUtils.js +4 -0
- package/lib/commonjs/libs/SBUUtils.js.map +1 -1
- package/lib/commonjs/libs/VoiceMessageConfig.js +30 -0
- package/lib/commonjs/libs/VoiceMessageConfig.js.map +1 -0
- package/lib/commonjs/localization/StringSet.type.js.map +1 -1
- package/lib/commonjs/localization/createBaseStringSet.js +24 -9
- package/lib/commonjs/localization/createBaseStringSet.js.map +1 -1
- package/lib/commonjs/platform/createFileService.expo.js +10 -0
- package/lib/commonjs/platform/createFileService.expo.js.map +1 -1
- package/lib/commonjs/platform/createFileService.native.js +19 -0
- package/lib/commonjs/platform/createFileService.native.js.map +1 -1
- package/lib/commonjs/platform/createPlayerService.expo.js +137 -0
- package/lib/commonjs/platform/createPlayerService.expo.js.map +1 -0
- package/lib/commonjs/platform/createPlayerService.native.js +139 -0
- package/lib/commonjs/platform/createPlayerService.native.js.map +1 -0
- package/lib/commonjs/platform/createRecorderService.expo.js +158 -0
- package/lib/commonjs/platform/createRecorderService.expo.js.map +1 -0
- package/lib/commonjs/platform/createRecorderService.native.js +157 -0
- package/lib/commonjs/platform/createRecorderService.native.js.map +1 -0
- package/lib/commonjs/platform/types.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/components/ChannelInput/MessageToReplyPreview.js +137 -0
- package/lib/module/components/ChannelInput/MessageToReplyPreview.js.map +1 -0
- package/lib/module/components/ChannelInput/SendInput.js +149 -314
- package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/module/components/ChannelInput/VoiceMessageInput.js +228 -0
- package/lib/module/components/ChannelInput/VoiceMessageInput.js.map +1 -0
- package/lib/module/components/ChannelInput/index.js +5 -1
- package/lib/module/components/ChannelInput/index.js.map +1 -1
- package/lib/module/components/ChannelMessageList/index.js +2 -2
- package/lib/module/components/ChannelMessageList/index.js.map +1 -1
- package/lib/module/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js +24 -13
- package/lib/module/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.js.map +1 -1
- package/lib/module/components/GroupChannelMessageRenderer/index.js +99 -6
- package/lib/module/components/GroupChannelMessageRenderer/index.js.map +1 -1
- package/lib/module/components/MessageSearchResultItem.js +2 -1
- package/lib/module/components/MessageSearchResultItem.js.map +1 -1
- package/lib/module/components/OpenChannelMessageRenderer/index.js +1 -0
- package/lib/module/components/OpenChannelMessageRenderer/index.js.map +1 -1
- package/lib/module/components/ReactionBottomSheets/ReactionUserListBottomSheet.js +2 -2
- package/lib/module/components/ReactionBottomSheets/ReactionUserListBottomSheet.js.map +1 -1
- package/lib/module/components/ReactionBottomSheets/index.js.map +1 -1
- package/lib/module/components/StatusComposition.js.map +1 -1
- package/lib/module/constants.js +2 -0
- package/lib/module/constants.js.map +1 -1
- package/lib/module/containers/GroupChannelPreviewContainer.js +2 -1
- package/lib/module/containers/GroupChannelPreviewContainer.js.map +1 -1
- package/lib/module/containers/InternalErrorBoundaryContainer.js.map +1 -1
- package/lib/module/containers/SendbirdUIKitContainer.js +74 -36
- package/lib/module/containers/SendbirdUIKitContainer.js.map +1 -1
- package/lib/module/contexts/PlatformServiceCtx.js +14 -11
- package/lib/module/contexts/PlatformServiceCtx.js.map +1 -1
- package/lib/module/contexts/ReactionCtx.js +3 -2
- package/lib/module/contexts/ReactionCtx.js.map +1 -1
- package/lib/module/contexts/SendbirdChatCtx.js +2 -0
- package/lib/module/contexts/SendbirdChatCtx.js.map +1 -1
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +28 -43
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/module/domain/groupChannel/module/moduleContext.js +111 -7
- package/lib/module/domain/groupChannel/module/moduleContext.js.map +1 -1
- package/lib/module/domain/groupChannel/types.js.map +1 -1
- package/lib/module/domain/userList/types.js.map +1 -1
- package/lib/module/fragments/createGroupChannelFragment.js +32 -6
- package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/module/fragments/createMessageSearchFragment.js +1 -1
- package/lib/module/fragments/createMessageSearchFragment.js.map +1 -1
- package/lib/module/hooks/useChannelInputItems.js +203 -0
- package/lib/module/hooks/useChannelInputItems.js.map +1 -0
- package/lib/module/hooks/useConnection.js +1 -1
- package/lib/module/hooks/useConnection.js.map +1 -1
- package/lib/module/hooks/useVoiceMessageInput.js +199 -0
- package/lib/module/hooks/useVoiceMessageInput.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/libs/MentionManager.js.map +1 -1
- package/lib/module/libs/SBUUtils.js +4 -0
- package/lib/module/libs/SBUUtils.js.map +1 -1
- package/lib/module/libs/VoiceMessageConfig.js +23 -0
- package/lib/module/libs/VoiceMessageConfig.js.map +1 -0
- package/lib/module/localization/StringSet.type.js.map +1 -1
- package/lib/module/localization/createBaseStringSet.js +25 -10
- package/lib/module/localization/createBaseStringSet.js.map +1 -1
- package/lib/module/platform/createFileService.expo.js +10 -0
- package/lib/module/platform/createFileService.expo.js.map +1 -1
- package/lib/module/platform/createFileService.native.js +19 -0
- package/lib/module/platform/createFileService.native.js.map +1 -1
- package/lib/module/platform/createPlayerService.expo.js +129 -0
- package/lib/module/platform/createPlayerService.expo.js.map +1 -0
- package/lib/module/platform/createPlayerService.native.js +132 -0
- package/lib/module/platform/createPlayerService.native.js.map +1 -0
- package/lib/module/platform/createRecorderService.expo.js +150 -0
- package/lib/module/platform/createRecorderService.expo.js.map +1 -0
- package/lib/module/platform/createRecorderService.native.js +149 -0
- package/lib/module/platform/createRecorderService.native.js.map +1 -0
- package/lib/module/platform/types.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/src/components/ChannelCover.d.ts +2 -1
- package/lib/typescript/src/components/ChannelInput/AttachmentsButton.d.ts +2 -1
- package/lib/typescript/src/components/ChannelInput/MessageToReplyPreview.d.ts +7 -0
- package/lib/typescript/src/components/ChannelInput/VoiceMessageInput.d.ts +11 -0
- package/lib/typescript/src/components/ChannelInput/index.d.ts +7 -3
- package/lib/typescript/src/components/ChannelMessageList/index.d.ts +1 -1
- package/lib/typescript/src/components/FileViewer.d.ts +2 -1
- package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageDateSeparator.d.ts +2 -1
- package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageFocusAnimation.d.ts +1 -1
- package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageOutgoingStatus.d.ts +1 -1
- package/lib/typescript/src/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.d.ts +4 -2
- package/lib/typescript/src/components/NewMessagesButton.d.ts +1 -1
- package/lib/typescript/src/components/OpenChannelMessageRenderer/OpenChannelMessageDateSeparator.d.ts +2 -1
- package/lib/typescript/src/components/ProviderLayout.d.ts +1 -1
- package/lib/typescript/src/components/ReactionAddons/BottomSheetReactionAddon.d.ts +2 -1
- package/lib/typescript/src/components/ReactionAddons/MessageReactionAddon.d.ts +2 -1
- package/lib/typescript/src/components/ReactionAddons/ReactionRoundedButton.d.ts +3 -2
- package/lib/typescript/src/components/ReactionAddons/index.d.ts +3 -2
- package/lib/typescript/src/components/ReactionBottomSheets/ReactionListBottomSheet.d.ts +2 -1
- package/lib/typescript/src/components/ReactionBottomSheets/ReactionUserListBottomSheet.d.ts +2 -1
- package/lib/typescript/src/components/ReactionBottomSheets/index.d.ts +4 -4
- package/lib/typescript/src/components/ScrollToBottomButton.d.ts +1 -1
- package/lib/typescript/src/components/StatusComposition.d.ts +4 -4
- package/lib/typescript/src/components/TypedPlaceholder.d.ts +2 -1
- package/lib/typescript/src/components/UserActionBar.d.ts +2 -1
- package/lib/typescript/src/components/UserSelectableBar.d.ts +2 -1
- package/lib/typescript/src/constants.d.ts +2 -0
- package/lib/typescript/src/containers/GroupChannelPreviewContainer.d.ts +2 -1
- package/lib/typescript/src/containers/InternalErrorBoundaryContainer.d.ts +3 -3
- package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +19 -8
- package/lib/typescript/src/contexts/LocalizationCtx.d.ts +1 -1
- package/lib/typescript/src/contexts/PlatformServiceCtx.d.ts +8 -8
- package/lib/typescript/src/contexts/ReactionCtx.d.ts +5 -2
- package/lib/typescript/src/contexts/SendbirdChatCtx.d.ts +6 -3
- package/lib/typescript/src/contexts/UserProfileCtx.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelInput.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelMessageList.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelSuggestedMentionList.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannel/types.d.ts +45 -0
- package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersList.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelBannedUsers/component/GroupChannelBannedUsersStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListList.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelList/component/GroupChannelListTypeSelector.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelModeration/component/GroupChannelModerationHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelModeration/component/GroupChannelModerationMenu.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersList.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelMutedMembers/component/GroupChannelMutedMembersStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelNotifications/component/GroupChannelNotificationsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelNotifications/component/GroupChannelNotificationsView.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsList.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelOperators/component/GroupChannelOperatorsStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsInfo.d.ts +2 -1
- package/lib/typescript/src/domain/groupChannelSettings/component/GroupChannelSettingsMenu.d.ts +2 -1
- package/lib/typescript/src/domain/messageSearch/component/MessageSearchHeader.d.ts +2 -1
- package/lib/typescript/src/domain/messageSearch/component/MessageSearchList.d.ts +2 -1
- package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/messageSearch/component/MessageSearchStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannel/component/OpenChannelHeader.d.ts +2 -2
- package/lib/typescript/src/domain/openChannel/component/OpenChannelInput.d.ts +1 -1
- package/lib/typescript/src/domain/openChannel/component/OpenChannelMessageList.d.ts +1 -1
- package/lib/typescript/src/domain/openChannel/component/OpenChannelStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/openChannel/component/OpenChannelStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersList.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelBannedUsers/component/OpenChannelBannedUsersStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateProfileInput.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelCreate/component/OpenChannelCreateStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelList/component/OpenChannelListHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelList/component/OpenChannelListList.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelList/component/OpenChannelListStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelList/component/OpenChannelListStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelModeration/component/OpenChannelModerationHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelModeration/component/OpenChannelModerationMenu.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsList.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelMutedParticipants/component/OpenChannelMutedParticipantsStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsList.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelOperators/component/OpenChannelOperatorsStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsHeader.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsInfo.d.ts +2 -1
- package/lib/typescript/src/domain/openChannelSettings/component/OpenChannelSettingsMenu.d.ts +2 -1
- package/lib/typescript/src/domain/userList/component/UserListHeader.d.ts +3 -3
- package/lib/typescript/src/domain/userList/component/UserListList.d.ts +1 -1
- package/lib/typescript/src/domain/userList/component/UserListStatusEmpty.d.ts +2 -1
- package/lib/typescript/src/domain/userList/component/UserListStatusLoading.d.ts +2 -1
- package/lib/typescript/src/domain/userList/types.d.ts +2 -2
- package/lib/typescript/src/hooks/useChannelInputItems.d.ts +10 -0
- package/lib/typescript/src/hooks/useVoiceMessageInput.d.ts +53 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/libs/MentionManager.d.ts +2 -1
- package/lib/typescript/src/libs/SBUUtils.d.ts +1 -0
- package/lib/typescript/src/libs/VoiceMessageConfig.d.ts +25 -0
- package/lib/typescript/src/localization/StringSet.type.d.ts +7 -0
- package/lib/typescript/src/platform/createPlayerService.expo.d.ts +7 -0
- package/lib/typescript/src/platform/createPlayerService.native.d.ts +9 -0
- package/lib/typescript/src/platform/createRecorderService.expo.d.ts +7 -0
- package/lib/typescript/src/platform/createRecorderService.native.d.ts +9 -0
- package/lib/typescript/src/platform/types.d.ts +100 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +9 -11
- package/src/components/ChannelInput/MessageToReplyPreview.tsx +133 -0
- package/src/components/ChannelInput/SendInput.tsx +129 -320
- package/src/components/ChannelInput/VoiceMessageInput.tsx +206 -0
- package/src/components/ChannelInput/index.tsx +12 -4
- package/src/components/ChannelMessageList/index.tsx +3 -1
- package/src/components/GroupChannelMessageRenderer/GroupChannelMessageParentMessage.tsx +24 -11
- package/src/components/GroupChannelMessageRenderer/index.tsx +80 -3
- package/src/components/MessageSearchResultItem.tsx +2 -1
- package/src/components/OpenChannelMessageRenderer/index.tsx +1 -0
- package/src/components/ReactionBottomSheets/ReactionUserListBottomSheet.tsx +2 -2
- package/src/components/ReactionBottomSheets/index.tsx +3 -2
- package/src/components/StatusComposition.tsx +3 -3
- package/src/constants.ts +2 -0
- package/src/containers/GroupChannelPreviewContainer.tsx +2 -0
- package/src/containers/InternalErrorBoundaryContainer.tsx +1 -1
- package/src/containers/SendbirdUIKitContainer.tsx +103 -59
- package/src/contexts/PlatformServiceCtx.tsx +22 -20
- package/src/contexts/ReactionCtx.tsx +7 -5
- package/src/contexts/SendbirdChatCtx.tsx +10 -2
- package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +29 -43
- package/src/domain/groupChannel/module/moduleContext.tsx +119 -7
- package/src/domain/groupChannel/types.ts +41 -0
- package/src/domain/userList/types.ts +2 -2
- package/src/fragments/createGroupChannelFragment.tsx +32 -5
- package/src/fragments/createMessageSearchFragment.tsx +1 -1
- package/src/hooks/useChannelInputItems.ts +215 -0
- package/src/hooks/useConnection.ts +1 -1
- package/src/hooks/useVoiceMessageInput.ts +237 -0
- package/src/index.ts +4 -0
- package/src/libs/MentionManager.tsx +1 -1
- package/src/libs/SBUUtils.ts +5 -0
- package/src/libs/VoiceMessageConfig.ts +28 -0
- package/src/localization/StringSet.type.ts +8 -0
- package/src/localization/createBaseStringSet.ts +27 -11
- package/src/platform/createFileService.expo.ts +10 -0
- package/src/platform/createFileService.native.ts +19 -0
- package/src/platform/createPlayerService.expo.tsx +142 -0
- package/src/platform/createPlayerService.native.tsx +148 -0
- package/src/platform/createRecorderService.expo.tsx +160 -0
- package/src/platform/createRecorderService.native.tsx +170 -0
- package/src/platform/types.ts +114 -1
- package/src/types.ts +1 -1
- 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 **/
|
package/src/libs/SBUUtils.ts
CHANGED
|
@@ -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) =>
|
|
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) =>
|
|
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
|
|
292
|
-
switch (
|
|
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
|
|
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}
|
|
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;
|