Skip to content

Commit

Permalink
Add Link Previews
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtus7 committed May 26, 2023
1 parent b02d902 commit 940cf8b
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/libs/ReportActionsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ function getMostRecentIOURequestActionID(reportActions) {
return _.last(sortedReportActions).reportActionID;
}

/**
* Returns true when the report action contains a link
*
* @param {reportAction} reportAction
* @returns {Boolean}
*/
function containsLink(reportAction) {
return !_.isEmpty(lodashGet(reportAction, ['message', 0, 'html']));
}

/**
* Returns true when the report action immediately before the specified index is a comment made by the same actor who who is leaving a comment in the action at the specified index.
* Also checks to ensure that the comment is not too old to be shown as a grouped comment.
Expand Down Expand Up @@ -387,6 +397,7 @@ export {
getLastVisibleAction,
getLastVisibleMessageText,
getMostRecentIOURequestActionID,
containsLink,
isDeletedAction,
shouldReportActionBeVisible,
isReportActionDeprecated,
Expand Down
14 changes: 14 additions & 0 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,19 @@ function openPaymentDetailsPage(chatReportID, iouReportID) {
);
}

/**
* Gets metadata info about links in the provided report
*
* @param {String} reportID
* @param {String} reportActionID
*/
function expandURLPreview(reportID, reportActionID) {
API.read('ExpandURLPreview', {
reportID,
reportActionID,
});
}

/**
* Gets transactions and data associated with the linked report (expense or IOU report)
*
Expand Down Expand Up @@ -1776,6 +1789,7 @@ export {
deleteReport,
navigateToConciergeChatAndDeleteReport,
setIsComposerFullSize,
expandURLPreview,
markCommentAsUnread,
readNewestAction,
readOldestAction,
Expand Down
98 changes: 98 additions & 0 deletions src/pages/home/report/LinkPreviewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from 'react';
import {View, Image} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {uniqBy} from 'lodash';
import Text from '../../../components/Text';
import styles from '../../../styles/styles';
import variables from '../../../styles/variables';
import colors from '../../../styles/colors';

const IMAGE_TYPES = ['jpg', 'jpeg', 'png'];

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. */
maxAmountOfPreviews: PropTypes.number,
};

const defaultProps = {
linkMetadata: [],
maxAmountOfPreviews: undefined
};

const LinkPreviewer = props => (
_.map(_.take(uniqBy(props.linkMetadata, 'url'), props.maxAmountOfPreviews > 0 ? Math.min(props.maxAmountOfPreviews, props.linkMetadata.length) : props.linkMetadata.length), (linkData) => {
const {
description, image, title, logo, publisher, url,
} = linkData;

return linkData && (
<View style={styles.linkPreviewWrapper} key={url}>
<View style={styles.flexRow}>
{logo && <Image style={styles.linkPreviewLogoImage} source={{uri: logo.url}} />}
{publisher && <Text fontSize={variables.fontSizeLabel} style={styles.pl2}>{publisher}</Text>}
</View>
{title && <Text fontSize={variables.fontSizeNormal} style={styles.pv2} color={colors.blueLinkPreview}>{title}</Text>}
{description && <Text fontSize={variables.fontSizeNormal}>{description}</Text>}
<View style={styles.flexRow}>
{image && IMAGE_TYPES.includes(image.type) && (
<Image
style={[styles.linkPreviewImage, {
aspectRatio: image.width / image.height,
}]}
resizeMode="contain"
source={{uri: image.url}}
/>
)}
</View>
</View>
);
})
);

LinkPreviewer.propTypes = propTypes;
LinkPreviewer.defaultProps = defaultProps;
LinkPreviewer.displayName = 'ReportLinkPreview';

export default LinkPreviewer;
14 changes: 14 additions & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import UnreadActionIndicator from '../../../components/UnreadActionIndicator';
import ReportActionItemMessageEdit from './ReportActionItemMessageEdit';
import ReportActionItemCreated from './ReportActionItemCreated';
import ReportActionItemThread from './ReportActionItemThread';
import LinkPreviewer from './LinkPreviewer';
import compose from '../../../libs/compose';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import ControlSelection from '../../../libs/ControlSelection';
Expand Down Expand Up @@ -117,6 +118,14 @@ function ReportActionItem(props) {
focusTextInputAfterAnimation(textInputRef.current, 100);
}, [isDraftEmpty]);

useEffect(() => {
if (!ReportActionsUtils.containsLink(props.action)) {
return;
}

Report.expandURLPreview(props.report.reportID, props.action.reportActionID);
}, [props.action, props.report.reportID]);

const toggleContextMenuFromActiveReportAction = useCallback(() => {
setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID));
}, [props.action.reportActionID]);
Expand Down Expand Up @@ -276,6 +285,11 @@ function ReportActionItem(props) {
return (
<>
{children}
{!_.isEmpty(props.action.linkMetadata) && (
<LinkPreviewer
linkMetadata={props.action.linkMetadata}
/>
)}
{hasReactions && (
<View style={props.draftMessage ? styles.chatItemReactionsDraftRight : {}}>
<ReportActionItemReactions
Expand Down
1 change: 1 addition & 0 deletions src/styles/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default {
white: '#E7ECE9',
blueLink: '#5AB0FF',
blueLinkHover: '#B0D9FF',
blueLinkPreview: '#2EAAE2',
greenDefaultButton: '#184E3D',
greenDefaultButtonHover: '#2C6755',
greenDefaultButtonPressed: '#467164',
Expand Down
25 changes: 25 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import CONST from '../CONST';
import cursor from './utilities/cursor';
import userSelect from './utilities/userSelect';

function getTransparentColor(color, transparency) {
return `${color}${transparency}`;
}

const picker = {
backgroundColor: themeColors.transparent,
color: themeColors.text,
Expand Down Expand Up @@ -3257,6 +3261,27 @@ const styles = {
textAlign: 'center',
},

linkPreviewWrapper: {
marginTop: 16,
borderLeftWidth: 4,
borderLeftColor: getTransparentColor(themeColors.inverse, 33),
paddingLeft: 12,
},

linkPreviewImage: {
flex: 1,
resizeMode: 'contain',
maxWidth: 350,
maxHeight: 350,
borderRadius: 8,
marginTop: 8,
},

linkPreviewLogoImage: {
height: 16,
width: 16,
},

validateCodeMessage: {
width: variables.modalContentMaxWidth,
textAlign: 'center',
Expand Down

0 comments on commit 940cf8b

Please sign in to comment.