diff --git a/src/pages/home/report/LinkPreviewer.js b/src/pages/home/report/LinkPreviewer.js deleted file mode 100644 index f62295a6fb71..000000000000 --- a/src/pages/home/report/LinkPreviewer.js +++ /dev/null @@ -1,139 +0,0 @@ -import {uniqBy} from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {Image, View} from 'react-native'; -import _ from 'underscore'; -import Text from '@components/Text'; -import TextLink from '@components/TextLink'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; -import variables from '@styles/variables'; - -const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; -const MAX_IMAGE_HEIGHT = 180; -const MAX_IMAGE_WIDTH = 340; - -const propTypes = { - /** Data about links provided in message. */ - linkMetadata: PropTypes.arrayOf( - PropTypes.shape({ - /** The URL of the link. */ - url: PropTypes.string, - - /** A description of the link. */ - description: PropTypes.string, - - /** The title of the link. */ - title: PropTypes.string, - - /** The publisher of the link. */ - publisher: PropTypes.string, - - /** The image associated with the link. */ - image: PropTypes.shape({ - /** The height of the image. */ - height: PropTypes.number, - - /** The width of the image. */ - width: PropTypes.number, - - /** The URL of the image. */ - url: PropTypes.string, - }), - - /** The provider logo associated with the link. */ - logo: PropTypes.shape({ - /** The height of the logo. */ - height: PropTypes.number, - - /** The width of the logo. */ - width: PropTypes.number, - - /** The URL of the logo. */ - url: PropTypes.string, - }), - }), - ), - - /** Maximum amount of visible link previews. -1 means unlimited amount of previews */ - maxAmountOfPreviews: PropTypes.number, -}; - -const defaultProps = { - linkMetadata: [], - maxAmountOfPreviews: -1, -}; - -function LinkPreviewer(props) { - const theme = useTheme(); - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - return _.map( - _.take(uniqBy(props.linkMetadata, 'url'), props.maxAmountOfPreviews >= 0 ? Math.min(props.maxAmountOfPreviews, props.linkMetadata.length) : props.linkMetadata.length), - (linkData) => { - if (_.isArray(linkData)) { - return; - } - const {description, image, title, logo, publisher, url} = linkData; - - return ( - linkData && ( - - - {!_.isEmpty(logo) && ( - - )} - {!_.isEmpty(publisher) && ( - - {publisher} - - )} - - {!_.isEmpty(title) && ( - - {title} - - )} - {!_.isEmpty(description) && {description}} - {!_.isEmpty(image) && IMAGE_TYPES.includes(image.type) && ( - - )} - - ) - ); - }, - ); -} - -LinkPreviewer.propTypes = propTypes; -LinkPreviewer.defaultProps = defaultProps; -LinkPreviewer.displayName = 'ReportLinkPreview'; - -export default LinkPreviewer; diff --git a/src/pages/home/report/LinkPreviewer.tsx b/src/pages/home/report/LinkPreviewer.tsx new file mode 100644 index 000000000000..209096d2fa15 --- /dev/null +++ b/src/pages/home/report/LinkPreviewer.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import {Image, View} from 'react-native'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import type {LinkMetadata} from '@src/types/onyx/ReportAction'; + +const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; +const MAX_IMAGE_HEIGHT = 180; +const MAX_IMAGE_WIDTH = 340; + +type LinkPreviewerProps = { + /** Data about links provided in message. */ + linkMetadata?: LinkMetadata[]; + + /** Maximum amount of visible link previews. -1 means unlimited amount of previews */ + maxAmountOfPreviews?: number; +}; + +function filterNonUniqueLinks(linkMetadata: LinkMetadata[]): LinkMetadata[] { + const linksMap = new Map(); + const result: LinkMetadata[] = []; + + linkMetadata.forEach((item) => { + if (!item.url || linksMap.has(item.url)) { + return; + } + + linksMap.set(item.url, item.url); + result.push(item); + }); + + return result; +} + +function LinkPreviewer({linkMetadata = [], maxAmountOfPreviews = -1}: LinkPreviewerProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const uniqueLinks = filterNonUniqueLinks(linkMetadata); + const maxAmmountOfLinks = maxAmountOfPreviews >= 0 ? Math.min(maxAmountOfPreviews, linkMetadata.length) : linkMetadata.length; + const linksToShow = uniqueLinks.slice(0, maxAmmountOfLinks); + return linksToShow.map((linkData) => { + if (!linkData && Array.isArray(linkData)) { + return; + } + const {description, image, title, logo, publisher, url} = linkData; + return ( + + + {logo && ( + + )} + {publisher && ( + + {publisher} + + )} + + {title && url && ( + + {title} + + )} + {description && {description}} + {image?.type && IMAGE_TYPES.includes(image.type) && image.width && image.height && ( + + )} + + ); + }); +} + +LinkPreviewer.displayName = 'ReportLinkPreview'; + +export default LinkPreviewer; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 8e56aaa67345..a881b63fbb95 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -63,6 +63,9 @@ type ImageMetadata = { /** The URL of the image. */ url?: string; + + /** The type of the image. */ + type?: string; }; type LinkMetadata = { @@ -197,4 +200,4 @@ type ReportAction = ReportActionBase & OriginalMessage; type ReportActions = Record; export default ReportAction; -export type {ReportActions, ReportActionBase, Message}; +export type {ReportActions, ReportActionBase, Message, LinkMetadata};