UNPKG

@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.

460 lines (458 loc) 17.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _message = require("@sendbird/chat/message"); var _uikitReactNativeFoundation = require("@gathertown/uikit-react-native-foundation"); var _uikitUtils = require("@gathertown/uikit-utils"); var _useContext = require("../../hooks/useContext"); var _SBUError = _interopRequireDefault(require("../../libs/SBUError")); var _SBUUtils = _interopRequireDefault(require("../../libs/SBUUtils")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const SendInput = /*#__PURE__*/(0, _react.forwardRef)(function SendInput(_ref, ref) { let { AttachmentsButton, onPressSendUserMessage, onPressSendFileMessage, text, onChangeText, onSelectionChange, mentionedUsers, inputDisabled, inputFrozen, inputMuted, channel, messageToReply, setMessageToReply } = _ref; const { mentionManager, sbOptions } = (0, _useContext.useSendbirdChat)(); const { select, colors, palette } = (0, _uikitReactNativeFoundation.useUIKitTheme)(); const ctx = (0, _react.useContext)(_uikitReactNativeFoundation.CustomComponentContext); const { STRINGS } = (0, _useContext.useLocalization)(); const { openSheet } = (0, _uikitReactNativeFoundation.useBottomSheet)(); const toast = (0, _uikitReactNativeFoundation.useToast)(); const { mediaService } = (0, _useContext.usePlatformService)(); const messageReplyParams = (0, _uikitUtils.useIIFE)(() => { const { groupChannel } = sbOptions.uikit; if (!channel.isGroupChannel() || groupChannel.channel.replyType === 'none' || !messageToReply) return {}; return { parentMessageId: messageToReply.messageId, isReplyToChannel: true }; }); const messageMentionParams = (0, _uikitUtils.useIIFE)(() => { const { groupChannel } = sbOptions.uikit; if (!channel.isGroupChannel() || !groupChannel.channel.enableMention) return {}; return { mentionType: _message.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'); _uikitUtils.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.default.createElement(_uikitReactNativeFoundation.ImageWithPlaceholder, { source: { uri: url }, style: styles.previewImage }); }; const getFileIconAsVideoThumbnail = url => { return /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.VideoThumbnail, { style: styles.previewImage, iconSize: 0, source: url, fetchThumbnailFromVideoSource: uri => mediaService.getVideoThumbnail({ url: uri, timeMills: 1000 }) }); }; const getFileIconAsSymbol = icon => { return /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.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 = (0, _uikitUtils.getMessageType)(messageToReply); switch (messageType) { case 'file.image': return getFileIconAsImage((0, _uikitUtils.getThumbnailUriFromFileMessage)(messageToReply)); case 'file.video': return getFileIconAsVideoThumbnail((0, _uikitUtils.getThumbnailUriFromFileMessage)(messageToReply)); default: return getFileIconAsSymbol((0, _uikitUtils.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.default.createElement(_reactNative.View, null, messageToReply && /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: { flexDirection: 'row', paddingLeft: 18, paddingRight: 16, paddingTop: 10, paddingBottom: 8, alignItems: 'center', borderTopWidth: 1, borderColor: colors.onBackground04 } }, /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: { flex: 1, flexDirection: 'row' } }, getFileIcon(messageToReply), /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: { flex: 1, flexDirection: 'column' } }, /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.Text, { numberOfLines: 1, style: { fontSize: 13, fontWeight: '900', marginBottom: 4 } }, STRINGS.LABELS.CHANNEL_INPUT_REPLY_PREVIEW_TITLE(messageToReply.sender)), /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.Text, { numberOfLines: 1, style: { fontSize: 13, color: colors.onBackground03 } }, STRINGS.LABELS.CHANNEL_INPUT_REPLY_PREVIEW_BODY(messageToReply)))), /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, { onPress: () => setMessageToReply === null || setMessageToReply === void 0 ? void 0 : setMessageToReply(undefined) }, /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.Icon, { icon: 'close', size: 24, color: colors.onBackground01, containerStyle: styles.iconSend }))), /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: styles.sendInputContainer }, AttachmentsButton && /*#__PURE__*/_react.default.createElement(AttachmentsButton, { onPress: onPressAttachment, disabled: inputDisabled }), /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.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.default.createElement(_reactNative.TouchableOpacity, { onPress: sendUserMessage, disabled: inputDisabled }, /*#__PURE__*/_react.default.createElement(_uikitReactNativeFoundation.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 } = (0, _useContext.useSendbirdChat)(); const { STRINGS } = (0, _useContext.useLocalization)(); const { fileService, mediaService } = (0, _useContext.usePlatformService)(); const { alert } = (0, _uikitReactNativeFoundation.useAlert)(); const toast = (0, _uikitReactNativeFoundation.useToast)(); const sheetItems = []; const input = (0, _uikitUtils.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.default.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.default.openSettings() }] }); } else { toast.show(STRINGS.TOAST.OPEN_CAMERA_ERROR, 'error'); } } }); if (mediaFile) { // Image compression if ((0, _uikitUtils.isImage)(mediaFile.uri, mediaFile.type) && (0, _uikitUtils.shouldCompressImage)(mediaFile.type, sbOptions.chat.imageCompressionEnabled)) { await _SBUUtils.default.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.default.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.default.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.default.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.default.openSettings() }] }); } else { toast.show(STRINGS.TOAST.OPEN_PHOTO_LIBRARY_ERROR, 'error'); } } }); if (mediaFiles && mediaFiles[0]) { const mediaFile = mediaFiles[0]; // Image compression if ((0, _uikitUtils.isImage)(mediaFile.uri, mediaFile.type) && (0, _uikitUtils.shouldCompressImage)(mediaFile.type, sbOptions.chat.imageCompressionEnabled)) { await _SBUUtils.default.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 ((0, _uikitUtils.isImage)(documentFile.uri, documentFile.type) && (0, _uikitUtils.shouldCompressImage)(documentFile.type, sbOptions.chat.imageCompressionEnabled)) { await _SBUUtils.default.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 = (0, _uikitReactNativeFoundation.createStyleSheet)({ sendInputContainer: { paddingVertical: 10, paddingHorizontal: 12, alignItems: 'center', flexDirection: 'row' }, input: { flex: 1, marginRight: 4, minHeight: 36, maxHeight: 36 * _reactNative.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' } }); var _default = SendInput; exports.default = _default; //# sourceMappingURL=SendInput.js.map