@gathertown/uikit-react-native
Version:
Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.
450 lines (449 loc) • 15.1 kB
JavaScript
import React, { forwardRef, useContext } from 'react';
import { Platform, TouchableOpacity, View } from 'react-native';
import { MentionType } from '@sendbird/chat/message';
import { CustomComponentContext, Icon, ImageWithPlaceholder, Text, TextInput, VideoThumbnail, createStyleSheet, useAlert, useBottomSheet, useToast, useUIKitTheme } from '@gathertown/uikit-react-native-foundation';
import { Logger, getFileIconFromMessageType, getMessageType, getThumbnailUriFromFileMessage, isImage, shouldCompressImage, useIIFE } from '@gathertown/uikit-utils';
import { useLocalization, usePlatformService, useSendbirdChat } from '../../hooks/useContext';
import SBUError from '../../libs/SBUError';
import SBUUtils from '../../libs/SBUUtils';
const SendInput = /*#__PURE__*/forwardRef(function SendInput(_ref, ref) {
let {
AttachmentsButton,
onPressSendUserMessage,
onPressSendFileMessage,
text,
onChangeText,
onSelectionChange,
mentionedUsers,
inputDisabled,
inputFrozen,
inputMuted,
channel,
messageToReply,
setMessageToReply
} = _ref;
const {
mentionManager,
sbOptions
} = useSendbirdChat();
const {
select,
colors,
palette
} = useUIKitTheme();
const ctx = useContext(CustomComponentContext);
const {
STRINGS
} = useLocalization();
const {
openSheet
} = useBottomSheet();
const toast = useToast();
const {
mediaService
} = usePlatformService();
const messageReplyParams = useIIFE(() => {
const {
groupChannel
} = sbOptions.uikit;
if (!channel.isGroupChannel() || groupChannel.channel.replyType === 'none' || !messageToReply) return {};
return {
parentMessageId: messageToReply.messageId,
isReplyToChannel: true
};
});
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 sheetItems = useChannelInputItems(channel, sendFileMessage);
const onPressAttachment = () => openSheet({
sheetItems
});
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;
return STRINGS.LABELS.CHANNEL_INPUT_PLACEHOLDER_ACTIVE;
};
const getFileIconAsImage = url => {
return /*#__PURE__*/React.createElement(ImageWithPlaceholder, {
source: {
uri: url
},
style: styles.previewImage
});
};
const getFileIconAsVideoThumbnail = url => {
return /*#__PURE__*/React.createElement(VideoThumbnail, {
style: styles.previewImage,
iconSize: 0,
source: url,
fetchThumbnailFromVideoSource: uri => mediaService.getVideoThumbnail({
url: uri,
timeMills: 1000
})
});
};
const getFileIconAsSymbol = icon => {
return /*#__PURE__*/React.createElement(Icon, {
icon: icon,
size: 20,
color: colors.onBackground02,
containerStyle: {
backgroundColor: select({
light: palette.background100,
dark: palette.background500
}),
width: 36,
height: 36,
borderRadius: 10,
marginRight: 10,
marginTop: 2
}
});
};
const getFileIcon = messageToReply => {
if (messageToReply !== null && messageToReply !== void 0 && messageToReply.isFileMessage()) {
const messageType = getMessageType(messageToReply);
switch (messageType) {
case 'file.image':
return getFileIconAsImage(getThumbnailUriFromFileMessage(messageToReply));
case 'file.video':
return getFileIconAsVideoThumbnail(getThumbnailUriFromFileMessage(messageToReply));
default:
return getFileIconAsSymbol(getFileIconFromMessageType(messageType));
}
}
return null;
};
if (ctx !== null && ctx !== void 0 && ctx.messageInput) {
return ctx.messageInput.renderSendInput({
onPressAttachment,
isDisabled: inputDisabled,
onSend: sendUserMessage,
ref,
onChangeText
});
}
return /*#__PURE__*/React.createElement(View, null, messageToReply && /*#__PURE__*/React.createElement(View, {
style: {
flexDirection: 'row',
paddingLeft: 18,
paddingRight: 16,
paddingTop: 10,
paddingBottom: 8,
alignItems: 'center',
borderTopWidth: 1,
borderColor: colors.onBackground04
}
}, /*#__PURE__*/React.createElement(View, {
style: {
flex: 1,
flexDirection: 'row'
}
}, getFileIcon(messageToReply), /*#__PURE__*/React.createElement(View, {
style: {
flex: 1,
flexDirection: 'column'
}
}, /*#__PURE__*/React.createElement(Text, {
numberOfLines: 1,
style: {
fontSize: 13,
fontWeight: '900',
marginBottom: 4
}
}, STRINGS.LABELS.CHANNEL_INPUT_REPLY_PREVIEW_TITLE(messageToReply.sender)), /*#__PURE__*/React.createElement(Text, {
numberOfLines: 1,
style: {
fontSize: 13,
color: colors.onBackground03
}
}, STRINGS.LABELS.CHANNEL_INPUT_REPLY_PREVIEW_BODY(messageToReply)))), /*#__PURE__*/React.createElement(TouchableOpacity, {
onPress: () => setMessageToReply === null || setMessageToReply === void 0 ? void 0 : setMessageToReply(undefined)
}, /*#__PURE__*/React.createElement(Icon, {
icon: 'close',
size: 24,
color: colors.onBackground01,
containerStyle: styles.iconSend
}))), /*#__PURE__*/React.createElement(View, {
style: styles.sendInputContainer
}, AttachmentsButton && /*#__PURE__*/React.createElement(AttachmentsButton, {
onPress: onPressAttachment,
disabled: inputDisabled
}), /*#__PURE__*/React.createElement(TextInput, {
ref: ref,
multiline: true,
disableFullscreenUI: true,
onSelectionChange: onSelectionChange,
editable: !inputDisabled,
onChangeText: onChangeText,
style: styles.input,
placeholder: getPlaceholder()
}, mentionManager.textToMentionedComponents(text, mentionedUsers, sbOptions.uikit.groupChannel.channel.enableMention)), Boolean(text.trim()) && /*#__PURE__*/React.createElement(TouchableOpacity, {
onPress: sendUserMessage,
disabled: inputDisabled
}, /*#__PURE__*/React.createElement(Icon, {
color: inputDisabled ? colors.ui.input.default.disabled.highlight : colors.ui.input.default.active.highlight,
icon: 'send',
size: 24,
containerStyle: styles.iconSend
}))));
});
const useChannelInputItems = (channel, sendFileMessage) => {
const {
sbOptions,
imageCompressionConfig
} = useSendbirdChat();
const {
STRINGS
} = useLocalization();
const {
fileService,
mediaService
} = usePlatformService();
const {
alert
} = useAlert();
const toast = useToast();
const sheetItems = [];
const input = useIIFE(() => {
switch (true) {
case channel.isOpenChannel():
return sbOptions.uikit.openChannel.channel.input;
case channel.isGroupChannel():
return sbOptions.uikit.groupChannel.channel.input;
default:
return {
enableDocument: true,
camera: {
enablePhoto: true,
enableVideo: true
},
gallery: {
enablePhoto: true,
enableVideo: true
}
};
}
});
if (input.camera.enablePhoto) {
sheetItems.push({
title: STRINGS.LABELS.CHANNEL_INPUT_ATTACHMENT_CAMERA_PHOTO,
icon: 'camera',
onPress: async () => {
const mediaFile = await fileService.openCamera({
mediaType: 'photo',
onOpenFailure: error => {
if (error.code === SBUError.CODE.ERR_PERMISSIONS_DENIED) {
alert({
title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(STRINGS.LABELS.PERMISSION_CAMERA, STRINGS.LABELS.PERMISSION_APP_NAME),
buttons: [{
text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK,
onPress: () => SBUUtils.openSettings()
}]
});
} else {
toast.show(STRINGS.TOAST.OPEN_CAMERA_ERROR, 'error');
}
}
});
if (mediaFile) {
// Image compression
if (isImage(mediaFile.uri, mediaFile.type) && shouldCompressImage(mediaFile.type, sbOptions.chat.imageCompressionEnabled)) {
await SBUUtils.safeRun(async () => {
const compressed = await mediaService.compressImage({
uri: mediaFile.uri,
maxWidth: imageCompressionConfig.width,
maxHeight: imageCompressionConfig.height,
compressionRate: imageCompressionConfig.compressionRate
});
if (compressed) {
mediaFile.uri = compressed.uri;
mediaFile.size = compressed.size;
}
});
}
sendFileMessage(mediaFile);
}
}
});
}
if (input.camera.enableVideo) {
sheetItems.push({
title: STRINGS.LABELS.CHANNEL_INPUT_ATTACHMENT_CAMERA_VIDEO,
icon: 'camera',
onPress: async () => {
const mediaFile = await fileService.openCamera({
mediaType: 'video',
onOpenFailure: error => {
if (error.code === SBUError.CODE.ERR_PERMISSIONS_DENIED) {
alert({
title: STRINGS.DIALOG.ALERT_PERMISSIONS_TITLE,
message: STRINGS.DIALOG.ALERT_PERMISSIONS_MESSAGE(STRINGS.LABELS.PERMISSION_CAMERA, STRINGS.LABELS.PERMISSION_APP_NAME),
buttons: [{
text: STRINGS.DIALOG.ALERT_PERMISSIONS_OK,
onPress: () => SBUUtils.openSettings()
}]
});
} else {
toast.show(STRINGS.TOAST.OPEN_CAMERA_ERROR, 'error');
}
}
});
if (mediaFile) {
sendFileMessage(mediaFile);
}
}
});
}
if (input.gallery.enablePhoto || input.gallery.enableVideo) {
const mediaType = (() => {
switch (true) {
case input.gallery.enablePhoto && input.gallery.enableVideo:
return 'all';
case input.gallery.enablePhoto && !input.gallery.enableVideo:
return 'photo';
case !input.gallery.enablePhoto && input.gallery.enableVideo:
return 'video';
default:
return 'all';
}
})();
sheetItems.push({
title: STRINGS.LABELS.CHANNEL_INPUT_ATTACHMENT_PHOTO_LIBRARY,
icon: 'photo',
onPress: async () => {
const mediaFiles = await fileService.openMediaLibrary({
selectionLimit: 1,
mediaType,
onOpenFailure: error => {
if (error.code === SBUError.CODE.ERR_PERMISSIONS_DENIED) {
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()
}]
});
} else {
toast.show(STRINGS.TOAST.OPEN_PHOTO_LIBRARY_ERROR, 'error');
}
}
});
if (mediaFiles && mediaFiles[0]) {
const mediaFile = mediaFiles[0];
// Image compression
if (isImage(mediaFile.uri, mediaFile.type) && shouldCompressImage(mediaFile.type, sbOptions.chat.imageCompressionEnabled)) {
await SBUUtils.safeRun(async () => {
const compressed = await mediaService.compressImage({
uri: mediaFile.uri,
maxWidth: imageCompressionConfig.width,
maxHeight: imageCompressionConfig.height,
compressionRate: imageCompressionConfig.compressionRate
});
if (compressed) {
mediaFile.uri = compressed.uri;
mediaFile.size = compressed.size;
}
});
}
sendFileMessage(mediaFile);
}
}
});
}
if (input.enableDocument) {
sheetItems.push({
title: STRINGS.LABELS.CHANNEL_INPUT_ATTACHMENT_FILES,
icon: 'document',
onPress: async () => {
const documentFile = await fileService.openDocument({
onOpenFailure: () => toast.show(STRINGS.TOAST.OPEN_FILES_ERROR, 'error')
});
if (documentFile) {
// Image compression
if (isImage(documentFile.uri, documentFile.type) && shouldCompressImage(documentFile.type, sbOptions.chat.imageCompressionEnabled)) {
await SBUUtils.safeRun(async () => {
const compressed = await mediaService.compressImage({
uri: documentFile.uri,
maxWidth: imageCompressionConfig.width,
maxHeight: imageCompressionConfig.height,
compressionRate: imageCompressionConfig.compressionRate
});
if (compressed) {
documentFile.uri = compressed.uri;
documentFile.size = compressed.size;
}
});
}
sendFileMessage(documentFile);
}
}
});
}
return sheetItems;
};
const styles = createStyleSheet({
sendInputContainer: {
paddingVertical: 10,
paddingHorizontal: 12,
alignItems: 'center',
flexDirection: 'row'
},
input: {
flex: 1,
marginRight: 4,
minHeight: 36,
maxHeight: 36 * Platform.select({
ios: 2.5,
default: 2
}),
borderRadius: 20
},
iconSend: {
marginLeft: 4,
padding: 4
},
previewImage: {
width: 36,
height: 36,
borderRadius: 10,
marginTop: 2,
marginRight: 10,
overflow: 'hidden'
}
});
export default SendInput;
//# sourceMappingURL=SendInput.js.map