import React, { useContext, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Clipboard from '@react-native-clipboard/clipboard'; import FastImage from 'react-native-fast-image'; import { dequal } from 'dequal'; import Touchable from './Touchable'; import openLink from '../../lib/methods/helpers/openLink'; import sharedStyles from '../../views/Styles'; import { themes } from '../../lib/constants'; import { TSupportedThemes, useTheme, withTheme } from '../../theme'; import { LISTENER } from '../Toast'; import EventEmitter from '../../lib/methods/helpers/events'; import I18n from '../../i18n'; import MessageContext from './Context'; import { IUrl } from '../../definitions'; import { DEFAULT_MESSAGE_HEIGHT } from './utils'; const styles = StyleSheet.create({ button: { marginTop: 6 }, container: { flex: 1, flexDirection: 'column', borderRadius: 4, borderWidth: 1 }, textContainer: { flex: 1, flexDirection: 'column', padding: 15, justifyContent: 'flex-start', alignItems: 'flex-start' }, title: { fontSize: 16, ...sharedStyles.textMedium }, description: { fontSize: 16, ...sharedStyles.textRegular }, marginTop: { marginTop: 4 }, image: { width: '100%', height: DEFAULT_MESSAGE_HEIGHT, borderTopLeftRadius: 4, borderTopRightRadius: 4 }, imageWithoutContent: { borderRadius: 4 }, loading: { height: 0, borderWidth: 0, marginTop: 0 } }); const UrlContent = React.memo( ({ title, description }: { title: string; description: string }) => { const { colors } = useTheme(); return ( {title ? ( {title} ) : null} {description ? ( {description} ) : null} ); }, (prevProps, nextProps) => { if (prevProps.title !== nextProps.title) { return false; } if (prevProps.description !== nextProps.description) { return false; } return true; } ); type TImageLoadedState = 'loading' | 'done' | 'error'; const Url = React.memo( ({ url, index, theme }: { url: IUrl; index: number; theme: TSupportedThemes }) => { const [imageLoadedState, setImageLoadedState] = useState('loading'); const { baseUrl, user } = useContext(MessageContext); if (!url || url?.ignoreParse || imageLoadedState === 'error') { return null; } const onPress = () => openLink(url.url, theme); const onLongPress = () => { Clipboard.setString(url.url); EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') }); }; const hasContent = url.title || url.description; let image = url.image || url.url; if (image) { image = image.includes('http') ? image : `${baseUrl}/${image}?rc_uid=${user.id}&rc_token=${user.token}`; } return ( 0 && styles.marginTop, styles.container, { backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor }, imageLoadedState === 'loading' && styles.loading ]} background={Touchable.Ripple(themes[theme].bannerBackground)} > <> {image ? ( setImageLoadedState('error')} onLoad={() => setImageLoadedState('done')} /> ) : null} {hasContent ? : null} ); }, (oldProps, newProps) => dequal(oldProps.url, newProps.url) && oldProps.theme === newProps.theme ); const Urls = React.memo( // TODO - didn't work - (React.ReactElement | null)[] | React.ReactElement | null ({ urls }: { urls?: IUrl[] }): any => { const { theme } = useTheme(); if (!urls || urls.length === 0) { return null; } return urls.map((url: IUrl, index: number) => ); }, (oldProps, newProps) => dequal(oldProps.urls, newProps.urls) ); UrlContent.displayName = 'MessageUrlContent'; Url.displayName = 'MessageUrl'; Urls.displayName = 'MessageUrls'; export default withTheme(Urls);