From 96b47ebf5db9d8a9a0281ce76696e07170dcda62 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Sun, 13 Aug 2023 21:33:03 +0100 Subject: [PATCH 01/18] Add Money Report Share page --- src/pages/ShareCodePage.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index a81d02c60b36..5bffa982257e 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -40,9 +40,27 @@ const defaultProps = { class ShareCodePage extends React.Component { qrCodeRef = React.createRef(); + /** + * @param {Boolean} isReport + * @return {String|string|*} + */ + getSubtitle(isReport){ + if (ReportUtils.isMoneyRequestReport(this.props.report)) { + const {workspaceName} = ReportUtils.getParentNavigationSubtitle(this.props.report); + return workspaceName; + } + + if (isReport){ + return ReportUtils.getChatRoomSubtitle(this.props.report); + } + + return this.props.formatPhoneNumber(this.props.session.email); + } + render() { const isReport = this.props.report != null && this.props.report.reportID != null; - const subtitle = ReportUtils.getChatRoomSubtitle(this.props.report); + const title = isReport ? ReportUtils.getReportName(this.props.report) : this.props.currentUserPersonalDetails.displayName; + const subtitle = this.getSubtitle(isReport); const urlWithTrailingSlash = Url.addTrailingForwardSlash(this.props.environmentURL); const url = isReport @@ -51,7 +69,6 @@ class ShareCodePage extends React.Component { const platform = getPlatform(); const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; - const formattedEmail = this.props.formatPhoneNumber(this.props.session.email); return ( @@ -65,8 +82,8 @@ class ShareCodePage extends React.Component { Date: Mon, 14 Aug 2023 13:54:45 +0100 Subject: [PATCH 02/18] Add Navigation to details page for IOU reports --- src/components/AvatarWithDisplayName.js | 21 +++++++++++++++++++- src/components/HeaderWithBackButton/index.js | 7 ++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 0f1300ebf03d..5e4f835d912f 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -18,6 +18,7 @@ import * as OptionsListUtils from '../libs/OptionsListUtils'; import Text from './Text'; import * as StyleUtils from '../styles/StyleUtils'; import ParentNavigationSubtitle from './ParentNavigationSubtitle'; +import PressableWithoutFeedback from "./Pressable/PressableWithoutFeedback"; const propTypes = { /** The report currently being looked at */ @@ -38,6 +39,8 @@ const propTypes = { /** Whether if it's an unauthenticated user */ isAnonymous: PropTypes.bool, + shouldEnableDetailPageNavigation: PropTypes.bool, + ...windowDimensionsPropTypes, ...withLocalizePropTypes, }; @@ -48,6 +51,7 @@ const defaultProps = { report: {}, isAnonymous: false, size: CONST.AVATAR_SIZE.DEFAULT, + shouldEnableDetailPageNavigation: false, }; function AvatarWithDisplayName(props) { @@ -61,7 +65,7 @@ function AvatarWithDisplayName(props) { const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); const isExpenseRequest = ReportUtils.isExpenseRequest(props.report); const defaultSubscriptSize = isExpenseRequest ? CONST.AVATAR_SIZE.SMALL_NORMAL : props.size; - return ( + const headerView = ( {Boolean(props.report && title) && ( @@ -107,6 +111,21 @@ function AvatarWithDisplayName(props) { )} ); + + if (!props.shouldEnableDetailPageNavigation){ + return headerView; + } + + return ( + ReportUtils.navigateToDetailsPage(props.report)} + style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} + accessibilityLabel={title} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} + > + {headerView} + + ) } AvatarWithDisplayName.propTypes = propTypes; AvatarWithDisplayName.displayName = 'AvatarWithDisplayName'; diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index 6e6164f4c4fc..ef55223c46e1 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -46,6 +46,7 @@ function HeaderWithBackButton({ horizontal: 0, }, threeDotsMenuItems = [], + shouldEnableDetailPageNavigation = false, children = null, }) { const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState(); @@ -74,14 +75,14 @@ function HeaderWithBackButton({ )} - {shouldShowAvatarWithDisplay && ( + {shouldShowAvatarWithDisplay ? ( - )} - {!shouldShowAvatarWithDisplay && ( + ) : (
Date: Mon, 14 Aug 2023 13:54:55 +0100 Subject: [PATCH 03/18] Add Navigation to details page for IOU reports --- src/components/MoneyReportHeader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index c4ce8cd173f1..39bf87e76cec 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -67,6 +67,7 @@ function MoneyReportHeader(props) { Date: Mon, 14 Aug 2023 13:55:20 +0100 Subject: [PATCH 04/18] Add Navigation to details page for IOU reports --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index c9fe359a7e88..6c2962256ef6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1451,7 +1451,7 @@ function getReport(reportID) { function navigateToDetailsPage(report) { const participantAccountIDs = lodashGet(report, 'participantAccountIDs', []); - if (isChatRoom(report) || isPolicyExpenseChat(report) || isChatThread(report)) { + if (isChatRoom(report) || isPolicyExpenseChat(report) || isChatThread(report) || isMoneyRequestReport(report)) { Navigation.navigate(ROUTES.getReportDetailsRoute(report.reportID)); return; } From 97a33071f0a8dcf071e92484f465f2f3261b1f8a Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Mon, 14 Aug 2023 14:08:57 +0100 Subject: [PATCH 05/18] Add details page for IOU reports --- src/components/AvatarWithDisplayName.js | 6 +-- src/components/MultipleAvatars.js | 49 ++++++++++++++++++++++--- src/pages/ReportDetailsPage.js | 22 ++++++++++- src/pages/ShareCodePage.js | 4 +- src/styles/styles.js | 16 ++++++++ 5 files changed, 86 insertions(+), 11 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 5e4f835d912f..6e26daa8ca9d 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -18,7 +18,7 @@ import * as OptionsListUtils from '../libs/OptionsListUtils'; import Text from './Text'; import * as StyleUtils from '../styles/StyleUtils'; import ParentNavigationSubtitle from './ParentNavigationSubtitle'; -import PressableWithoutFeedback from "./Pressable/PressableWithoutFeedback"; +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; const propTypes = { /** The report currently being looked at */ @@ -112,7 +112,7 @@ function AvatarWithDisplayName(props) { ); - if (!props.shouldEnableDetailPageNavigation){ + if (!props.shouldEnableDetailPageNavigation) { return headerView; } @@ -125,7 +125,7 @@ function AvatarWithDisplayName(props) { > {headerView} - ) + ); } AvatarWithDisplayName.propTypes = propTypes; AvatarWithDisplayName.displayName = 'AvatarWithDisplayName'; diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index 4c6ba1307fb7..40f94ee981bf 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -84,6 +84,9 @@ function getContainerStyles(size, isInReportAction) { case CONST.AVATAR_SIZE.MEDIUM: containerStyles = [styles.emptyAvatarMedium, styles.emptyAvatarMargin]; break; + case CONST.AVATAR_SIZE.LARGE: + containerStyles = [styles.emptyAvatarLarge, styles.mb2, styles.mr2]; + break; default: containerStyles = [styles.emptyAvatar, isInReportAction ? styles.emptyAvatarMarginChat : styles.emptyAvatarMargin]; } @@ -92,9 +95,39 @@ function getContainerStyles(size, isInReportAction) { } function MultipleAvatars(props) { let avatarContainerStyles = getContainerStyles(props.size, props.isInReportAction); - const singleAvatarStyle = props.size === CONST.AVATAR_SIZE.SMALL ? styles.singleAvatarSmall : styles.singleAvatar; - const secondAvatarStyles = [props.size === CONST.AVATAR_SIZE.SMALL ? styles.secondAvatarSmall : styles.secondAvatar, ...props.secondAvatarStyle]; + const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => { + if (props.size === CONST.AVATAR_SIZE.SMALL) { + return { + singleAvatarStyle: styles.singleAvatarSmall, + secondAvatarStyles: styles.secondAvatarSmall, + }; + } + + if (props.size === CONST.AVATAR_SIZE.LARGE) { + return { + singleAvatarStyle: styles.singleAvatarMedium, + secondAvatarStyles: styles.secondAvatarMedium, + }; + } + + return { + singleAvatarStyle: styles.singleAvatar, + secondAvatarStyles: styles.secondAvatar, + }; + }, [props.size]); + const tooltipTexts = props.shouldShowTooltip ? _.pluck(props.icons, 'name') : ['']; + const avatarSize = useMemo(() => { + if (props.isFocusMode) { + return CONST.AVATAR_SIZE.MID_SUBSCRIPT; + } + + if (props.size === CONST.AVATAR_SIZE.LARGE) { + return CONST.AVATAR_SIZE.MEDIUM; + } + + return CONST.AVATAR_SIZE.SMALLER; + }, [props.isFocusMode, props.size]); const avatarRows = useMemo(() => { // If we're not displaying avatars in rows or the number of icons is less than or equal to the max avatars in a row, return a single row @@ -244,14 +277,20 @@ function MultipleAvatars(props) { - + {props.icons.length === 2 ? ( ReportUtils.isChatThread(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); + const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(props.report), [props.report]); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); + const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const canLeaveRoom = useMemo(() => ReportUtils.canLeaveRoom(props.report, !_.isEmpty(policy)), [policy, props.report]); const participants = useMemo(() => lodashGet(props.report, 'participantAccountIDs', []), [props.report]); @@ -130,6 +134,8 @@ function ReportDetailsPage(props) { return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); + const icons = useMemo(() => ReportUtils.getIcons(props.report, props.personalDetails, props.policies), [props.report, props.personalDetails, props.policies]); + const chatRoomSubtitleText = chatRoomSubtitle ? ( - + {isMoneyRequestReport ? ( + + ) : ( + + )} @@ -173,6 +186,13 @@ function ReportDetailsPage(props) { ) : ( chatRoomSubtitleText )} + {!_.isEmpty(parentNavigationSubtitleData) && isMoneyRequestReport && ( + + )} {_.map(menuItems, (item) => { diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 5bffa982257e..4a3ef3a1920c 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -44,13 +44,13 @@ class ShareCodePage extends React.Component { * @param {Boolean} isReport * @return {String|string|*} */ - getSubtitle(isReport){ + getSubtitle(isReport) { if (ReportUtils.isMoneyRequestReport(this.props.report)) { const {workspaceName} = ReportUtils.getParentNavigationSubtitle(this.props.report); return workspaceName; } - if (isReport){ + if (isReport) { return ReportUtils.getChatRoomSubtitle(this.props.report); } diff --git a/src/styles/styles.js b/src/styles/styles.js index 581410e0e5a5..934f5a4e61da 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1868,6 +1868,13 @@ const styles = { borderRadius: 18, }, + singleAvatarMedium: { + height: 52, + width: 52, + backgroundColor: themeColors.icon, + borderRadius: 52, + }, + secondAvatar: { position: 'absolute', right: -18, @@ -1886,6 +1893,15 @@ const styles = { borderColor: 'transparent', }, + secondAvatarMedium: { + position: 'absolute', + right: -36, + bottom: -36, + borderWidth: 4, + borderRadius: 52, + borderColor: 'transparent', + }, + secondAvatarSubscript: { position: 'absolute', right: -6, From 5b0f9d58b234a00f27da3525f7bce288d746a66d Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Mon, 14 Aug 2023 14:09:40 +0100 Subject: [PATCH 06/18] Add settings page for IOU reports --- src/libs/ReportUtils.js | 4 +- .../settings/Report/ReportSettingsPage.js | 76 +++++++++++-------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 6c2962256ef6..64b205b160ae 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2888,7 +2888,7 @@ function getPolicy(policyID) { * @returns {Boolean} */ function shouldDisableSettings(report) { - return !isPolicyExpenseChat(report) && !isChatRoom(report) && !isChatThread(report); + return !isMoneyRequestReport(report) && !isPolicyExpenseChat(report) && !isChatRoom(report) && !isChatThread(report); } /** @@ -2897,7 +2897,7 @@ function shouldDisableSettings(report) { * @returns {Boolean} */ function shouldDisableRename(report, policy) { - if (isDefaultRoom(report) || isArchivedRoom(report) || isChatThread(report)) { + if (isDefaultRoom(report) || isArchivedRoom(report) || isChatThread(report) || isMoneyRequestReport(report) || isPolicyExpenseChat(report)) { return true; } diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index b21c4b971dca..85981f8444ad 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -58,10 +58,11 @@ function ReportSettingsPage(props) { // The workspace the report is on, null if the user isn't a member of the workspace const linkedWorkspace = useMemo(() => _.find(policies, (policy) => policy && policy.id === report.policyID), [policies, report.policyID]); const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); + const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); // We only want policy owners and admins to be able to modify the welcome message. const shouldDisableWelcomeMessage = - ReportUtils.isArchivedRoom(report) || !ReportUtils.isChatRoom(report) || _.isEmpty(linkedWorkspace) || linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; + isMoneyRequestReport || ReportUtils.isArchivedRoom(report) || !ReportUtils.isChatRoom(report) || _.isEmpty(linkedWorkspace) || linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; const shouldDisableSettings = _.isEmpty(report) || ReportUtils.shouldDisableSettings(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); @@ -69,7 +70,13 @@ function ReportSettingsPage(props) { const writeCapability = ReportUtils.isAdminRoom(report) ? CONST.REPORT.WRITE_CAPABILITIES.ADMINS : report.writeCapability || CONST.REPORT.WRITE_CAPABILITIES.ALL; const writeCapabilityText = translate(`writeCapabilityPage.writeCapability.${writeCapability}`); - const shouldAllowWriteCapabilityEditing = lodashGet(linkedWorkspace, 'role', '') === CONST.POLICY.ROLE.ADMIN && !ReportUtils.isAdminRoom(report); + const shouldAllowWriteCapabilityEditing = lodashGet(linkedWorkspace, 'role', '') === CONST.POLICY.ROLE.ADMIN && !ReportUtils.isAdminRoom(report) && !isMoneyRequestReport; + + const shouldShowNotificationPref = !isMoneyRequestReport; + const roomNameLabel = translate(isMoneyRequestReport ? 'workspace.editor.nameInputLabel' : 'newRoomPage.roomName'); + const reportName = ReportUtils.getReportName(props.report); + + const shouldShowWriteCapability = !isMoneyRequestReport; return ( @@ -79,12 +86,14 @@ function ReportSettingsPage(props) { onBackButtonPress={() => Navigation.goBack(ROUTES.getReportDetailsRoute(report.reportID))} /> - Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(report.reportID))} - /> + {shouldShowNotificationPref && ( + Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(report.reportID))} + /> + )} {shouldShowRoomName && ( - {translate('newRoomPage.roomName')} + {roomNameLabel} - {report.reportName} + {reportName} ) : ( @@ -117,29 +126,30 @@ function ReportSettingsPage(props) { )} )} - {shouldAllowWriteCapabilityEditing ? ( - Navigation.navigate(ROUTES.getReportSettingsWriteCapabilityRoute(report.reportID))} - /> - ) : ( - - - {translate('writeCapabilityPage.label')} - - - {writeCapabilityText} - - - )} + {shouldShowWriteCapability && + (shouldAllowWriteCapabilityEditing ? ( + Navigation.navigate(ROUTES.getReportSettingsWriteCapabilityRoute(report.reportID))} + /> + ) : ( + + + {translate('writeCapabilityPage.label')} + + + {writeCapabilityText} + + + ))} {Boolean(linkedWorkspace) && ( From 15df3daf725d0c99eb5dea4f53b0b30e3dc428e1 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Mon, 14 Aug 2023 17:34:20 +0100 Subject: [PATCH 07/18] Generate subtitle for iou report --- src/pages/ShareCodePage.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 4a3ef3a1920c..8bf5f4c2bf5b 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -1,5 +1,6 @@ import React from 'react'; import {View, ScrollView} from 'react-native'; +import _ from 'underscore'; import ScreenWrapper from '../components/ScreenWrapper'; import HeaderWithBackButton from '../components/HeaderWithBackButton'; import Navigation from '../libs/Navigation/Navigation'; @@ -45,9 +46,13 @@ class ShareCodePage extends React.Component { * @return {String|string|*} */ getSubtitle(isReport) { + if (ReportUtils.isExpenseReport(this.props.report)) { + return ReportUtils.getPolicyName(this.props.report); + } if (ReportUtils.isMoneyRequestReport(this.props.report)) { - const {workspaceName} = ReportUtils.getParentNavigationSubtitle(this.props.report); - return workspaceName; + // generate subtitle from participants + const participantAccountIDs = [this.props.report.managerID, this.props.report.ownerAccountID]; + return _.map(participantAccountIDs, (accountID) => ReportUtils.getDisplayNameForParticipant(accountID)).join(' & '); } if (isReport) { From 0def258cfef3918b718536bf12955a5edd130a9e Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 15 Aug 2023 00:04:36 +0100 Subject: [PATCH 08/18] Adjust border Width --- src/styles/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 934f5a4e61da..eefcb472e49a 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1897,7 +1897,7 @@ const styles = { position: 'absolute', right: -36, bottom: -36, - borderWidth: 4, + borderWidth: 3, borderRadius: 52, borderColor: 'transparent', }, From 78c280da29dca8155dcc25231926d066b66e41b1 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 15 Aug 2023 00:21:47 +0100 Subject: [PATCH 09/18] Simplify Avatar Style Calculation --- src/components/MultipleAvatars.js | 36 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index 40f94ee981bf..7d41d984a53b 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -71,6 +71,21 @@ const defaultProps = { maxAvatarsInRow: CONST.AVATAR_ROW_SIZE.DEFAULT, }; +const avatarSizeToStylesMap = { + [CONST.AVATAR_SIZE.SMALL]: { + singleAvatarStyle: styles.singleAvatarSmall, + secondAvatarStyles: styles.secondAvatarSmall, + }, + [CONST.AVATAR_SIZE.LARGE]: { + singleAvatarStyle: styles.singleAvatarMedium, + secondAvatarStyles: styles.secondAvatarMedium, + }, + default: { + singleAvatarStyle: styles.singleAvatar, + secondAvatarStyles: styles.secondAvatar, + }, +}; + function getContainerStyles(size, isInReportAction) { let containerStyles; @@ -95,26 +110,7 @@ function getContainerStyles(size, isInReportAction) { } function MultipleAvatars(props) { let avatarContainerStyles = getContainerStyles(props.size, props.isInReportAction); - const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => { - if (props.size === CONST.AVATAR_SIZE.SMALL) { - return { - singleAvatarStyle: styles.singleAvatarSmall, - secondAvatarStyles: styles.secondAvatarSmall, - }; - } - - if (props.size === CONST.AVATAR_SIZE.LARGE) { - return { - singleAvatarStyle: styles.singleAvatarMedium, - secondAvatarStyles: styles.secondAvatarMedium, - }; - } - - return { - singleAvatarStyle: styles.singleAvatar, - secondAvatarStyles: styles.secondAvatar, - }; - }, [props.size]); + const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[props.size] || avatarSizeToStylesMap.default, [props.size]); const tooltipTexts = props.shouldShowTooltip ? _.pluck(props.icons, 'name') : ['']; const avatarSize = useMemo(() => { From eb5b02f2ac55aac6d6034413e9348ec06a498f44 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Sat, 19 Aug 2023 17:42:33 +0100 Subject: [PATCH 10/18] [Fix] Get members array for money request reports --- src/libs/ReportUtils.js | 14 ++++++++++++++ src/pages/ReportDetailsPage.js | 3 +-- src/pages/ReportParticipantsPage.js | 7 ++----- src/pages/ShareCodePage.js | 3 +-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e08a97f8be6f..9c19d35be9f8 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3030,6 +3030,19 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID }; } +/** + * Returns an array of the participants Ids of a report + * + * @param {Object} report + * @returns {Array} + */ +function getParticipantsIDs(report) { + if (isMoneyRequestReport(report)) { + return [report.managerID, report.ownerAccountID]; + } + return lodashGet(report, 'participantAccountIDs', []); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -3152,4 +3165,5 @@ export { shouldDisableRename, hasSingleParticipant, getTaskAssigneeChatOnyxData, + getParticipantsIDs, }; diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index c2e075551aaa..eeb3f8b0c7b9 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import {View, ScrollView} from 'react-native'; -import lodashGet from 'lodash/get'; import RoomHeaderAvatars from '../components/RoomHeaderAvatars'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; @@ -74,7 +73,7 @@ function ReportDetailsPage(props) { const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const canLeaveRoom = useMemo(() => ReportUtils.canLeaveRoom(props.report, !_.isEmpty(policy)), [policy, props.report]); - const participants = useMemo(() => lodashGet(props.report, 'participantAccountIDs', []), [props.report]); + const participants = useMemo(() => ReportUtils.getParticipantsIDs(props.report), [props.report]); const menuItems = useMemo(() => { const items = [ diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 407176c19bb9..6189bca9327c 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -54,10 +54,8 @@ const defaultProps = { * @param {Object} translate The localize * @return {Array} */ -const getAllParticipants = (report, personalDetails, translate) => { - const {participantAccountIDs} = report; - - return _.chain(participantAccountIDs) +const getAllParticipants = (report, personalDetails, translate) => + _.chain(ReportUtils.getParticipantsIDs(report)) .map((accountID, index) => { const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || translate('common.hidden'), avatar: ''}); const userLogin = LocalePhoneNumber.formatPhoneNumber(userPersonalDetail.login || '') || translate('common.hidden'); @@ -83,7 +81,6 @@ const getAllParticipants = (report, personalDetails, translate) => { }) .sortBy((participant) => participant.displayName.toLowerCase()) .value(); -}; function ReportParticipantsPage(props) { const participants = _.map(getAllParticipants(props.report, props.personalDetails, props.translate), (participant) => ({ diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 8bf5f4c2bf5b..a016d749b41c 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -51,8 +51,7 @@ class ShareCodePage extends React.Component { } if (ReportUtils.isMoneyRequestReport(this.props.report)) { // generate subtitle from participants - const participantAccountIDs = [this.props.report.managerID, this.props.report.ownerAccountID]; - return _.map(participantAccountIDs, (accountID) => ReportUtils.getDisplayNameForParticipant(accountID)).join(' & '); + return _.map(ReportUtils.getParticipantsIDs(this.props.report), (accountID) => ReportUtils.getDisplayNameForParticipant(accountID)).join(' & '); } if (isReport) { From 1dd318c39a599d53382d806b68f3678b52bb21ea Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Sat, 19 Aug 2023 17:43:31 +0100 Subject: [PATCH 11/18] [Fix] Add missing notificationPreference prop to iou optimistic report --- src/libs/ReportUtils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 9c19d35be9f8..96feeb80d858 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1676,6 +1676,7 @@ function buildOptimisticIOUReport(payeeAccountID, payerAccountID, total, chatRep // We don't translate reportName because the server response is always in English reportName: `${payerEmail} owes ${formattedTotal}`, + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, }; } @@ -1713,6 +1714,7 @@ function buildOptimisticExpenseReport(chatReportID, policyID, payeeAccountID, to state: CONST.REPORT.STATE.SUBMITTED, stateNum: CONST.REPORT.STATE_NUM.PROCESSING, total: storedTotal, + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, }; } From 53e2aedd358db955a0d114c02694b03acadd5899 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 29 Aug 2023 11:38:15 +0100 Subject: [PATCH 12/18] [Fix] Open details page for iou/expense reports --- src/components/AvatarWithDisplayName.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 6de5476c1a4b..c83814723a2f 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -58,7 +58,12 @@ const defaultProps = { shouldEnableDetailPageNavigation: false, }; -const showActorDetails = (report) => { +const showActorDetails = (report, shouldEnableDetailPageNavigation = false) => { + // We should navigate to the details page if the report is a IOU/expense report + if (shouldEnableDetailPageNavigation) { + return ReportUtils.navigateToDetailsPage(report); + } + if (ReportUtils.isExpenseReport(report)) { Navigation.navigate(ROUTES.getProfileRoute(report.ownerAccountID)); return; @@ -99,7 +104,7 @@ function AvatarWithDisplayName(props) { {Boolean(props.report && title) && ( showActorDetails(props.report)} + onPress={() => showActorDetails(props.report, props.shouldEnableDetailPageNavigation)} accessibilityLabel={title} accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} > From dc409298a4748735836bcfc2339e97accc48ad46 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 29 Aug 2023 11:50:01 +0100 Subject: [PATCH 13/18] [Chore] Add empty checks --- src/libs/ReportUtils.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index bc191d5e877c..c514a8fcb586 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1502,7 +1502,7 @@ function getModifiedExpenseMessage(reportAction) { * * @param {Object} oldTransaction * @param {Object} transactionChanges - * @param {Boolen} isFromExpenseReport + * @param {Boolean} isFromExpenseReport * @returns {Object} */ function getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport) { @@ -3428,10 +3428,14 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID * @returns {Array} */ function getParticipantsIDs(report) { + if (!report) { + return []; + } + // Build participants list for IOU/expense reports if (isMoneyRequestReport(report)) { - return [report.managerID, report.ownerAccountID]; + return [report.managerID || '', report.ownerAccountID || '']; } - return lodashGet(report, 'participantAccountIDs', []); + return report.participantAccountIDs || []; } /** From 73157429de28f0aa130423e9e1df72a9b563b28b Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Thu, 31 Aug 2023 10:59:36 +0100 Subject: [PATCH 14/18] [Chore] Remove hidden participants --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index c514a8fcb586..7a0ee0b837a8 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3433,7 +3433,7 @@ function getParticipantsIDs(report) { } // Build participants list for IOU/expense reports if (isMoneyRequestReport(report)) { - return [report.managerID || '', report.ownerAccountID || '']; + return _.compact([report.managerID, report.ownerAccountID]); } return report.participantAccountIDs || []; } From 99d2b6abbe8bd1e15e69133a9dddb02b1e725b0a Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Thu, 31 Aug 2023 11:01:17 +0100 Subject: [PATCH 15/18] [Fix] Display members header for expense reports --- src/pages/ReportParticipantsPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 045ff0d26847..a41a0344c5a3 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -97,7 +97,8 @@ function ReportParticipantsPage(props) { ReportUtils.isChatRoom(props.report) || ReportUtils.isPolicyExpenseChat(props.report) || ReportUtils.isChatThread(props.report) || - ReportUtils.isTaskReport(props.report) + ReportUtils.isTaskReport(props.report) || + ReportUtils.isMoneyRequestReport(props.report) ? 'common.members' : 'common.details', )} From 0df60dc98dbb0ad3d520999f637bfc3dc96f482a Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Thu, 31 Aug 2023 11:23:34 +0100 Subject: [PATCH 16/18] [Fix] Build participants list for IOU/expense reports --- src/libs/ReportUtils.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 7a0ee0b837a8..99ebc0280f0b 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3431,11 +3431,17 @@ function getParticipantsIDs(report) { if (!report) { return []; } + + const participants = report.participantAccountIDs || []; + // Build participants list for IOU/expense reports if (isMoneyRequestReport(report)) { - return _.compact([report.managerID, report.ownerAccountID]); + return _.chain([report.managerID, report.ownerAccountID, ...participants]) + .compact() + .uniq() + .value(); } - return report.participantAccountIDs || []; + return participants; } /** From b5c9c8ec489d9f4da94866d2a892c89f4447f036 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 26 Sep 2023 21:59:21 +0100 Subject: [PATCH 17/18] replace themeColors with theme --- src/styles/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index b79fcd45e18d..563f075e0f91 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1858,7 +1858,7 @@ const styles = (theme) => ({ singleAvatarMedium: { height: 52, width: 52, - backgroundColor: themeColors.icon, + backgroundColor: theme.icon, borderRadius: 52, }, From 498d22444d80e3060658ef36d643b0c7a4dbe405 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Mon, 2 Oct 2023 02:00:46 +0100 Subject: [PATCH 18/18] [Chore] Hide privateNotes for IOU/expense reports --- src/pages/ReportDetailsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index ccf90f0e92b8..319b39c94c68 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -116,7 +116,7 @@ function ReportDetailsPage(props) { } // Prevent displaying private notes option for threads and task reports - if (!isThread && !ReportUtils.isTaskReport(props.report)) { + if (!isThread && !isMoneyRequestReport && !ReportUtils.isTaskReport(props.report)) { items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.PRIVATE_NOTES, translationKey: 'privateNotes.title', @@ -138,7 +138,7 @@ function ReportDetailsPage(props) { } return items; - }, [props.report, participants, isArchivedRoom, shouldDisableSettings, isThread, isUserCreatedPolicyRoom, canLeaveRoom]); + }, [isArchivedRoom, participants.length, shouldDisableSettings, isThread, isMoneyRequestReport, props.report, isUserCreatedPolicyRoom, canLeaveRoom]); const displayNamesWithTooltips = useMemo(() => { const hasMultipleParticipants = participants.length > 1;