From 921679923c2f95120d228e9e1df327e04d4ea45e Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 15 Sep 2023 12:50:52 +0800 Subject: [PATCH 001/174] Show RBR in Transaction row in LHN for requestor --- src/libs/OptionsListUtils.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7629a1acc0a6..b94f652ac348 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -18,6 +18,9 @@ import * as UserUtils from './UserUtils'; import * as ReportActionUtils from './ReportActionsUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as ErrorUtils from './ErrorUtils'; +import * as ReportActionsUtils from "./ReportActionsUtils"; +import * as TransactionUtils from './TransactionUtils'; +import {isCurrentUserSubmitter} from "./ReportUtils"; /** * OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can @@ -351,20 +354,19 @@ function getSearchText(report, reportName, personalDetailList, isChatRoomOrPolic function getAllReportErrors(report, reportActions) { const reportErrors = report.errors || {}; const reportErrorFields = report.errorFields || {}; - const reportActionErrors = {}; - _.each(reportActions, (action) => { - if (action && !_.isEmpty(action.errors)) { - _.extend(reportActionErrors, action.errors); - } else if (ReportActionUtils.isReportPreviewAction(action)) { - const iouReportID = ReportActionUtils.getIOUReportIDFromReportActionPreview(action); - - // Instead of adding all Smartscan errors, let's just add a generic error if there are any. This - // will be more performant and provide the same result in the UI - if (ReportUtils.hasMissingSmartscanFields(iouReportID)) { - _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); - } + let reportActionErrors = _.reduce( + reportActions, + (prevReportActionErrors, action) => (!action || _.isEmpty(action.errors) ? prevReportActionErrors : _.extend(prevReportActionErrors, action.errors)), + {}, + ); + + const parentReportAction = ReportActionsUtils.getParentReportAction(report); + if (parentReportAction.actorAccountID === currentUserAccountID && ReportActionsUtils.isTransactionThread(parentReportAction)) { + const transaction = TransactionUtils.getLinkedTransaction(parentReportAction); + if (TransactionUtils.hasMissingSmartscanFields(transaction)) { + _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } - }); + } // All error objects related to the report. Each object in the sources contains error messages keyed by microtime const errorSources = { From 540b784be7a0ce3e7347f76e9fb9d94b687ddaa5 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 15 Sep 2023 12:51:21 +0800 Subject: [PATCH 002/174] remove unneeded imports --- src/libs/OptionsListUtils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index b94f652ac348..f1c1a517f6d4 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -18,9 +18,8 @@ import * as UserUtils from './UserUtils'; import * as ReportActionUtils from './ReportActionsUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as ErrorUtils from './ErrorUtils'; -import * as ReportActionsUtils from "./ReportActionsUtils"; +import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; -import {isCurrentUserSubmitter} from "./ReportUtils"; /** * OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can From a324d01cfc243d9ef50d3a39e6c8156928918a39 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 15 Sep 2023 14:19:19 +0800 Subject: [PATCH 003/174] lint --- src/libs/OptionsListUtils.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index f1c1a517f6d4..b945082d78c4 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -18,7 +18,6 @@ import * as UserUtils from './UserUtils'; import * as ReportActionUtils from './ReportActionsUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as ErrorUtils from './ErrorUtils'; -import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; /** @@ -353,14 +352,14 @@ function getSearchText(report, reportName, personalDetailList, isChatRoomOrPolic function getAllReportErrors(report, reportActions) { const reportErrors = report.errors || {}; const reportErrorFields = report.errorFields || {}; - let reportActionErrors = _.reduce( + const reportActionErrors = _.reduce( reportActions, (prevReportActionErrors, action) => (!action || _.isEmpty(action.errors) ? prevReportActionErrors : _.extend(prevReportActionErrors, action.errors)), {}, ); - const parentReportAction = ReportActionsUtils.getParentReportAction(report); - if (parentReportAction.actorAccountID === currentUserAccountID && ReportActionsUtils.isTransactionThread(parentReportAction)) { + const parentReportAction = ReportActionUtils.getParentReportAction(report); + if (parentReportAction.actorAccountID === currentUserAccountID && ReportActionUtils.isTransactionThread(parentReportAction)) { const transaction = TransactionUtils.getLinkedTransaction(parentReportAction); if (TransactionUtils.hasMissingSmartscanFields(transaction)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); From d6eca15fe76de9c0bba2c66fcfb135a1c18c89b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 15 Sep 2023 16:54:27 +0200 Subject: [PATCH 004/174] displayName fallback to Hidden --- src/libs/ReportUtils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f24959c4bac2..df030ddd3756 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1103,6 +1103,9 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false) { const personalDetails = getPersonalDetailsForAccountID(accountID); const longName = personalDetails.displayName; const shortName = personalDetails.firstName || longName; + if (!longName && !personalDetails.login) { + return Localize.translateLocal('common.hidden'); + } return shouldUseShortForm ? shortName : longName; } From 92a81a8175fd6330f935ed1e2fe8548a0e399146 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Sun, 17 Sep 2023 19:44:23 +0200 Subject: [PATCH 005/174] Init migration --- src/libs/actions/{TestTool.js => TestTool.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/libs/actions/{TestTool.js => TestTool.ts} (100%) diff --git a/src/libs/actions/TestTool.js b/src/libs/actions/TestTool.ts similarity index 100% rename from src/libs/actions/TestTool.js rename to src/libs/actions/TestTool.ts From 212718dafee46367da507ab7a67f57ff22d6e132 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 18 Sep 2023 16:55:58 +0800 Subject: [PATCH 006/174] Add red dot to IOU report --- src/libs/OptionsListUtils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index b945082d78c4..173afa3985a7 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -364,6 +364,10 @@ function getAllReportErrors(report, reportActions) { if (TransactionUtils.hasMissingSmartscanFields(transaction)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } + } else if (ReportUtils.isIOUReport(report)) { + if (ReportUtils.hasMissingSmartscanFields(report.reportID)) { + _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); + } } // All error objects related to the report. Each object in the sources contains error messages keyed by microtime From 349220bdc750f6084ab4ec3218b5bf297b582c44 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 18 Sep 2023 16:56:19 +0800 Subject: [PATCH 007/174] also request reports --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 173afa3985a7..3fbe92109447 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -364,7 +364,7 @@ function getAllReportErrors(report, reportActions) { if (TransactionUtils.hasMissingSmartscanFields(transaction)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } - } else if (ReportUtils.isIOUReport(report)) { + } else if (ReportUtils.isIOUReport(report) || ReportUtils.isExpenseReport(report)) { if (ReportUtils.hasMissingSmartscanFields(report.reportID)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } From cbd28d3758af5d28b0787a9799aa8683eb3a0757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 25 Sep 2023 12:43:07 +0200 Subject: [PATCH 008/174] fix/search list shows hidden for unkown yet contact --- src/components/OptionRow.js | 1 + src/libs/ReportUtils.js | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index a07510f7603d..2644feaccb58 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -162,6 +162,7 @@ class OptionRow extends Component { const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips( (this.props.option.participantsList || (this.props.option.accountID ? [this.props.option] : [])).slice(0, 10), isMultipleParticipant, + true, ); let subscriptColor = themeColors.appBG; if (this.props.optionIsFocused) { diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index df030ddd3756..6d2e99dbd4f4 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1094,16 +1094,17 @@ function getPersonalDetailsForAccountID(accountID) { * * @param {Number} accountID * @param {Boolean} [shouldUseShortForm] + * @param {Boolean} shouldNotFallbackToHidden * @returns {String} */ -function getDisplayNameForParticipant(accountID, shouldUseShortForm = false) { +function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, shouldNotFallbackToHidden = false) { if (!accountID) { return ''; } const personalDetails = getPersonalDetailsForAccountID(accountID); const longName = personalDetails.displayName; const shortName = personalDetails.firstName || longName; - if (!longName && !personalDetails.login) { + if (!longName && !personalDetails.login && !shouldNotFallbackToHidden) { return Localize.translateLocal('common.hidden'); } return shouldUseShortForm ? shortName : longName; @@ -1112,12 +1113,13 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false) { /** * @param {Object} personalDetailsList * @param {Boolean} isMultipleParticipantReport + * @param {Boolean} shouldNotFallbackToHidden * @returns {Array} */ -function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantReport) { +function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantReport, shouldNotFallbackToHidden) { return _.map(personalDetailsList, (user) => { const accountID = Number(user.accountID); - const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport) || user.login || ''; + const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldNotFallbackToHidden) || user.login || ''; const avatar = UserUtils.getDefaultAvatar(accountID); let pronouns = user.pronouns; From 95af7e41853576ef201d9d57404a1ed7990009f8 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 26 Sep 2023 15:56:29 +0800 Subject: [PATCH 009/174] avoid deprecated methods --- src/libs/OptionsListUtils.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index d63272b8a591..dbf50b894e08 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -92,6 +92,18 @@ Onyx.connect({ }, }); +let allTransactions = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (val) => { + if (!val) { + return; + } + allTransactions = _.pick(val, (transaction) => transaction); + }, +}); + /** * Get the option for a policy expense report. * @param {Object} report @@ -354,9 +366,10 @@ function getAllReportErrors(report, reportActions) { {}, ); - const parentReportAction = ReportActionUtils.getParentReportAction(report); + const parentReportAction = !report.parentReportID || !report.parentReportActionID ? {} : lodashGet(allSortedReportActions, [report.parentReportID, report.parentReportActionID], {}); if (parentReportAction.actorAccountID === currentUserAccountID && ReportActionUtils.isTransactionThread(parentReportAction)) { - const transaction = TransactionUtils.getLinkedTransaction(parentReportAction); + const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], ''); + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] || {}; if (TransactionUtils.hasMissingSmartscanFields(transaction)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } From 02c07aa736b7b5b2fba8edb9034f5cae7b59daba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 27 Sep 2023 16:56:45 +0200 Subject: [PATCH 010/174] more fixes --- src/components/OptionRow.js | 1 - src/libs/OptionsListUtils.js | 5 +++-- src/libs/ReportUtils.js | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index 2644feaccb58..a07510f7603d 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -162,7 +162,6 @@ class OptionRow extends Component { const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips( (this.props.option.participantsList || (this.props.option.accountID ? [this.props.option] : [])).slice(0, 10), isMultipleParticipant, - true, ); let subscriptColor = themeColors.appBG; if (this.props.optionIsFocused) { diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7629a1acc0a6..12b820a5ba60 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -430,7 +430,7 @@ function getLastMessageTextForReport(report) { * @param {Boolean} [options.forcePolicyNamePreview] * @returns {Object} */ -function createOption(accountIDs, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false}) { +function createOption(accountIDs, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false, shouldNotFallbackToHidden = false}) { const result = { text: null, alternateText: null, @@ -528,7 +528,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { } reportName = ReportUtils.getReportName(report); } else { - reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0]); + reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0], false, shouldNotFallbackToHidden); result.keyForList = String(accountIDs[0]); result.alternateText = LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetails, [accountIDs[0], 'login'], '')); } @@ -998,6 +998,7 @@ function getOptions( }; userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { showChatPreviewLine, + shouldNotFallbackToHidden: true, }); userToInvite.isOptimisticAccount = true; userToInvite.login = searchValue; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 6d2e99dbd4f4..0d04dcd5c031 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1089,6 +1089,11 @@ function getPersonalDetailsForAccountID(accountID) { ); } +function hasOnlyAvatarField(obj) { + const keys = _.keys(obj); + return keys.length === 1 && keys[0] === 'avatar'; +} + /** * Get the displayName for a single report participant. * @@ -1102,6 +1107,10 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, sho return ''; } const personalDetails = getPersonalDetailsForAccountID(accountID); + // check if it's invite account + if (hasOnlyAvatarField(personalDetails)) { + return ''; + } const longName = personalDetails.displayName; const shortName = personalDetails.firstName || longName; if (!longName && !personalDetails.login && !shouldNotFallbackToHidden) { From 30513addfdca6f9221493a5bbdcab4837436f72b Mon Sep 17 00:00:00 2001 From: Sebastian Szewczyk Date: Sat, 30 Sep 2023 22:25:27 +0200 Subject: [PATCH 011/174] Optimized context usages and set up eslint rule to prevent unnecessary rerender in the future --- .eslintrc.js | 2 + .../AnimatedStep/AnimatedStepProvider.js | 5 +- .../AttachmentCarousel/Pager/index.js | 39 ++++++++++----- src/components/DragAndDrop/Provider/index.js | 5 +- src/components/PopoverProvider/index.js | 16 +++---- .../PopoverProvider/index.native.js | 16 +++---- src/components/ScrollViewWithContext.js | 12 +++-- src/components/withKeyboardState.js | 7 ++- src/components/withWindowDimensions/index.js | 48 +++++++++---------- .../withWindowDimensions/index.native.js | 45 ++++++++--------- src/pages/home/report/ReportActionItem.js | 23 ++++----- .../TwoFactorAuth/TwoFactorAuthSteps.js | 7 ++- 12 files changed, 118 insertions(+), 107 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b5b4add538f6..afcea91acc34 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,9 +37,11 @@ module.exports = { overrides: [ { files: ['*.js', '*.jsx', '*.ts', '*.tsx'], + plugins: ['react'], rules: { 'rulesdir/onyx-props-must-have-default': 'off', 'react-native-a11y/has-accessibility-hint': ['off'], + 'react/jsx-no-constructed-context-values': 'error', 'react-native-a11y/has-valid-accessibility-descriptors': [ 'error', { diff --git a/src/components/AnimatedStep/AnimatedStepProvider.js b/src/components/AnimatedStep/AnimatedStepProvider.js index 280fbd1a2776..86d40b5bddeb 100644 --- a/src/components/AnimatedStep/AnimatedStepProvider.js +++ b/src/components/AnimatedStep/AnimatedStepProvider.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useMemo, useState} from 'react'; import PropTypes from 'prop-types'; import AnimatedStepContext from './AnimatedStepContext'; import CONST from '../../CONST'; @@ -9,8 +9,9 @@ const propTypes = { function AnimatedStepProvider({children}) { const [animationDirection, setAnimationDirection] = useState(CONST.ANIMATION_DIRECTION.IN); + const contextValue = useMemo(() => ({animationDirection, setAnimationDirection}), [animationDirection, setAnimationDirection]); - return {children}; + return {children}; } AnimatedStepProvider.propTypes = propTypes; diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.js b/src/components/Attachments/AttachmentCarousel/Pager/index.js index 9779963dfc4a..4ca4e77e5136 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/index.js +++ b/src/components/Attachments/AttachmentCarousel/Pager/index.js @@ -1,5 +1,5 @@ /* eslint-disable es/no-optional-chaining */ -import React, {useRef, useState, useImperativeHandle} from 'react'; +import React, {useRef, useState, useImperativeHandle, useMemo} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {GestureHandlerRootView, createNativeWrapper} from 'react-native-gesture-handler'; @@ -126,21 +126,34 @@ function AttachmentCarouselPager({ scrollEnabled: shouldPagerScroll.value, })); + const contextValue = useMemo(() => ({ + canvasWidth: containerWidth, + canvasHeight: containerHeight, + isScrolling, + pagerRef, + shouldPagerScroll, + onPinchGestureChange, + onTap, + onSwipe, + onSwipeSuccess, + onSwipeDown, + }), [ + containerWidth, + containerHeight, + isScrolling, + pagerRef, + shouldPagerScroll, + onPinchGestureChange, + onTap, + onSwipe, + onSwipeSuccess, + onSwipeDown, + ]); + return ( ({isDraggingOver, setOnDropHandler, dropZoneID: dropZoneID.current}), [isDraggingOver, setOnDropHandler]); return ( - + (dropZone.current = e)} style={[styles.flex1, styles.w100, styles.h100]} diff --git a/src/components/PopoverProvider/index.js b/src/components/PopoverProvider/index.js index efa230d920d5..c0c62cb70cb2 100644 --- a/src/components/PopoverProvider/index.js +++ b/src/components/PopoverProvider/index.js @@ -111,15 +111,15 @@ function PopoverContextProvider(props) { [closePopover], ); + const contextValue = React.useMemo(() => ({ + onOpen, + close: closePopover, + popover: activePopoverRef.current, + isOpen, + }), [onOpen, closePopover, isOpen]); + return ( - + {props.children} ); diff --git a/src/components/PopoverProvider/index.native.js b/src/components/PopoverProvider/index.native.js index f34abcb1fa62..aa22bc7cd605 100644 --- a/src/components/PopoverProvider/index.native.js +++ b/src/components/PopoverProvider/index.native.js @@ -15,15 +15,15 @@ const PopoverContext = React.createContext({ }); function PopoverContextProvider(props) { + const contextValue = React.useMemo(() => ({ + onOpen: () => {}, + close: () => {}, + popover: {}, + isOpen: false, + }), []); + return ( - {}, - close: () => {}, - popover: {}, - isOpen: false, - }} - > + {props.children} ); diff --git a/src/components/ScrollViewWithContext.js b/src/components/ScrollViewWithContext.js index bf0e7c6d62e8..b858f741f1fe 100644 --- a/src/components/ScrollViewWithContext.js +++ b/src/components/ScrollViewWithContext.js @@ -1,4 +1,4 @@ -import React, {useState, useRef} from 'react'; +import React, {useState, useRef, useMemo} from 'react'; import {ScrollView} from 'react-native'; const MIN_SMOOTH_SCROLL_EVENT_THROTTLE = 16; @@ -27,6 +27,11 @@ function ScrollViewWithContext({onScroll, scrollEventThrottle, children, innerRe setContentOffsetY(event.nativeEvent.contentOffset.y); }; + const contextValue = useMemo(() => ({ + scrollViewRef, + contentOffsetY, + }), [scrollViewRef, contentOffsetY]); + return ( {children} diff --git a/src/components/withKeyboardState.js b/src/components/withKeyboardState.js index 8ddf667b4e30..dbd90af19b03 100755 --- a/src/components/withKeyboardState.js +++ b/src/components/withKeyboardState.js @@ -1,4 +1,4 @@ -import React, {forwardRef, createContext, useEffect, useState} from 'react'; +import React, {forwardRef, createContext, useEffect, useState, useMemo} from 'react'; import {Keyboard} from 'react-native'; import PropTypes from 'prop-types'; import getComponentDisplayName from '../libs/getComponentDisplayName'; @@ -31,7 +31,10 @@ function KeyboardStateProvider(props) { }; }, []); - return {children}; + const contextValue = useMemo(() => ({ + isKeyboardShown, + }), [isKeyboardShown]); + return {children}; } KeyboardStateProvider.propTypes = keyboardStateProviderPropTypes; diff --git a/src/components/withWindowDimensions/index.js b/src/components/withWindowDimensions/index.js index 37d5c94688a2..bd08fb673c2a 100644 --- a/src/components/withWindowDimensions/index.js +++ b/src/components/withWindowDimensions/index.js @@ -1,8 +1,8 @@ -import React, {forwardRef, createContext, useState, useEffect} from 'react'; +import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; import {Dimensions} from 'react-native'; -import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; @@ -62,30 +62,28 @@ function WindowDimensionsProvider(props) { dimensionsEventListener.remove(); }; }, []); - + const insets = useSafeAreaInsets(); + const adjustment = getWindowHeightAdjustment(insets); + const contextValue = useMemo(() => { + const isExtraSmallScreenWidth = windowDimension.windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; + const isSmallScreenWidth = windowDimension.windowWidth <= variables.mobileResponsiveWidthBreakpoint; + const isMediumScreenWidth = !isSmallScreenWidth && windowDimension.windowWidth <= variables.tabletResponsiveWidthBreakpoint; + const isLargeScreenWidth = !isSmallScreenWidth && !isMediumScreenWidth; + return { + windowHeight: windowDimension.windowHeight + adjustment, + windowWidth: windowDimension.windowWidth, + isExtraSmallScreenWidth, + isSmallScreenWidth, + isMediumScreenWidth, + isLargeScreenWidth, + }; + }, [windowDimension.windowHeight, windowDimension.windowWidth, adjustment]); return ( - - {(insets) => { - const isExtraSmallScreenWidth = windowDimension.windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; - const isSmallScreenWidth = windowDimension.windowWidth <= variables.mobileResponsiveWidthBreakpoint; - const isMediumScreenWidth = !isSmallScreenWidth && windowDimension.windowWidth <= variables.tabletResponsiveWidthBreakpoint; - const isLargeScreenWidth = !isSmallScreenWidth && !isMediumScreenWidth; - return ( - - {props.children} - - ); - }} - + + {props.children} + ); } diff --git a/src/components/withWindowDimensions/index.native.js b/src/components/withWindowDimensions/index.native.js index e147a20c9f4e..bd0b6574ddf4 100644 --- a/src/components/withWindowDimensions/index.native.js +++ b/src/components/withWindowDimensions/index.native.js @@ -1,7 +1,7 @@ -import React, {forwardRef, createContext, useState, useEffect} from 'react'; +import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; import PropTypes from 'prop-types'; import {Dimensions} from 'react-native'; -import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; @@ -60,30 +60,25 @@ function WindowDimensionsProvider(props) { dimensionsEventListener.remove(); }; }, []); - + const insets = useSafeAreaInsets(); + const adjustment = getWindowHeightAdjustment(insets); + const contextValue = useMemo(() => { + const isExtraSmallScreenWidth = windowDimension.windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; + return { + windowHeight: windowDimension.windowHeight + adjustment, + windowWidth: windowDimension.windowWidth, + isExtraSmallScreenWidth, + isSmallScreenWidth: true, + isMediumScreenWidth: false, + isLargeScreenWidth: false, + }; + }, [windowDimension.windowHeight, windowDimension.windowWidth, adjustment]); return ( - - {(insets) => { - const isExtraSmallScreenWidth = windowDimension.windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; - const isSmallScreenWidth = true; - const isMediumScreenWidth = false; - const isLargeScreenWidth = false; - return ( - - {props.children} - - ); - }} - + + {props.children} + ); } diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d109c972ec69..ecaa8c0a84d4 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; -import React, {useState, useRef, useEffect, memo, useCallback, useContext} from 'react'; +import React, {useState, useRef, useEffect, memo, useCallback, useContext, useMemo} from 'react'; import {InteractionManager, View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -263,6 +263,13 @@ function ReportActionItem(props) { [props.report, props.action, props.emojiReactions], ); + const contextValue = useMemo(() => ({ + anchor: popoverAnchorRef, + report: props.report, + action: props.action, + checkIfContextMenuActive: toggleContextMenuFromActiveReportAction, + }), [props.report, props.action, toggleContextMenuFromActiveReportAction]); + /** * Get the content of ReportActionItem * @param {Boolean} hovered whether the ReportActionItem is hovered @@ -356,12 +363,7 @@ function ReportActionItem(props) { const hasBeenFlagged = !_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], moderationDecision); children = ( {!props.draftMessage ? ( @@ -504,12 +506,7 @@ function ReportActionItem(props) { if (ReportActionsUtils.isTransactionThread(parentReportAction)) { return ( ({setStep: handleSetStep}), [handleSetStep]); const renderStep = () => { switch (currentStep) { @@ -60,9 +61,7 @@ function TwoFactorAuthSteps({account = defaultAccount}) { return ( {renderStep()} From d57390ebf69c5541084b675d90de0732f7dfd1f4 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 3 Oct 2023 23:12:49 +0800 Subject: [PATCH 012/174] fix transaction report --- src/libs/OptionsListUtils.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7bb94014658d..c8d21ffb4c77 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -68,14 +68,16 @@ Onyx.connect({ const lastReportActions = {}; const allSortedReportActions = {}; +const allReportActions = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, callback: (actions, key) => { if (!key || !actions) { return; } - const sortedReportActions = ReportActionUtils.getSortedReportActions(_.toArray(actions), true); const reportID = CollectionUtils.extractCollectionItemID(key); + allReportActions[reportID] = actions; + const sortedReportActions = ReportActionUtils.getSortedReportActions(_.toArray(actions), true); allSortedReportActions[reportID] = sortedReportActions; lastReportActions[reportID] = _.first(sortedReportActions); }, @@ -366,7 +368,8 @@ function getAllReportErrors(report, reportActions) { {}, ); - const parentReportAction = !report.parentReportID || !report.parentReportActionID ? {} : lodashGet(allSortedReportActions, [report.parentReportID, report.parentReportActionID], {}); + const parentReportAction = !report.parentReportID || !report.parentReportActionID ? {} : lodashGet(allReportActions, [report.parentReportID, report.parentReportActionID], {}); + if (parentReportAction.actorAccountID === currentUserAccountID && ReportActionUtils.isTransactionThread(parentReportAction)) { const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], ''); const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] || {}; From 81e87d3494da66bb1a46bcf1f052c88cc4bd40b5 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 3 Oct 2023 23:22:53 +0800 Subject: [PATCH 013/174] only show for payee in IOU report --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index c8d21ffb4c77..95db528263dd 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -376,7 +376,7 @@ function getAllReportErrors(report, reportActions) { if (TransactionUtils.hasMissingSmartscanFields(transaction)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } - } else if (ReportUtils.isIOUReport(report) || ReportUtils.isExpenseReport(report)) { + } else if ((ReportUtils.isIOUReport(report) || ReportUtils.isExpenseReport(report)) && report.ownerAccountID === currentUserAccountID) { if (ReportUtils.hasMissingSmartscanFields(report.reportID)) { _.extend(reportActionErrors, {smartscan: ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage')}); } From b663bb6a956bdc63917dc8b753a3586be7fe3822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 4 Oct 2023 10:13:23 +0200 Subject: [PATCH 014/174] small refactors --- src/libs/OptionsListUtils.js | 4 ++-- src/libs/ReportUtils.js | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 12b820a5ba60..cac758162ddd 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -430,7 +430,7 @@ function getLastMessageTextForReport(report) { * @param {Boolean} [options.forcePolicyNamePreview] * @returns {Object} */ -function createOption(accountIDs, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false, shouldNotFallbackToHidden = false}) { +function createOption(accountIDs, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false, shouldFallbackToHidden = true}) { const result = { text: null, alternateText: null, @@ -528,7 +528,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { } reportName = ReportUtils.getReportName(report); } else { - reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0], false, shouldNotFallbackToHidden); + reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0], false, shouldFallbackToHidden); result.keyForList = String(accountIDs[0]); result.alternateText = LocalePhoneNumber.formatPhoneNumber(lodashGet(personalDetails, [accountIDs[0], 'login'], '')); } diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 0d04dcd5c031..40fac8ae163f 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1089,8 +1089,15 @@ function getPersonalDetailsForAccountID(accountID) { ); } -function hasOnlyAvatarField(obj) { - const keys = _.keys(obj); +/** + * Checks if the personalDetailsObject has only one key named `avatar` and returns a bool flag. + * This can be used to check if the user account is an "invite" one - is not a Expensify account yet. + * + * @param {Object} personalDetailsObject + * @returns {Boolean} + */ +function hasOnlyAvatarField(personalDetailsObject) { + const keys = _.keys(personalDetailsObject); return keys.length === 1 && keys[0] === 'avatar'; } @@ -1099,21 +1106,21 @@ function hasOnlyAvatarField(obj) { * * @param {Number} accountID * @param {Boolean} [shouldUseShortForm] - * @param {Boolean} shouldNotFallbackToHidden + * @param {Boolean} shouldFallbackToHidden * @returns {String} */ -function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, shouldNotFallbackToHidden = false) { +function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, shouldFallbackToHidden = true) { if (!accountID) { return ''; } const personalDetails = getPersonalDetailsForAccountID(accountID); - // check if it's invite account + // check if it's an invite account if (hasOnlyAvatarField(personalDetails)) { return ''; } const longName = personalDetails.displayName; const shortName = personalDetails.firstName || longName; - if (!longName && !personalDetails.login && !shouldNotFallbackToHidden) { + if (!longName && !personalDetails.login && shouldFallbackToHidden) { return Localize.translateLocal('common.hidden'); } return shouldUseShortForm ? shortName : longName; @@ -1122,13 +1129,13 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, sho /** * @param {Object} personalDetailsList * @param {Boolean} isMultipleParticipantReport - * @param {Boolean} shouldNotFallbackToHidden + * @param {Boolean} shouldFallbackToHidden * @returns {Array} */ -function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantReport, shouldNotFallbackToHidden) { +function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantReport, shouldFallbackToHidden) { return _.map(personalDetailsList, (user) => { const accountID = Number(user.accountID); - const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldNotFallbackToHidden) || user.login || ''; + const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldFallbackToHidden) || user.login || ''; const avatar = UserUtils.getDefaultAvatar(accountID); let pronouns = user.pronouns; From 45bec2466c2261dc83e1099473996e3d147a146d Mon Sep 17 00:00:00 2001 From: Sheena Trepanier Date: Wed, 4 Oct 2023 12:40:01 -0700 Subject: [PATCH 015/174] Update Expenses.md Corrected three grammatical issues in the doc while QAing this resource. --- .../policy-and-domain-settings/Expenses.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md b/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md index 424338120010..388bb5d5cbc9 100644 --- a/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md +++ b/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md @@ -4,7 +4,7 @@ description: Expense Settings --- # Overview -Expensify offers multiple ways to customize how expenses are created in your workspace. In this doc, you’ll learn how to set up and expense basics, distance expenses, and time expenses. +Expensify offers multiple ways to customize how expenses are created in your workspace. In this doc, you’ll learn how to set up expense basics, distance expenses, and time expenses. Whether you’re flying solo with your Individual workspace or submitting with a team on your Group workspace, we have settings to support how you use Expensify. @@ -69,7 +69,7 @@ Preliminary setup steps include: 3. Click **Add A Mileage Rate** to add as many rates as you need, 4. Set the reimbursable amount per mile or kilometer. -Note: _If a rate is toggled off it is immediately disabled. This means that users are no longer able to select it when creating a new distance expense. If only one rate is available then this rate will be toggled on by default._ +Note: _If a rate is toggled off it is immediately disabled. This means that users are no longer able to select it when creating a new distance expense. If only one rate is available then that rate will be toggled on by default._ ## Set an hourly rate @@ -96,7 +96,7 @@ Note: _If a report has audit alerts on it, you'll need to Review the report and ## Tracking tax on mileage expenses -If you’re tracking tax in Expensify you can also track tax on distance expenses. The first step is to enable tax the workspace. You can do this by going to **Settings** > **Workspaces** > **Individual** or **Group** > [_Workspace Name_] > **Tax**. +If you’re tracking tax in Expensify you can also track tax on distance expenses. The first step is to enable tax in the workspace. You can do this by going to **Settings** > **Workspaces** > **Individual** or **Group** > [_Workspace Name_] > **Tax**. Once tax is enabled on a workspace level you will see a toggle to _Track Tax_ in the Distance section of the workspace settings. If tax is disabled on the workspace the Track Tax toggle will not display. From 1144ad483d09795fabc7f6f3b72e1b5c4c6fef8f Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:30:19 +0200 Subject: [PATCH 016/174] fix --- src/components/DraggableList/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/DraggableList/index.tsx b/src/components/DraggableList/index.tsx index 674a95179e5d..ea9ac548e850 100644 --- a/src/components/DraggableList/index.tsx +++ b/src/components/DraggableList/index.tsx @@ -73,6 +73,7 @@ function DraggableList( Date: Thu, 5 Oct 2023 16:39:22 +0200 Subject: [PATCH 017/174] fix native --- src/components/DraggableList/index.native.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/DraggableList/index.native.tsx b/src/components/DraggableList/index.native.tsx index 9f180ba35b2e..d4e00601c05c 100644 --- a/src/components/DraggableList/index.native.tsx +++ b/src/components/DraggableList/index.native.tsx @@ -2,11 +2,15 @@ import React from 'react'; import DraggableFlatList from 'react-native-draggable-flatlist'; import {FlatList} from 'react-native-gesture-handler'; import type {DraggableListProps} from './types'; +import styles from '../../styles/styles'; function DraggableList({renderClone, shouldUsePortal, ...viewProps}: DraggableListProps, ref: React.ForwardedRef>) { return ( From e311f75a5ee1a132f1bd3e36559c9f000c1bd9d5 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Fri, 6 Oct 2023 08:08:35 +0200 Subject: [PATCH 018/174] cleanup --- src/components/DistanceRequest/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/DistanceRequest/index.js b/src/components/DistanceRequest/index.js index 822567182d4a..daab6d6e8341 100644 --- a/src/components/DistanceRequest/index.js +++ b/src/components/DistanceRequest/index.js @@ -229,7 +229,4 @@ export default withOnyx({ transaction: { key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID || 0}`, }, - mapboxAccessToken: { - key: ONYXKEYS.MAPBOX_ACCESS_TOKEN, - }, })(DistanceRequest); From 980b5edbe7b36ef2db21e81c20e4d72a4af1bac3 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:31:25 +0200 Subject: [PATCH 019/174] fix scrolling on the native --- src/components/DraggableList/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DraggableList/index.native.tsx b/src/components/DraggableList/index.native.tsx index d4e00601c05c..e3b7558c1e21 100644 --- a/src/components/DraggableList/index.native.tsx +++ b/src/components/DraggableList/index.native.tsx @@ -9,7 +9,7 @@ function DraggableList({renderClone, shouldUsePortal, ...viewProps}: Draggabl Date: Mon, 9 Oct 2023 15:02:28 +0200 Subject: [PATCH 020/174] change padding --- 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 d4aacfff96aa..1fa251f54344 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3680,7 +3680,7 @@ const styles = (theme) => ({ mapViewContainer: { ...flex.flex1, - ...spacing.p4, + ...spacing.p2, ...spacing.flex1, minHeight: 300, maxHeight: 500, From d1add2b1fd619ecb967d8426c2d6ca7dbb281589 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Mon, 9 Oct 2023 21:22:59 +0500 Subject: [PATCH 021/174] fix: money request report scroll fix --- .../ReportActionItem/MoneyReportView.js | 65 +++--- .../ReportActionItem/MoneyRequestView.js | 201 +++++++++--------- 2 files changed, 132 insertions(+), 134 deletions(-) diff --git a/src/components/ReportActionItem/MoneyReportView.js b/src/components/ReportActionItem/MoneyReportView.js index bfdcc59bf89f..3d1c68283630 100644 --- a/src/components/ReportActionItem/MoneyReportView.js +++ b/src/components/ReportActionItem/MoneyReportView.js @@ -7,7 +7,6 @@ import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import * as ReportUtils from '../../libs/ReportUtils'; import * as StyleUtils from '../../styles/StyleUtils'; -import CONST from '../../CONST'; import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; @@ -33,40 +32,40 @@ function MoneyReportView(props) { const {translate} = useLocalize(); return ( - - - - - - - - {translate('common.total')} - - - - {isSettled && ( - - - - )} - - {formattedAmount} - + + + + + + + {translate('common.total')} + + + + {isSettled && ( + + + + )} + + {formattedAmount} + + + - ); } diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 079bc64d96bf..837fc5be5b46 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -135,124 +135,123 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should const getPendingFieldAction = (fieldPath) => lodashGet(transaction, fieldPath) || pendingAction; return ( - - - - - - {hasReceipt && ( - - - - - - )} - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT))} - brickRoadIndicator={hasErrors && transactionAmount === 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} - error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''} - /> - - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DESCRIPTION))} - wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} - numberOfLinesTitle={0} - /> - - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))} - brickRoadIndicator={hasErrors && transactionDate === '' ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} - error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''} - /> - - {isDistanceRequest ? ( - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} - /> - - ) : ( - + + + + {hasReceipt && ( + + + + + + )} + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))} - brickRoadIndicator={hasErrors && isEmptyMerchant ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} - error={hasErrors && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} + onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT))} + brickRoadIndicator={hasErrors && transactionAmount === 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''} /> - )} - {shouldShowCategory && ( - + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.CATEGORY))} + onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DESCRIPTION))} + wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} + numberOfLinesTitle={0} /> - )} - {shouldShowTag && ( - + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} + onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))} + brickRoadIndicator={hasErrors && transactionDate === '' ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''} /> - )} - {shouldShowBillable && ( - - {translate('common.billable')} - IOU.editMoneyRequest(transaction.transactionID, report.reportID, {billable: value})} - /> - - )} - + {isDistanceRequest ? ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + /> + + ) : ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))} + brickRoadIndicator={hasErrors && isEmptyMerchant ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + error={hasErrors && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} + /> + + )} + {shouldShowCategory && ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.CATEGORY))} + /> + + )} + {shouldShowTag && ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} + /> + + )} + {shouldShowBillable && ( + + {translate('common.billable')} + IOU.editMoneyRequest(transaction.transactionID, report.reportID, {billable: value})} + /> + + )} + + ); } From c58c53efacbba416aabaca271b83fc3dd7f1c692 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Tue, 10 Oct 2023 12:28:01 +0700 Subject: [PATCH 022/174] Fix report action context menu stuck when open search page with keyboard shortcut Signed-off-by: Tsaqif --- src/components/PopoverProvider/index.js | 4 ++++ src/components/PopoverWithoutOverlay/index.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/PopoverProvider/index.js b/src/components/PopoverProvider/index.js index efa230d920d5..20ad18f93c7d 100644 --- a/src/components/PopoverProvider/index.js +++ b/src/components/PopoverProvider/index.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; const propTypes = { children: PropTypes.node.isRequired, @@ -22,7 +23,9 @@ function PopoverContextProvider(props) { if (!activePopoverRef.current || (anchorRef && anchorRef !== activePopoverRef.current.anchorRef)) { return; } + activePopoverRef.current.close(); + lodashGet(activePopoverRef, 'current.onCloseCallback', () => {})(); activePopoverRef.current = null; setIsOpen(false); }, []); @@ -106,6 +109,7 @@ function PopoverContextProvider(props) { closePopover(activePopoverRef.current.anchorRef); } activePopoverRef.current = popoverParams; + lodashGet(activePopoverRef, 'current.onOpenCallback', () => {})(); setIsOpen(true); }, [closePopover], diff --git a/src/components/PopoverWithoutOverlay/index.js b/src/components/PopoverWithoutOverlay/index.js index d35637958f1d..b7ebe29688d0 100644 --- a/src/components/PopoverWithoutOverlay/index.js +++ b/src/components/PopoverWithoutOverlay/index.js @@ -34,6 +34,8 @@ function Popover(props) { ref: props.withoutOverlayRef, close: props.onClose, anchorRef: props.anchorRef, + onCloseCallback: () => Modal.setCloseModal(null), + onOpenCallback: () => Modal.setCloseModal(() => props.onClose(props.anchorRef)), }); } else { props.onModalHide(); @@ -48,7 +50,6 @@ function Popover(props) { return; } firstRenderRef.current = false; - Modal.setCloseModal(props.isVisible ? () => props.onClose(props.anchorRef) : null); // We want this effect to run strictly ONLY when isVisible prop changes // eslint-disable-next-line react-hooks/exhaustive-deps From d01e77acf210c12e8ac02a096306f7d4474a6241 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:31:06 +0200 Subject: [PATCH 023/174] match styles with scan view --- src/components/ConfirmedRoute.js | 2 +- .../DistanceRequest/DistanceRequestFooter.js | 3 ++- src/components/MapView/MapViewTypes.ts | 3 +++ src/components/MapView/PendingMapView.tsx | 4 ++-- src/styles/styles.js | 15 ++++++++++++--- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/components/ConfirmedRoute.js b/src/components/ConfirmedRoute.js index dab30e60ca55..0b9f21758b04 100644 --- a/src/components/ConfirmedRoute.js +++ b/src/components/ConfirmedRoute.js @@ -97,7 +97,7 @@ function ConfirmedRoute({mapboxAccessToken, transaction}) { location: lodashGet(waypointMarkers, [0, 'coordinate'], CONST.MAPBOX.DEFAULT_COORDINATE), }} directionCoordinates={coordinates} - style={styles.mapView} + style={[styles.mapView, styles.mapConfirmationView]} waypoints={waypointMarkers} styleURL={CONST.MAPBOX.STYLE_URL} /> diff --git a/src/components/DistanceRequest/DistanceRequestFooter.js b/src/components/DistanceRequest/DistanceRequestFooter.js index 574dda3d0dd6..2ab14c57d5e6 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.js +++ b/src/components/DistanceRequest/DistanceRequestFooter.js @@ -131,7 +131,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, hasRo location: CONST.MAPBOX.DEFAULT_COORDINATE, }} directionCoordinates={lodashGet(transaction, 'routes.route0.geometry.coordinates', [])} - style={styles.mapView} + style={[styles.mapView, styles.mapEditView]} waypoints={waypointMarkers} styleURL={CONST.MAPBOX.STYLE_URL} overlayStyle={styles.m4} @@ -140,6 +140,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, hasRo )} diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts index dc56cb4642c4..6cc52ac91d18 100644 --- a/src/components/MapView/MapViewTypes.ts +++ b/src/components/MapView/MapViewTypes.ts @@ -33,6 +33,9 @@ type PendingMapViewProps = { /** Subtitle message below the title */ subtitle?: string; + + /** Style applied to PendingMapView */ + style?: StyleProp; }; // Initial state of the map diff --git a/src/components/MapView/PendingMapView.tsx b/src/components/MapView/PendingMapView.tsx index 6a35d2a9c369..d97d4aaee16f 100644 --- a/src/components/MapView/PendingMapView.tsx +++ b/src/components/MapView/PendingMapView.tsx @@ -8,11 +8,11 @@ import {PendingMapViewProps} from './MapViewTypes'; import BlockingView from '../BlockingViews/BlockingView'; import * as Expensicons from '../Icon/Expensicons'; -function PendingMapView({title = '', subtitle = ''}: PendingMapViewProps) { +function PendingMapView({title = '', subtitle = '', style}: PendingMapViewProps) { const hasTextContent = !_.isEmpty(title) || !_.isEmpty(subtitle); return ( - + {hasTextContent ? ( ({ mapViewContainer: { ...flex.flex1, - ...spacing.p2, ...spacing.flex1, minHeight: 300, maxHeight: 500, }, mapView: { - flex: 1, - borderRadius: 16, + ...flex.flex1, overflow: 'hidden', + backgroundColor: theme.highlightBG, + }, + + mapEditView: { + borderRadius: 28, + borderWidth: 8, + borderColor: theme.appBG, + }, + + mapConfirmationView: { + borderRadius: 16, }, mapViewOverlay: { From b4b68f87fb2e3d47eb32c520ad774c4a8745a7b7 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Tue, 10 Oct 2023 15:30:45 +0100 Subject: [PATCH 024/174] feat: show next steps for draft reports --- src/ONYXKEYS.ts | 1 + src/components/MoneyReportHeader.js | 24 +++++++-- src/components/MoneyReportHeaderStatusBar.js | 57 ++++++++++++++++++++ src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/nextStepPropTypes.js | 44 +++++++++++++++ 6 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/components/MoneyReportHeaderStatusBar.js create mode 100644 src/pages/nextStepPropTypes.js diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0a17d3a1d2f7..fd398ee6ec41 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -252,6 +252,7 @@ const ONYXKEYS = { REPORT_USER_IS_LEAVING_ROOM: 'reportUserIsLeavingRoom_', SECURITY_GROUP: 'securityGroup_', TRANSACTION: 'transactions_', + NEXT_STEP: 'reportNextStep_', // Manual request tab selector SELECTED_TAB: 'selectedTab_', diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index fbd686fac692..801ba4c87adc 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -1,4 +1,5 @@ import React, {useMemo} from 'react'; +import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -15,11 +16,13 @@ import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; +import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; import SettlementButton from './SettlementButton'; import Button from './Button'; import * as IOU from '../libs/actions/IOU'; import * as CurrencyUtils from '../libs/CurrencyUtils'; import reportPropTypes from '../pages/reportPropTypes'; +import nextStepPropTypes from '../pages/nextStepPropTypes'; const propTypes = { /** The report currently being looked at */ @@ -40,6 +43,9 @@ const propTypes = { /** The chat report this report is linked to */ chatReport: reportPropTypes, + /** The next step for the report */ + nextStep: nextStepPropTypes, + /** Personal details so we can get the ones for the report participants */ personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, @@ -54,13 +60,14 @@ const propTypes = { const defaultProps = { chatReport: {}, + nextStep: {}, session: { email: null, }, policy: {}, }; -function MoneyReportHeader({session, personalDetails, policy, chatReport, report: moneyRequestReport, isSmallScreenWidth}) { +function MoneyReportHeader({session, personalDetails, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { const {translate} = useLocalize(); const reportTotal = ReportUtils.getMoneyRequestTotal(moneyRequestReport); const isApproved = ReportUtils.isReportApproved(moneyRequestReport); @@ -81,7 +88,8 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report return isManager && !isDraft && !isApproved && !isSettled; }, [policyType, isManager, isDraft, isApproved, isSettled]); const shouldShowSubmitButton = isDraft; - const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton; + const shouldShowNextSteps = isDraft && nextStep && (!_.isEmpty(nextStep.message) || !_.isEmpty(nextStep.expenseMessage)); + const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextSteps; const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(reportTotal, moneyRequestReport.currency); @@ -96,7 +104,8 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} - shouldShowBorderBottom={!shouldShowAnyButton || !isSmallScreenWidth} + // Shows border if no buttons or next steps are showing below the header + shouldShowBorderBottom={!(shouldShowAnyButton && isSmallScreenWidth) && !(shouldShowNextSteps && !isSmallScreenWidth)} > {shouldShowSettlementButton && !isSmallScreenWidth && ( @@ -141,6 +150,12 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report )} + {shouldShowNextSteps && ( + + )} {shouldShowSettlementButton && isSmallScreenWidth && ( `${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`, }, + nextStep: { + key: ({report}) => `${ONYXKEYS.COLLECTION.NEXT_STEP}${report.reportID}`, + }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/MoneyReportHeaderStatusBar.js b/src/components/MoneyReportHeaderStatusBar.js new file mode 100644 index 000000000000..aa2cca18ac4c --- /dev/null +++ b/src/components/MoneyReportHeaderStatusBar.js @@ -0,0 +1,57 @@ +import React, {useMemo} from 'react'; +import {Text, View} from 'react-native'; +import _ from 'underscore'; +import Str from 'expensify-common/lib/str'; +import PropTypes from 'prop-types'; +import styles from '../styles/styles'; +import useLocalize from '../hooks/useLocalize'; +import nextStepPropTypes from '../pages/nextStepPropTypes'; +import RenderHTML from './RenderHTML'; + +const propTypes = { + /** The next step for the report */ + nextStep: nextStepPropTypes, + + showBorderBottom: PropTypes.bool, +}; + +const defaultProps = { + nextStep: {}, + showBorderBottom: true, +}; + +function MoneyReportHeaderStatusBar({nextStep, showBorderBottom}) { + const {translate} = useLocalize(); + + const messageContent = useMemo(() => { + let nextStepHTML = ''; + + const messageArray = _.isEmpty(nextStep.expenseMessage) ? nextStep.message : nextStep.expenseMessage; + _.each(messageArray, (part) => { + const tagType = part.type || 'span'; + nextStepHTML += `<${tagType}>${Str.safeEscape(part.text)}`; + }); + + return nextStepHTML + .replace(/%expenses/g, 'this expense') + .replace(/%Expenses/g, 'This expense') + .replace(/%tobe/g, 'is'); + }, [nextStep.expenseMessage, nextStep.message]); + + return ( + + + {translate('iou.nextSteps')} + + + + + + ); +} + +MoneyReportHeaderStatusBar.displayName = 'MoneyReportHeaderStatusBar'; +MoneyReportHeaderStatusBar.propTypes = propTypes; +MoneyReportHeaderStatusBar.defaultProps = defaultProps; + +export default MoneyReportHeaderStatusBar; diff --git a/src/languages/en.ts b/src/languages/en.ts index dc0080619eda..1be59c353f56 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -534,6 +534,7 @@ export default { settledElsewhere: 'Paid elsewhere', settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount} with Expensify`, payElsewhere: 'Pay elsewhere', + nextSteps: 'Next Steps', requestAmount: ({amount}: RequestAmountParams) => `request ${amount}`, requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `requested ${formattedAmount}${comment ? ` for ${comment}` : ''}`, splitAmount: ({amount}: SplitAmountParams) => `split ${amount}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index a21fc884e867..4b325df0ef35 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -526,6 +526,7 @@ export default { settledElsewhere: 'Pagado de otra forma', settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount} con Expensify`, payElsewhere: 'Pagar de otra forma', + nextSteps: 'Pasos Siguientes', requestAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`, requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicité ${formattedAmount}${comment ? ` para ${comment}` : ''}`, splitAmount: ({amount}: SplitAmountParams) => `dividir ${amount}`, diff --git a/src/pages/nextStepPropTypes.js b/src/pages/nextStepPropTypes.js new file mode 100644 index 000000000000..f515c3bcad72 --- /dev/null +++ b/src/pages/nextStepPropTypes.js @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types'; + +const messagePropType = PropTypes.shape({text: PropTypes.string, type: PropTypes.string, action: PropTypes.string}); + +export default PropTypes.shape({ + /** The message parts of the next step */ + message: PropTypes.arrayOf(messagePropType), + + /** The title for the next step */ + title: PropTypes.string, + + /** Whether the the user must take some sort of action in order to unblock the report */ + requiresUserAction: PropTypes.bool, + + /** The type of next step */ + type: PropTypes.oneOf(['neutral', 'alert', null]), + + /** If the "Undo submit" button should be visible */ + showUndoSubmit: PropTypes.bool, + + /** If the next step should be displayed on mobile */ + showForMobile: PropTypes.bool, + + /** If the next step should be displayed at the expense level */ + showForExpense: PropTypes.bool, + + /** An optional alternate message to display on expenses instead of what is provided in the "message" field */ + expenseMessage: PropTypes.arrayOf(messagePropType), + + /** The next person in the approval chain of the report */ + nextReceiver: PropTypes.string, + + /** An array of buttons to be displayed next to the next step */ + buttons: PropTypes.objectOf( + PropTypes.shape({ + text: PropTypes.string, + tooltip: PropTypes.string, + disabled: PropTypes.bool, + hidden: PropTypes.bool, + // eslint-disable-next-line react/forbid-prop-types + data: PropTypes.array, + }), + ), +}); From 7d65a15338be07f813c97925eb41c731d3cc7421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 10 Oct 2023 16:53:00 +0200 Subject: [PATCH 025/174] iou display name for report action item when hidden --- src/pages/home/report/ReportActionItemSingle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 60de11bdf218..3e8eab26f4b6 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -112,12 +112,12 @@ function ReportActionItemSingle(props) { // If this is a report preview, display names and avatars of both people involved let secondaryAvatar = {}; - const primaryDisplayName = displayName; + const primaryDisplayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; const secondaryUserDetails = props.personalDetailsList[secondaryAccountId] || {}; - const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); + const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { source: UserUtils.getAvatar(secondaryUserDetails.avatar, secondaryAccountId), From 3db91a2cf9f824c106700b660edf3739595b44d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 10 Oct 2023 17:20:08 +0200 Subject: [PATCH 026/174] fix/dont allow users without login to split payment/money request --- src/pages/iou/steps/MoneyRequestConfirmPage.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 3881221d5c52..865025deddfe 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -69,10 +69,13 @@ function MoneyRequestConfirmPage(props) { const reportID = useRef(lodashGet(props.route, 'params.reportID', '')); const participants = useMemo( () => - _.map(props.iou.participants, (participant) => { - const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false); - return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, props.personalDetails); - }), + _.chain(props.iou.participants) + .map((participant) => { + const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false); + return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, props.personalDetails); + }) + .filter((participant) => !!participant.login) + .value(), [props.iou.participants, props.personalDetails], ); const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST; From 3b2da52e01cea3974cde6dd5eb39c5e64375b55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 10 Oct 2023 17:24:15 +0200 Subject: [PATCH 027/174] dont show participants time when there's no display name --- src/pages/home/report/ParticipantLocalTime.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/home/report/ParticipantLocalTime.js b/src/pages/home/report/ParticipantLocalTime.js index 058346287b11..2e000c2de1d3 100644 --- a/src/pages/home/report/ParticipantLocalTime.js +++ b/src/pages/home/report/ParticipantLocalTime.js @@ -45,6 +45,10 @@ function ParticipantLocalTime(props) { const reportRecipientDisplayName = lodashGet(props, 'participant.firstName') || lodashGet(props, 'participant.displayName'); + if (!reportRecipientDisplayName) { + return null; + } + return ( Date: Tue, 10 Oct 2023 17:44:22 +0200 Subject: [PATCH 028/174] linting --- 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 197d6b5db6fd..c359be40211a 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1212,7 +1212,7 @@ function getDisplayNameForParticipant(accountID, shouldUseShortForm = false, sho * @returns {Array} */ function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantReport, shouldFallbackToHidden) { - return _.map(personalDetailsList, (user) => { + return _.map(personalDetailsList, (user) => { const accountID = Number(user.accountID); const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldFallbackToHidden) || user.login || ''; const avatar = UserUtils.getDefaultAvatar(accountID); From b6a9476a9bb1b8ba35069d7e2fc65c001d011b1f Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Wed, 11 Oct 2023 14:10:11 +0100 Subject: [PATCH 029/174] fix: text wrap in native devices --- src/components/MoneyReportHeader.js | 15 ++++++++------- src/components/MoneyReportHeaderStatusBar.js | 10 +++------- src/pages/nextStepPropTypes.js | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 801ba4c87adc..472cdb0d3c8c 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -150,14 +150,14 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt )} + {shouldShowNextSteps && ( - + + + )} {shouldShowSettlementButton && isSmallScreenWidth && ( - + )} {shouldShowApproveButton && isSmallScreenWidth && ( - +