@sendbird/uikit-react-native
Version:
Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.
283 lines • 9.58 kB
JavaScript
import React, { forwardRef } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { MentionType, MessageMetaArray } from '@sendbird/chat/message';
import { Icon, Modal, TextInput, createStyleSheet, useAlert, useBottomSheet, useToast, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';
import { Logger, useDeferredModalState, useIIFE } from '@sendbird/uikit-utils';
import { VOICE_MESSAGE_META_ARRAY_DURATION_KEY, VOICE_MESSAGE_META_ARRAY_MESSAGE_TYPE_KEY } from '../../constants';
import { useChannelInputItems } from '../../hooks/useChannelInputItems';
import { useLocalization, usePlatformService, useSendbirdChat } from '../../hooks/useContext';
import SBUUtils from '../../libs/SBUUtils';
const SendInput = /*#__PURE__*/forwardRef(function SendInput(_ref, ref) {
let {
style,
VoiceMessageInput,
MessageToReplyPreview,
AttachmentsButton,
onPressSendUserMessage,
onPressSendFileMessage,
text,
onChangeText,
onSelectionChange,
mentionedUsers,
inputDisabled,
inputFrozen,
inputMuted,
channel,
messageToReply,
setMessageToReply,
messageForThread
} = _ref;
const {
playerService,
recorderService
} = usePlatformService();
const {
mentionManager,
sbOptions
} = useSendbirdChat();
const {
STRINGS
} = useLocalization();
const {
openSheet
} = useBottomSheet();
const toast = useToast();
const {
onClose,
onDismiss,
visible: voiceMessageInputVisible,
setVisible: setVoiceMessageInputVisible
} = useDeferredModalState();
const messageReplyParams = useIIFE(() => {
const {
groupChannel
} = sbOptions.uikit;
if (channel.isGroupChannel()) {
if (groupChannel.channel.replyType === 'quote_reply' && messageToReply) {
return {
parentMessageId: messageToReply.messageId,
isReplyToChannel: true
};
} else if (groupChannel.channel.replyType === 'thread' && messageForThread) {
return {
parentMessageId: messageForThread.messageId,
isReplyToChannel: true
};
}
}
return {};
});
const messageMentionParams = useIIFE(() => {
const {
groupChannel
} = sbOptions.uikit;
if (!channel.isGroupChannel() || !groupChannel.channel.enableMention) return {};
return {
mentionType: MentionType.USERS,
mentionedUserIds: mentionedUsers.map(it => it.user.userId),
mentionedMessageTemplate: mentionManager.textToMentionedMessageTemplate(text, mentionedUsers, groupChannel.channel.enableMention)
};
});
const onFailureToSend = error => {
toast.show(STRINGS.TOAST.SEND_MSG_ERROR, 'error');
Logger.error(STRINGS.TOAST.SEND_MSG_ERROR, error);
};
const sendUserMessage = () => {
onPressSendUserMessage({
message: text,
...messageMentionParams,
...messageReplyParams
}).catch(onFailureToSend);
onChangeText('');
setMessageToReply === null || setMessageToReply === void 0 ? void 0 : setMessageToReply();
};
const sendFileMessage = file => {
onPressSendFileMessage({
file,
...messageReplyParams
}).catch(onFailureToSend);
setMessageToReply === null || setMessageToReply === void 0 ? void 0 : setMessageToReply();
};
const sendVoiceMessage = (file, durationMills) => {
if (inputMuted) {
toast.show(STRINGS.TOAST.USER_MUTED_ERROR, 'error');
Logger.error(STRINGS.TOAST.USER_MUTED_ERROR);
} else if (inputFrozen) {
toast.show(STRINGS.TOAST.CHANNEL_FROZEN_ERROR, 'error');
Logger.error(STRINGS.TOAST.CHANNEL_FROZEN_ERROR);
} else {
onPressSendFileMessage({
file,
metaArrays: [new MessageMetaArray({
key: VOICE_MESSAGE_META_ARRAY_DURATION_KEY,
value: [String(durationMills)]
}), new MessageMetaArray({
key: VOICE_MESSAGE_META_ARRAY_MESSAGE_TYPE_KEY,
value: [`voice/${recorderService.options.extension}`]
})],
...messageReplyParams
}).catch(onFailureToSend);
}
onChangeText('');
setMessageToReply === null || setMessageToReply === void 0 ? void 0 : setMessageToReply();
};
const sheetItems = useChannelInputItems(channel, sendFileMessage);
const getPlaceholder = () => {
if (inputMuted) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_MUTED;
if (inputFrozen) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_DISABLED;
if (inputDisabled) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_DISABLED;
if (messageToReply) return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_REPLY;
if (messageForThread) {
if (messageForThread.threadInfo && messageForThread.threadInfo.replyCount > 0) {
return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_REPLY_TO_THREAD;
} else {
return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_REPLY_IN_THREAD;
}
}
return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_ACTIVE;
};
const voiceMessageEnabled = channel.isGroupChannel() && sbOptions.uikit.groupChannel.channel.enableVoiceMessage;
const sendButtonVisible = Boolean(text.trim());
return /*#__PURE__*/React.createElement(View, null, MessageToReplyPreview && /*#__PURE__*/React.createElement(MessageToReplyPreview, {
messageToReply: messageToReply,
setMessageToReply: setMessageToReply
}), /*#__PURE__*/React.createElement(View, {
style: styles.sendInputContainer
}, AttachmentsButton && /*#__PURE__*/React.createElement(AttachmentsButton, {
onPress: () => openSheet({
sheetItems
}),
disabled: inputDisabled
}), /*#__PURE__*/React.createElement(TextInput, {
ref: ref,
multiline: true,
disableFullscreenUI: true,
onSelectionChange: onSelectionChange,
editable: !inputDisabled,
onChangeText: onChangeText,
style: style,
placeholder: getPlaceholder()
}, mentionManager.textToMentionedComponents(text, mentionedUsers, sbOptions.uikit.groupChannel.channel.enableMention)), voiceMessageEnabled && /*#__PURE__*/React.createElement(VoiceMessageButton, {
visible: !sendButtonVisible,
disabled: inputDisabled,
onPress: () => setVoiceMessageInputVisible(true)
}), /*#__PURE__*/React.createElement(UserMessageSendButton, {
visible: sendButtonVisible,
disabled: inputDisabled,
onPress: sendUserMessage
}), voiceMessageEnabled && VoiceMessageInput && /*#__PURE__*/React.createElement(Modal, {
disableBackgroundClose: true,
onClose: onClose,
onDismiss: () => {
onDismiss();
Promise.allSettled([playerService.reset(), recorderService.reset()]);
},
backgroundStyle: {
justifyContent: 'flex-end'
},
visible: voiceMessageInputVisible,
type: 'slide-no-gesture'
}, /*#__PURE__*/React.createElement(VoiceMessageInput, {
onClose: onClose,
onSend: _ref2 => {
let {
file,
duration
} = _ref2;
return sendVoiceMessage(file, duration);
}
}))));
});
const VoiceMessageButton = _ref3 => {
let {
visible,
disabled,
onPress
} = _ref3;
const {
STRINGS
} = useLocalization();
const {
alert
} = useAlert();
const {
playerService,
recorderService
} = usePlatformService();
const {
colors
} = useUIKitTheme();
if (!visible) return null;
const onPressWithPermissionCheck = async () => {
const recorderGranted = await recorderService.requestPermission();
if (!recorderGranted) {
alert({
title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(STRINGS.LABELS.PERMISSION_MICROPHONE, STRINGS.LABELS.PERMISSION_APP_NAME),
buttons: [{
text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK,
onPress: () => SBUUtils.openSettings()
}]
});
Logger.error('Failed to request permission for recorder');
return;
}
const playerGranted = await playerService.requestPermission();
if (!playerGranted) {
alert({
title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(STRINGS.LABELS.PERMISSION_DEVICE_STORAGE, STRINGS.LABELS.PERMISSION_APP_NAME),
buttons: [{
text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK,
onPress: () => SBUUtils.openSettings()
}]
});
Logger.error('Failed to request permission for player');
return;
}
onPress();
};
return /*#__PURE__*/React.createElement(TouchableOpacity, {
onPress: onPressWithPermissionCheck,
disabled: disabled
}, /*#__PURE__*/React.createElement(Icon, {
color: disabled ? colors.ui.input.default.disabled.highlight : colors.ui.input.default.active.highlight,
icon: 'audio-on',
size: 24,
containerStyle: styles.sendIcon
}));
};
const UserMessageSendButton = _ref4 => {
let {
visible,
disabled,
onPress
} = _ref4;
const {
colors
} = useUIKitTheme();
if (!visible) return null;
return /*#__PURE__*/React.createElement(TouchableOpacity, {
onPress: onPress,
disabled: disabled
}, /*#__PURE__*/React.createElement(Icon, {
color: disabled ? colors.ui.input.default.disabled.highlight : colors.ui.input.default.active.highlight,
icon: 'send',
size: 24,
containerStyle: styles.sendIcon
}));
};
const styles = createStyleSheet({
sendInputContainer: {
paddingVertical: 10,
paddingHorizontal: 12,
alignItems: 'center',
flexDirection: 'row'
},
sendIcon: {
marginLeft: 4,
padding: 4
}
});
export default SendInput;
//# sourceMappingURL=SendInput.js.map