@flyerhq/react-native-link-preview
Version:
Fully customizable preview of the URL extracted from the provided text.
148 lines • 6.8 kB
JavaScript
import * as React from 'react';
import { Image, LayoutAnimation, Linking, StyleSheet, Text, TouchableWithoutFeedback, View, } from 'react-native';
import { getPreviewData, oneOf } from './utils';
export const LinkPreview = React.memo(({ containerStyle, enableAnimation, header, metadataContainerStyle, metadataTextContainerStyle, onPreviewDataFetched, previewData, renderDescription, renderHeader, renderImage, renderLinkPreview, renderMinimizedImage, renderText, renderTitle, requestTimeout = 5000, text, textContainerStyle, touchableWithoutFeedbackProps, }) => {
const [containerWidth, setContainerWidth] = React.useState(0);
const [data, setData] = React.useState(previewData);
const aspectRatio = (data === null || data === void 0 ? void 0 : data.image)
? data.image.width / data.image.height
: undefined;
React.useEffect(() => {
let isCancelled = false;
if (previewData) {
setData(previewData);
return;
}
const fetchData = async () => {
setData(undefined);
const newData = await getPreviewData(text, requestTimeout);
// Set data only if component is still mounted
/* istanbul ignore next */
if (!isCancelled) {
// No need to cover LayoutAnimation
/* istanbul ignore next */
if (enableAnimation) {
LayoutAnimation.easeInEaseOut();
}
setData(newData);
onPreviewDataFetched === null || onPreviewDataFetched === void 0 ? void 0 : onPreviewDataFetched(newData);
}
};
fetchData();
return () => {
isCancelled = true;
};
}, [
enableAnimation,
onPreviewDataFetched,
previewData,
requestTimeout,
text,
]);
const handleContainerLayout = React.useCallback((event) => {
setContainerWidth(event.nativeEvent.layout.width);
}, []);
const handlePress = () => (data === null || data === void 0 ? void 0 : data.link) && Linking.openURL(data.link);
const renderDescriptionNode = (description) => {
return oneOf(renderDescription, React.createElement(Text, { numberOfLines: 3, style: styles.description }, description))(description);
};
const renderHeaderNode = () => {
return header
? oneOf(renderHeader, React.createElement(Text, { numberOfLines: 1, style: styles.header }, header))(header)
: null;
};
const renderImageNode = (image) => {
// aspectRatio shouldn't be undefined, just an additional check
/* istanbul ignore next */
const ar = aspectRatio !== null && aspectRatio !== void 0 ? aspectRatio : 1;
return oneOf(renderImage, React.createElement(Image, { accessibilityRole: 'image', resizeMode: 'contain', source: { uri: image.url }, style: StyleSheet.flatten([
styles.image,
ar < 1
? {
height: containerWidth,
minWidth: 170,
width: containerWidth * ar,
}
: {
height: containerWidth / ar,
maxHeight: containerWidth,
width: containerWidth,
},
]) }))(image);
};
const renderLinkPreviewNode = () => {
return oneOf(renderLinkPreview, React.createElement(React.Fragment, null,
React.createElement(View, { style: StyleSheet.flatten([
styles.textContainer,
textContainerStyle,
]) },
renderHeaderNode(),
renderTextNode(),
((data === null || data === void 0 ? void 0 : data.description) ||
((data === null || data === void 0 ? void 0 : data.image) &&
aspectRatio === 1 &&
((data === null || data === void 0 ? void 0 : data.description) || (data === null || data === void 0 ? void 0 : data.title))) ||
(data === null || data === void 0 ? void 0 : data.title)) && (React.createElement(View, { style: StyleSheet.flatten([
styles.metadataContainer,
metadataContainerStyle,
]) },
React.createElement(View, { style: StyleSheet.flatten([
styles.metadataTextContainer,
metadataTextContainerStyle,
]) },
(data === null || data === void 0 ? void 0 : data.title) && renderTitleNode(data.title),
(data === null || data === void 0 ? void 0 : data.description) && renderDescriptionNode(data.description)),
(data === null || data === void 0 ? void 0 : data.image) &&
aspectRatio === 1 &&
renderMinimizedImageNode(data.image)))),
(data === null || data === void 0 ? void 0 : data.image) &&
(aspectRatio !== 1 || (!(data === null || data === void 0 ? void 0 : data.description) && !data.title)) &&
renderImageNode(data.image)))({
aspectRatio,
containerWidth,
previewData: data,
});
};
const renderMinimizedImageNode = (image) => {
return oneOf(renderMinimizedImage, React.createElement(Image, { accessibilityRole: 'image', source: { uri: image.url }, style: styles.minimizedImage }))(image);
};
const renderTextNode = () => oneOf(renderText, React.createElement(Text, null, text))(text);
const renderTitleNode = (title) => {
return oneOf(renderTitle, React.createElement(Text, { numberOfLines: 2, style: styles.title }, title))(title);
};
return (React.createElement(TouchableWithoutFeedback, { accessibilityRole: 'button', onPress: handlePress, ...touchableWithoutFeedbackProps },
React.createElement(View, { onLayout: handleContainerLayout, style: containerStyle }, renderLinkPreviewNode())));
});
const styles = StyleSheet.create({
description: {
marginTop: 4,
},
header: {
marginBottom: 6,
},
image: {
alignSelf: 'center',
backgroundColor: '#f7f7f8',
},
metadataContainer: {
flexDirection: 'row',
marginTop: 16,
},
metadataTextContainer: {
flex: 1,
},
minimizedImage: {
borderRadius: 12,
height: 48,
marginLeft: 4,
width: 48,
},
textContainer: {
marginHorizontal: 24,
marginVertical: 16,
},
title: {
fontWeight: 'bold',
},
});
//# sourceMappingURL=LinkPreview.js.map