UNPKG

fastcomments-react-native-sdk

Version:

React Native FastComments Components. Add live commenting to any React Native application.

205 lines (204 loc) 10.7 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { FastCommentsImageAsset } from "../types"; import { Text, View, ActivityIndicator, ScrollView, TouchableOpacity, Image } from "react-native"; import { useEffect, useState } from "react"; import { RichText, useEditorBridge, useEditorContent, DEFAULT_TOOLBAR_ITEMS } from '@10play/tentap-editor'; export function CommentTextArea10Tap({ emoticonBarConfig, focusObserver, state, styles, output, onFocus: _onFocus, pickImage, pickGIF, value, toolbarButtons, }) { const maxLength = state.config.maxCommentCharacterLength || 2000; const hasDarkBackground = state.config.hasDarkBackground; const [imageUploadProgress, setImageUploadProgress] = useState(null); // Default toolbar button configuration - code disabled by default const defaultButtons = { bold: true, italic: true, underline: true, strikethrough: true, code: false, image: true, gif: true, }; const buttons = { ...defaultButtons, ...toolbarButtons }; const editor = useEditorBridge({ autofocus: false, avoidIosKeyboard: true, initialContent: value || '<p></p>', }); // Handle focus/blur events useEffect(() => { // 10tap-editor handles focus/blur internally // We'll manage focus state through the editor bridge if needed return () => { }; }, [_onFocus]); const content = useEditorContent(editor, { type: 'html' }); useEffect(() => { if (value !== undefined && value !== content) { editor.setContent(value); } }, [value]); useEffect(() => { if (focusObserver) { focusObserver.setFocused = (focused) => { if (focused) { editor.focus(); } else { editor.blur(); } }; } }, [editor, focusObserver]); output.getValue = () => { return content?.substring(0, maxLength) || ''; }; const handleImageUpload = async () => { if (!pickImage) return; try { const photoData = await pickImage(); if (!photoData) return; if (typeof photoData === 'string' && photoData.startsWith('http')) { editor.setImage(photoData); } else { setImageUploadProgress(0); const formData = new FormData(); formData.append('file', photoData); const xhr = new XMLHttpRequest(); xhr.open('POST', state.apiHost + '/upload-image/' + state.config.tenantId); xhr.upload.onprogress = (progressEvent) => { if (progressEvent.lengthComputable) { const progress = progressEvent.loaded / progressEvent.total; setImageUploadProgress(progress); } }; const url = await new Promise((resolve, reject) => { xhr.onload = () => { setImageUploadProgress(null); if (xhr.status === 200) { const url = JSON.parse(xhr.response).url; resolve(url); } else { reject(new Error(xhr.response)); } }; xhr.onerror = () => { setImageUploadProgress(null); reject(new Error('Upload failed')); }; xhr.send(formData); }); editor.setImage(url); } } catch (error) { console.error('Image upload failed:', error); setImageUploadProgress(null); } }; const handleGIFPick = async () => { if (!pickGIF) return; try { const gifUrl = await pickGIF(); if (gifUrl) { editor.setImage(gifUrl); } } catch (error) { console.error('GIF pick failed:', error); } }; const customToolbarItems = [...DEFAULT_TOOLBAR_ITEMS]; if (pickImage) { customToolbarItems.push({ onPress: () => { handleImageUpload(); return () => { }; }, disabled: () => false, active: () => false, image: () => state.imageAssets[hasDarkBackground ? FastCommentsImageAsset.ICON_IMAGE_UPLOAD_WHITE : FastCommentsImageAsset.ICON_IMAGE_UPLOAD], }); } if (pickGIF) { customToolbarItems.push({ onPress: () => { handleGIFPick(); return () => { }; }, disabled: () => false, active: () => false, image: () => state.imageAssets[hasDarkBackground ? FastCommentsImageAsset.ICON_GIF : FastCommentsImageAsset.ICON_GIF], }); } // Setup emoticon bar config if (emoticonBarConfig) { emoticonBarConfig.addEmoticon = (src) => { editor.setImage(src); }; } // Common button style for toolbar buttons - using app's existing colors const toolbarButtonStyle = { backgroundColor: hasDarkBackground ? '#444' : 'white', paddingHorizontal: 8, paddingVertical: 6, borderRadius: 4, borderWidth: 1, borderColor: styles.commentTextArea?.textarea?.borderColor || (hasDarkBackground ? '#555' : '#a2a2a2'), minWidth: 28, alignItems: 'center', justifyContent: 'center', marginRight: 6, }; return (_jsxs(View, { style: { width: '100%', flex: 1 }, children: [_jsx(View, { style: [ styles.commentTextArea?.textarea, { minHeight: state.config.useSingleLineCommentInput ? 40 : 100, borderRadius: styles.commentTextArea?.textarea?.borderRadius || 11, overflow: 'hidden', paddingHorizontal: 8, paddingVertical: 4, // Minimal vertical padding // backgroundColor will come from styles.commentTextArea?.textarea?.backgroundColor if set, otherwise transparent } ], children: _jsx(RichText, { editor: editor, style: { minHeight: state.config.useSingleLineCommentInput ? 32 : 92, flex: 1, backgroundColor: 'transparent', // Ensure RichText itself is transparent } }) }), emoticonBarConfig?.emoticons && (_jsx(ScrollView, { horizontal: true, style: styles.commentTextAreaEmoticonBar?.root, children: emoticonBarConfig.emoticons.map(([src, element], index) => (_jsx(TouchableOpacity, { onPress: () => emoticonBarConfig.addEmoticon?.(src), style: styles.commentTextAreaEmoticonBar?.button, children: element }, src + index))) })), _jsxs(View, { style: { backgroundColor: hasDarkBackground ? '#2c2c2c' : '#f8f8f8', paddingHorizontal: 12, paddingVertical: 8, flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', flexWrap: 'wrap', }, children: [buttons.bold && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: () => editor.toggleBold(), activeOpacity: 0.7, children: _jsx(Text, { style: { fontWeight: 'bold', fontSize: 14, color: hasDarkBackground ? '#fff' : '#333' }, children: "B" }) })), buttons.italic && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: () => editor.toggleItalic(), activeOpacity: 0.7, children: _jsx(Text, { style: { fontStyle: 'italic', fontSize: 14, color: hasDarkBackground ? '#fff' : '#333' }, children: "I" }) })), buttons.underline && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: () => editor.toggleUnderline(), activeOpacity: 0.7, children: _jsx(Text, { style: { textDecorationLine: 'underline', fontSize: 14, color: hasDarkBackground ? '#fff' : '#333' }, children: "U" }) })), buttons.strikethrough && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: () => editor.toggleStrike(), activeOpacity: 0.7, children: _jsx(Text, { style: { textDecorationLine: 'line-through', fontSize: 14, color: hasDarkBackground ? '#fff' : '#333' }, children: "S" }) })), buttons.code && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: () => editor.toggleCode(), activeOpacity: 0.7, children: _jsx(Text, { style: { fontFamily: 'monospace', fontSize: 12, color: hasDarkBackground ? '#fff' : '#333' }, children: "<>" }) })), buttons.image && pickImage && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: handleImageUpload, activeOpacity: 0.7, children: _jsx(Image, { source: state.imageAssets[hasDarkBackground ? FastCommentsImageAsset.ICON_IMAGE_UPLOAD_WHITE : FastCommentsImageAsset.ICON_IMAGE_UPLOAD], style: { width: 16, height: 16 } }) })), buttons.gif && pickGIF && (_jsx(TouchableOpacity, { style: toolbarButtonStyle, onPress: handleGIFPick, activeOpacity: 0.7, children: _jsx(Image, { source: state.imageAssets[FastCommentsImageAsset.ICON_GIF], style: { width: 16, height: 16 } }) }))] }), imageUploadProgress !== null && (_jsx(View, { style: styles.commentTextArea?.imageUploadModalCenteredView, children: _jsxs(View, { style: styles.commentTextArea?.imageUploadModalContent, children: [_jsx(ActivityIndicator, { size: styles.commentTextArea?.imageUploadModalProgressSpinnerSize }), _jsxs(Text, { style: styles.commentTextArea?.imageUploadModalProgressText, children: [Math.round(imageUploadProgress * 100), "%"] })] }) }))] })); }