From e92901687bd21152499eca4cde4167d0f68e58da Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 19:01:19 +0530 Subject: [PATCH 01/57] fixed currency selection on confirm step --- src/libs/actions/IOU.js | 9 +++++++++ .../iou/request/step/IOURequestStepAmount.js | 19 ++++++++++++++++--- .../request/step/IOURequestStepCurrency.js | 4 +--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fb4e9f02f1b6..37e72b2133e3 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -198,6 +198,14 @@ function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } +/** + * @param {String} transactionID + * @param {String} originalCurrency + */ +function setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, originalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {originalCurrency}); +} + /** * @param {String} transactionID * @param {String} comment @@ -3566,6 +3574,7 @@ export { setMoneyRequestCategory_temporaryForRefactor, setMoneyRequestCreated_temporaryForRefactor, setMoneyRequestCurrency_temporaryForRefactor, + setMoneyRequestOriginalCurrency_temporaryForRefactor, setMoneyRequestDescription_temporaryForRefactor, setMoneyRequestMerchant_temporaryForRefactor, setMoneyRequestParticipants_temporaryForRefactor, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 84e0ac8533c5..55826244434f 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; -import React, {useCallback, useRef} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -62,15 +62,16 @@ function IOURequestStepAmount({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, transaction, - transaction: {currency: originalCurrency}, + transaction: {currency: currentCurrency}, policyTaxRates, policy, }) { const {translate} = useLocalize(); const textInput = useRef(null); const focusTimeoutRef = useRef(null); + const isSaveButtonPressed = useRef(false); const iouRequestType = getRequestType(transaction); - const currency = selectedCurrency || originalCurrency; + const currency = selectedCurrency || currentCurrency; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -87,6 +88,17 @@ function IOURequestStepAmount({ }, []), ); + useEffect(() => { + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + return () => { + if (isSaveButtonPressed.current) { + return; + } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const navigateBack = () => { Navigation.goBack(backTo || ROUTES.HOME); }; @@ -99,6 +111,7 @@ function IOURequestStepAmount({ * @param {Number} amount */ const navigateToNextPage = ({amount}) => { + isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); if ((iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) && isTaxTrackingEnabled) { diff --git a/src/pages/iou/request/step/IOURequestStepCurrency.js b/src/pages/iou/request/step/IOURequestStepCurrency.js index b4281de4d16e..ea6173408371 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrency.js +++ b/src/pages/iou/request/step/IOURequestStepCurrency.js @@ -82,9 +82,7 @@ function IOURequestStepCurrency({ */ const confirmCurrencySelection = (option) => { Keyboard.dismiss(); - if (pageIndex !== 'confirm') { - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); - } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); navigateBack(option.currencyCode); }; From ef3810cf358833bd609a8f75db282118d69d6bdc Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:14:02 +0530 Subject: [PATCH 02/57] fixed minor bugs regarding currency change --- src/libs/actions/IOU.js | 14 ++++++++++++-- src/pages/iou/request/step/IOURequestStepAmount.js | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 37e72b2133e3..098685206c2c 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -177,8 +177,13 @@ function clearMoneyRequest(transactionID) { * @param {String} transactionID * @param {Number} amount * @param {String} currency + * @param {Boolean} [removeOriginalCurrency] */ -function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency) { +function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency, removeOriginalCurrency = false) { + if (removeOriginalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency, originalCurrency: null}); + return; + } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency}); } @@ -193,8 +198,13 @@ function setMoneyRequestCreated_temporaryForRefactor(transactionID, created) { /** * @param {String} transactionID * @param {String} currency + * @param {Boolean} [removeOriginalCurrency] */ -function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) { +function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency, removeOriginalCurrency = false) { + if (removeOriginalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency, originalCurrency: null}); + return; + } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 55826244434f..99dd9ba25ae8 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -70,6 +70,7 @@ function IOURequestStepAmount({ const textInput = useRef(null); const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); + const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || currentCurrency; @@ -89,12 +90,13 @@ function IOURequestStepAmount({ ); useEffect(() => { + originalCurrency.current = currentCurrency; IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); return () => { if (isSaveButtonPressed.current) { return; } - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -120,7 +122,7 @@ function IOURequestStepAmount({ IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); } - IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); + IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, true); if (backTo) { Navigation.goBack(backTo); From ca2763dd1ffa2bbbeeebb0123b6b5f98f48de9a8 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:17:06 +0530 Subject: [PATCH 03/57] fixed deep linking bugs --- src/pages/iou/request/step/IOURequestStepAmount.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 99dd9ba25ae8..8e16af248dc4 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -90,8 +90,12 @@ function IOURequestStepAmount({ ); useEffect(() => { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + if (transaction.originalCurrency) { + originalCurrency.current = transaction.originalCurrency; + } else { + originalCurrency.current = currentCurrency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + } return () => { if (isSaveButtonPressed.current) { return; From 939684581574a4e63218d80805d231ee3a18b3fa Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:20:10 +0530 Subject: [PATCH 04/57] mirroring changes in tax step --- .../step/IOURequestStepTaxAmountPage.js | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 8ee3abb56d00..70b2ccd7e270 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useRef} from 'react'; +import React, {useCallback, useRef, useEffect} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -56,7 +56,7 @@ function IOURequestStepTaxAmountPage({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, transaction, - transaction: {currency: originalCurrency}, + transaction: {currency: currentCurrency}, report, policyTaxRates, }) { @@ -65,10 +65,29 @@ function IOURequestStepTaxAmountPage({ const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); - const currency = selectedCurrency || originalCurrency; + const currency = selectedCurrency || currentCurrency; const focusTimeoutRef = useRef(null); + const isSaveButtonPressed = useRef(false); + const originalCurrency = useRef(null); + + useEffect(() => { + if (transaction.originalCurrency) { + originalCurrency.current = transaction.originalCurrency; + } else { + originalCurrency.current = currentCurrency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + } + return () => { + if (isSaveButtonPressed.current) { + return; + } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); @@ -93,10 +112,11 @@ function IOURequestStepTaxAmountPage({ }; const updateTaxAmount = (currentAmount) => { + isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD, true); if (backTo) { Navigation.goBack(backTo); From db92b88156a975a959177cd93eade70856a20339 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:22:59 +0530 Subject: [PATCH 05/57] fix lint --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 70b2ccd7e270..9cfd8f7ddf26 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useRef, useEffect} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; From f27c0371bd62b2f08811aecadb07e803b1888f50 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:32:26 +0530 Subject: [PATCH 06/57] removed unecessary param --- src/pages/iou/request/step/IOURequestStepAmount.js | 9 ++++----- src/pages/iou/request/step/IOURequestStepCurrency.js | 10 +++------- .../iou/request/step/IOURequestStepTaxAmountPage.js | 10 ++++------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 8e16af248dc4..f91e7cea533b 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -59,10 +59,10 @@ const getTaxAmount = (transaction, defaultTaxValue, amount) => { function IOURequestStepAmount({ report, route: { - params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + params: {iouType, reportID, transactionID, backTo}, }, transaction, - transaction: {currency: currentCurrency}, + transaction: {currency}, policyTaxRates, policy, }) { @@ -72,7 +72,6 @@ function IOURequestStepAmount({ const isSaveButtonPressed = useRef(false); const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); - const currency = selectedCurrency || currentCurrency; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -93,8 +92,8 @@ function IOURequestStepAmount({ if (transaction.originalCurrency) { originalCurrency.current = transaction.originalCurrency; } else { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + originalCurrency.current = currency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency); } return () => { if (isSaveButtonPressed.current) { diff --git a/src/pages/iou/request/step/IOURequestStepCurrency.js b/src/pages/iou/request/step/IOURequestStepCurrency.js index ea6173408371..06af0ecf3ca4 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrency.js +++ b/src/pages/iou/request/step/IOURequestStepCurrency.js @@ -59,18 +59,14 @@ function IOURequestStepCurrency({ const [searchValue, setSearchValue] = useState(''); const optionsSelectorRef = useRef(); - const navigateBack = (selectedCurrency = undefined) => { + const navigateBack = () => { // If the currency selection was done from the confirmation step (eg. + > request money > manual > confirm > amount > currency) // then the user needs taken back to the confirmation page instead of the initial amount page. This is because the route params // are only able to handle one backTo param at a time and the user needs to go back to the amount page before going back // to the confirmation page if (pageIndex === 'confirm') { const routeToAmountPageWithConfirmationAsBackTo = getUrlWithBackToParam(backTo, `/${ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)}`); - if (selectedCurrency) { - Navigation.navigate(`${routeToAmountPageWithConfirmationAsBackTo}¤cy=${selectedCurrency}`); - } else { - Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo); - } + Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo); return; } Navigation.goBack(backTo || ROUTES.HOME); @@ -83,7 +79,7 @@ function IOURequestStepCurrency({ const confirmCurrencySelection = (option) => { Keyboard.dismiss(); IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); - navigateBack(option.currencyCode); + navigateBack(); }; const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => { diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 9cfd8f7ddf26..5c968b8415a4 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -53,10 +53,10 @@ const getTaxAmount = (transaction, defaultTaxValue) => { function IOURequestStepTaxAmountPage({ route: { - params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + params: {iouType, reportID, transactionID, backTo}, }, transaction, - transaction: {currency: currentCurrency}, + transaction: {currency}, report, policyTaxRates, }) { @@ -65,8 +65,6 @@ function IOURequestStepTaxAmountPage({ const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); - const currency = selectedCurrency || currentCurrency; - const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); @@ -76,8 +74,8 @@ function IOURequestStepTaxAmountPage({ if (transaction.originalCurrency) { originalCurrency.current = transaction.originalCurrency; } else { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + originalCurrency.current = currency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency); } return () => { if (isSaveButtonPressed.current) { From fad567c1071c523ca2b432477b56731a146f0aed Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Jan 2024 16:00:38 +0000 Subject: [PATCH 07/57] refactor: move methods to personalDetailsUtils, remove unused one --- src/libs/PersonalDetailsUtils.ts | 69 +++++++++++++++++ src/libs/actions/PersonalDetails.ts | 74 +------------------ src/libs/actions/User.ts | 4 +- .../EnablePayments/AdditionalDetailsStep.js | 6 +- 4 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 3346094adeec..10b966a1a688 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,5 +1,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import Str from 'expensify-common/lib/str'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; @@ -7,6 +8,12 @@ import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as UserUtils from './UserUtils'; + +type FirstAndLastName = { + firstName: string; + lastName: string; +}; + let personalDetails: Array = []; let allPersonalDetails: OnyxEntry = {}; Onyx.connect({ @@ -198,6 +205,66 @@ function getEffectiveDisplayName(personalDetail?: PersonalDetails): string | und return undefined; } +/** + * Creates a new displayName for a user based on passed personal details or login. + * + * @param login - user's login + * @param passedPersonalDetails - details object with firstName and lastName + * @returns - The effective display name + */ +function createDisplayName(login: string, passedPersonalDetails: Pick | OnyxEntry): string { + // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it + // so that the option looks cleaner in our UI. + const userLogin = LocalePhoneNumber.formatPhoneNumber(login); + + if (!passedPersonalDetails) { + return userLogin; + } + + const firstName = passedPersonalDetails.firstName ?? ''; + const lastName = passedPersonalDetails.lastName ?? ''; + const fullName = `${firstName} ${lastName}`.trim(); + + // It's possible for fullName to be empty string, so we must use "||" to fallback to userLogin. + return fullName || userLogin; +} + +/** + * Gets the first and last name from the user's personal details. + * If the login is the same as the displayName, then they don't exist, + * so we return empty strings instead. + * + * @param login - user's login + * @param displayName - user display name + * @param firstName + * @param lastName + */ +function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstName, lastName}: PersonalDetails): FirstAndLastName { + // It's possible for firstName to be empty string, so we must use "||" to consider lastName instead. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (firstName || lastName) { + return {firstName: firstName ?? '', lastName: lastName ?? ''}; + } + if (login && Str.removeSMSDomain(login) === displayName) { + return {firstName: '', lastName: ''}; + } + + if (displayName) { + const firstSpaceIndex = displayName.indexOf(' '); + const lastSpaceIndex = displayName.lastIndexOf(' '); + if (firstSpaceIndex === -1) { + return {firstName: displayName, lastName: ''}; + } + + return { + firstName: displayName.substring(0, firstSpaceIndex).trim(), + lastName: displayName.substring(lastSpaceIndex).trim(), + }; + } + + return {firstName: '', lastName: ''}; +} + export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -208,4 +275,6 @@ export { getFormattedStreet, getStreetLines, getEffectiveDisplayName, + createDisplayName, + extractFirstAndLastNameFromAvailableDetails }; diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 508cca34fb88..b09b664ff745 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -1,10 +1,8 @@ -import Str from 'expensify-common/lib/str'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; import DateUtils from '@libs/DateUtils'; -import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -15,11 +13,6 @@ import type {DateOfBirthForm, PersonalDetails, PersonalDetailsList, PrivatePerso import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import * as Session from './Session'; -type FirstAndLastName = { - firstName: string; - lastName: string; -}; - let currentUserEmail = ''; let currentUserAccountID = -1; Onyx.connect({ @@ -42,68 +35,6 @@ Onyx.connect({ callback: (val) => (privatePersonalDetails = val), }); -/** - * Creates a new displayName for a user based on passed personal details or login. - */ -function createDisplayName(login: string, personalDetails: Pick | OnyxEntry): string { - // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it - // so that the option looks cleaner in our UI. - const userLogin = LocalePhoneNumber.formatPhoneNumber(login); - - if (!personalDetails) { - return userLogin; - } - - const firstName = personalDetails.firstName ?? ''; - const lastName = personalDetails.lastName ?? ''; - const fullName = `${firstName} ${lastName}`.trim(); - - // It's possible for fullName to be empty string, so we must use "||" to fallback to userLogin. - return fullName || userLogin; -} - -/** - * Gets the first and last name from the user's personal details. - * If the login is the same as the displayName, then they don't exist, - * so we return empty strings instead. - */ -function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstName, lastName}: PersonalDetails): FirstAndLastName { - // It's possible for firstName to be empty string, so we must use "||" to consider lastName instead. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (firstName || lastName) { - return {firstName: firstName ?? '', lastName: lastName ?? ''}; - } - if (login && Str.removeSMSDomain(login) === displayName) { - return {firstName: '', lastName: ''}; - } - - if (displayName) { - const firstSpaceIndex = displayName.indexOf(' '); - const lastSpaceIndex = displayName.lastIndexOf(' '); - if (firstSpaceIndex === -1) { - return {firstName: displayName, lastName: ''}; - } - - return { - firstName: displayName.substring(0, firstSpaceIndex).trim(), - lastName: displayName.substring(lastSpaceIndex).trim(), - }; - } - - return {firstName: '', lastName: ''}; -} - -/** - * Convert country names obtained from the backend to their respective ISO codes - * This is for backward compatibility of stored data before E/App#15507 - */ -function getCountryISO(countryName: string): string { - if (!countryName || countryName.length === 2) { - return countryName; - } - - return Object.entries(CONST.ALL_COUNTRIES).find(([, value]) => value === countryName)?.[0] ?? ''; -} function updatePronouns(pronouns: string) { if (currentUserAccountID) { @@ -149,7 +80,7 @@ function updateDisplayName(firstName: string, lastName: string) { [currentUserAccountID]: { firstName, lastName, - displayName: createDisplayName(currentUserEmail ?? '', { + displayName: PersonalDetailsUtils.createDisplayName(currentUserEmail ?? '', { firstName, lastName, }), @@ -563,9 +494,6 @@ function getPrivatePersonalDetails(): OnyxEntry { export { clearAvatarErrors, deleteAvatar, - extractFirstAndLastNameFromAvailableDetails, - getCountryISO, - createDisplayName, getPrivatePersonalDetails, openPersonalDetailsPage, openPublicProfilePage, diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index df5709ac68e2..1766b1a29677 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -10,6 +10,7 @@ import * as SequentialQueue from '@libs/Network/SequentialQueue'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -23,7 +24,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Link from './Link'; import * as OnyxUpdates from './OnyxUpdates'; -import * as PersonalDetails from './PersonalDetails'; import * as Report from './Report'; import * as Session from './Session'; @@ -716,7 +716,7 @@ function setContactMethodAsDefault(newDefaultContactMethod: string) { value: { [currentUserAccountID]: { login: newDefaultContactMethod, - displayName: PersonalDetails.createDisplayName(newDefaultContactMethod, myPersonalDetails), + displayName: PersonalDetailsUtils.createDisplayName(newDefaultContactMethod, myPersonalDetails), }, }, }, diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 5ca51e208b49..0371a70ede4b 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -18,8 +18,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ValidationUtils from '@libs/ValidationUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import AddressForm from '@pages/ReimbursementAccount/AddressForm'; -import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -194,7 +194,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP label={translate(fieldNameTranslationKeys.legalFirstName)} accessibilityLabel={translate(fieldNameTranslationKeys.legalFirstName)} role={CONST.ROLE.PRESENTATION} - defaultValue={PersonalDetails.extractFirstAndLastNameFromAvailableDetails(currentUserPersonalDetails).firstName} + defaultValue={PersonalDetailsUtils.extractFirstAndLastNameFromAvailableDetails(currentUserPersonalDetails).firstName} shouldSaveDraft /> Date: Fri, 19 Jan 2024 10:04:55 +0000 Subject: [PATCH 08/57] fix: run prettier --- src/libs/PersonalDetailsUtils.ts | 5 ++--- src/libs/actions/PersonalDetails.ts | 1 - src/libs/actions/User.ts | 2 +- src/pages/EnablePayments/AdditionalDetailsStep.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 85c439c11736..945c0b581b16 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,6 +1,6 @@ +import Str from 'expensify-common/lib/str'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import Str from 'expensify-common/lib/str'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; @@ -9,7 +9,6 @@ import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as UserUtils from './UserUtils'; - type FirstAndLastName = { firstName: string; lastName: string; @@ -273,5 +272,5 @@ export { getStreetLines, getEffectiveDisplayName, createDisplayName, - extractFirstAndLastNameFromAvailableDetails + extractFirstAndLastNameFromAvailableDetails, }; diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index b09b664ff745..afd0dc6037e8 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -35,7 +35,6 @@ Onyx.connect({ callback: (val) => (privatePersonalDetails = val), }); - function updatePronouns(pronouns: string) { if (currentUserAccountID) { type UpdatePronounsParams = { diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 1766b1a29677..b304be62b407 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -7,10 +7,10 @@ import * as API from '@libs/API'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 0371a70ede4b..cf769bf58fd3 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -16,9 +16,9 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultPro import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ValidationUtils from '@libs/ValidationUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import AddressForm from '@pages/ReimbursementAccount/AddressForm'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; From d7fdf8010bea5f2aa1222b79365d70d57552c29e Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 16:59:38 +0530 Subject: [PATCH 09/57] added full screen loader for better UX --- .../step/IOURequestStepConfirmation.js | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 9df2564ae38d..84adf87bbae2 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -5,6 +5,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import categoryPropTypes from '@components/categoryPropTypes'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestConfirmationList from '@components/MoneyTemporaryForRefactorRequestConfirmationList'; @@ -103,6 +104,15 @@ function IOURequestStepConfirmation({ ); const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); + useEffect(() => { + if (!transaction || !transaction.originalCurrency) { + return; + } + // If user somehow lands on this page without the currency reset, then reset it here. + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency, true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat); if (policyExpenseChat) { @@ -318,6 +328,8 @@ function IOURequestStepConfirmation({ IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, billable); }; + const isLoading = !!(transaction && transaction.originalCurrency); + return ( - + {isLoading ? ( + + ) : ( + + )} )} From 61d6fcf64a5e6eb0a645fcfd4fa16a8b6388ddba Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 12:08:27 +0100 Subject: [PATCH 10/57] feat: add offline handling for money reports with various currencies --- src/components/MoneyReportHeader.tsx | 3 +++ src/components/ReportActionItem/MoneyReportView.tsx | 3 ++- src/libs/ReportUtils.ts | 13 +++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index afdc62218f95..d072dea34356 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -55,6 +55,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport); const isApproved = ReportUtils.isReportApproved(moneyRequestReport); const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); + const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport); const policyType = policy?.type; const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport); @@ -140,6 +141,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldShowApproveButton={shouldShowApproveButton} style={[styles.pv2]} formattedAmount={formattedAmount} + isDisabled={!canAllowSettlement} /> )} @@ -171,6 +173,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} formattedAmount={formattedAmount} + isDisabled={!canAllowSettlement} /> )} diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 3d1710de1432..ecc5ef0df831 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -40,6 +40,7 @@ function MoneyReportView({report, policyReportFields, shouldShowHorizontalRule}: const {isSmallScreenWidth} = useWindowDimensions(); const {canUseReportFields} = usePermissions(); const isSettled = ReportUtils.isSettled(report.reportID); + const isTotalUpdated = ReportUtils.hasUpdatedTotal(report); const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(report); @@ -110,7 +111,7 @@ function MoneyReportView({report, policyReportFields, shouldShowHorizontalRule}: )} {formattedTotalAmount} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 91fa7b35e824..10b7ce8965dd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4540,6 +4540,18 @@ function shouldDisplayThreadReplies(reportAction: OnyxEntry, repor return hasReplies && !!reportAction?.childCommenterCount && !isThreadFirstChat(reportAction, reportID); } +/** + * Check if money report has any transactions updated optimistically + */ +function hasUpdatedTotal(report: Report): boolean { + const transactions = TransactionUtils.getAllReportTransactions(report.reportID); + + const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); + const hasTransactionWithDifferentCurrency = transactions.some((transaction) => transaction.currency !== report.currency); + + return !(hasPendingTransaction && hasTransactionWithDifferentCurrency); +} + /** * Disable reply in thread action if: * @@ -4748,6 +4760,7 @@ export { doesReportBelongToWorkspace, getChildReportNotificationPreference, isReportFieldOfTypeTitle, + hasUpdatedTotal, }; export type { From aa34234694a467d0fbc637052a31960d1f107973 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 16:05:50 +0100 Subject: [PATCH 11/57] fix: change the way settlement button on ReportPreview is handled --- src/components/ReportActionItem/ReportPreview.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 204c9b5e31d4..f6d8ba34a8bc 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -166,6 +166,8 @@ function ReportPreview(props) { const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(props.iouReport); const isApproved = ReportUtils.isReportApproved(props.iouReport); + const canAllowSettlement = ReportUtils.hasUpdatedTotal(props.iouReport); + console.log('%%%%%\n', 'canAllowSettlement: ', canAllowSettlement); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(props.iouReport); const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(props.iouReportID); const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; @@ -333,6 +335,7 @@ function ReportPreview(props) { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }} + isDisabled={!canAllowSettlement} /> )} {shouldShowSubmitButton && ( From 870502329845d9d031137c4968a0609a1037d14b Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 16:13:20 +0100 Subject: [PATCH 12/57] fix: remove console logs --- src/components/ReportActionItem/ReportPreview.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index f6d8ba34a8bc..52847bf0366b 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -167,7 +167,6 @@ function ReportPreview(props) { const isApproved = ReportUtils.isReportApproved(props.iouReport); const canAllowSettlement = ReportUtils.hasUpdatedTotal(props.iouReport); - console.log('%%%%%\n', 'canAllowSettlement: ', canAllowSettlement); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(props.iouReport); const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(props.iouReportID); const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; From e3afca1a5fae2ede960201ac031ff70ec05b7934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 24 Jan 2024 17:23:50 +0100 Subject: [PATCH 13/57] header fix --- .../MoneyTemporaryForRefactorRequestParticipantsSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 0949081435c4..34048e4dd7bc 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -229,7 +229,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const headerMessage = useMemo( () => OptionsListUtils.getHeaderMessage( - _.get(newChatOptions, 'personalDetails.length', 0) + _.get(newChatOptions, 'recentReports.length', 0) !== 0, + _.get(newChatOptions, 'personalDetails', []).length + _.get(newChatOptions, 'recentReports', []).length !== 0, Boolean(newChatOptions.userToInvite), searchTerm.trim(), maxParticipantsReached, From 7e7f40335ef2b90bcede320b5680d88965c35df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 24 Jan 2024 17:30:01 +0100 Subject: [PATCH 14/57] header fix on second component --- .../MoneyRequestParticipantsSelector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index 9567b17ecdf5..12dd2df5c784 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -249,13 +249,13 @@ function MoneyRequestParticipantsSelector({ const headerMessage = useMemo( () => OptionsListUtils.getHeaderMessage( - newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, + _.get(newChatOptions, 'personalDetails', []).length + _.get(newChatOptions, 'recentReports', []).length !== 0, Boolean(newChatOptions.userToInvite), searchTerm.trim(), maxParticipantsReached, _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())), ), - [maxParticipantsReached, newChatOptions.personalDetails.length, newChatOptions.recentReports.length, newChatOptions.userToInvite, participants, searchTerm], + [maxParticipantsReached, newChatOptions, participants, searchTerm], ); // When search term updates we will fetch any reports From 41bc02b041f2047db87d055199bf4382cb625903 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 25 Jan 2024 14:46:50 +0700 Subject: [PATCH 15/57] remove MoneyRequestTagPae --- src/ROUTES.ts | 10 +- src/SCREENS.ts | 1 - ...oraryForRefactorRequestConfirmationList.js | 39 +++--- .../ReportActionItem/MoneyRequestView.js | 4 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig.ts | 1 - src/libs/Navigation/types.ts | 9 +- src/pages/iou/MoneyRequestTagPage.js | 127 ------------------ .../iou/request/step/IOURequestStepTag.js | 15 ++- 9 files changed, 48 insertions(+), 159 deletions(-) delete mode 100644 src/pages/iou/MoneyRequestTagPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 9c4375b84ab6..eb2c1728d596 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -287,10 +287,6 @@ const ROUTES = { route: ':iouType/new/category/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/category/${reportID}` as const, }, - MONEY_REQUEST_TAG: { - route: ':iouType/new/tag/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}` as const, - }, MONEY_REQUEST_MERCHANT: { route: ':iouType/new/merchant/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}` as const, @@ -374,9 +370,9 @@ const ROUTES = { getUrlWithBackToParam(`${action}/${iouType}/scan/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_TAG: { - route: 'create/:iouType/tag/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/tag/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/tag/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_WAYPOINT: { route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2bf40caede57..ded041b0f5b3 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -146,7 +146,6 @@ const SCREENS = { DATE: 'Money_Request_Date', DESCRIPTION: 'Money_Request_Description', CATEGORY: 'Money_Request_Category', - TAG: 'Money_Request_Tag', MERCHANT: 'Money_Request_Merchant', WAYPOINT: 'Money_Request_Waypoint', EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint', diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 2aff0444a59e..bdd3fa98672f 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -810,21 +810,30 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ rightLabel={canUseViolations && Boolean(policy.requiresCategory) ? translate('common.required') : ''} /> )} - {shouldShowTags && ( - - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) - } - style={[styles.moneyRequestMenuItem]} - disabled={didConfirm} - interactive={!isReadOnly} - rightLabel={canUseViolations && Boolean(policy.requiresTag) ? translate('common.required') : ''} - /> - )} + {shouldShowTags || + (true && ( + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction.transactionID, + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } + style={[styles.moneyRequestMenuItem]} + disabled={didConfirm} + interactive={!isReadOnly} + rightLabel={canUseViolations && Boolean(policy.requiresTag) ? translate('common.required') : ''} + /> + ))} {shouldShowTax && ( Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction.transactionID, report.reportID)) + } brickRoadIndicator={hasViolations('tag') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} /> {canUseViolations && } diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index c9325206e5b2..b9a3deb5f702 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -98,7 +98,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/MoneyRequestDatePage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.DESCRIPTION]: () => require('../../../pages/iou/MoneyRequestDescriptionPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CATEGORY]: () => require('../../../pages/iou/MoneyRequestCategoryPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.TAG]: () => require('../../../pages/iou/MoneyRequestTagPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.MERCHANT]: () => require('../../../pages/iou/MoneyRequestMerchantPage').default as React.ComponentType, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: () => require('../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: () => require('../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 5df2bcf0e57b..6f184168b9f1 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -433,7 +433,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.MONEY_REQUEST.CURRENCY]: ROUTES.MONEY_REQUEST_CURRENCY.route, [SCREENS.MONEY_REQUEST.DESCRIPTION]: ROUTES.MONEY_REQUEST_DESCRIPTION.route, [SCREENS.MONEY_REQUEST.CATEGORY]: ROUTES.MONEY_REQUEST_CATEGORY.route, - [SCREENS.MONEY_REQUEST.TAG]: ROUTES.MONEY_REQUEST_TAG.route, [SCREENS.MONEY_REQUEST.MERCHANT]: ROUTES.MONEY_REQUEST_MERCHANT.route, [SCREENS.MONEY_REQUEST.RECEIPT]: ROUTES.MONEY_REQUEST_RECEIPT.route, [SCREENS.MONEY_REQUEST.DISTANCE]: ROUTES.MONEY_REQUEST_DISTANCE.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2371c764f42a..60bc0b54e41e 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -211,12 +211,15 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; - [SCREENS.MONEY_REQUEST.TAG]: { + [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: { iouType: string; + transactionID: string; reportID: string; + backTo: string; }; - [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: { - iouType: string; + [SCREENS.MONEY_REQUEST.STEP_TAG]: { + action: ValueOf; + iouType: ValueOf; transactionID: string; reportID: string; backTo: string; diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js deleted file mode 100644 index 60e40d665580..000000000000 --- a/src/pages/iou/MoneyRequestTagPage.js +++ /dev/null @@ -1,127 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TagPicker from '@components/TagPicker'; -import tagPropTypes from '@components/tagPropTypes'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as IOU from '@userActions/IOU'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen via route :iouType/new/tag/:reportID? */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - /* Onyx props */ - /** The report currently being used */ - report: reportPropTypes, - - /** Collection of tags attached to a policy */ - policyTags: tagPropTypes, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, -}; - -const defaultProps = { - report: {}, - policyTags: {}, - iou: iouDefaultProps, -}; - -function MoneyRequestTagPage({route, report, policyTags, iou}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const iouType = lodashGet(route, 'params.iouType', ''); - - // Fetches the first tag list of the policy - const tagListKey = _.first(_.keys(policyTags)); - const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); - - const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); - }; - - const updateTag = (selectedTag) => { - if (selectedTag.searchText === iou.tag) { - IOU.resetMoneyRequestTag(); - } else { - IOU.setMoneyRequestTag(selectedTag.searchText); - } - navigateBack(); - }; - - return ( - - {({insets}) => ( - <> - - {translate('iou.tagSelection', {tagName: policyTagListName})} - - - )} - - ); -} - -MoneyRequestTagPage.displayName = 'MoneyRequestTagPage'; -MoneyRequestTagPage.propTypes = propTypes; -MoneyRequestTagPage.defaultProps = defaultProps; - -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - report: { - key: ({route, iou}) => { - const reportID = IOU.getIOUReportID(iou, route); - - return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; - }, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - policyTags: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, - }, - }), -)(MoneyRequestTagPage); diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js index 7e2ccbe1a9dd..51bda8e232ce 100644 --- a/src/pages/iou/request/step/IOURequestStepTag.js +++ b/src/pages/iou/request/step/IOURequestStepTag.js @@ -12,6 +12,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; @@ -44,7 +45,7 @@ function IOURequestStepTag({ policyTags, report, route: { - params: {transactionID, backTo}, + params: {action, transactionID, backTo}, }, transaction: {tag}, }) { @@ -54,6 +55,7 @@ function IOURequestStepTag({ // Fetches the first tag list of the policy const tagListKey = _.first(_.keys(policyTags)); const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); + const isEditting = action === CONST.IOU.ACTION.EDIT; const navigateBack = () => { Navigation.goBack(backTo || ROUTES.HOME); @@ -64,10 +66,17 @@ function IOURequestStepTag({ * @param {String} selectedTag.searchText */ const updateTag = (selectedTag) => { - if (selectedTag.searchText === tag) { + const isSelectedTag = selectedTag.searchText === tag; + const updatedTag = !isSelectedTag ? selectedTag.searchText : ''; + if (isEditting) { + IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag); + Navigation.dismissModal(); + return; + } + if (isSelectedTag) { IOU.resetMoneyRequestTag_temporaryForRefactor(transactionID); } else { - IOU.setMoneyRequestTag_temporaryForRefactor(transactionID, selectedTag.searchText); + IOU.setMoneyRequestTag_temporaryForRefactor(transactionID, updatedTag); } navigateBack(); }; From ee81374361e67c1d1c4e7b59aad5792da5d9d507 Mon Sep 17 00:00:00 2001 From: caitlinwhite1 Date: Thu, 25 Jan 2024 14:46:19 -0600 Subject: [PATCH 16/57] Delete docs/articles/expensify-classic/getting-started/Individual-Users.md moving to "Create a Workspace for Yourself" --- .../getting-started/Individual-Users.md | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 docs/articles/expensify-classic/getting-started/Individual-Users.md diff --git a/docs/articles/expensify-classic/getting-started/Individual-Users.md b/docs/articles/expensify-classic/getting-started/Individual-Users.md deleted file mode 100644 index 12029f80388b..000000000000 --- a/docs/articles/expensify-classic/getting-started/Individual-Users.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Individual Users -description: Learn how Expensify can help you track and submit your personal or self-employed business expenses. ---- -# Overview -If you're an individual using Expensify, the Track and Submit plans are designed to assist self-employed users in effectively managing both their personal and business finances. - -# How to use the Track plan - -The Track plan is tailored for solo Expensify users who don't require expense submission to others. Individuals or sole proprietors can choose the Track plan to customize their Individual Workspace to align with their personal expense tracking requirements. - -You can select the Track plan from the Workspace settings. Navigate to **Settings > Workspace > Individual > *[Workspace Name]* > Plan** to select Track. -You can also do this from the Pricing page at https://www.expensify.com/pricing. - -The Track plan includes a predefined set of categories designed to align with IRS Schedule C expense categories. However, you have the flexibility to add extra categories as needed. For a more detailed breakdown, you can also set up tags to create another layer of coding. - -The Track plan offers 25 free SmartScans per month. If you require more than 25 SmartScans, you can upgrade to a Monthly Individual subscription at a cost of $4.99 USD per month. - -# How to use the Submit plan -The Submit plan is designed for individuals who need to keep track of their expenses and share them with someone else, such as their boss, accountant, or even a housemate. It's specifically tailored for single users who want to both track and submit their expenses efficiently. - -You can select the Track plan from the Workspace settings. Navigate to **Settings > Workspaces > Individual > *[Workspace Name]* > Plan** to select "Submit" or from the Pricing page at https://www.expensify.com/pricing. - -You will select who your expenses get sent to under **Settings > Workspace > Individual > *[Workspace Name]* > Reports**. If the recipient already has an Expensify account, they'll be able to see the report directly in the Expensify app. Otherwise, non-Expensify users will receive a PDF copy of the report attached to the email so it can be processed. - -The Submit plan includes a predefined set of categories designed to align with IRS Schedule C expense categories. However, you have the flexibility to add extra categories as needed. For a more detailed breakdown, you can also set up tags to create another layer of coding. - -The Submit plan offers 25 free SmartScans per month.If you require more than 25 SmartScans, you can upgrade to a Monthly Individual subscription at a cost of $4.99 USD per month. - -# FAQ - -## Who should use the Track plan? -An individual who wants to store receipts, look to track spending by category to help with budgeting and a self-employed user who needs to track receipts and mileage for tax purposes. - -## Who should use the Submit plan? -An individual who seeks to utilize the features of the track plan to monitor their expenses while also requiring the ability to submit those expenses to someone else. - -## How can I keep track of personal and business expenses in the same account? -You have the capability to create distinct "business" and "personal" tags and assign them to your expenses for proper categorization. By doing so, you can effectively code your expenses based on their nature. Additionally, you can utilize filters to ensure that you only view the expenses that are relevant to your specific needs, whether they are business-related or personal. - -## How can I export expenses for tax purposes? -From the expense page, you have the option to select all of your expenses and export them to a CSV (Comma-Separated Values) file. This CSV file can be conveniently imported directly into your tax software for easier tax preparation. - From 4459b37bb3762b41bf2a4eed338e761424fd0a6a Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Fri, 26 Jan 2024 21:07:39 +0400 Subject: [PATCH 17/57] Init changes to migrate --- src/ONYXKEYS.ts | 2 +- src/components/DisplayNames/types.ts | 2 +- src/languages/en.ts | 1 + src/libs/ErrorUtils.ts | 2 +- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportUtils.ts | 6 +- src/libs/ValidationUtils.ts | 5 +- src/libs/actions/Report.ts | 4 +- .../home/report/withReportOrNotFound.tsx | 8 +- .../Report/NotificationPreferencePage.js | 59 -------------- .../Report/NotificationPreferencePage.tsx | 55 +++++++++++++ ...SettingsPage.js => ReportSettingsPage.tsx} | 80 +++++-------------- .../{RoomNamePage.js => RoomNamePage.tsx} | 53 ++++++------ ...abilityPage.js => WriteCapabilityPage.tsx} | 49 ++++++------ src/types/onyx/Form.ts | 6 +- src/types/onyx/index.ts | 3 +- 16 files changed, 149 insertions(+), 188 deletions(-) delete mode 100644 src/pages/settings/Report/NotificationPreferencePage.js create mode 100644 src/pages/settings/Report/NotificationPreferencePage.tsx rename src/pages/settings/Report/{ReportSettingsPage.js => ReportSettingsPage.tsx} (78%) rename src/pages/settings/Report/{RoomNamePage.js => RoomNamePage.tsx} (80%) rename src/pages/settings/Report/{WriteCapabilityPage.js => WriteCapabilityPage.tsx} (52%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7abf6db1769d..3c9c3aba11b7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -486,7 +486,7 @@ type OnyxValues = { [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: OnyxTypes.DisplayNameForm; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM_DRAFT]: OnyxTypes.DisplayNameForm; - [ONYXKEYS.FORMS.ROOM_NAME_FORM]: OnyxTypes.Form; + [ONYXKEYS.FORMS.ROOM_NAME_FORM]: OnyxTypes.RoomNameForm; [ONYXKEYS.FORMS.ROOM_NAME_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM_DRAFT]: OnyxTypes.Form; diff --git a/src/components/DisplayNames/types.ts b/src/components/DisplayNames/types.ts index 2e6f36d5cc07..7da1819c9f01 100644 --- a/src/components/DisplayNames/types.ts +++ b/src/components/DisplayNames/types.ts @@ -20,7 +20,7 @@ type DisplayNamesProps = { fullTitle: string; /** Array of objects that map display names to their corresponding tooltip */ - displayNamesWithTooltips: DisplayNameWithTooltip[]; + displayNamesWithTooltips?: DisplayNameWithTooltip[]; /** Number of lines before wrapping */ numberOfLines: number; diff --git a/src/languages/en.ts b/src/languages/en.ts index 8a959b5da550..80c73f547e99 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -673,6 +673,7 @@ export default { always: 'Immediately', daily: 'Daily', mute: 'Mute', + hidden: 'Hidden', }, }, loginField: { diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 18aa262c2079..29db104c322d 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -101,7 +101,7 @@ type ErrorsList = Record; * @param errors - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey) { +function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey | [TKey, Record] ) { if (!message || !inputID) { return; } diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index b8ed62f93082..0d406bd78bd9 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -110,7 +110,7 @@ function isExpensifyGuideTeam(email: string): boolean { /** * Checks if the current user is an admin of the policy. */ -const isPolicyAdmin = (policy: OnyxEntry): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; +const isPolicyAdmin = (policy?: OnyxEntry): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; const isPolicyMember = (policyID: string, policies: Record): boolean => Object.values(policies).some((policy) => policy?.id === policyID); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fa51ca06e68e..eef3e894e4ec 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4222,7 +4222,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array, policy: OnyxEntry): boolean { +function shouldDisableRename(report: OnyxEntry, policy?: OnyxEntry): boolean { if (isDefaultRoom(report) || isArchivedRoom(report) || isThread(report) || isMoneyRequestReport(report) || isPolicyExpenseChat(report)) { return true; } @@ -4241,7 +4241,7 @@ function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry, policy: OnyxEntry): boolean { +function canEditWriteCapability(report: OnyxEntry, policy?: OnyxEntry): boolean { return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report) && !isThread(report); } @@ -4499,7 +4499,7 @@ function getRoom(type: ValueOf, policyID: string) /** * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. */ -function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntry): boolean { +function shouldDisableWelcomeMessage(report: OnyxEntry, policy?: OnyxEntry): boolean { return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } /** diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 7eff51c354df..ee8234f946c1 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -11,6 +11,7 @@ import DateUtils from './DateUtils'; import * as LoginUtils from './LoginUtils'; import {parsePhoneNumber} from './PhoneNumber'; import StringUtils from './StringUtils'; +import type {OnyxCollection} from "react-native-onyx"; /** * Implements the Luhn Algorithm, a checksum formula used to validate credit card @@ -354,8 +355,8 @@ function isReservedRoomName(roomName: string): boolean { /** * Checks if the room name already exists. */ -function isExistingRoomName(roomName: string, reports: Record, policyID: string): boolean { - return Object.values(reports).some((report) => report && report.policyID === policyID && report.reportName === roomName); +function isExistingRoomName(roomName: string, reports: OnyxCollection, policyID?: string): boolean { + return Object.values(reports ?? {}).some((report) => report && report.policyID === policyID && report.reportName === roomName); } /** diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e084a99df0c7..54f20e672e5b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1729,7 +1729,7 @@ function navigateToConciergeChatAndDeleteReport(reportID: string) { /** * @param policyRoomName The updated name for the policy room */ -function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string) { +function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName?: string) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; @@ -1777,7 +1777,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomNam type UpdatePolicyRoomNameParameters = { reportID: string; - policyRoomName: string; + policyRoomName?: string; }; const parameters: UpdatePolicyRoomNameParameters = {reportID, policyRoomName}; diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index 7613bafeacdc..a8facc3e1c76 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -22,16 +22,16 @@ type OnyxProps = { isLoadingReportData: OnyxEntry; }; -type ComponentProps = OnyxProps & { +type WithReportOrNotFoundProps = OnyxProps & { route: RouteProp<{params: {reportID: string}}>; }; export default function ( shouldRequireReportID = true, -): ( +): ( WrappedComponent: React.ComponentType>, ) => React.ComponentType, keyof OnyxProps>> { - return function (WrappedComponent: ComponentType>) { + return function (WrappedComponent: ComponentType>) { function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const contentShown = React.useRef(false); @@ -89,3 +89,5 @@ export default function ( })(React.forwardRef(WithReportOrNotFound)); }; } + +export type {WithReportOrNotFoundProps}; diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js deleted file mode 100644 index c6044bd81efe..000000000000 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import _ from 'underscore'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import compose from '@libs/compose'; -import * as ReportUtils from '@libs/ReportUtils'; -import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Report from '@userActions/Report'; -import CONST from '@src/CONST'; - -const propTypes = { - ...withLocalizePropTypes, - - /** The report for which we are setting notification preferences */ - report: reportPropTypes.isRequired, -}; - -function NotificationPreferencePage(props) { - const shouldDisableNotificationPreferences = ReportUtils.isArchivedRoom(props.report); - const notificationPreferenceOptions = _.map( - _.filter(_.values(CONST.REPORT.NOTIFICATION_PREFERENCE), (pref) => pref !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN), - (preference) => ({ - value: preference, - text: props.translate(`notificationPreferencesPage.notificationPreferences.${preference}`), - keyForList: preference, - isSelected: preference === props.report.notificationPreference, - }), - ); - - return ( - - - ReportUtils.goBackToDetailsPage(props.report)} - /> - - Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, option.value, true, undefined, undefined, props.report) - } - initiallyFocusedOptionKey={_.find(notificationPreferenceOptions, (locale) => locale.isSelected).keyForList} - /> - - - ); -} - -NotificationPreferencePage.displayName = 'NotificationPreferencePage'; -NotificationPreferencePage.propTypes = propTypes; - -export default compose(withLocalize, withReportOrNotFound())(NotificationPreferencePage); diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx new file mode 100644 index 000000000000..8d3ed23f6845 --- /dev/null +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import * as ReportUtils from '@libs/ReportUtils'; +import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; +import * as ReportActions from '@userActions/Report'; +import CONST from '@src/CONST'; +import useLocalize from "@hooks/useLocalize"; +import type {Report} from '@src/types/onyx'; + +type NotificationPreferencePageProps = WithReportOrNotFoundProps & { + report: Report; +} + +function NotificationPreferencePage({report}:NotificationPreferencePageProps) { + const {translate} = useLocalize() + const shouldDisableNotificationPreferences = ReportUtils.isArchivedRoom(report); + const notificationPreferenceOptions = + Object.values(CONST.REPORT.NOTIFICATION_PREFERENCE).filter((pref) => pref !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) + .map((preference) => ({ + value: preference, + text: translate(`notificationPreferencesPage.notificationPreferences.${preference}`), + keyForList: preference, + isSelected: preference === report.notificationPreference, + }) + ); + + return ( + + + ReportUtils.goBackToDetailsPage(report)} + /> + + ReportActions.updateNotificationPreference(report.reportID, report.notificationPreference, option.value, true, undefined, undefined, report) + } + initiallyFocusedOptionKey={Object.values(notificationPreferenceOptions ?? {}).find((locale) => locale.isSelected)?.keyForList} + /> + + + ); +} + +NotificationPreferencePage.displayName = 'NotificationPreferencePage'; + +export default withReportOrNotFound()(NotificationPreferencePage); diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.tsx similarity index 78% rename from src/pages/settings/Report/ReportSettingsPage.js rename to src/pages/settings/Report/ReportSettingsPage.tsx index c7cfd9c7850d..7d4598d549ff 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -1,9 +1,6 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import isEmpty from 'lodash/isEmpty'; import React, {useMemo} from 'react'; import {ScrollView, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DisplayNames from '@components/DisplayNames'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -15,71 +12,46 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import {getGroupChatName} from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Report from '@userActions/Report'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; +import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Report} from '@src/types/onyx'; -const propTypes = { - /** Route params */ - route: PropTypes.shape({ - params: PropTypes.shape({ - /** Report ID passed via route r/:reportID/settings */ - reportID: PropTypes.string, - }), - }).isRequired, - /* Onyx Props */ - - /** The active report */ - report: reportPropTypes.isRequired, - - /** The policies which the user has access to and which the report could be tied to */ - policies: PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - - /** ID of the policy */ - id: PropTypes.string, - }), -}; - -const defaultProps = { - policies: {}, -}; +type ReportSettingsPageProps = WithReportOrNotFoundProps & { + report: Report +} -function ReportSettingsPage(props) { +function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const styles = useThemeStyles(); - const {report, policies} = props; const {translate} = useLocalize(); // 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 linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find( (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, but not in thread chat const shouldDisableWelcomeMessage = ReportUtils.shouldDisableWelcomeMessage(report, linkedWorkspace); - const shouldDisableSettings = _.isEmpty(report) || ReportUtils.isArchivedRoom(report); + const shouldDisableSettings = isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); const notificationPreference = - report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN + report.notificationPreference && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN ? translate(`notificationPreferencesPage.notificationPreferences.${report.notificationPreference}`) : ''; - const writeCapability = ReportUtils.isAdminRoom(report) ? CONST.REPORT.WRITE_CAPABILITIES.ADMINS : report.writeCapability || CONST.REPORT.WRITE_CAPABILITIES.ALL; + 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 = useMemo(() => ReportUtils.canEditWriteCapability(report, linkedWorkspace), [report, linkedWorkspace]); const shouldShowNotificationPref = !isMoneyRequestReport && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const roomNameLabel = translate(isMoneyRequestReport ? 'workspace.editor.nameInputLabel' : 'newRoomPage.roomName'); - const reportName = ReportUtils.isGroupChat(props.report) ? getGroupChatName(props.report) : ReportUtils.getReportName(props.report); + const reportName = ReportUtils.isGroupChat(report) ? getGroupChatName(report) : ReportUtils.getReportName(report); const shouldShowWriteCapability = !isMoneyRequestReport; @@ -101,10 +73,10 @@ function ReportSettingsPage(props) { )} {shouldShowRoomName && ( Report.clearPolicyRoomNameErrors(report.reportID)} + onClose={() => ReportActions.clearPolicyRoomNameErrors(report.reportID)} > {shouldDisableRename ? ( @@ -115,7 +87,7 @@ function ReportSettingsPage(props) { {roomNameLabel} - {translate(`newRoomPage.visibilityOptions.${report.visibility}`)} + {report.visibility && translate(`newRoomPage.visibilityOptions.${report.visibility}`)} - {translate(`newRoomPage.${report.visibility}Description`)} + {report.visibility && translate(`newRoomPage.${report.visibility}Description`)} )} @@ -206,14 +178,6 @@ function ReportSettingsPage(props) { ); } -ReportSettingsPage.propTypes = propTypes; -ReportSettingsPage.defaultProps = defaultProps; ReportSettingsPage.displayName = 'ReportSettingsPage'; -export default compose( - withReportOrNotFound(), - withOnyx({ - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - }), -)(ReportSettingsPage); + +export default withReportOrNotFound()(ReportSettingsPage); diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.tsx similarity index 80% rename from src/pages/settings/Report/RoomNamePage.js rename to src/pages/settings/Report/RoomNamePage.tsx index 5f64faca50fc..793fb04ca68c 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -1,15 +1,14 @@ import {useIsFocused} from '@react-navigation/native'; -import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import RoomNameInput from '@components/RoomNameInput'; import ScreenWrapper from '@components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; @@ -17,39 +16,37 @@ import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Report from '@userActions/Report'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; +import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import useLocalize from "@hooks/useLocalize"; +import type {Policy, Report} from "@src/types/onyx"; +import type {OnyxFormValuesFields} from "@components/Form/types"; +import type {AnimatedTextInputRef} from "@components/RNTextInput"; -const propTypes = { - ...withLocalizePropTypes, - - /** The room report for which the name is being edited */ - report: reportPropTypes.isRequired, - +type RoomNamePageOnyxProps = { /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), + reports: OnyxCollection, /** Policy of the report for which the name is being edited */ - policy: PropTypes.shape({ - role: PropTypes.string, - owner: PropTypes.string, - }), -}; -const defaultProps = { - reports: {}, - policy: {}, + policy: OnyxEntry +} + +type RoomNamePageProps = RoomNamePageOnyxProps & WithReportOrNotFoundProps & { + /** The room report for which the name is being edited */ + report: Report }; -function RoomNamePage({policy, report, reports, translate}) { +function RoomNamePage({ report, policy, reports }: RoomNamePageProps) { const styles = useThemeStyles(); - const roomNameInputRef = useRef(null); + const roomNameInputRef = useRef(null); const isFocused = useIsFocused(); + const {translate} = useLocalize() const validate = useCallback( - (values) => { + (values: OnyxFormValuesFields) => { const errors = {}; // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method @@ -78,7 +75,7 @@ function RoomNamePage({policy, report, reports, translate}) { return ( roomNameInputRef.current && roomNameInputRef.current.focus()} + onEntryTransitionEnd={() => roomNameInputRef.current?.focus()} includeSafeAreaPaddingBottom={false} testID={RoomNamePage.displayName} > @@ -90,7 +87,7 @@ function RoomNamePage({policy, report, reports, translate}) { Report.updatePolicyRoomNameAndNavigate(report, values.roomName)} + onSubmit={(values) => ReportActions.updatePolicyRoomNameAndNavigate(report, values.roomName)} validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline @@ -110,14 +107,10 @@ function RoomNamePage({policy, report, reports, translate}) { ); } -RoomNamePage.propTypes = propTypes; -RoomNamePage.defaultProps = defaultProps; RoomNamePage.displayName = 'RoomNamePage'; export default compose( - withLocalize, - withReportOrNotFound(), - withOnyx({ + withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, }, @@ -125,4 +118,6 @@ export default compose( key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), + withReportOrNotFound(), + )(RoomNamePage); diff --git a/src/pages/settings/Report/WriteCapabilityPage.js b/src/pages/settings/Report/WriteCapabilityPage.tsx similarity index 52% rename from src/pages/settings/Report/WriteCapabilityPage.js rename to src/pages/settings/Report/WriteCapabilityPage.tsx index fc587b028f7d..244a47fee113 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.js +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -1,43 +1,43 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; -import {policyDefaultProps, policyPropTypes} from '@pages/workspace/withPolicy'; -import * as Report from '@userActions/Report'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; +import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import useLocalize from "@hooks/useLocalize"; +import type {Policy, Report} from "@src/types/onyx"; -const propTypes = { - ...withLocalizePropTypes, - ...policyPropTypes, +type WriteCapabilityPageOnyxProps = { + /** The policy object for the current route */ + policy: OnyxEntry; +}; +type WriteCapabilityPageProps = WriteCapabilityPageOnyxProps & WithReportOrNotFoundProps & { /** The report for which we are setting write capability */ - report: reportPropTypes.isRequired, + report: Report, }; -const defaultProps = { - ...policyDefaultProps, -}; -function WriteCapabilityPage(props) { - const writeCapabilityOptions = _.map(CONST.REPORT.WRITE_CAPABILITIES, (value) => ({ +function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { + const {translate} = useLocalize() + const writeCapabilityOptions = Object.values(CONST.REPORT.WRITE_CAPABILITIES).map((value) => ({ value, - text: props.translate(`writeCapabilityPage.writeCapability.${value}`), + text: translate(`writeCapabilityPage.writeCapability.${value}`), keyForList: value, - isSelected: value === (props.report.writeCapability || CONST.REPORT.WRITE_CAPABILITIES.ALL), + isSelected: value === (report.writeCapability ?? CONST.REPORT.WRITE_CAPABILITIES.ALL), })); - const isAbleToEdit = ReportUtils.canEditWriteCapability(props.report, props.policy); + const isAbleToEdit = ReportUtils.canEditWriteCapability(report, policy); return ( Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))} /> Report.updateWriteCapabilityAndNavigate(props.report, option.value)} - initiallyFocusedOptionKey={_.find(writeCapabilityOptions, (locale) => locale.isSelected).keyForList} + onSelectRow={(option) => ReportActions.updateWriteCapabilityAndNavigate(report, option.value)} + initiallyFocusedOptionKey={Object.values(writeCapabilityOptions).find((locale) => locale.isSelected)?.keyForList} /> @@ -61,15 +61,12 @@ function WriteCapabilityPage(props) { } WriteCapabilityPage.displayName = 'WriteCapabilityPage'; -WriteCapabilityPage.propTypes = propTypes; -WriteCapabilityPage.defaultProps = defaultProps; export default compose( - withLocalize, - withReportOrNotFound(), - withOnyx({ + withOnyx({ policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), + withReportOrNotFound(), )(WriteCapabilityPage); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index c3bcec2a2d3b..142984d7f4b1 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -54,6 +54,10 @@ type PrivateNotesForm = Form<{ privateNotes: string; }>; +type RoomNameForm = Form<{ + roomName?: string; +}>; + export default Form; -export type {AddDebitCardForm, DateOfBirthForm, PrivateNotesForm, DisplayNameForm, FormValueType, NewRoomForm, BaseForm, IKnowATeacherForm, IntroSchoolPrincipalForm}; +export type {AddDebitCardForm, DateOfBirthForm, PrivateNotesForm, DisplayNameForm, FormValueType, NewRoomForm, BaseForm, IKnowATeacherForm, IntroSchoolPrincipalForm, RoomNameForm}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 5b04cae58671..a003b8d87e87 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -9,7 +9,7 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; import type Download from './Download'; -import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm} from './Form'; +import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm, RoomNameForm} from './Form'; import type Form from './Form'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; @@ -151,4 +151,5 @@ export type { IKnowATeacherForm, IntroSchoolPrincipalForm, PrivateNotesForm, + RoomNameForm }; From 853932d0963bd48fe779b706d16c0bd95c6b061c Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Mon, 29 Jan 2024 20:51:12 +0400 Subject: [PATCH 18/57] Fix screens crash --- src/pages/settings/Report/RoomNamePage.tsx | 2 +- src/pages/settings/Report/WriteCapabilityPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 793fb04ca68c..091db8f247f6 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -110,6 +110,7 @@ function RoomNamePage({ report, policy, reports }: RoomNamePageProps) { RoomNamePage.displayName = 'RoomNamePage'; export default compose( + withReportOrNotFound(), withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, @@ -118,6 +119,5 @@ export default compose( key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), - withReportOrNotFound(), )(RoomNamePage); diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index 244a47fee113..b4de7518f7ba 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -63,10 +63,10 @@ function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { WriteCapabilityPage.displayName = 'WriteCapabilityPage'; export default compose( + withReportOrNotFound(), withOnyx({ policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), - withReportOrNotFound(), )(WriteCapabilityPage); From b544a69c5066a160919f246889f50b7dd112c78d Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Mon, 29 Jan 2024 20:58:08 +0400 Subject: [PATCH 19/57] add translation key for es --- src/languages/es.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 16686f54f1e1..f99765a66673 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -668,6 +668,7 @@ export default { always: 'Inmediatamente', daily: 'Cada día', mute: 'Nunca', + hidden: 'Oculta', }, }, loginField: { From ff010eb9ffb9cec9248c3a5a4c7cec8a5876daf8 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Mon, 29 Jan 2024 21:19:53 +0400 Subject: [PATCH 20/57] Fix TS and lint errors --- src/libs/ValidationUtils.ts | 2 +- src/libs/actions/Report.ts | 2 +- src/pages/settings/Report/RoomNamePage.tsx | 8 +++----- src/pages/settings/Report/WriteCapabilityPage.tsx | 7 +++---- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index ee8234f946c1..bc5caa92bbbd 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -6,12 +6,12 @@ import isObject from 'lodash/isObject'; import CONST from '@src/CONST'; import type {Report} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import type {OnyxCollection} from "react-native-onyx"; import * as CardUtils from './CardUtils'; import DateUtils from './DateUtils'; import * as LoginUtils from './LoginUtils'; import {parsePhoneNumber} from './PhoneNumber'; import StringUtils from './StringUtils'; -import type {OnyxCollection} from "react-native-onyx"; /** * Implements the Luhn Algorithm, a checksum formula used to validate credit card diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6222c09a898e..f2744f8a8269 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1673,7 +1673,7 @@ function navigateToConciergeChatAndDeleteReport(reportID: string) { /** * @param policyRoomName The updated name for the policy room */ -function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string) { +function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName?: string) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 091db8f247f6..1daf20013731 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -109,8 +109,7 @@ function RoomNamePage({ report, policy, reports }: RoomNamePageProps) { RoomNamePage.displayName = 'RoomNamePage'; -export default compose( - withReportOrNotFound(), +export default withReportOrNotFound()( withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, @@ -118,6 +117,5 @@ export default compose( policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, - }), - -)(RoomNamePage); + })(RoomNamePage) +); diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index b4de7518f7ba..14526ba3c0ac 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -62,11 +62,10 @@ function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { WriteCapabilityPage.displayName = 'WriteCapabilityPage'; -export default compose( - withReportOrNotFound(), +export default withReportOrNotFound()( withOnyx({ policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, - }), -)(WriteCapabilityPage); + })(WriteCapabilityPage), +); From 52670ab458bfa5ca6ef9354db8287c08915c4312 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Mon, 29 Jan 2024 21:31:52 +0400 Subject: [PATCH 21/57] Fix Ts error --- src/components/DisplayNames/DisplayNamesWithTooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx index ce0ae7ddcf4f..1cacb0e20c5d 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx @@ -56,7 +56,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit > {shouldUseFullTitle ? ReportUtils.formatReportLastMessageText(fullTitle) - : displayNamesWithTooltips.map(({displayName, accountID, avatar, login}, index) => ( + : displayNamesWithTooltips?.map(({displayName, accountID, avatar, login}, index) => ( // eslint-disable-next-line react/no-array-index-key Date: Mon, 29 Jan 2024 21:41:25 +0400 Subject: [PATCH 22/57] Fix lint errors --- src/pages/settings/Report/RoomNamePage.tsx | 1 - src/pages/settings/Report/WriteCapabilityPage.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 1daf20013731..a3e24de4c325 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -10,7 +10,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import RoomNameInput from '@components/RoomNameInput'; import ScreenWrapper from '@components/ScreenWrapper'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index 14526ba3c0ac..f03415a2a56f 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -5,7 +5,6 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; From fe014b76fefdb9c68718a587dfb223a6d4860fa3 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Mon, 29 Jan 2024 21:46:15 +0400 Subject: [PATCH 23/57] Run prettier --- src/libs/ErrorUtils.ts | 2 +- src/libs/ValidationUtils.ts | 2 +- .../Report/NotificationPreferencePage.tsx | 19 +++++------- .../settings/Report/ReportSettingsPage.tsx | 9 +++--- src/pages/settings/Report/RoomNamePage.tsx | 31 ++++++++++--------- .../settings/Report/WriteCapabilityPage.tsx | 18 +++++------ src/types/onyx/index.ts | 2 +- 7 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 9acda5ec722a..8eff66c10751 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -101,7 +101,7 @@ type ErrorsList = Record; * @param errors - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey | [TKey, Record] ) { +function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey | [TKey, Record]) { if (!message || !inputID) { return; } diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index bc5caa92bbbd..af9e394e0b7f 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -3,10 +3,10 @@ import {URL_REGEX_WITH_REQUIRED_PROTOCOL} from 'expensify-common/lib/Url'; import isDate from 'lodash/isDate'; import isEmpty from 'lodash/isEmpty'; import isObject from 'lodash/isObject'; +import type {OnyxCollection} from 'react-native-onyx'; import CONST from '@src/CONST'; import type {Report} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; -import type {OnyxCollection} from "react-native-onyx"; import * as CardUtils from './CardUtils'; import DateUtils from './DateUtils'; import * as LoginUtils from './LoginUtils'; diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx index 8d3ed23f6845..c5fdaa25fac5 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.tsx +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -3,30 +3,29 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; +import useLocalize from '@hooks/useLocalize'; import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; -import useLocalize from "@hooks/useLocalize"; import type {Report} from '@src/types/onyx'; type NotificationPreferencePageProps = WithReportOrNotFoundProps & { report: Report; -} +}; -function NotificationPreferencePage({report}:NotificationPreferencePageProps) { - const {translate} = useLocalize() +function NotificationPreferencePage({report}: NotificationPreferencePageProps) { + const {translate} = useLocalize(); const shouldDisableNotificationPreferences = ReportUtils.isArchivedRoom(report); - const notificationPreferenceOptions = - Object.values(CONST.REPORT.NOTIFICATION_PREFERENCE).filter((pref) => pref !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) + const notificationPreferenceOptions = Object.values(CONST.REPORT.NOTIFICATION_PREFERENCE) + .filter((pref) => pref !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) .map((preference) => ({ value: preference, text: translate(`notificationPreferencesPage.notificationPreferences.${preference}`), keyForList: preference, isSelected: preference === report.notificationPreference, - }) - ); + })); return ( - ReportActions.updateNotificationPreference(report.reportID, report.notificationPreference, option.value, true, undefined, undefined, report) - } + onSelectRow={(option) => ReportActions.updateNotificationPreference(report.reportID, report.notificationPreference, option.value, true, undefined, undefined, report)} initiallyFocusedOptionKey={Object.values(notificationPreferenceOptions ?? {}).find((locale) => locale.isSelected)?.keyForList} /> diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 7d4598d549ff..d6f3a30c9d63 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -22,16 +22,15 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; - type ReportSettingsPageProps = WithReportOrNotFoundProps & { - report: Report -} + report: Report; +}; function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); // The workspace the report is on, null if the user isn't a member of the workspace - const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find( (policy) => policy && policy.id === report.policyID), [policies, report.policyID]); + const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report.policyID), [policies, report.policyID]); const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -87,7 +86,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { {roomNameLabel} , + reports: OnyxCollection; /** Policy of the report for which the name is being edited */ - policy: OnyxEntry -} - -type RoomNamePageProps = RoomNamePageOnyxProps & WithReportOrNotFoundProps & { - /** The room report for which the name is being edited */ - report: Report + policy: OnyxEntry; }; -function RoomNamePage({ report, policy, reports }: RoomNamePageProps) { +type RoomNamePageProps = RoomNamePageOnyxProps & + WithReportOrNotFoundProps & { + /** The room report for which the name is being edited */ + report: Report; + }; + +function RoomNamePage({report, policy, reports}: RoomNamePageProps) { const styles = useThemeStyles(); const roomNameInputRef = useRef(null); const isFocused = useIsFocused(); - const {translate} = useLocalize() + const {translate} = useLocalize(); const validate = useCallback( - (values: OnyxFormValuesFields) => { + (values: OnyxFormValuesFields) => { const errors = {}; // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method @@ -116,5 +117,5 @@ export default withReportOrNotFound()( policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, - })(RoomNamePage) + })(RoomNamePage), ); diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index f03415a2a56f..49accdff8e13 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -5,6 +5,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; @@ -13,22 +14,21 @@ import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import useLocalize from "@hooks/useLocalize"; -import type {Policy, Report} from "@src/types/onyx"; +import type {Policy, Report} from '@src/types/onyx'; type WriteCapabilityPageOnyxProps = { /** The policy object for the current route */ - policy: OnyxEntry; -}; - -type WriteCapabilityPageProps = WriteCapabilityPageOnyxProps & WithReportOrNotFoundProps & { - /** The report for which we are setting write capability */ - report: Report, + policy: OnyxEntry; }; +type WriteCapabilityPageProps = WriteCapabilityPageOnyxProps & + WithReportOrNotFoundProps & { + /** The report for which we are setting write capability */ + report: Report; + }; function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { - const {translate} = useLocalize() + const {translate} = useLocalize(); const writeCapabilityOptions = Object.values(CONST.REPORT.WRITE_CAPABILITIES).map((value) => ({ value, text: translate(`writeCapabilityPage.writeCapability.${value}`), diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index a003b8d87e87..223681de1521 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -151,5 +151,5 @@ export type { IKnowATeacherForm, IntroSchoolPrincipalForm, PrivateNotesForm, - RoomNameForm + RoomNameForm, }; From b0970b6357bd36ac23899ff00cc7e010130c86a5 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 30 Jan 2024 09:15:14 +0000 Subject: [PATCH 24/57] fix: remove params --- src/libs/PersonalDetailsUtils.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 945c0b581b16..2391874c83f3 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -203,10 +203,6 @@ function getEffectiveDisplayName(personalDetail?: PersonalDetails): string | und /** * Creates a new displayName for a user based on passed personal details or login. - * - * @param login - user's login - * @param passedPersonalDetails - details object with firstName and lastName - * @returns - The effective display name */ function createDisplayName(login: string, passedPersonalDetails: Pick | OnyxEntry): string { // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it @@ -229,11 +225,6 @@ function createDisplayName(login: string, passedPersonalDetails: Pick Date: Tue, 30 Jan 2024 14:53:17 +0100 Subject: [PATCH 25/57] remove unused popover ref callbacks --- src/components/PopoverProvider/index.tsx | 6 ------ src/components/PopoverProvider/types.ts | 2 -- 2 files changed, 8 deletions(-) diff --git a/src/components/PopoverProvider/index.tsx b/src/components/PopoverProvider/index.tsx index a738d1f9798a..69728d7be126 100644 --- a/src/components/PopoverProvider/index.tsx +++ b/src/components/PopoverProvider/index.tsx @@ -27,9 +27,6 @@ function PopoverContextProvider(props: PopoverContextProps) { } activePopoverRef.current.close(); - if (activePopoverRef.current.onCloseCallback) { - activePopoverRef.current.onCloseCallback(); - } activePopoverRef.current = null; setIsOpen(false); }, []); @@ -107,9 +104,6 @@ function PopoverContextProvider(props: PopoverContextProps) { closePopover(activePopoverRef.current.anchorRef); } activePopoverRef.current = popoverParams; - if (popoverParams?.onOpenCallback) { - popoverParams.onOpenCallback(); - } setIsOpen(true); }, [closePopover], diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index 49705d7ea7a8..2a366ae2a712 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -16,8 +16,6 @@ type AnchorRef = { ref: RefObject; close: (anchorRef?: RefObject) => void; anchorRef: RefObject; - onOpenCallback?: () => void; - onCloseCallback?: () => void; }; export type {PopoverContextProps, PopoverContextValue, AnchorRef}; From 414136a8d9e7299b3cc6217893bc98e37307f2a5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 31 Jan 2024 11:15:51 +0700 Subject: [PATCH 26/57] 34612 --- ...oraryForRefactorRequestConfirmationList.js | 47 +++++++++---------- .../iou/request/step/IOURequestStepTag.js | 4 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index bdd3fa98672f..98be34c41bf9 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -810,30 +810,29 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ rightLabel={canUseViolations && Boolean(policy.requiresCategory) ? translate('common.required') : ''} /> )} - {shouldShowTags || - (true && ( - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.CREATE, - iouType, - transaction.transactionID, - reportID, - Navigation.getActiveRouteWithoutParams(), - ), - ) - } - style={[styles.moneyRequestMenuItem]} - disabled={didConfirm} - interactive={!isReadOnly} - rightLabel={canUseViolations && Boolean(policy.requiresTag) ? translate('common.required') : ''} - /> - ))} + {shouldShowTags && ( + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction.transactionID, + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } + style={[styles.moneyRequestMenuItem]} + disabled={didConfirm} + interactive={!isReadOnly} + rightLabel={canUseViolations && lodashGet(policy, 'requiresTag', false) ? translate('common.required') : ''} + /> + )} {shouldShowTax && ( { Navigation.goBack(backTo || ROUTES.HOME); @@ -68,7 +68,7 @@ function IOURequestStepTag({ const updateTag = (selectedTag) => { const isSelectedTag = selectedTag.searchText === tag; const updatedTag = !isSelectedTag ? selectedTag.searchText : ''; - if (isEditting) { + if (isEditing) { IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag); Navigation.dismissModal(); return; From 17f3d4844c2db4ca8a52d8e8bc990be39aaba1e1 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 31 Jan 2024 14:21:08 +0700 Subject: [PATCH 27/57] fix lint --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 98be34c41bf9..122001861827 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -818,13 +818,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.CREATE, - iouType, - transaction.transactionID, - reportID, - Navigation.getActiveRouteWithoutParams(), - ), + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} From c78b434b5da6f1ba7e2f7f35d0a4967b2c9fb8fc Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 1 Feb 2024 11:32:12 +0700 Subject: [PATCH 28/57] refactor logic to update tag and implement for split flow --- .../MoneyRequestConfirmationList.js | 10 +++++++- ...oraryForRefactorRequestConfirmationList.js | 2 +- src/libs/actions/IOU.js | 24 +------------------ .../step/IOURequestStepParticipants.js | 2 +- .../iou/request/step/IOURequestStepTag.js | 14 ++++++----- .../MoneyRequestParticipantsPage.js | 1 - 6 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index afabb40fd9f4..7508943d4769 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -769,7 +769,15 @@ function MoneyRequestConfirmationList(props) { numberOfLinesTitle={2} onPress={() => { if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.TAG)); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( + CONST.IOU.ACTION.EDIT, + CONST.IOU.TYPE.SPLIT, + props.transaction.transactionID, + props.reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(props.iouType, props.reportID)); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 122001861827..8dc9d3e419c0 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -258,7 +258,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); + const shouldShowCategories = iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories)); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 5b503e4a5046..8c771149c5f3 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -234,17 +234,10 @@ function resetMoneyRequestCategory_temporaryForRefactor(transactionID) { * @param {String} transactionID * @param {String} tag */ -function setMoneyRequestTag_temporaryForRefactor(transactionID, tag) { +function setMoneyRequestTag(transactionID, tag) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag}); } -/* - * @param {String} transactionID - */ -function resetMoneyRequestTag_temporaryForRefactor(transactionID) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag: null}); -} - /** * @param {String} transactionID * @param {Boolean} billable @@ -3635,17 +3628,6 @@ function resetMoneyRequestCategory() { Onyx.merge(ONYXKEYS.IOU, {category: ''}); } -/* - * @param {String} tag - */ -function setMoneyRequestTag(tag) { - Onyx.merge(ONYXKEYS.IOU, {tag}); -} - -function resetMoneyRequestTag() { - Onyx.merge(ONYXKEYS.IOU, {tag: ''}); -} - /** * @param {String} transactionID * @param {Object} taxRate @@ -3726,7 +3708,6 @@ function navigateToNextPage(iou, iouType, report, path = '') { .value(); setMoneyRequestParticipants(participants); resetMoneyRequestCategory(); - resetMoneyRequestTag(); } Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); return; @@ -3795,8 +3776,6 @@ export { resetMoneyRequestCategory, resetMoneyRequestCategory_temporaryForRefactor, resetMoneyRequestInfo, - resetMoneyRequestTag, - resetMoneyRequestTag_temporaryForRefactor, clearMoneyRequest, setMoneyRequestAmount_temporaryForRefactor, setMoneyRequestBillable_temporaryForRefactor, @@ -3807,7 +3786,6 @@ export { setMoneyRequestMerchant_temporaryForRefactor, setMoneyRequestParticipants_temporaryForRefactor, setMoneyRequestReceipt, - setMoneyRequestTag_temporaryForRefactor, setMoneyRequestAmount, setMoneyRequestBillable, setMoneyRequestCategory, diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 0d1177e231c4..5f1b22cab128 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -70,7 +70,7 @@ function IOURequestStepParticipants({ const goToNextStep = useCallback(() => { const nextStepIOUType = numberOfParticipants.current === 1 ? iouType : CONST.IOU.TYPE.SPLIT; - IOU.resetMoneyRequestTag_temporaryForRefactor(transactionID); + IOU.setMoneyRequestTag(transactionID, ''); IOU.resetMoneyRequestCategory_temporaryForRefactor(transactionID); Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(nextStepIOUType, transactionID, selectedReportID.current || reportID)); }, [iouType, transactionID, reportID]); diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js index 31dadf624f61..e4390c5abbde 100644 --- a/src/pages/iou/request/step/IOURequestStepTag.js +++ b/src/pages/iou/request/step/IOURequestStepTag.js @@ -45,7 +45,7 @@ function IOURequestStepTag({ policyTags, report, route: { - params: {action, transactionID, backTo}, + params: {action, transactionID, backTo, iouType}, }, transaction: {tag}, }) { @@ -56,6 +56,7 @@ function IOURequestStepTag({ const tagListKey = _.first(_.keys(policyTags)); const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); const isEditing = action === CONST.IOU.ACTION.EDIT; + const isBillSplit = iouType === CONST.IOU.TYPE.SPLIT; const navigateBack = () => { Navigation.goBack(backTo || ROUTES.HOME); @@ -68,16 +69,17 @@ function IOURequestStepTag({ const updateTag = (selectedTag) => { const isSelectedTag = selectedTag.searchText === tag; const updatedTag = !isSelectedTag ? selectedTag.searchText : ''; + if (isBillSplit) { + IOU.setDraftSplitTransaction(transactionID, {tag: selectedTag.searchText}); + navigateBack(); + return; + } if (isEditing) { IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag); Navigation.dismissModal(); return; } - if (isSelectedTag) { - IOU.resetMoneyRequestTag_temporaryForRefactor(transactionID); - } else { - IOU.setMoneyRequestTag_temporaryForRefactor(transactionID, updatedTag); - } + IOU.setMoneyRequestTag(transactionID, updatedTag); navigateBack(); }; diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 216154be9cd4..ea57d88579ae 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -88,7 +88,6 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { const navigateToConfirmationStep = (moneyRequestType) => { IOU.setMoneyRequestId(moneyRequestType); IOU.resetMoneyRequestCategory(); - IOU.resetMoneyRequestTag(); Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID)); }; From 17f25fc09548849e11291aef361fcdfa8482d66f Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Thu, 1 Feb 2024 17:47:11 +0400 Subject: [PATCH 29/57] Addressed to reviewer comments --- src/ONYXKEYS.ts | 2 +- src/languages/es.ts | 2 +- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportUtils.ts | 6 +++--- src/libs/actions/Report.ts | 4 ++-- src/pages/home/report/withReportOrNotFound.tsx | 8 ++++---- src/types/onyx/Form.ts | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c4f66138cffc..ab5ae384a5be 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -491,7 +491,7 @@ type OnyxValues = { [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: OnyxTypes.DisplayNameForm; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM_DRAFT]: OnyxTypes.DisplayNameForm; [ONYXKEYS.FORMS.ROOM_NAME_FORM]: OnyxTypes.RoomNameForm; - [ONYXKEYS.FORMS.ROOM_NAME_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.ROOM_NAME_FORM_DRAFT]: OnyxTypes.RoomNameForm; [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.LEGAL_NAME_FORM]: OnyxTypes.Form; diff --git a/src/languages/es.ts b/src/languages/es.ts index f99765a66673..52e49e75fdd4 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -668,7 +668,7 @@ export default { always: 'Inmediatamente', daily: 'Cada día', mute: 'Nunca', - hidden: 'Oculta', + hidden: 'Oculto', }, }, loginField: { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 0d406bd78bd9..2f0a6367041d 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -110,7 +110,7 @@ function isExpensifyGuideTeam(email: string): boolean { /** * Checks if the current user is an admin of the policy. */ -const isPolicyAdmin = (policy?: OnyxEntry): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; +const isPolicyAdmin = (policy: OnyxEntry | undefined): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; const isPolicyMember = (policyID: string, policies: Record): boolean => Object.values(policies).some((policy) => policy?.id === policyID); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 40842492eb0a..feb3a1baf3c4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4199,7 +4199,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array, policy?: OnyxEntry): boolean { +function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry | undefined): boolean { if (isDefaultRoom(report) || isArchivedRoom(report) || isThread(report) || isMoneyRequestReport(report) || isPolicyExpenseChat(report)) { return true; } @@ -4218,7 +4218,7 @@ function shouldDisableRename(report: OnyxEntry, policy?: OnyxEntry, policy?: OnyxEntry): boolean { +function canEditWriteCapability(report: OnyxEntry, policy: OnyxEntry | undefined): boolean { return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report) && !isThread(report); } @@ -4506,7 +4506,7 @@ function getRoom(type: ValueOf, policyID: string) /** * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. */ -function shouldDisableWelcomeMessage(report: OnyxEntry, policy?: OnyxEntry): boolean { +function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntry | undefined): boolean { return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } /** diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f2744f8a8269..72fc73fc1d5a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1673,7 +1673,7 @@ function navigateToConciergeChatAndDeleteReport(reportID: string) { /** * @param policyRoomName The updated name for the policy room */ -function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName?: string) { +function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string | undefined) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; @@ -1719,7 +1719,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomNam }, ]; - const parameters: UpdatePolicyRoomNameParams = {reportID, policyRoomName}; + const parameters: UpdatePolicyRoomNameParams = {reportID, policyRoomName: policyRoomName ?? ''}; API.write(WRITE_COMMANDS.UPDATE_POLICY_ROOM_NAME, parameters, {optimisticData, successData, failureData}); Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index a8facc3e1c76..385280c9d2c2 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -11,7 +11,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; -type OnyxProps = { +type WithReportOrNotFoundOnyxProps = { /** The report currently being looked at */ report: OnyxEntry; /** The policies which the user has access to */ @@ -22,7 +22,7 @@ type OnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithReportOrNotFoundProps = OnyxProps & { +type WithReportOrNotFoundProps = WithReportOrNotFoundOnyxProps & { route: RouteProp<{params: {reportID: string}}>; }; @@ -30,7 +30,7 @@ export default function ( shouldRequireReportID = true, ): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof OnyxProps>> { +) => React.ComponentType, keyof WithReportOrNotFoundOnyxProps>> { return function (WrappedComponent: ComponentType>) { function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const contentShown = React.useRef(false); @@ -73,7 +73,7 @@ export default function ( WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - return withOnyx, OnyxProps>({ + return withOnyx, WithReportOrNotFoundOnyxProps>({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, }, diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 142984d7f4b1..6aeeeb72e3a8 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -55,7 +55,7 @@ type PrivateNotesForm = Form<{ }>; type RoomNameForm = Form<{ - roomName?: string; + roomName: string; }>; export default Form; From 7495142498a18e70e8b21add51272229d7c72964 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Thu, 1 Feb 2024 19:44:00 +0400 Subject: [PATCH 30/57] Addressed to reviewer comments --- src/libs/ErrorUtils.ts | 2 +- .../Report/NotificationPreferencePage.tsx | 14 ++++--- .../settings/Report/ReportSettingsPage.tsx | 40 +++++++++---------- src/pages/settings/Report/RoomNamePage.tsx | 22 +++++----- .../settings/Report/WriteCapabilityPage.tsx | 20 +++++----- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 8eff66c10751..f0f94839de21 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -101,7 +101,7 @@ type ErrorsList = Record; * @param errors - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey | [TKey, Record]) { +function addErrorMessage(errors: ErrorsList, inputID?: string, message?: TKey | Localize.MaybePhraseKey) { if (!message || !inputID) { return; } diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx index c5fdaa25fac5..30b9ffa3c2d2 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.tsx +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -1,3 +1,4 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -5,15 +6,14 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import useLocalize from '@hooks/useLocalize'; import * as ReportUtils from '@libs/ReportUtils'; +import type {ReportSettingsNavigatorParamList} from '@navigation/types'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; -import type {Report} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; -type NotificationPreferencePageProps = WithReportOrNotFoundProps & { - report: Report; -}; +type NotificationPreferencePageProps = WithReportOrNotFoundProps & StackScreenProps; function NotificationPreferencePage({report}: NotificationPreferencePageProps) { const {translate} = useLocalize(); @@ -24,7 +24,7 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { value: preference, text: translate(`notificationPreferencesPage.notificationPreferences.${preference}`), keyForList: preference, - isSelected: preference === report.notificationPreference, + isSelected: preference === report?.notificationPreference, })); return ( @@ -39,7 +39,9 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { /> ReportActions.updateNotificationPreference(report.reportID, report.notificationPreference, option.value, true, undefined, undefined, report)} + onSelectRow={(option) => + ReportActions.updateNotificationPreference(report?.reportID ?? '', report?.notificationPreference, option.value, true, undefined, undefined, report) + } initiallyFocusedOptionKey={Object.values(notificationPreferenceOptions ?? {}).find((locale) => locale.isSelected)?.keyForList} /> diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index d6f3a30c9d63..790f61c0503e 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -1,3 +1,4 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import isEmpty from 'lodash/isEmpty'; import React, {useMemo} from 'react'; import {ScrollView, View} from 'react-native'; @@ -15,22 +16,21 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {getGroupChatName} from '@libs/GroupChatUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; +import type {ReportSettingsNavigatorParamList} from '@navigation/types'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import type {Report} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; -type ReportSettingsPageProps = WithReportOrNotFoundProps & { - report: Report; -}; +type ReportSettingsPageProps = WithReportOrNotFoundProps & StackScreenProps; function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); // The workspace the report is on, null if the user isn't a member of the workspace - const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report.policyID), [policies, report.policyID]); + const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report?.policyID), [policies, report?.policyID]); const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -40,15 +40,15 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const shouldDisableSettings = isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); const notificationPreference = - report.notificationPreference && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN + report?.notificationPreference && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN ? translate(`notificationPreferencesPage.notificationPreferences.${report.notificationPreference}`) : ''; - const writeCapability = ReportUtils.isAdminRoom(report) ? CONST.REPORT.WRITE_CAPABILITIES.ADMINS : report.writeCapability ?? CONST.REPORT.WRITE_CAPABILITIES.ALL; + 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 = useMemo(() => ReportUtils.canEditWriteCapability(report, linkedWorkspace), [report, linkedWorkspace]); - const shouldShowNotificationPref = !isMoneyRequestReport && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; + const shouldShowNotificationPref = !isMoneyRequestReport && report?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const roomNameLabel = translate(isMoneyRequestReport ? 'workspace.editor.nameInputLabel' : 'newRoomPage.roomName'); const reportName = ReportUtils.isGroupChat(report) ? getGroupChatName(report) : ReportUtils.getReportName(report); @@ -59,7 +59,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? ''))} /> {shouldShowNotificationPref && ( @@ -67,15 +67,15 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={notificationPreference} description={translate('notificationPreferencesPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report.reportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report?.reportID ?? ''))} /> )} {shouldShowRoomName && ( ReportActions.clearPolicyRoomNameErrors(report.reportID)} + onClose={() => ReportActions.clearPolicyRoomNameErrors(report?.reportID ?? '')} > {shouldDisableRename ? ( @@ -96,9 +96,9 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { ) : ( Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(report.reportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(report?.reportID ?? ''))} /> )} @@ -109,7 +109,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={writeCapabilityText} description={translate('writeCapabilityPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(report.reportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(report?.reportID ?? ''))} /> ) : ( @@ -145,7 +145,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { /> )} - {Boolean(report.visibility) && ( + {Boolean(report?.visibility) && ( - {report.visibility && translate(`newRoomPage.visibilityOptions.${report.visibility}`)} + {report?.visibility && translate(`newRoomPage.visibilityOptions.${report.visibility}`)} - {report.visibility && translate(`newRoomPage.${report.visibility}Description`)} + {report?.visibility && translate(`newRoomPage.${report.visibility}Description`)} )} @@ -167,7 +167,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(report.reportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(report?.reportID ?? ''))} shouldShowRightIcon /> )} diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 2e4a91e7f861..759147f95d95 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -1,4 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; +import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -17,13 +18,16 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; +import type {ReportSettingsNavigatorParamList} from '@navigation/types'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import type {Policy, Report} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type RoomNamePageOnyxProps = { /** All reports shared with the user */ @@ -33,11 +37,7 @@ type RoomNamePageOnyxProps = { policy: OnyxEntry; }; -type RoomNamePageProps = RoomNamePageOnyxProps & - WithReportOrNotFoundProps & { - /** The room report for which the name is being edited */ - report: Report; - }; +type RoomNamePageProps = RoomNamePageOnyxProps & WithReportOrNotFoundProps & StackScreenProps; function RoomNamePage({report, policy, reports}: RoomNamePageProps) { const styles = useThemeStyles(); @@ -50,7 +50,7 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { const errors = {}; // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method - if (values.roomName === report.reportName) { + if (values.roomName === report?.reportName) { return errors; } @@ -63,7 +63,7 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); - } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report.policyID)) { + } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report?.policyID)) { // The room name can't be set to one that already exists on the policy ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); } @@ -82,12 +82,12 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? ''))} /> ReportActions.updatePolicyRoomNameAndNavigate(report, values.roomName)} + onSubmit={(values) => !isEmptyObject(report) && ReportActions.updatePolicyRoomNameAndNavigate(report, values.roomName)} validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline @@ -97,7 +97,7 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { InputComponent={RoomNameInput} ref={roomNameInputRef} inputID="roomName" - defaultValue={report.reportName} + defaultValue={report?.reportName} isFocused={isFocused} /> @@ -115,7 +115,7 @@ export default withReportOrNotFound()( key: ONYXKEYS.COLLECTION.REPORT, }, policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, }, })(RoomNamePage), ); diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index 49accdff8e13..7af951d685d4 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -1,3 +1,4 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -8,13 +9,16 @@ import SelectionList from '@components/SelectionList'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; +import type {ReportSettingsNavigatorParamList} from '@navigation/types'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import type {Policy} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WriteCapabilityPageOnyxProps = { /** The policy object for the current route */ @@ -22,10 +26,8 @@ type WriteCapabilityPageOnyxProps = { }; type WriteCapabilityPageProps = WriteCapabilityPageOnyxProps & - WithReportOrNotFoundProps & { - /** The report for which we are setting write capability */ - report: Report; - }; + WithReportOrNotFoundProps & + StackScreenProps; function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { const {translate} = useLocalize(); @@ -33,7 +35,7 @@ function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { value, text: translate(`writeCapabilityPage.writeCapability.${value}`), keyForList: value, - isSelected: value === (report.writeCapability ?? CONST.REPORT.WRITE_CAPABILITIES.ALL), + isSelected: value === (report?.writeCapability ?? CONST.REPORT.WRITE_CAPABILITIES.ALL), })); const isAbleToEdit = ReportUtils.canEditWriteCapability(report, policy); @@ -47,11 +49,11 @@ function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? ''))} /> ReportActions.updateWriteCapabilityAndNavigate(report, option.value)} + onSelectRow={(option) => !isEmptyObject(report) && ReportActions.updateWriteCapabilityAndNavigate(report, option.value)} initiallyFocusedOptionKey={Object.values(writeCapabilityOptions).find((locale) => locale.isSelected)?.keyForList} /> @@ -64,7 +66,7 @@ WriteCapabilityPage.displayName = 'WriteCapabilityPage'; export default withReportOrNotFound()( withOnyx({ policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, }, })(WriteCapabilityPage), ); From e2c935ce0b148c01b4b72c1c50599a3505309c71 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Thu, 1 Feb 2024 19:55:48 +0400 Subject: [PATCH 31/57] run prettier --- src/types/onyx/Form.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 905270427ac8..fd0af2e5139f 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -77,5 +77,5 @@ export type { IntroSchoolPrincipalForm, PersonalBankAccountForm, ReportFieldEditForm, - RoomNameForm + RoomNameForm, }; From ba70fc913ea282749ad6d6b831f348ab39a4bb26 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Thu, 1 Feb 2024 20:18:23 +0400 Subject: [PATCH 32/57] Addressed to reviewer comments --- src/pages/settings/Report/ReportSettingsPage.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 790f61c0503e..3dd096c96a7c 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -27,6 +27,7 @@ import type SCREENS from '@src/SCREENS'; type ReportSettingsPageProps = WithReportOrNotFoundProps & StackScreenProps; function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { + const reportID = report?.reportID ?? ''; const styles = useThemeStyles(); const {translate} = useLocalize(); // The workspace the report is on, null if the user isn't a member of the workspace @@ -59,7 +60,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? ''))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID ?? ''))} /> {shouldShowNotificationPref && ( @@ -67,7 +68,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={notificationPreference} description={translate('notificationPreferencesPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report?.reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(reportID ?? ''))} /> )} {shouldShowRoomName && ( @@ -75,7 +76,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { pendingAction={report?.pendingFields?.reportName} errors={report?.errorFields?.reportName} errorRowStyles={[styles.ph5]} - onClose={() => ReportActions.clearPolicyRoomNameErrors(report?.reportID ?? '')} + onClose={() => ReportActions.clearPolicyRoomNameErrors(reportID ?? '')} > {shouldDisableRename ? ( @@ -98,7 +99,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={report?.reportName} description={translate('newRoomPage.roomName')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(report?.reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(reportID ?? ''))} /> )} @@ -109,7 +110,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={writeCapabilityText} description={translate('writeCapabilityPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(report?.reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(reportID ?? ''))} /> ) : ( @@ -167,7 +168,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(report?.reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(reportID ?? ''))} shouldShowRightIcon /> )} From d07a1d54323ef1ca78e47b8f0eebf96eae0ebc9e Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Thu, 1 Feb 2024 21:20:14 +0400 Subject: [PATCH 33/57] Addressed to reviewer comments --- src/libs/ReportUtils.ts | 6 +++--- src/libs/ValidationUtils.ts | 2 +- src/libs/actions/Report.ts | 2 +- src/pages/settings/Report/ReportSettingsPage.tsx | 2 +- src/pages/settings/Report/RoomNamePage.tsx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index acbe2472fea5..3d21911c83e8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4269,7 +4269,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array, policy: OnyxEntry | undefined): boolean { +function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry): boolean { if (isDefaultRoom(report) || isArchivedRoom(report) || isThread(report) || isMoneyRequestReport(report) || isPolicyExpenseChat(report)) { return true; } @@ -4287,7 +4287,7 @@ function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry, policy: OnyxEntry | undefined): boolean { +function canEditWriteCapability(report: OnyxEntry, policy: OnyxEntry): boolean { return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report) && !isThread(report); } @@ -4575,7 +4575,7 @@ function getRoom(type: ValueOf, policyID: string) /** * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. */ -function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntry | undefined): boolean { +function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntry): boolean { return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } /** diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index af9e394e0b7f..132fed6d1ada 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -355,7 +355,7 @@ function isReservedRoomName(roomName: string): boolean { /** * Checks if the room name already exists. */ -function isExistingRoomName(roomName: string, reports: OnyxCollection, policyID?: string): boolean { +function isExistingRoomName(roomName: string, reports: OnyxCollection, policyID: string): boolean { return Object.values(reports ?? {}).some((report) => report && report.policyID === policyID && report.reportName === roomName); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 2eedd090b90c..3d375ec26413 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1806,7 +1806,7 @@ function navigateToConciergeChatAndDeleteReport(reportID: string) { /** * @param policyRoomName The updated name for the policy room */ -function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string | undefined) { +function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 3dd096c96a7c..28d189359860 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -31,7 +31,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); // The workspace the report is on, null if the user isn't a member of the workspace - const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report?.policyID), [policies, report?.policyID]); + const linkedWorkspace = useMemo(() => Object.values(policies ?? {}).find((policy) => policy && policy.id === report?.policyID) ?? null, [policies, report?.policyID]); const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 759147f95d95..6b2fd751f01f 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -63,7 +63,7 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); - } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report?.policyID)) { + } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report?.policyID ?? '')) { // The room name can't be set to one that already exists on the policy ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); } From cf691f46881595605e8bef0a86bbd91459037928 Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Thu, 1 Feb 2024 18:20:46 +0100 Subject: [PATCH 34/57] Revert "[CP Staging] Revert "[NO QA] Remove areChatRoomsEnabled checks"" --- src/libs/PolicyUtils.ts | 5 +---- src/types/onyx/Policy.ts | 3 --- tests/utils/LHNTestUtils.js | 1 - tests/utils/collections/policies.ts | 1 - 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index b8ed62f93082..5a8fe907d479 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -15,10 +15,7 @@ type UnitRate = {rate: number}; * These are policies that we can use to create reports with in NewDot. */ function getActivePolicies(policies: OnyxCollection): Policy[] | undefined { - return Object.values(policies ?? {}).filter( - (policy): policy is Policy => - policy !== null && policy && (policy.isPolicyExpenseChatEnabled || policy.areChatRoomsEnabled) && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - ); + return Object.values(policies ?? {}).filter((policy): policy is Policy => policy !== null && policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); } /** diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index fe50bbb497d2..39c5f5e9c78e 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -76,9 +76,6 @@ type Policy = { /** The custom units data for this policy */ customUnits?: Record; - /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ - areChatRoomsEnabled: boolean; - /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 04246c1c438a..3e40063dd040 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -252,7 +252,6 @@ function getFakePolicy(id = 1, name = 'Workspace-Test-001') { avatar: '', employeeList: [], isPolicyExpenseChatEnabled: true, - areChatRoomsEnabled: true, lastModified: 1697323926777105, autoReporting: true, autoReportingFrequency: 'immediate', diff --git a/tests/utils/collections/policies.ts b/tests/utils/collections/policies.ts index 4223c7e41941..7ecf152122d3 100644 --- a/tests/utils/collections/policies.ts +++ b/tests/utils/collections/policies.ts @@ -7,7 +7,6 @@ export default function createRandomPolicy(index: number): Policy { id: index.toString(), name: randWord(), type: rand(Object.values(CONST.POLICY.TYPE)), - areChatRoomsEnabled: randBoolean(), autoReporting: randBoolean(), isPolicyExpenseChatEnabled: randBoolean(), autoReportingFrequency: rand(Object.values(CONST.POLICY.AUTO_REPORTING_FREQUENCIES)), From db8de4dfe3fb2ab5f8ccb7ff4750c7c7862b35a1 Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Thu, 1 Feb 2024 14:22:55 -0300 Subject: [PATCH 35/57] Remove more usages --- src/libs/actions/Policy.ts | 3 --- src/libs/actions/TeachersUnite.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index fbe92aeb378d..dfc5f4c705b8 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1160,7 +1160,6 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol isPolicyExpenseChatEnabled: true, outputCurrency, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - areChatRoomsEnabled: true, customUnits, makeMeAdmin, }, @@ -1221,7 +1220,6 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName isPolicyExpenseChatEnabled: true, outputCurrency, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - areChatRoomsEnabled: true, customUnits, }, }, @@ -1600,7 +1598,6 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { // Setting the currency to USD as we can only add the VBBA for this policy currency right now outputCurrency: CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - areChatRoomsEnabled: true, customUnits, }; diff --git a/src/libs/actions/TeachersUnite.ts b/src/libs/actions/TeachersUnite.ts index 055d1f2b53a2..ab48609e2d53 100644 --- a/src/libs/actions/TeachersUnite.ts +++ b/src/libs/actions/TeachersUnite.ts @@ -91,7 +91,6 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName: value: { id: policyID, isPolicyExpenseChatEnabled: true, - areChatRoomsEnabled: true, type: CONST.POLICY.TYPE.CORPORATE, name: policyName, role: CONST.POLICY.ROLE.USER, From d4f2e9f75d0dcfd5d2b2c359837f1adbe1a26dca Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 2 Feb 2024 04:25:28 +0530 Subject: [PATCH 36/57] fixes crash on long press expensify classic option --- src/pages/settings/InitialSettingsPage.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index dfa4262549fc..3997fe0bec75 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -171,7 +171,7 @@ function InitialSettingsPage(props) { action: () => { Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); }, - link: Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), + link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), }, { translationKey: 'initialSettingsPage.signOut', @@ -225,6 +225,15 @@ function InitialSettingsPage(props) { * @returns {String|undefined} the user's wallet balance */ const getWalletBalance = (isPaymentItem) => (isPaymentItem ? CurrencyUtils.convertToDisplayString(props.userWallet.currentBalance) : undefined); + + const onSecondaryInteraction = (link, event) => { + if (typeof link === 'function') { + link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); + } else if (link) { + ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); + } + }; + return ( {translate(menuItemsData.sectionTranslationKey)} @@ -259,9 +268,7 @@ function InitialSettingsPage(props) { ref={popoverAnchor} hoverAndPressStyle={styles.hoveredComponentBG} shouldBlockSelection={Boolean(item.link)} - onSecondaryInteraction={ - !_.isEmpty(item.link) ? (e) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, item.link, popoverAnchor.current) : undefined - } + onSecondaryInteraction={item.link ? (event) => onSecondaryInteraction(item.link, event) : undefined} focused={activeRoute && item.routeName && activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')} isPaneMenu /> From ff3aee9ce7f6b38b94ebb97277da3f4a0e048ef8 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 2 Feb 2024 04:43:20 +0530 Subject: [PATCH 37/57] fixes variable naming --- src/pages/settings/InitialSettingsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 3997fe0bec75..8dfce10fc081 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -226,7 +226,7 @@ function InitialSettingsPage(props) { */ const getWalletBalance = (isPaymentItem) => (isPaymentItem ? CurrencyUtils.convertToDisplayString(props.userWallet.currentBalance) : undefined); - const onSecondaryInteraction = (link, event) => { + const openPopover = (link, event) => { if (typeof link === 'function') { link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); } else if (link) { @@ -268,7 +268,7 @@ function InitialSettingsPage(props) { ref={popoverAnchor} hoverAndPressStyle={styles.hoveredComponentBG} shouldBlockSelection={Boolean(item.link)} - onSecondaryInteraction={item.link ? (event) => onSecondaryInteraction(item.link, event) : undefined} + onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} focused={activeRoute && item.routeName && activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')} isPaneMenu /> From db641ff82f3a3051d1666773235fd8c6fc3f0095 Mon Sep 17 00:00:00 2001 From: Hayata Suenaga Date: Thu, 1 Feb 2024 18:25:26 -0800 Subject: [PATCH 38/57] fix typo --- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 9b763120b30d..2ff71af530f2 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -340,7 +340,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r subtitle={translate('workspace.emptyWorkspace.subtitle')} ctaText={translate('workspace.new.newWorkspace')} ctaAccessibilityLabel={translate('workspace.new.newWorkspace')} - onCtaPress={() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()} + onCTAPress={() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()} // @ts-expect-error TODO: Remove once FeatureList (https://github.com/Expensify/App/issues/25039) is migrated to TS illustration={LottieAnimations.WorkspacePlanet} illustrationBackgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.WORKSPACES].backgroundColor} From 53e3ae8bc685cf29314872422732bada5f2e17bb Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 2 Feb 2024 10:28:33 +0700 Subject: [PATCH 39/57] revert hard code and rename variable --- ...oraryForRefactorRequestConfirmationList.js | 2 +- .../iou/request/step/IOURequestStepTag.js | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 8dc9d3e419c0..122001861827 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -258,7 +258,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories)); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js index e4390c5abbde..1297b98a0814 100644 --- a/src/pages/iou/request/step/IOURequestStepTag.js +++ b/src/pages/iou/request/step/IOURequestStepTag.js @@ -56,7 +56,7 @@ function IOURequestStepTag({ const tagListKey = _.first(_.keys(policyTags)); const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); const isEditing = action === CONST.IOU.ACTION.EDIT; - const isBillSplit = iouType === CONST.IOU.TYPE.SPLIT; + const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const navigateBack = () => { Navigation.goBack(backTo || ROUTES.HOME); @@ -69,7 +69,7 @@ function IOURequestStepTag({ const updateTag = (selectedTag) => { const isSelectedTag = selectedTag.searchText === tag; const updatedTag = !isSelectedTag ? selectedTag.searchText : ''; - if (isBillSplit) { + if (isSplitBill) { IOU.setDraftSplitTransaction(transactionID, {tag: selectedTag.searchText}); navigateBack(); return; @@ -90,13 +90,18 @@ function IOURequestStepTag({ shouldShowWrapper testID={IOURequestStepTag.displayName} > - {translate('iou.tagSelection', {tagName: policyTagListName})} - + {({insets}) => ( + <> + {translate('iou.tagSelection', {tagName: policyTagListName})} + + + )} ); } From 8e0f7733317986e8dad4ae1771a5c8a730e5d1b5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 2 Feb 2024 16:09:17 +0700 Subject: [PATCH 40/57] fix extra padding of back button and tooltip --- src/components/HeaderWithBackButton/index.tsx | 4 ++-- src/components/ReportHeaderSkeletonView.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 078cb37c7e0d..64fe512eaff2 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -70,7 +70,7 @@ function HeaderWithBackButton({ // Hover on some part of close icons will not work on Electron if dragArea is true // https://github.com/Expensify/App/issues/29598 dataSet={{dragArea: false}} - style={[styles.headerBar, shouldShowBorderBottom && styles.borderBottom, shouldShowBackButton ? styles.pl0 : styles.pl5, shouldOverlay && StyleSheet.absoluteFillObject]} + style={[styles.headerBar, shouldShowBorderBottom && styles.borderBottom, shouldShowBackButton && styles.pl2, shouldOverlay && StyleSheet.absoluteFillObject]} > {shouldShowBackButton && ( @@ -87,7 +87,7 @@ function HeaderWithBackButton({ onBackButtonPress(); } }} - style={[styles.LHNToggle]} + style={[styles.touchableButtonImage]} role="button" accessibilityLabel={translate('common.back')} nativeID={CONST.BACK_BUTTON_NATIVE_ID} diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx index 2113abd85e88..5332e0c5032c 100644 --- a/src/components/ReportHeaderSkeletonView.tsx +++ b/src/components/ReportHeaderSkeletonView.tsx @@ -24,12 +24,12 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = () const {isSmallScreenWidth} = useWindowDimensions(); return ( - - + + {isSmallScreenWidth && ( From 0b2f0e6e1b0b73a9c3e649f086aae7afdfc06164 Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Fri, 2 Feb 2024 14:17:17 +0400 Subject: [PATCH 41/57] Addressed to reviewer comments --- src/libs/API/parameters/UpdatePolicyRoomNameParams.ts | 2 +- src/libs/actions/Report.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/API/parameters/UpdatePolicyRoomNameParams.ts b/src/libs/API/parameters/UpdatePolicyRoomNameParams.ts index 74f936cbbb46..65b858b7c20f 100644 --- a/src/libs/API/parameters/UpdatePolicyRoomNameParams.ts +++ b/src/libs/API/parameters/UpdatePolicyRoomNameParams.ts @@ -1,6 +1,6 @@ type UpdatePolicyRoomNameParams = { reportID: string; - policyRoomName?: string; + policyRoomName: string; }; export default UpdatePolicyRoomNameParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3d375ec26413..221c7f8d4f88 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1852,7 +1852,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomNam }, ]; - const parameters: UpdatePolicyRoomNameParams = {reportID, policyRoomName: policyRoomName ?? ''}; + const parameters: UpdatePolicyRoomNameParams = {reportID, policyRoomName}; API.write(WRITE_COMMANDS.UPDATE_POLICY_ROOM_NAME, parameters, {optimisticData, successData, failureData}); Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); From 1a4717aa879275223a0ff82630ef232bb5f424a7 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 2 Feb 2024 15:10:49 +0100 Subject: [PATCH 42/57] Fix type error in linkTo --- src/libs/Navigation/linkTo.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 3767e1ea010c..49dcee71eda4 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -216,16 +216,18 @@ export default function linkTo(navigation: NavigationContainerRef Date: Fri, 2 Feb 2024 12:10:42 -0300 Subject: [PATCH 43/57] Filter out personal policies in room page --- src/pages/workspace/WorkspaceNewRoomPage.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index b616b519ff32..fa128b2bfce6 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -106,11 +106,14 @@ function WorkspaceNewRoomPage(props) { const workspaceOptions = useMemo( () => - _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({ - label: policy.name, - key: policy.id, - value: policy.id, - })), + _.map( + _.filter(PolicyUtils.getActivePolicies(props.policies), (policy) => policy.type !== CONST.POLICY.TYPE.PERSONAL), + (policy) => ({ + label: policy.name, + key: policy.id, + value: policy.id, + }), + ), [props.policies], ); const [policyID, setPolicyID] = useState(() => { From 2bf606f75475c0d07b0634ffc3a452dde69a4ac8 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 3 Feb 2024 09:24:10 +0530 Subject: [PATCH 44/57] added comment --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 84adf87bbae2..c177e4dfd585 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -328,6 +328,8 @@ function IOURequestStepConfirmation({ IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, billable); }; + // This loading indicator is shown because the transaction originalCurrency is being updated later than the component mounts. + // To prevent the component from rendering with the wrong currency, we show a loading indicator until the correct currency is set. const isLoading = !!(transaction && transaction.originalCurrency); return ( From 25207b4bde089e2ea61ed47b23d242ada76f97f0 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 3 Feb 2024 19:22:44 +0300 Subject: [PATCH 45/57] rm extra ul --- .../getting-started/Join-your-company's-workspace.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/getting-started/Join-your-company's-workspace.md b/docs/articles/expensify-classic/getting-started/Join-your-company's-workspace.md index 099f381e6010..a31b972e683c 100644 --- a/docs/articles/expensify-classic/getting-started/Join-your-company's-workspace.md +++ b/docs/articles/expensify-classic/getting-started/Join-your-company's-workspace.md @@ -212,7 +212,6 @@ Once you’ve created your expenses, they may be automatically added to an expen
  • Attach PDF: Select this checkbox to attach a copy of your report to the email.
  • Tap Submit.
  • - {% include end-option.html %} @@ -255,4 +254,4 @@ Add an extra layer of security to help keep your financial data safe and secure When you log in to Expensify in the future, you’ll open your authenticator app to get the code and enter it into Expensify. A new code regenerates every few seconds, so the code is always different. If the code time runs out, you can generate a new code as needed. - \ No newline at end of file + From 53dcfc1c0f8e38f60203a56e39f3278ba7b3d34c Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Sat, 3 Feb 2024 21:28:21 +0100 Subject: [PATCH 46/57] fix: remove empty line --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index af6c9372f27a..c3de0f9f749f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4657,7 +4657,6 @@ function hasUpdatedTotal(report: OnyxEntry): boolean { } const transactions = TransactionUtils.getAllReportTransactions(report.reportID); - const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); const hasTransactionWithDifferentCurrency = transactions.some((transaction) => transaction.currency !== report.currency); From b742d6642e2c94d6accdbaf9322fd8e6bc77ff4a Mon Sep 17 00:00:00 2001 From: Shahe Shahinyan Date: Sun, 4 Feb 2024 14:23:24 +0400 Subject: [PATCH 47/57] Addressed to reviewer comments --- src/libs/PolicyUtils.ts | 2 +- .../Report/NotificationPreferencePage.tsx | 4 +-- .../settings/Report/ReportSettingsPage.tsx | 26 +++++++++---------- src/pages/settings/Report/RoomNamePage.tsx | 3 +-- .../settings/Report/WriteCapabilityPage.tsx | 5 ++-- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 426071a81894..b6ee4ab3a353 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -107,7 +107,7 @@ function isExpensifyGuideTeam(email: string): boolean { /** * Checks if the current user is an admin of the policy. */ -const isPolicyAdmin = (policy: OnyxEntry | undefined): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; +const isPolicyAdmin = (policy: OnyxEntry): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; const isPolicyMember = (policyID: string, policies: Record): boolean => Object.values(policies).some((policy) => policy?.id === policyID); diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx index 30b9ffa3c2d2..05f3483f7ce8 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.tsx +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -40,9 +40,9 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { - ReportActions.updateNotificationPreference(report?.reportID ?? '', report?.notificationPreference, option.value, true, undefined, undefined, report) + report && ReportActions.updateNotificationPreference(report.reportID, report.notificationPreference, option.value, true, undefined, undefined, report) } - initiallyFocusedOptionKey={Object.values(notificationPreferenceOptions ?? {}).find((locale) => locale.isSelected)?.keyForList} + initiallyFocusedOptionKey={notificationPreferenceOptions.find((locale) => locale.isSelected)?.keyForList} /> diff --git a/src/pages/settings/Report/ReportSettingsPage.tsx b/src/pages/settings/Report/ReportSettingsPage.tsx index 28d189359860..37408ef61df2 100644 --- a/src/pages/settings/Report/ReportSettingsPage.tsx +++ b/src/pages/settings/Report/ReportSettingsPage.tsx @@ -1,5 +1,4 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import isEmpty from 'lodash/isEmpty'; import React, {useMemo} from 'react'; import {ScrollView, View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -23,6 +22,7 @@ import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type ReportSettingsPageProps = WithReportOrNotFoundProps & StackScreenProps; @@ -38,7 +38,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { // We only want policy owners and admins to be able to modify the welcome message, but not in thread chat const shouldDisableWelcomeMessage = ReportUtils.shouldDisableWelcomeMessage(report, linkedWorkspace); - const shouldDisableSettings = isEmpty(report) || ReportUtils.isArchivedRoom(report); + const shouldDisableSettings = isEmptyObject(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); const notificationPreference = report?.notificationPreference && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN @@ -60,7 +60,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID ?? ''))} + onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID))} /> {shouldShowNotificationPref && ( @@ -68,7 +68,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={notificationPreference} description={translate('notificationPreferencesPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(reportID))} /> )} {shouldShowRoomName && ( @@ -76,7 +76,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { pendingAction={report?.pendingFields?.reportName} errors={report?.errorFields?.reportName} errorRowStyles={[styles.ph5]} - onClose={() => ReportActions.clearPolicyRoomNameErrors(reportID ?? '')} + onClose={() => ReportActions.clearPolicyRoomNameErrors(reportID)} > {shouldDisableRename ? ( @@ -99,7 +99,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={report?.reportName} description={translate('newRoomPage.roomName')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_ROOM_NAME.getRoute(reportID))} /> )} @@ -110,7 +110,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { shouldShowRightIcon title={writeCapabilityText} description={translate('writeCapabilityPage.label')} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.getRoute(reportID))} /> ) : ( @@ -129,7 +129,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { ))} - {Boolean(linkedWorkspace) && ( + {linkedWorkspace !== null && ( )} - {Boolean(report?.visibility) && ( + {report?.visibility !== undefined && ( - {report?.visibility && translate(`newRoomPage.visibilityOptions.${report.visibility}`)} + {translate(`newRoomPage.visibilityOptions.${report.visibility}`)} - {report?.visibility && translate(`newRoomPage.${report.visibility}Description`)} + {report.visibility && translate(`newRoomPage.${report.visibility}Description`)} )} @@ -168,7 +168,7 @@ function ReportSettingsPage({report, policies}: ReportSettingsPageProps) { Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(reportID ?? ''))} + onPress={() => Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(reportID))} shouldShowRightIcon /> )} diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 6b2fd751f01f..30226bc6f502 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -27,7 +27,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy, Report} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; type RoomNamePageOnyxProps = { /** All reports shared with the user */ @@ -87,7 +86,7 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { !isEmptyObject(report) && ReportActions.updatePolicyRoomNameAndNavigate(report, values.roomName)} + onSubmit={(values) => report && ReportActions.updatePolicyRoomNameAndNavigate(report, values.roomName)} validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline diff --git a/src/pages/settings/Report/WriteCapabilityPage.tsx b/src/pages/settings/Report/WriteCapabilityPage.tsx index 7af951d685d4..5f5fe73e5199 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.tsx +++ b/src/pages/settings/Report/WriteCapabilityPage.tsx @@ -18,7 +18,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WriteCapabilityPageOnyxProps = { /** The policy object for the current route */ @@ -53,8 +52,8 @@ function WriteCapabilityPage({report, policy}: WriteCapabilityPageProps) { /> !isEmptyObject(report) && ReportActions.updateWriteCapabilityAndNavigate(report, option.value)} - initiallyFocusedOptionKey={Object.values(writeCapabilityOptions).find((locale) => locale.isSelected)?.keyForList} + onSelectRow={(option) => report && ReportActions.updateWriteCapabilityAndNavigate(report, option.value)} + initiallyFocusedOptionKey={writeCapabilityOptions.find((locale) => locale.isSelected)?.keyForList} /> From c90b7675cd6341c1515fa93364c120a0a719e59a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:47:45 +0100 Subject: [PATCH 48/57] Use keyForList in OptionsSelector's isSelected logic --- src/components/OptionsList/BaseOptionsList.tsx | 10 +--------- src/libs/OptionsListUtils.ts | 2 ++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 1975c0f1a88e..6f265bfb8440 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -175,15 +175,7 @@ function BaseOptionsList( const renderItem: SectionListRenderItem = ({item, index, section}) => { const isItemDisabled = isDisabled || !!section.isDisabled || !!item.isDisabled; const isSelected = selectedOptions?.some((option) => { - if (option.accountID && option.accountID === item.accountID) { - return true; - } - - if (option.reportID && option.reportID === item.reportID) { - return true; - } - - if (option.policyID && option.policyID === item.policyID) { + if (option.keyForList && option.keyForList === item.keyForList) { return true; } diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 8cbe5bfa2d23..b6518b361381 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -66,6 +66,7 @@ type PayeePersonalDetails = { descriptiveText: string; login: string; accountID: number; + keyForList: string; }; type CategorySection = { @@ -1737,6 +1738,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: Person descriptiveText: amountText, login: personalDetail.login ?? '', accountID: personalDetail.accountID, + keyForList: String(personalDetail.accountID), }; } From a2d628e21f5ddfedb4249c43d80b5025220a5116 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Sun, 4 Feb 2024 19:53:45 +0530 Subject: [PATCH 49/57] fix: IOU - Enter click does not deselect/select user in split bill. Signed-off-by: Krishna Gupta --- src/components/ButtonWithDropdownMenu.tsx | 7 +++++++ src/components/MoneyRequestConfirmationList.js | 2 ++ .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 ++ src/components/SettlementButton.js | 6 ++++++ 4 files changed, 17 insertions(+) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 676912de6b60..18e30079094f 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -59,6 +59,9 @@ type ButtonWithDropdownMenuProps = { /* ref for the button */ buttonRef: RefObject; + + /** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */ + enterKeyEventListenerPriority?: number; }; function ButtonWithDropdownMenu({ @@ -76,6 +79,7 @@ function ButtonWithDropdownMenu({ onPress, options, onOptionSelected, + enterKeyEventListenerPriority = 0, }: ButtonWithDropdownMenuProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -126,6 +130,7 @@ function ButtonWithDropdownMenu({ large={isButtonSizeLarge} medium={!isButtonSizeLarge} innerStyles={[innerStyleDropButton]} + enterKeyEventListenerPriority={enterKeyEventListenerPriority} />