import React, { JSX, useCallback, useRef, useState } from "react"; import { Alert, DimensionValue, Image, LayoutChangeEvent, Linking, Platform, Text, View, } from "react-native"; import { DefaultLinkPreview } from "./resources"; import { useTheme } from "../../theme"; import { CometChatTheme } from "../../theme/type"; import { useCometChatTranslation } from "../../shared/resources/CometChatLocalizeNew"; /** * Props for the LinkPreviewBubble component. */ export interface LinkPreviewBubbleInterface { /** Custom child view to render below the preview */ ChildView?: () => JSX.Element; /** Description text to display */ description?: string; /** Custom style object for the bubble */ style?: { /** Custom body style */ bodyStyle?: (CometChatTheme["linkPreviewBubbleStyles"] & {})["bodyStyle"]; /** Custom header image style */ headerImageStyle?: (CometChatTheme["linkPreviewBubbleStyles"] & {})["headerImageStyle"]; /** Custom header image container style */ headerImageContainerStyle?: (CometChatTheme["linkPreviewBubbleStyles"] & {})["headerImageContainerStyle"]; }; /** URL of the link to preview */ link: string; /** Callback when the preview is pressed */ onPress?: () => void; /** Title text to display */ title: string; /** Image URL for the preview header */ image: string; /** Favicon URL to display */ favicon: string; } /** * 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: LinkPreviewBubbleInterface) => { const { style, link, title, ChildView, image, onPress, description, favicon } = props; const theme = useTheme(); const {t}=useCometChatTranslation() const _style = style; const [imageSource, setImageSource] = useState( image ? { uri: image.startsWith("https:") ? image : `https:${image.split("http:")[1]}` } : undefined ); const [imageHeight, setImageHeight] = useState(); const [imageWidth, setImageWidth] = useState(); const [faviconSource, setFaviconSource] = useState({ uri: favicon.startsWith("https:") ? favicon : `https:${favicon.split("http:")[1]}`, }); const [faviconError, setFaviconError] = useState(false); 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 () => { 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(t("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: LayoutChangeEvent) => { if (imageHeight && imageWidth) { return; } const containerWidth = event.nativeEvent.layout.width; Image.getSize( image, (width: number, height: number) => { setImageHeight(height); if (typeof style?.headerImageContainerStyle?.maxHeight === "number") { setImageHeight( height < style?.headerImageContainerStyle?.maxHeight ? height : style?.headerImageContainerStyle.maxHeight ); } setImageWidth(width < containerWidth ? width : containerWidth); }, (error: any) => { console.log(error); } ); }, [image, imageHeight, imageWidth, style] ); return ( {imageSource ? ( { setImageSource(DefaultLinkPreview); }} /> ) : null} {title} {!imageSource && !faviconError && ( { setFaviconError(true); }} /> )} {description} {link} {ChildView && } ); };