@cometchat/chat-uikit-react-native
Version:
Ready-to-use Chat UI Components for React Native
120 lines (118 loc) • 4.78 kB
JavaScript
import React, { useCallback, useRef, useState } from "react";
import { Alert, Image, Linking, Platform, Text, View, } from "react-native";
import { localize } from "../../shared/resources/CometChatLocalize";
import { DefaultLinkPreview } from "./resources";
import { useTheme } from "../../theme";
/**
* LinkPreviewBubble component displays a preview of a link with image, title, description, and favicon.
*
* @param props - LinkPreviewBubbleInterface properties.
* @returns A JSX.Element rendering the link preview bubble.
*/
export const LinkPreviewBubble = (props) => {
const { style, link, title, ChildView, image, onPress, description, favicon } = props;
const theme = useTheme();
const _style = style;
const [imageSource, setImageSource] = useState({
uri: image.startsWith("https:") ? image : `https:${image.split("http:")[1]}`,
});
const [imageHeight, setImageHeight] = useState();
const [imageWidth, setImageWidth] = useState();
const [faviconSource, setFaviconSource] = useState({
uri: favicon.startsWith("https:") ? favicon : `https:${favicon.split("http:")[1]}`,
});
const pressTime = useRef(0);
/**
* Records the time when the touch starts.
*/
const handleTouchStart = () => {
pressTime.current = Date.now();
};
/**
* Handles the touch end event. If the press duration is less than 500ms, it triggers the onPress callback or opens the link.
*/
const handleTouchEnd = async () => {
if (pressTime.current === null && Platform.OS === "ios")
return;
const endTime = Date.now();
const pressDuration = endTime - (pressTime.current ?? 0);
if (pressDuration < 500) {
if (onPress) {
onPress();
}
else {
const opened = await Linking.openURL(link);
if (!opened) {
Alert.alert(localize("SOMETHING_WRONG"));
}
}
}
};
/**
* Handles the touch move event. For iOS, it cancels the press action.
*/
const onTouchMove = () => {
if (Platform.OS === "ios") {
pressTime.current = null;
}
};
/**
* Callback for layout changes of the container.
* Sets the image width and height based on the container size.
*
* @param event - The layout change event.
*/
const onLayoutHandler = useCallback((event) => {
if (imageHeight && imageWidth) {
return;
}
const containerWidth = event.nativeEvent.layout.width;
Image.getSize(image, (width, height) => {
setImageHeight(height);
if (typeof style?.headerImageContainerStyle?.maxHeight === "number") {
setImageHeight(height < style?.headerImageContainerStyle?.maxHeight
? height
: style?.headerImageContainerStyle.maxHeight);
}
setImageWidth(width < containerWidth ? width : containerWidth);
}, (error) => {
console.log(error);
});
}, [image, imageHeight, imageWidth, style]);
return (<View style={{ flex: 1 }} onLayout={onLayoutHandler}>
<View style={style?.headerImageContainerStyle} onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} onTouchMove={onTouchMove}>
<Image source={imageSource} style={[{ height: imageHeight, width: imageWidth }, style?.headerImageStyle]} onError={(err) => {
setImageSource(DefaultLinkPreview);
}}/>
</View>
<View style={style?.bodyStyle?.containerStyle}>
<View style={{ flexDirection: "row" }}>
<View style={style?.bodyStyle?.titleContainerStyle}>
<Text style={style?.bodyStyle?.titleStyle} ellipsizeMode='tail'>
{title}
</Text>
</View>
<View style={style?.bodyStyle?.faviconContainerStyle}>
<Image source={faviconSource} style={style?.bodyStyle?.faviconStyle} onError={(err) => {
setImageSource(DefaultLinkPreview);
}}/>
</View>
</View>
<View style={style?.bodyStyle?.subtitleContainerStyle}>
<Text style={style?.bodyStyle?.subtitleTitle} numberOfLines={2} ellipsizeMode='tail'>
{description}
</Text>
<Text style={style?.bodyStyle?.subtitleTitle} numberOfLines={2} ellipsizeMode='tail'>
{link}
</Text>
</View>
</View>
<View style={{
paddingVertical: theme.spacing.padding.p3,
paddingHorizontal: style?.bodyStyle?.containerStyle.padding,
}}>
{ChildView && <ChildView />}
</View>
</View>);
};
//# sourceMappingURL=LinkPreviewBubble.js.map