UNPKG

@cometchat/chat-uikit-react-native

Version:

Ready-to-use Chat UI Components for React Native

82 lines (80 loc) 3.69 kB
import React, { useLayoutEffect, useState, useEffect } from "react"; import { Platform, Text, View, TouchableOpacity, } from "react-native"; import { localize } from "../../resources"; export const CometChatTextBubble = (props) => { const { textContainerStyle } = props; return (<View style={textContainerStyle}> <CometChatTextBubbleText {...props}/> </View>); }; export const CometChatTextBubbleText = (props) => { const { text = "", textFormatters, textStyle } = props; const [formattedText, setFormattedText] = useState(""); // This flag will be set if the text exceeds 4 lines in collapsed mode. const [isTruncated, setIsTruncated] = useState(false); const [expanded, setExpanded] = useState(false); // States to store measured heights for iOS fallback. const [fullHeight, setFullHeight] = useState(0); const [collapsedHeight, setCollapsedHeight] = useState(0); // Format text if any formatters are provided. useLayoutEffect(() => { let finalText = text; if (textFormatters && textFormatters.length) { for (let i = 0; i < textFormatters.length; i++) { finalText = textFormatters[i].getFormattedText(finalText); } } setFormattedText(finalText); // Reset the truncated flag when text changes. setIsTruncated(false); }, [text, textFormatters]); // For Android, onTextLayout reliably provides the number of lines. const handleTextLayout = (event) => { const { lines } = event.nativeEvent; if (lines.length > 4) { setIsTruncated(true); } else { setIsTruncated(false); } }; // onLayout callback for the collapsed text (iOS) const handleCollapsedLayout = (event) => { setCollapsedHeight(event.nativeEvent.layout.height); }; // onLayout callback for the hidden full text (iOS) const handleFullTextLayout = (event) => { setFullHeight(event.nativeEvent.layout.height); }; // For iOS, when not expanded, compare the hidden full height with the collapsed text height. useEffect(() => { if (Platform.OS === "ios" && !expanded && fullHeight && collapsedHeight) { setIsTruncated(fullHeight > collapsedHeight); } // Do not update isTruncated when expanded is true so that the toggle remains. }, [fullHeight, collapsedHeight, expanded]); return (<View> <Text key={expanded ? "expanded" : "collapsed"} // Force re-render when expanded toggles style={textStyle} // Android uses onTextLayout for measuring lines. onTextLayout={Platform.OS !== "ios" ? handleTextLayout : undefined} // iOS: only measure collapsed height when not expanded. onLayout={Platform.OS === "ios" && !expanded ? handleCollapsedLayout : undefined} numberOfLines={expanded ? undefined : 4} ellipsizeMode="tail"> {formattedText} </Text> {/* Hidden full text for iOS measurement */} {Platform.OS === "ios" && (<Text style={[textStyle, { position: "absolute", opacity: 0, zIndex: -1 }]} onLayout={handleFullTextLayout}> {formattedText} </Text>)} {/* Only show the toggle if the text was truncated in collapsed mode */} {isTruncated && (<View style={{ alignItems: "flex-end", marginTop: 8 }}> <TouchableOpacity onPress={() => setExpanded(!expanded)}> <Text style={textStyle}> {expanded ? localize("SHOW_LESS") : localize("READ_MORE")} </Text> </TouchableOpacity> </View>)} </View>); }; export default CometChatTextBubble; //# sourceMappingURL=CometChatTextBubble.js.map