From 43e030b5d42bf5f19bc7dcb93a6cc3d44890ba3c Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Tue, 23 Apr 2024 03:32:30 +0100 Subject: [PATCH 001/109] fix(money request): missing tags in violations translations --- .../ReportActionItem/MoneyRequestView.tsx | 2 +- src/hooks/useViolations.ts | 41 ++++++++++++++++--- src/libs/Violations/ViolationsUtils.ts | 2 + 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 73fc7e9bae6e..9032c8026012 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -162,7 +162,7 @@ function MoneyRequestView({ // A flag for showing tax rate const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy); - const {getViolationsForField} = useViolations(transactionViolations ?? []); + const {getViolationsForField} = useViolations(transactionViolations ?? [], transaction, policy, policyTagList); const hasViolations = useCallback( (field: ViolationField, data?: OnyxTypes.TransactionViolation['data']): boolean => !!canUseViolations && getViolationsForField(field, data).length > 0, [canUseViolations, getViolationsForField], diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 7aadb4ce4ca3..1d25a5e6f3d2 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -1,6 +1,8 @@ import {useCallback, useMemo} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import CONST from '@src/CONST'; -import type {TransactionViolation, ViolationName} from '@src/types/onyx'; +import type {Policy, PolicyTagList, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; /** * Names of Fields where violations can occur. @@ -48,7 +50,12 @@ const violationFields: Record = { type ViolationsMap = Map; -function useViolations(violations: TransactionViolation[]) { +function useViolations( + violations: TransactionViolation[], + transaction: OnyxEntry = {} as Transaction, + policy: OnyxEntry = {} as Policy, + policyTagList: OnyxEntry = {}, +) { const violationsByField = useMemo((): ViolationsMap => { const filteredViolations = violations.filter((violation) => violation.type === CONST.VIOLATION_TYPES.VIOLATION); const violationGroups = new Map(); @@ -63,10 +70,11 @@ function useViolations(violations: TransactionViolation[]) { const getViolationsForField = useCallback( (field: ViolationField, data?: TransactionViolation['data']) => { const currentViolations = violationsByField.get(field) ?? []; + const firstViolation = currentViolations[0]; // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation // tagListIndex can be 0 so we compare with undefined - if (currentViolations[0]?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(currentViolations[0]?.data?.errorIndexes)) { + if (firstViolation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(firstViolation?.data?.errorIndexes)) { return currentViolations .filter((violation) => violation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) .map((violation) => ({ @@ -78,14 +86,37 @@ function useViolations(violations: TransactionViolation[]) { })); } + // missingTag has special logic because if its data is null, we need to convert it to someTagLevelsRequired + if (firstViolation?.name === 'missingTag' && firstViolation?.data === null) { + const newViolations = + Object.keys(policyTagList ?? {}).length === 1 + ? ViolationsUtils.getTagViolationsForSingleLevelTags(transaction ?? ({} as Transaction), violations, !!policy?.requiresTag, policyTagList ?? {}) + : ViolationsUtils.getTagViolationsForMultiLevelTags(transaction ?? ({} as Transaction), violations, !!policy?.requiresTag, policyTagList ?? {}); + const newViolation = newViolations.find( + (currentViolation) => currentViolation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(currentViolation?.data?.errorIndexes), + ); + if (newViolation) { + return newViolations + .filter((currentViolation) => currentViolation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) + .map((currentViolation) => ({ + ...currentViolation, + data: { + ...currentViolation.data, + tagName: data?.tagListName, + }, + })); + } + return test; + } + // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on - if (currentViolations[0]?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && currentViolations[0]?.data?.tagName) { + if (firstViolation?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && firstViolation?.data?.tagName) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } return currentViolations; }, - [violationsByField], + [policy?.requiresTag, policyTagList, transaction, violations, violationsByField], ); return { diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 42f58be1d699..3565a3d28b9a 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -110,6 +110,8 @@ function getTagViolationsForMultiLevelTags( } const ViolationsUtils = { + getTagViolationsForSingleLevelTags, + getTagViolationsForMultiLevelTags, /** * Checks a transaction for policy violations and returns an object with Onyx method, key and updated transaction * violations. From b9d8b24bfa1dd3346fe92f82d3e4542b50d98b17 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Tue, 23 Apr 2024 03:39:22 +0100 Subject: [PATCH 002/109] refactor: remove unexpected return --- src/hooks/useViolations.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 1d25a5e6f3d2..cf02545e20b0 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -106,7 +106,6 @@ function useViolations( }, })); } - return test; } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on From 0a0cac1e1baa810315b5686c0cfb6332e0c9573e Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 24 Apr 2024 22:04:08 +0100 Subject: [PATCH 003/109] chore: apply PR suggestions --- .../ReportActionItem/MoneyRequestView.tsx | 9 +++-- src/hooks/useViolations.ts | 36 ++++--------------- src/libs/PolicyUtils.ts | 8 +++++ src/libs/Violations/ViolationsUtils.ts | 17 +++++++-- src/libs/actions/IOU.ts | 14 ++++++-- src/types/onyx/PolicyTag.ts | 6 ++++ src/types/onyx/TransactionViolation.ts | 1 + 7 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index b9bf70f22a9d..2cb1d1d6ae84 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -174,7 +174,7 @@ function MoneyRequestView({ // A flag for showing tax rate const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy); - const {getViolationsForField} = useViolations(transactionViolations ?? [], transaction, policy, policyTagList); + const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback( (field: ViolationField, data?: OnyxTypes.TransactionViolation['data']): boolean => !!canUseViolations && getViolationsForField(field, data).length > 0, [canUseViolations, getViolationsForField], @@ -467,11 +467,16 @@ function MoneyRequestView({ getErrorForField('tag', { tagListIndex: index, tagListName: name, + policyHasDependentTagLists: PolicyUtils.hasDependentTags(policy, policyTagList), }) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined } - error={getErrorForField('tag', {tagListIndex: index, tagListName: name})} + error={getErrorForField('tag', { + tagListIndex: index, + tagListName: name, + policyHasDependentTagLists: PolicyUtils.hasDependentTags(policy, policyTagList), + })} /> ))} diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index cf02545e20b0..eb6138799973 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -1,8 +1,6 @@ import {useCallback, useMemo} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import CONST from '@src/CONST'; -import type {Policy, PolicyTagList, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; +import type {TransactionViolation, ViolationName} from '@src/types/onyx'; /** * Names of Fields where violations can occur. @@ -50,12 +48,7 @@ const violationFields: Record = { type ViolationsMap = Map; -function useViolations( - violations: TransactionViolation[], - transaction: OnyxEntry = {} as Transaction, - policy: OnyxEntry = {} as Policy, - policyTagList: OnyxEntry = {}, -) { +function useViolations(violations: TransactionViolation[]) { const violationsByField = useMemo((): ViolationsMap => { const filteredViolations = violations.filter((violation) => violation.type === CONST.VIOLATION_TYPES.VIOLATION); const violationGroups = new Map(); @@ -86,26 +79,9 @@ function useViolations( })); } - // missingTag has special logic because if its data is null, we need to convert it to someTagLevelsRequired - if (firstViolation?.name === 'missingTag' && firstViolation?.data === null) { - const newViolations = - Object.keys(policyTagList ?? {}).length === 1 - ? ViolationsUtils.getTagViolationsForSingleLevelTags(transaction ?? ({} as Transaction), violations, !!policy?.requiresTag, policyTagList ?? {}) - : ViolationsUtils.getTagViolationsForMultiLevelTags(transaction ?? ({} as Transaction), violations, !!policy?.requiresTag, policyTagList ?? {}); - const newViolation = newViolations.find( - (currentViolation) => currentViolation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(currentViolation?.data?.errorIndexes), - ); - if (newViolation) { - return newViolations - .filter((currentViolation) => currentViolation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) - .map((currentViolation) => ({ - ...currentViolation, - data: { - ...currentViolation.data, - tagName: data?.tagListName, - }, - })); - } + // missingTag has special logic because we have to take into account dependent tags + if (firstViolation?.data?.policyHasDependentTagLists && firstViolation?.name === 'missingTag' && firstViolation?.data?.tagName === data?.tagListName) { + return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on @@ -115,7 +91,7 @@ function useViolations( return currentViolations; }, - [policy?.requiresTag, policyTagList, transaction, violations, violationsByField], + [violationsByField], ); return { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 731dc5700c8e..3b60e0cba262 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -378,6 +378,13 @@ function getPolicy(policyID: string | undefined): Policy | EmptyObject { return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; } +function hasDependentTags(policy: OnyxEntry, policyTagList: OnyxEntry) { + return ( + !!policy?.hasMultipleTagLists && + Object.values(policyTagList ?? {}).some((tagList) => Object.values(tagList.tags).some((tag) => !!tag.rules?.parentTagsFilter || !!tag.parentTagsFilter)) + ); +} + export { getActivePolicies, hasAccountingConnections, @@ -421,6 +428,7 @@ export { getSubmitToAccountID, getAdminEmployees, getPolicy, + hasDependentTags, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 3565a3d28b9a..ffa346754d8a 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -56,6 +56,7 @@ function getTagViolationsForMultiLevelTags( transactionViolations: TransactionViolation[], policyRequiresTags: boolean, policyTagList: PolicyTagList, + hasDependentTags: boolean, ): TransactionViolation[] { const policyTagKeys = getSortedTagKeys(policyTagList); const selectedTags = updatedTransaction.tag?.split(CONST.COLON) ?? []; @@ -64,6 +65,17 @@ function getTagViolationsForMultiLevelTags( (violation) => violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY, ); + if (hasDependentTags && !updatedTransaction.tag) { + Object.values(policyTagList).forEach((tagList) => { + newTransactionViolations.push({ + name: CONST.VIOLATIONS.MISSING_TAG, + type: CONST.VIOLATION_TYPES.VIOLATION, + data: {tagName: tagList.name}, + }); + }); + return newTransactionViolations; + } + // We first get the errorIndexes for someTagLevelsRequired. If it's not empty, we puth SOME_TAG_LEVELS_REQUIRED in Onyx. // Otherwise, we put TAG_OUT_OF_POLICY in Onyx (when applicable) const errorIndexes = []; @@ -110,8 +122,6 @@ function getTagViolationsForMultiLevelTags( } const ViolationsUtils = { - getTagViolationsForSingleLevelTags, - getTagViolationsForMultiLevelTags, /** * Checks a transaction for policy violations and returns an object with Onyx method, key and updated transaction * violations. @@ -123,6 +133,7 @@ const ViolationsUtils = { policyTagList: PolicyTagList, policyRequiresCategories: boolean, policyCategories: PolicyCategories, + hasDependentTags: boolean, ): OnyxUpdate { const isPartialTransaction = TransactionUtils.isPartialMerchant(TransactionUtils.getMerchant(updatedTransaction)) && TransactionUtils.isAmountMissing(updatedTransaction); if (isPartialTransaction) { @@ -168,7 +179,7 @@ const ViolationsUtils = { newTransactionViolations = Object.keys(policyTagList).length === 1 ? getTagViolationsForSingleLevelTags(updatedTransaction, newTransactionViolations, policyRequiresTags, policyTagList) - : getTagViolationsForMultiLevelTags(updatedTransaction, newTransactionViolations, policyRequiresTags, policyTagList); + : getTagViolationsForMultiLevelTags(updatedTransaction, newTransactionViolations, policyRequiresTags, policyTagList, hasDependentTags); } return { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 30ab2688b593..64eb7ba3a645 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -765,7 +765,7 @@ function buildOnyxDataForMoneyRequest( return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}, true); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); @@ -1102,7 +1102,15 @@ function buildOnyxDataForTrackExpense( return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData( + transaction, + [], + !!policy.requiresTag, + policyTagList ?? {}, + !!policy.requiresCategory, + policyCategories ?? {}, + PolicyUtils.hasDependentTags(policy, policyTagList ?? {}), + ); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); @@ -2054,6 +2062,7 @@ function getUpdateMoneyRequestParams( policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}, + PolicyUtils.hasDependentTags(policy, policyTagList ?? {}), ), ); failureData.push({ @@ -4375,6 +4384,7 @@ function editRegularMoneyRequest( policyTags, !!policy.requiresCategory, policyCategories, + PolicyUtils.hasDependentTags(policy, policyTags), ); optimisticData.push(updatedViolationsOnyxData); failureData.push({ diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index 37e979fb58f6..88b5297dbd38 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -13,6 +13,12 @@ type PolicyTag = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** A list of errors keyed by microtime */ errors?: OnyxCommon.Errors | null; + + rules?: { + parentTagsFilter?: string; + }; + + parentTagsFilter?: string; }>; type PolicyTags = Record; diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 28de4582bd5e..6859c6749567 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -10,6 +10,7 @@ type TransactionViolation = { type: string; name: ViolationName; data?: { + policyHasDependentTagLists?: boolean; rejectedBy?: string; rejectReason?: string; formattedLimit?: string; From a174c60a79bd5e8cd92eb6afd8913e76f60dd4ee Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Thu, 25 Apr 2024 22:18:10 +0100 Subject: [PATCH 004/109] fix: tag name missing in error UI --- src/hooks/useViolations.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index eb6138799973..65e17785500d 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -80,8 +80,16 @@ function useViolations(violations: TransactionViolation[]) { } // missingTag has special logic because we have to take into account dependent tags - if (firstViolation?.data?.policyHasDependentTagLists && firstViolation?.name === 'missingTag' && firstViolation?.data?.tagName === data?.tagListName) { - return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); + if (data?.policyHasDependentTagLists && firstViolation?.name === 'missingTag' && data?.tagListName) { + return [ + { + ...firstViolation, + data: { + ...firstViolation.data, + tagName: data?.tagListName, + }, + }, + ]; } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on From 72f85755fab249ec89a8e8b674094baec90d261e Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 29 Apr 2024 21:41:03 +0100 Subject: [PATCH 005/109] refactor: apply pull request suggestions --- .../ReportActionItem/MoneyRequestView.tsx | 4 +- src/hooks/useViolations.ts | 25 +++---- src/libs/PolicyUtils.ts | 8 +-- src/libs/Violations/ViolationsUtils.ts | 71 ++++++++++++------- src/libs/actions/IOU.ts | 10 ++- src/types/onyx/PolicyTag.ts | 4 ++ src/types/onyx/TransactionViolation.ts | 2 +- 7 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2cb1d1d6ae84..fa3707605044 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -467,7 +467,7 @@ function MoneyRequestView({ getErrorForField('tag', { tagListIndex: index, tagListName: name, - policyHasDependentTagLists: PolicyUtils.hasDependentTags(policy, policyTagList), + policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), }) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined @@ -475,7 +475,7 @@ function MoneyRequestView({ error={getErrorForField('tag', { tagListIndex: index, tagListName: name, - policyHasDependentTagLists: PolicyUtils.hasDependentTags(policy, policyTagList), + policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), })} /> diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 65e17785500d..578db947ed57 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -63,29 +63,30 @@ function useViolations(violations: TransactionViolation[]) { const getViolationsForField = useCallback( (field: ViolationField, data?: TransactionViolation['data']) => { const currentViolations = violationsByField.get(field) ?? []; - const firstViolation = currentViolations[0]; + const violation = currentViolations[0]; // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation // tagListIndex can be 0 so we compare with undefined - if (firstViolation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(firstViolation?.data?.errorIndexes)) { + if (violation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(violation?.data?.errorIndexes)) { return currentViolations - .filter((violation) => violation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) - .map((violation) => ({ - ...violation, + .filter((currentViolation) => currentViolation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) + .map((currentViolation) => ({ + ...currentViolation, data: { - ...violation.data, + ...currentViolation.data, tagName: data?.tagListName, }, })); } - // missingTag has special logic because we have to take into account dependent tags - if (data?.policyHasDependentTagLists && firstViolation?.name === 'missingTag' && data?.tagListName) { + // missingTag has special logic for policies with dependent tags, because only violation is returned for all tags + // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here + if (data?.policyHasDependentTags && violation?.name === 'missingTag' && data?.tagListName) { return [ { - ...firstViolation, + ...violation, data: { - ...firstViolation.data, + ...violation.data, tagName: data?.tagListName, }, }, @@ -93,8 +94,8 @@ function useViolations(violations: TransactionViolation[]) { } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on - if (firstViolation?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && firstViolation?.data?.tagName) { - return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); + if (violation?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && violation?.data?.tagName) { + return currentViolations.filter((currentViolation) => currentViolation.data?.tagName === data?.tagListName); } return currentViolations; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 657a3387ce7f..374ffa2436a0 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -391,10 +391,10 @@ function canSendInvoice(policies: OnyxCollection): boolean { } function hasDependentTags(policy: OnyxEntry, policyTagList: OnyxEntry) { - return ( - !!policy?.hasMultipleTagLists && - Object.values(policyTagList ?? {}).some((tagList) => Object.values(tagList.tags).some((tag) => !!tag.rules?.parentTagsFilter || !!tag.parentTagsFilter)) - ); + if (!policy?.hasMultipleTagLists) { + return false; + } + return Object.values(policyTagList ?? {}).some((tagList) => Object.values(tagList.tags).some((tag) => !!tag.rules?.parentTagsFilter || !!tag.parentTagsFilter)); } export { diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index ffa346754d8a..0ef102318d78 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -49,36 +49,31 @@ function getTagViolationsForSingleLevelTags( } /** - * Calculates some tag levels required and missing tag violations for the given transaction + * Calculates missing tag violations for policies with dependent tags, + * by returning one per tag with its corresponding tagName in the data */ -function getTagViolationsForMultiLevelTags( - updatedTransaction: Transaction, - transactionViolations: TransactionViolation[], - policyRequiresTags: boolean, - policyTagList: PolicyTagList, - hasDependentTags: boolean, -): TransactionViolation[] { - const policyTagKeys = getSortedTagKeys(policyTagList); - const selectedTags = updatedTransaction.tag?.split(CONST.COLON) ?? []; - let newTransactionViolations = [...transactionViolations]; - newTransactionViolations = newTransactionViolations.filter( - (violation) => violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY, - ); +function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[]) { + return [ + ...transactionViolations, + ...Object.values(policyTagList).map((tagList) => ({ + name: CONST.VIOLATIONS.MISSING_TAG, + type: CONST.VIOLATION_TYPES.VIOLATION, + data: {tagName: tagList.name}, + })), + ]; +} - if (hasDependentTags && !updatedTransaction.tag) { - Object.values(policyTagList).forEach((tagList) => { - newTransactionViolations.push({ - name: CONST.VIOLATIONS.MISSING_TAG, - type: CONST.VIOLATION_TYPES.VIOLATION, - data: {tagName: tagList.name}, - }); - }); - return newTransactionViolations; - } +/** + * Calculates missing tag violations for policies with independent tags, + * by returning one per tag with its corresponding tagName in the data + */ +function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], policyTagKeys: string[], selectedTags: string[]) { + let newTransactionViolations = [...transactionViolations]; // We first get the errorIndexes for someTagLevelsRequired. If it's not empty, we puth SOME_TAG_LEVELS_REQUIRED in Onyx. // Otherwise, we put TAG_OUT_OF_POLICY in Onyx (when applicable) const errorIndexes = []; + for (let i = 0; i < policyTagKeys.length; i++) { const isTagRequired = policyTagList[policyTagKeys[i]].required ?? true; const isTagSelected = Boolean(selectedTags[i]); @@ -86,6 +81,7 @@ function getTagViolationsForMultiLevelTags( errorIndexes.push(i); } } + if (errorIndexes.length !== 0) { newTransactionViolations.push({ name: CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, @@ -96,10 +92,12 @@ function getTagViolationsForMultiLevelTags( }); } else { let hasInvalidTag = false; + for (let i = 0; i < policyTagKeys.length; i++) { const selectedTag = selectedTags[i]; const tags = policyTagList[policyTagKeys[i]].tags; const isTagInPolicy = Object.values(tags).some((tag) => tag.name === selectedTag && Boolean(tag.enabled)); + if (!isTagInPolicy) { newTransactionViolations.push({ name: CONST.VIOLATIONS.TAG_OUT_OF_POLICY, @@ -112,15 +110,40 @@ function getTagViolationsForMultiLevelTags( break; } } + if (!hasInvalidTag) { newTransactionViolations = reject(newTransactionViolations, { name: CONST.VIOLATIONS.TAG_OUT_OF_POLICY, }); } } + return newTransactionViolations; } +/** + * Calculates some tag levels required and missing tag violations for the given transaction + */ +function getTagViolationsForMultiLevelTags( + updatedTransaction: Transaction, + transactionViolations: TransactionViolation[], + policyRequiresTags: boolean, + policyTagList: PolicyTagList, + hasDependentTags: boolean, +): TransactionViolation[] { + const policyTagKeys = getSortedTagKeys(policyTagList); + const selectedTags = updatedTransaction.tag?.split(CONST.COLON) ?? []; + const filteredTransactionViolations = transactionViolations.filter( + (violation) => violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY, + ); + + if (hasDependentTags && !updatedTransaction.tag) { + return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations); + } + + return getTagViolationForIndependentTags(policyTagList, filteredTransactionViolations, policyTagKeys, selectedTags); +} + const ViolationsUtils = { /** * Checks a transaction for policy violations and returns an object with Onyx method, key and updated transaction diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1df688ce3bed..f605663793d2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -779,7 +779,15 @@ function buildOnyxDataForMoneyRequest( return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}, true); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData( + transaction, + [], + !!policy.requiresTag, + policyTagList ?? {}, + !!policy.requiresCategory, + policyCategories ?? {}, + PolicyUtils.hasDependentTags(policy, policyTagList ?? {}), + ); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index 88b5297dbd38..9d48b14ff444 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -15,6 +15,10 @@ type PolicyTag = OnyxCommon.OnyxValueWithOfflineFeedback<{ errors?: OnyxCommon.Errors | null; rules?: { + /** + * String representation of regex to match against parent tag. Eg, if San Francisco is a child tag of California + * its parentTagsFilter will be ^California$ + */ parentTagsFilter?: string; }; diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 6859c6749567..39d0dc394753 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -10,7 +10,7 @@ type TransactionViolation = { type: string; name: ViolationName; data?: { - policyHasDependentTagLists?: boolean; + policyHasDependentTags?: boolean; rejectedBy?: string; rejectReason?: string; formattedLimit?: string; From 5c7e16ce6ad6aea0268431926dc965e2408c12a9 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 29 Apr 2024 22:24:24 +0100 Subject: [PATCH 006/109] chore(typescript): add missing argument --- tests/unit/ViolationUtilsTest.ts | 58 ++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index b967617918c1..02668da462e0 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -43,7 +43,7 @@ describe('getViolationsOnyxData', () => { }); it('should return an object with correct shape and with empty transactionViolations array', () => { - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result).toEqual({ onyxMethod: Onyx.METHOD.SET, @@ -57,7 +57,7 @@ describe('getViolationsOnyxData', () => { {name: 'duplicatedTransaction', type: CONST.VIOLATION_TYPES.VIOLATION}, {name: 'receiptRequired', type: CONST.VIOLATION_TYPES.VIOLATION}, ]; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining(transactionViolations)); }); @@ -70,24 +70,32 @@ describe('getViolationsOnyxData', () => { it('should add missingCategory violation if no category is included', () => { transaction.category = undefined; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([missingCategoryViolation, ...transactionViolations])); }); it('should add categoryOutOfPolicy violation when category is not in policy', () => { transaction.category = 'Bananas'; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([categoryOutOfPolicyViolation, ...transactionViolations])); }); it('should not include a categoryOutOfPolicy violation when category is in policy', () => { - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).not.toContainEqual(categoryOutOfPolicyViolation); }); it('should not add a category violation when the transaction is partial', () => { const partialTransaction = {...transaction, amount: 0, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, category: undefined}; - const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData( + partialTransaction, + transactionViolations, + policyRequiresTags, + policyTags, + policyRequiresCategories, + policyCategories, + false, + ); expect(result.value).not.toContainEqual(missingCategoryViolation); }); @@ -98,7 +106,7 @@ describe('getViolationsOnyxData', () => { {name: 'receiptRequired', type: CONST.VIOLATION_TYPES.VIOLATION}, ]; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([categoryOutOfPolicyViolation, ...transactionViolations])); }); @@ -110,7 +118,7 @@ describe('getViolationsOnyxData', () => { {name: 'receiptRequired', type: CONST.VIOLATION_TYPES.VIOLATION}, ]; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([missingCategoryViolation, ...transactionViolations])); }); @@ -122,7 +130,7 @@ describe('getViolationsOnyxData', () => { }); it('should not add any violations when categories are not required', () => { - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).not.toContainEqual([categoryOutOfPolicyViolation]); expect(result.value).not.toContainEqual([missingCategoryViolation]); @@ -147,7 +155,7 @@ describe('getViolationsOnyxData', () => { }); it("shouldn't update the transactionViolations if the policy requires tags and the transaction has a tag from the policy", () => { - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(transactionViolations); }); @@ -155,7 +163,7 @@ describe('getViolationsOnyxData', () => { it('should add a missingTag violation if none is provided and policy requires tags', () => { transaction.tag = undefined; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([{...missingTagViolation}])); }); @@ -163,14 +171,22 @@ describe('getViolationsOnyxData', () => { it('should add a tagOutOfPolicy violation when policy requires tags and tag is not in the policy', () => { policyTags = {}; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual([]); }); it('should not add a tag violation when the transaction is partial', () => { const partialTransaction = {...transaction, amount: 0, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, tag: undefined}; - const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData( + partialTransaction, + transactionViolations, + policyRequiresTags, + policyTags, + policyRequiresCategories, + policyCategories, + false, + ); expect(result.value).not.toContainEqual(missingTagViolation); }); @@ -181,7 +197,7 @@ describe('getViolationsOnyxData', () => { {name: 'receiptRequired', type: CONST.VIOLATION_TYPES.VIOLATION}, ]; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([{...tagOutOfPolicyViolation}, ...transactionViolations])); }); @@ -193,7 +209,7 @@ describe('getViolationsOnyxData', () => { {name: 'receiptRequired', type: CONST.VIOLATION_TYPES.VIOLATION}, ]; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual(expect.arrayContaining([{...missingTagViolation}, ...transactionViolations])); }); @@ -205,7 +221,7 @@ describe('getViolationsOnyxData', () => { }); it('should not add any violations when tags are not required', () => { - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).not.toContainEqual([tagOutOfPolicyViolation]); expect(result.value).not.toContainEqual([missingTagViolation]); @@ -260,30 +276,30 @@ describe('getViolationsOnyxData', () => { }; // Test case where transaction has no tags - let result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + let result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual([someTagLevelsRequiredViolation]); // Test case where transaction has 1 tag transaction.tag = 'Africa'; someTagLevelsRequiredViolation.data = {errorIndexes: [1, 2]}; - result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual([someTagLevelsRequiredViolation]); // Test case where transaction has 2 tags transaction.tag = 'Africa::Project1'; someTagLevelsRequiredViolation.data = {errorIndexes: [1]}; - result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual([someTagLevelsRequiredViolation]); // Test case where transaction has all tags transaction.tag = 'Africa:Accounting:Project1'; - result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); expect(result.value).toEqual([]); }); it('should return tagOutOfPolicy when a tag is not enabled in the policy but is set in the transaction', () => { policyTags.Department.tags.Accounting.enabled = false; transaction.tag = 'Africa:Accounting:Project1'; - const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories); + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, false); const violation = {...tagOutOfPolicyViolation, data: {tagName: 'Department'}}; expect(result.value).toEqual([violation]); }); From 3d6478f10c327d7b57e628b98295a3185124b232 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 29 Apr 2024 22:25:26 +0100 Subject: [PATCH 007/109] chore(tests): add violation case for missing dependent tags --- tests/unit/ViolationUtilsTest.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index 02668da462e0..5e8f93a56dbc 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -303,5 +303,13 @@ describe('getViolationsOnyxData', () => { const violation = {...tagOutOfPolicyViolation, data: {tagName: 'Department'}}; expect(result.value).toEqual([violation]); }); + it('should return missingTag when all dependent tags are enabled in the policy but are not set in the transaction', () => { + const missingDepartmentTag = {...missingTagViolation, data: {tagName: 'Department'}}; + const missingRegionTag = {...missingTagViolation, data: {tagName: 'Region'}}; + const missingProjectTag = {...missingTagViolation, data: {tagName: 'Project'}}; + transaction.tag = undefined; + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policyRequiresTags, policyTags, policyRequiresCategories, policyCategories, true); + expect(result.value).toEqual(expect.arrayContaining([missingDepartmentTag, missingRegionTag, missingProjectTag])); + }); }); }); From e29768615c1e43b9708e4048aa0feb64041e6c7f Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 3 May 2024 09:29:44 +0100 Subject: [PATCH 008/109] refactor: apply pull request suggestions --- src/hooks/useViolations.ts | 23 +++++++++++------------ src/libs/Violations/ViolationsUtils.ts | 11 ++++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 578db947ed57..6257a9251d08 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -63,30 +63,29 @@ function useViolations(violations: TransactionViolation[]) { const getViolationsForField = useCallback( (field: ViolationField, data?: TransactionViolation['data']) => { const currentViolations = violationsByField.get(field) ?? []; - const violation = currentViolations[0]; // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation // tagListIndex can be 0 so we compare with undefined - if (violation?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(violation?.data?.errorIndexes)) { + if (currentViolations[0]?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(currentViolations[0]?.data?.errorIndexes)) { return currentViolations - .filter((currentViolation) => currentViolation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) - .map((currentViolation) => ({ - ...currentViolation, + .filter((violation) => violation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) + .map((violation) => ({ + ...violation, data: { - ...currentViolation.data, + ...violation.data, tagName: data?.tagListName, }, })); } - // missingTag has special logic for policies with dependent tags, because only violation is returned for all tags + // missingTag has special logic for policies with dependent tags, because only one violation is returned for all tags // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here - if (data?.policyHasDependentTags && violation?.name === 'missingTag' && data?.tagListName) { + if (data?.policyHasDependentTags && currentViolations[0]?.name === 'missingTag' && data?.tagListName) { return [ { - ...violation, + ...currentViolations[0], data: { - ...violation.data, + ...currentViolations[0].data, tagName: data?.tagListName, }, }, @@ -94,8 +93,8 @@ function useViolations(violations: TransactionViolation[]) { } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on - if (violation?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && violation?.data?.tagName) { - return currentViolations.filter((currentViolation) => currentViolation.data?.tagName === data?.tagListName); + if (currentViolations[0]?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && currentViolations[0]?.data?.tagName) { + return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } return currentViolations; diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 0ef102318d78..c7bcd66aef5e 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -67,7 +67,9 @@ function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transact * Calculates missing tag violations for policies with independent tags, * by returning one per tag with its corresponding tagName in the data */ -function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], policyTagKeys: string[], selectedTags: string[]) { +function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], transaction: Transaction) { + const policyTagKeys = getSortedTagKeys(policyTagList); + const selectedTags = transaction.tag?.split(CONST.COLON) ?? []; let newTransactionViolations = [...transactionViolations]; // We first get the errorIndexes for someTagLevelsRequired. If it's not empty, we puth SOME_TAG_LEVELS_REQUIRED in Onyx. @@ -131,17 +133,16 @@ function getTagViolationsForMultiLevelTags( policyTagList: PolicyTagList, hasDependentTags: boolean, ): TransactionViolation[] { - const policyTagKeys = getSortedTagKeys(policyTagList); - const selectedTags = updatedTransaction.tag?.split(CONST.COLON) ?? []; const filteredTransactionViolations = transactionViolations.filter( - (violation) => violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY, + (violation) => + violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY && violation.name !== CONST.VIOLATIONS.MISSING_TAG, ); if (hasDependentTags && !updatedTransaction.tag) { return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations); } - return getTagViolationForIndependentTags(policyTagList, filteredTransactionViolations, policyTagKeys, selectedTags); + return getTagViolationForIndependentTags(policyTagList, filteredTransactionViolations, updatedTransaction); } const ViolationsUtils = { From a8ef1c390be0a38c687303101ec3169ddda7de15 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 3 May 2024 10:19:07 +0100 Subject: [PATCH 009/109] chore(typescript): add missing argument --- src/libs/actions/IOU.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7b962e712cf2..e552c5eac7d0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1087,7 +1087,15 @@ function buildOnyxDataForInvoice( return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTagList ?? {}, !!policy.requiresCategory, policyCategories ?? {}); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData( + transaction, + [], + !!policy.requiresTag, + policyTagList ?? {}, + !!policy.requiresCategory, + policyCategories ?? {}, + PolicyUtils.hasDependentTags(policy, policyTagList ?? {}), + ); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); From 9f0585e6f576bd3df8441b92c34671793b315df2 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Sat, 11 May 2024 19:08:30 +0530 Subject: [PATCH 010/109] fix: Status - Emoji in custom status holder is not centered. Signed-off-by: Krishna Gupta --- src/styles/index.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 060fb1c5ba90..b1b92869c4b7 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4333,7 +4333,20 @@ const styles = (theme: ThemeColors) => }, emojiStatusLHN: { - fontSize: 9, + ...(Browser.getBrowser() && {transform: 'scale(.65)', lineHeight: 18, fontSize: 15}), + ...(Browser.getBrowser() && + Browser.isSafari() && + !Browser.isMobileSafari() && { + transform: 'scale(0.65)', + fontSize: 13, + lineHeight: 15, + }), + // transform: 'scale(.65)', + // lineHeight: 15, + // transform: 'scale(.65)', + // lineHeight: 18, + // fontSize: 15, + // overflow: 'visible', }, onboardingVideoPlayer: { From f06518ef56d9af58342820ad4eb1a4c01792999f Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 13 May 2024 11:55:51 +0530 Subject: [PATCH 011/109] remove redundant code. Signed-off-by: Krishna Gupta --- src/styles/index.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index b1b92869c4b7..2d90d89d2406 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4333,20 +4333,16 @@ const styles = (theme: ThemeColors) => }, emojiStatusLHN: { - ...(Browser.getBrowser() && {transform: 'scale(.65)', lineHeight: 18, fontSize: 15}), + fontSize: 9, + ...(Browser.getBrowser() && !Browser.isMobile() && {transform: 'scale(.65)', lineHeight: 18, fontSize: 15, overflow: 'visible'}), ...(Browser.getBrowser() && Browser.isSafari() && - !Browser.isMobileSafari() && { + !Browser.isMobile() && { transform: 'scale(0.65)', fontSize: 13, lineHeight: 15, + overflow: 'visible', }), - // transform: 'scale(.65)', - // lineHeight: 15, - // transform: 'scale(.65)', - // lineHeight: 18, - // fontSize: 15, - // overflow: 'visible', }, onboardingVideoPlayer: { From 2263ee97af35ca6ddd2dfdab2e32ddd67190581f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 14 May 2024 22:16:29 +0800 Subject: [PATCH 012/109] fix wrong selection after replacing all values --- src/components/MoneyRequestAmountInput.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index a59b50e5bdb7..6201df3e8e6f 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -123,6 +123,8 @@ function MoneyRequestAmountInput( }); const forwardDeletePressedRef = useRef(false); + // The ref is used to ignore any onSelectionChange event that happens while we are updating the selection manually in setNewAmount + const willSelectionBeUpdatedManually = useRef(false); /** * Sets the selection and the amount accordingly to the value passed to the input @@ -145,6 +147,7 @@ function MoneyRequestAmountInput( // setCurrentAmount contains another setState(setSelection) making it error-prone since it is leading to setSelection being called twice for a single setCurrentAmount call. This solution introducing the hasSelectionBeenSet flag was chosen for its simplicity and lower risk of future errors https://github.com/Expensify/App/issues/23300#issuecomment-1766314724. + willSelectionBeUpdatedManually.current = true; let hasSelectionBeenSet = false; setCurrentAmount((prevAmount) => { const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(finalAmount); @@ -152,6 +155,7 @@ function MoneyRequestAmountInput( if (!hasSelectionBeenSet) { hasSelectionBeenSet = true; setSelection((prevSelection) => getNewSelection(prevSelection, isForwardDelete ? strippedAmount.length : prevAmount.length, strippedAmount.length)); + willSelectionBeUpdatedManually.current = false; } onAmountChange?.(strippedAmount); return strippedAmount; @@ -266,6 +270,10 @@ function MoneyRequestAmountInput( selectedCurrencyCode={currency} selection={selection} onSelectionChange={(e: NativeSyntheticEvent) => { + if (willSelectionBeUpdatedManually.current) { + willSelectionBeUpdatedManually.current = false; + return; + } if (!shouldUpdateSelection) { return; } From 772d1b2ca03fe1f713a9c88ae5c7ecb13274bbaf Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 15 May 2024 02:30:17 +0530 Subject: [PATCH 013/109] Disable write capability for invoice rooms --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b8c306b549a7..966db769d070 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5785,7 +5785,7 @@ function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry, policy: OnyxEntry): boolean { - return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report) && !isThread(report); + return PolicyUtils.isPolicyAdmin(policy) && !isAdminRoom(report) && !isArchivedRoom(report) && !isThread(report) && !isInvoiceRoom(report); } /** From 4e0968d9a4385d1e9c30c008fce19e11067bcea3 Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 18 May 2024 17:13:00 +0700 Subject: [PATCH 014/109] fix Inconsistency while pasting highlighted mention in room and expense description --- src/pages/RoomDescriptionPage.tsx | 1 + src/pages/workspace/WorkspaceNewRoomPage.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index 3992dff188e2..2cab7a4ed40b 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -97,6 +97,7 @@ function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { value={description} onChangeText={handleReportDescriptionChange} autoCapitalize="none" + isMarkdownEnabled /> diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index 5716812ced16..5004bac942cd 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -295,6 +295,7 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli maxLength={CONST.REPORT_DESCRIPTION.MAX_LENGTH} autoCapitalize="none" shouldInterceptSwipe + isMarkdownEnabled /> From ce10020ffdc791278c1f1a83fcfca44f170d2c30 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 21 May 2024 12:32:37 +0200 Subject: [PATCH 015/109] Fixing the chat icon navigation --- src/ONYXKEYS.ts | 4 ++++ .../ActiveWorkspaceProvider.tsx | 15 +++++++++--- .../BottomTabBar.tsx | 23 +++++++++++-------- src/libs/actions/Policy.ts | 5 ++++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index ddf37fba2354..5d91ddcfa04a 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -75,6 +75,9 @@ const ONYXKEYS = { * rates and units for different workspaces at the same time. */ WORKSPACE_RATE_AND_UNIT: 'workspaceRateAndUnit', + /** Contains the last active workspace ID */ + ACTIVE_WORKSPACE_ID: 'activeWorkspaceID', + /** Contains a list of all currencies available to the user - user can * select a currency based on the list */ CURRENCY_LIST: 'currencyList', @@ -601,6 +604,7 @@ type OnyxValuesMapping = { [ONYXKEYS.PERSONAL_DETAILS_METADATA]: Record; [ONYXKEYS.TASK]: OnyxTypes.Task; [ONYXKEYS.WORKSPACE_RATE_AND_UNIT]: OnyxTypes.WorkspaceRateAndUnit; + [ONYXKEYS.ACTIVE_WORKSPACE_ID]: string | undefined; [ONYXKEYS.CURRENCY_LIST]: OnyxTypes.CurrencyList; [ONYXKEYS.UPDATE_AVAILABLE]: boolean; [ONYXKEYS.SCREEN_SHARE_REQUEST]: OnyxTypes.ScreenShareRequest; diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx index 884b9a2a2d95..2d1b62ba9a65 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx +++ b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx @@ -1,16 +1,25 @@ -import React, {useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; +import * as Policy from '@libs/actions/Policy'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ActiveWorkspaceContext from './ActiveWorkspaceContext'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { - const [activeWorkspaceID, setActiveWorkspaceID] = useState(undefined); + const [activeWorkspaceID, updateActiveWorkspaceID] = useState(undefined); + + const setActiveWorkspaceID = useCallback( + (workspaceID: string | undefined) => { + Policy.setActiveWorkspaceID(workspaceID); + updateActiveWorkspaceID(workspaceID); + }, + [updateActiveWorkspaceID], + ); const value = useMemo( () => ({ activeWorkspaceID, setActiveWorkspaceID, }), - [activeWorkspaceID], + [activeWorkspaceID, setActiveWorkspaceID], ); return {children}; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 7f26177eeb0f..71da79a2f7cd 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -1,5 +1,5 @@ import {useNavigation, useNavigationState} from '@react-navigation/native'; -import React, {useEffect} from 'react'; +import React, {useCallback, useEffect} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -24,20 +24,19 @@ import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; type PurposeForUsingExpensifyModalOnyxProps = { isLoadingApp: OnyxEntry; + activeWorkspaceID: OnyxEntry; }; type PurposeForUsingExpensifyModalProps = PurposeForUsingExpensifyModalOnyxProps; -function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps) { +function BottomTabBar({isLoadingApp = false, activeWorkspaceID}: PurposeForUsingExpensifyModalProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const {activeWorkspaceID} = useActiveWorkspace(); - const navigation = useNavigation(); useEffect(() => { @@ -66,15 +65,18 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return topmostBottomTabRoute?.name ?? SCREENS.HOME; }); - const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); + const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID as string | undefined); + + const navigateToChats = useCallback(() => { + const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/r` as Route) : ROUTES.HOME; + Navigation.navigate(route); + }, [activeWorkspaceID]); return ( { - Navigation.navigate(ROUTES.HOME); - }} + onPress={navigateToChats} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.chats')} wrapperStyle={styles.flex1} @@ -105,4 +107,7 @@ export default withOnyx Date: Tue, 21 May 2024 16:24:24 +0530 Subject: [PATCH 016/109] aligm status emoji to center. Signed-off-by: Krishna Gupta --- .../home/sidebar/AvatarWithOptionalStatus.tsx | 14 ++++++++------ src/styles/index.ts | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx index 609e4044002e..5d4af7ea4961 100644 --- a/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx +++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx @@ -19,12 +19,14 @@ function AvatarWithOptionalStatus({emojiStatus = '', isSelected = false}: Avatar - - {emojiStatus} - + + + {emojiStatus} + + ); diff --git a/src/styles/index.ts b/src/styles/index.ts index 2b3207eee4a8..5f160009d923 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4335,11 +4335,11 @@ const styles = (theme: ThemeColors) => emojiStatusLHN: { fontSize: 9, - ...(Browser.getBrowser() && !Browser.isMobile() && {transform: 'scale(.65)', lineHeight: 18, fontSize: 15, overflow: 'visible'}), + ...(Browser.getBrowser() && !Browser.isMobile() && {transform: 'scale(.5)', fontSize: 22, overflow: 'visible'}), ...(Browser.getBrowser() && Browser.isSafari() && !Browser.isMobile() && { - transform: 'scale(0.65)', + transform: 'scale(0.7)', fontSize: 13, lineHeight: 15, overflow: 'visible', From 6454fe21c9a9c3d06bcd5a66b5d33f38cc238648 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Fri, 24 May 2024 21:51:08 +0530 Subject: [PATCH 017/109] hide overflowing emoji from emoji status container. Signed-off-by: Krishna Gupta --- src/styles/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index 192b2779e05d..e567026a2e7b 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4380,6 +4380,7 @@ const styles = (theme: ThemeColors) => bottom: -4, borderColor: theme.highlightBG, borderWidth: 2, + overflow: 'hidden', }, moneyRequestViewImage: { ...spacing.mh5, From 1a6199d7cf10a531ab60ce236a44e27e2cd836ae Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 28 May 2024 10:50:29 +0200 Subject: [PATCH 018/109] Use session storage to save the current active workspace --- src/CONST.ts | 1 + src/ONYXKEYS.ts | 4 ---- .../ActiveWorkspace/ActiveWorkspaceProvider.tsx | 17 +++++++++-------- .../BottomTabBar.tsx | 8 ++------ 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d2be946cdb2b..8c27a61e9776 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4740,6 +4740,7 @@ const CONST = { SESSION_STORAGE_KEYS: { INITIAL_URL: 'INITIAL_URL', + ACTIVE_WORKSPACE_ID: 'ACTIVE_WORKSPACE_ID', }, DOT_SEPARATOR: '•', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 4e858e32495c..e669d4740f98 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -75,9 +75,6 @@ const ONYXKEYS = { * rates and units for different workspaces at the same time. */ WORKSPACE_RATE_AND_UNIT: 'workspaceRateAndUnit', - /** Contains the last active workspace ID */ - ACTIVE_WORKSPACE_ID: 'activeWorkspaceID', - /** Contains a list of all currencies available to the user - user can * select a currency based on the list */ CURRENCY_LIST: 'currencyList', @@ -607,7 +604,6 @@ type OnyxValuesMapping = { [ONYXKEYS.PERSONAL_DETAILS_METADATA]: Record; [ONYXKEYS.TASK]: OnyxTypes.Task; [ONYXKEYS.WORKSPACE_RATE_AND_UNIT]: OnyxTypes.WorkspaceRateAndUnit; - [ONYXKEYS.ACTIVE_WORKSPACE_ID]: string | undefined; [ONYXKEYS.CURRENCY_LIST]: OnyxTypes.CurrencyList; [ONYXKEYS.UPDATE_AVAILABLE]: boolean; [ONYXKEYS.SCREEN_SHARE_REQUEST]: OnyxTypes.ScreenShareRequest; diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx index 2d1b62ba9a65..419dbef5d171 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx +++ b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx @@ -1,18 +1,19 @@ import React, {useCallback, useMemo, useState} from 'react'; -import * as Policy from '@libs/actions/Policy'; +import CONST from '@src/CONST'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ActiveWorkspaceContext from './ActiveWorkspaceContext'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { const [activeWorkspaceID, updateActiveWorkspaceID] = useState(undefined); - const setActiveWorkspaceID = useCallback( - (workspaceID: string | undefined) => { - Policy.setActiveWorkspaceID(workspaceID); - updateActiveWorkspaceID(workspaceID); - }, - [updateActiveWorkspaceID], - ); + const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { + updateActiveWorkspaceID(workspaceID); + if (workspaceID) { + sessionStorage.setItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID, workspaceID); + } else { + sessionStorage.removeItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); + } + }, []); const value = useMemo( () => ({ diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 71da79a2f7cd..3e48b89ad5c0 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -7,7 +7,6 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; -import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -29,15 +28,15 @@ import SCREENS from '@src/SCREENS'; type PurposeForUsingExpensifyModalOnyxProps = { isLoadingApp: OnyxEntry; - activeWorkspaceID: OnyxEntry; }; type PurposeForUsingExpensifyModalProps = PurposeForUsingExpensifyModalOnyxProps; -function BottomTabBar({isLoadingApp = false, activeWorkspaceID}: PurposeForUsingExpensifyModalProps) { +function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); + const activeWorkspaceID = sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); useEffect(() => { const navigationState = navigation.getState() as State | undefined; @@ -107,7 +106,4 @@ export default withOnyx Date: Wed, 29 May 2024 01:35:15 +0700 Subject: [PATCH 019/109] fix cannot add new line using enter --- src/CONST.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index d2be946cdb2b..02de81e61aa4 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3386,6 +3386,8 @@ const CONST = { * @deprecated Please stop using the accessibilityRole prop and use the role prop instead. */ IMAGE: 'image', + + TEXTBOX: 'textbox', }, /** * Acceptable values for the `role` attribute on react native components. From 5e206959fb7c2fe723c892ff7e30f840ed7f1d58 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 29 May 2024 09:38:05 +0100 Subject: [PATCH 020/109] fix: allTagLevels required violation showing on tags that are already filled --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 ++ src/hooks/useViolations.ts | 11 ++++++++--- src/types/onyx/TransactionViolation.ts | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index d2b92b9a0c41..73844630e2cc 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -479,6 +479,7 @@ function MoneyRequestView({ getErrorForField('tag', { tagListIndex: index, tagListName: name, + tagListValue: TransactionUtils.getTagForDisplay(transaction, index), policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), }) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR @@ -487,6 +488,7 @@ function MoneyRequestView({ errorText={getErrorForField('tag', { tagListIndex: index, tagListName: name, + tagListValue: TransactionUtils.getTagForDisplay(transaction, index), policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), })} /> diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 582d997f436a..7dad3be026fe 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -67,7 +67,7 @@ function useViolations(violations: TransactionViolation[]) { // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation // tagListIndex can be 0 so we compare with undefined - if (currentViolations[0]?.name === 'someTagLevelsRequired' && data?.tagListIndex !== undefined && Array.isArray(currentViolations[0]?.data?.errorIndexes)) { + if (currentViolations[0]?.name === CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && data?.tagListIndex !== undefined && Array.isArray(currentViolations[0]?.data?.errorIndexes)) { return currentViolations .filter((violation) => violation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) .map((violation) => ({ @@ -81,7 +81,7 @@ function useViolations(violations: TransactionViolation[]) { // missingTag has special logic for policies with dependent tags, because only one violation is returned for all tags // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here - if (data?.policyHasDependentTags && currentViolations[0]?.name === 'missingTag' && data?.tagListName) { + if (data?.policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { return [ { ...currentViolations[0], @@ -94,7 +94,12 @@ function useViolations(violations: TransactionViolation[]) { } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on - if (currentViolations[0]?.name === 'tagOutOfPolicy' && data?.tagListName !== undefined && currentViolations[0]?.data?.tagName) { + if (currentViolations[0]?.name === CONST.VIOLATIONS.TAG_OUT_OF_POLICY && data?.tagListName !== undefined && currentViolations[0]?.data?.tagName) { + return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); + } + + // allTagLevelsRequired has special logic because we have to account for tags that are already filled + if (currentViolations[0]?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && data?.tagListValue) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 3dfa5fc8ba42..99ce3d86dfe1 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -29,6 +29,7 @@ type TransactionViolation = { taxName?: string; tagListIndex?: number; tagListName?: string; + tagListValue?: string; errorIndexes?: number[]; pendingPattern?: boolean; }; From 6ae01c121e425f40d221567bac76e7a242750621 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Thu, 30 May 2024 00:04:24 +0200 Subject: [PATCH 021/109] Removing the openWalletPage call from the SettlementButton component --- src/components/SettlementButton.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index f56c4dd1a863..945f8be1f327 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -143,10 +143,6 @@ function SettlementButton({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); - useEffect(() => { - PaymentMethods.openWalletPage(); - }, []); - const session = useSession(); const chatReport = ReportUtils.getReport(chatReportID); const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); From e7196bba4a224e6e5fc39eebc901e56ef7de1009 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Thu, 30 May 2024 00:34:10 +0200 Subject: [PATCH 022/109] cleaning --- src/components/SettlementButton.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 945f8be1f327..a994c4df8ab5 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo} from 'react'; +import React, {useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -8,7 +8,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; -import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; From 2974defa78ac22160fa41fc92252c981a1ae58c5 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 31 May 2024 12:26:51 +0700 Subject: [PATCH 023/109] Add confirmation prompt when approving held request via report preview --- .../ReportActionItem/ReportPreview.tsx | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index be3b104018db..933381b9e116 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -1,5 +1,5 @@ import truncate from 'lodash/truncate'; -import React, {useMemo} from 'react'; +import React, {useMemo, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; @@ -9,6 +9,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu'; import SettlementButton from '@components/SettlementButton'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; @@ -16,6 +17,7 @@ import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import ControlSelection from '@libs/ControlSelection'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -119,6 +121,12 @@ function ReportPreview({ [transactions, iouReportID, action], ); + const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); + const [requestType, setRequestType] = useState<'pay' | 'approve'>(); + const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy); + const {isSmallScreenWidth} = useWindowDimensions(); + const [paymentType, setPaymentType] = useState(); + const managerID = iouReport?.managerID ?? 0; const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); @@ -162,6 +170,28 @@ function ReportPreview({ [chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled], ); + const confirmPayment = (type?: PaymentMethodType | undefined) => { + if (!type) { + return; + } + setPaymentType(type); + setRequestType('pay'); + if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { + setIsHoldMenuVisible(true); + } else if (chatReport) { + IOU.payMoneyRequest(type, chatReport, iouReport as Report, false); + } + }; + + const confirmApproval = () => { + setRequestType('approve'); + if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { + setIsHoldMenuVisible(true); + } else { + IOU.approveMoneyRequest(iouReport as Report, true); + } + }; + const getDisplayAmount = (): string => { if (totalDisplaySpend) { return CurrencyUtils.convertToDisplayString(totalDisplaySpend, iouReport?.currency); @@ -368,7 +398,8 @@ function ReportPreview({ policyID={policyID} chatReportID={chatReportID} iouReport={iouReport} - onPress={(paymentType?: PaymentMethodType) => chatReport && iouReport && paymentType && IOU.payMoneyRequest(paymentType, chatReport, iouReport)} + onPress={confirmPayment} + confirmApproval={confirmApproval} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} shouldHidePaymentOptions={!shouldShowPayButton} @@ -399,6 +430,19 @@ function ReportPreview({ + {isHoldMenuVisible && requestType !== undefined && ( + setIsHoldMenuVisible(false)} + isVisible={isHoldMenuVisible} + paymentType={paymentType} + chatReport={chatReport} + moneyRequestReport={iouReport as Report} + /> + )} ); } From 68aa796fddcffcc8f4f1b128d9ad0af46c5ace9e Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 31 May 2024 13:19:00 +0700 Subject: [PATCH 024/109] fix lint --- src/components/ReportActionItem/ReportPreview.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 933381b9e116..755ca06ecb4d 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -179,6 +179,7 @@ function ReportPreview({ if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport) { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style IOU.payMoneyRequest(type, chatReport, iouReport as Report, false); } }; @@ -188,6 +189,7 @@ function ReportPreview({ if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style IOU.approveMoneyRequest(iouReport as Report, true); } }; @@ -440,6 +442,7 @@ function ReportPreview({ isVisible={isHoldMenuVisible} paymentType={paymentType} chatReport={chatReport} + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style moneyRequestReport={iouReport as Report} /> )} From 4c6771566bc96f37dac77dcbe386b2b1e64a7822 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 31 May 2024 13:34:12 +0700 Subject: [PATCH 025/109] safely check iouReport --- src/components/ReportActionItem/ReportPreview.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 755ca06ecb4d..ef34c6ba2982 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -178,9 +178,8 @@ function ReportPreview({ setRequestType('pay'); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); - } else if (chatReport) { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - IOU.payMoneyRequest(type, chatReport, iouReport as Report, false); + } else if (chatReport && iouReport) { + IOU.payMoneyRequest(type, chatReport, iouReport, false); } }; @@ -189,8 +188,7 @@ function ReportPreview({ if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - IOU.approveMoneyRequest(iouReport as Report, true); + IOU.approveMoneyRequest(iouReport ?? {}, true); } }; @@ -432,7 +430,7 @@ function ReportPreview({ - {isHoldMenuVisible && requestType !== undefined && ( + {isHoldMenuVisible && requestType !== undefined && !!iouReport && ( )} From 21b35f7eb2f6ce6abad4c431c1876d0bdb0745e0 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 31 May 2024 15:15:23 +0700 Subject: [PATCH 026/109] fix test --- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index ef34c6ba2982..15e33b2d4d89 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -430,7 +430,7 @@ function ReportPreview({ - {isHoldMenuVisible && requestType !== undefined && !!iouReport && ( + {isHoldMenuVisible && !!iouReport && requestType !== undefined && ( Date: Fri, 31 May 2024 16:25:11 +0700 Subject: [PATCH 027/109] fix perf test --- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 15e33b2d4d89..e1db781c5d16 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -178,8 +178,8 @@ function ReportPreview({ setRequestType('pay'); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); - } else if (chatReport && iouReport) { - IOU.payMoneyRequest(type, chatReport, iouReport, false); + } else if (chatReport) { + IOU.payMoneyRequest(type, chatReport, iouReport!, false); } }; From fca320f7dc7a7b44e9c63d5a15012e92416ce8e9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 31 May 2024 16:34:56 +0700 Subject: [PATCH 028/109] fix lint --- src/components/ReportActionItem/ReportPreview.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index e1db781c5d16..5fcf1c53a337 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -179,7 +179,8 @@ function ReportPreview({ if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport) { - IOU.payMoneyRequest(type, chatReport, iouReport!, false); + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + IOU.payMoneyRequest(type, chatReport, iouReport as Report, false); } }; From 0c7c71d46fecc8cde46f009d8ccb472316786ef8 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 12:19:55 +0700 Subject: [PATCH 029/109] fix chat doesn't scroll to bottom --- src/libs/actions/IOU.ts | 7 ++++++- src/libs/actions/Report.ts | 2 +- src/pages/home/report/ReportActionsList.tsx | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 128ee5b85b7b..2702dd4b7b0c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1983,12 +1983,17 @@ function getMoneyRequestInformation( reportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction as ReportPreviewAction, false, comment, optimisticTransaction); } else { reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, iouReport, comment, optimisticTransaction); + chatReport.lastVisibleActionCreated = reportPreviewAction.created; // Generated ReportPreview action is a parent report action of the iou report. // We are setting the iou report's parentReportActionID to display subtitle correctly in IOU page when offline. iouReport.parentReportActionID = reportPreviewAction.reportActionID; } + if (iouAction) { + iouReport.lastVisibleActionCreated = iouAction.created; + } + const shouldCreateOptimisticPersonalDetails = isNewChatReport && !allPersonalDetails[payerAccountID]; // Add optimistic personal details for participant const optimisticPersonalDetailListAction = shouldCreateOptimisticPersonalDetails @@ -4625,7 +4630,7 @@ function startSplitBill({ API.write(WRITE_COMMANDS.START_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); Navigation.dismissModalWithReport(splitChatReport); - Report.notifyNewAction(splitChatReport.chatReportID ?? '', currentUserAccountID); + Report.notifyNewAction(splitChatReport.reportID ?? '', currentUserAccountID); } /** Used for editing a split expense while it's still scanning or when SmartScan fails, it completes a split expense started by startSplitBill above. diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7c73ef4a1eac..1dd67332cb99 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -478,7 +478,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { const lastCommentText = ReportUtils.formatReportLastMessageText(lastComment?.text ?? ''); const optimisticReport: Partial = { - lastVisibleActionCreated: currentTime, + lastVisibleActionCreated: lastAction?.created, lastMessageTranslationKey: lastComment?.translationKey ?? '', lastMessageText: lastCommentText, lastMessageHtml: lastCommentText, diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index c700fea4fb85..6ad01455b543 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -211,15 +211,17 @@ function ReportActionsList({ ); // whisper action doesn't affect lastVisibleActionCreated, so we should not take it into account while checking if there is the newest report action - const newestVisibleReportAction = useMemo(() => sortedVisibleReportActions.find((item) => !ReportActionsUtils.isWhisperAction(item)) ?? null, [sortedVisibleReportActions]); + // const newestVisibleReportAction = useMemo(() => sortedVisibleReportActions.find((item) => !ReportActionsUtils.isWhisperAction(item)) ?? null, [sortedVisibleReportActions]); const lastActionIndex = sortedVisibleReportActions[0]?.reportActionID; const reportActionSize = useRef(sortedVisibleReportActions.length); - const hasNewestReportAction = newestVisibleReportAction?.created === report.lastVisibleActionCreated; + const hasNewestReportAction = sortedVisibleReportActions[0]?.created === report.lastVisibleActionCreated; const hasNewestReportActionRef = useRef(hasNewestReportAction); hasNewestReportActionRef.current = hasNewestReportAction; const previousLastIndex = useRef(lastActionIndex); + console.log(sortedVisibleReportActions[0].created, report.lastVisibleActionCreated) + const isLastPendingActionIsDelete = sortedReportActions?.[0]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; const linkedReportActionID = route.params?.reportActionID ?? ''; @@ -346,6 +348,7 @@ function ReportActionsList({ (isFromCurrentUser: boolean) => { // If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where // they are now in the list. + console.log(hasNewestReportActionRef.current); if (!isFromCurrentUser || !hasNewestReportActionRef.current) { return; } From cd48cd5f50f60c908b6df795ebd294b8fe61e20b Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 3 Jun 2024 16:43:48 +0700 Subject: [PATCH 030/109] Fix: OpenWorkspaceView API call is made on pages that do not need it --- src/pages/workspace/WorkspacePageWithSections.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 1 + src/pages/workspace/bills/WorkspaceBillsPage.tsx | 1 + src/pages/workspace/card/WorkspaceCardPage.tsx | 1 + src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx | 1 + .../reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx | 1 - .../workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx | 1 - .../workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx | 1 - src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx | 1 - src/pages/workspace/travel/WorkspaceTravelPage.tsx | 1 + src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 1 - 11 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 58288f213818..738a0663b94b 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -106,7 +106,7 @@ function WorkspacePageWithSections({ reimbursementAccount = CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA, route, shouldUseScrollView = false, - shouldSkipVBBACall = false, + shouldSkipVBBACall = true, shouldShowBackButton = false, user, shouldShowLoading = true, diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index be6143ff6240..4d9ed3e138fd 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -125,6 +125,7 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi shouldShowOfflineIndicatorInWideScreen shouldShowNonAdmin icon={Illustrations.House} + shouldSkipVBBACall={false} > {(hasVBA?: boolean) => ( diff --git a/src/pages/workspace/bills/WorkspaceBillsPage.tsx b/src/pages/workspace/bills/WorkspaceBillsPage.tsx index 9e1810d74793..cdd733d59141 100644 --- a/src/pages/workspace/bills/WorkspaceBillsPage.tsx +++ b/src/pages/workspace/bills/WorkspaceBillsPage.tsx @@ -23,6 +23,7 @@ function WorkspaceBillsPage({route}: WorkspaceBillsPageProps) { shouldUseScrollView headerText={translate('workspace.common.bills')} route={route} + shouldSkipVBBACall={false} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_BILLS} shouldShowOfflineIndicatorInWideScreen > diff --git a/src/pages/workspace/card/WorkspaceCardPage.tsx b/src/pages/workspace/card/WorkspaceCardPage.tsx index eeec0edf106b..c79a335376d6 100644 --- a/src/pages/workspace/card/WorkspaceCardPage.tsx +++ b/src/pages/workspace/card/WorkspaceCardPage.tsx @@ -24,6 +24,7 @@ function WorkspaceCardPage({route}: WorkspaceCardPageProps) { shouldUseScrollView headerText={translate('workspace.common.card')} route={route} + shouldSkipVBBACall={false} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_CARD} shouldShowOfflineIndicatorInWideScreen > diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx index ef0a73788dfa..f1a973d5c47d 100644 --- a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx +++ b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx @@ -23,6 +23,7 @@ function WorkspaceInvoicesPage({route}: WorkspaceInvoicesPageProps) { headerText={translate('workspace.common.invoices')} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_INVOICES} shouldShowOfflineIndicatorInWideScreen + shouldSkipVBBACall={false} route={route} > {(hasVBA?: boolean, policyID?: string) => ( diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx index 6faf23af289e..b99157b0231a 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx @@ -100,7 +100,6 @@ function WorkspaceRateAndUnitPage(props: WorkspaceRateAndUnitPageProps) { headerText={translate('workspace.reimburse.trackDistance')} route={props.route} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_REIMBURSE} - shouldSkipVBBACall backButtonRoute="" shouldShowLoading={false} shouldShowBackButton diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx index 9c7eaa82167f..52fc35db59d6 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx @@ -63,7 +63,6 @@ function WorkspaceRatePage(props: WorkspaceRatePageProps) { headerText={translate('workspace.reimburse.trackDistanceRate')} route={props.route} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_REIMBURSE} - shouldSkipVBBACall backButtonRoute={ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(props.policy?.id ?? '')} shouldShowLoading={false} shouldShowBackButton diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx index 92d76b4a33c6..8580b112c8db 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx @@ -51,7 +51,6 @@ function WorkspaceUnitPage(props: WorkspaceUnitPageProps) { headerText={translate('workspace.reimburse.trackDistanceUnit')} route={props.route} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_REIMBURSE} - shouldSkipVBBACall backButtonRoute={ROUTES.WORKSPACE_RATE_AND_UNIT.getRoute(props.policy?.id ?? '')} shouldShowLoading={false} shouldShowBackButton diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx index 6bb15a8a17ce..d929ec10748a 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx @@ -20,7 +20,6 @@ function WorkspaceReimbursePage({route, policy}: WorkspaceReimbursePageProps) { headerText={translate('workspace.common.reimburse')} route={route} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_REIMBURSE} - shouldSkipVBBACall shouldShowLoading={false} shouldShowOfflineIndicatorInWideScreen > diff --git a/src/pages/workspace/travel/WorkspaceTravelPage.tsx b/src/pages/workspace/travel/WorkspaceTravelPage.tsx index 1acae9e6d359..eb61397d10b2 100644 --- a/src/pages/workspace/travel/WorkspaceTravelPage.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelPage.tsx @@ -25,6 +25,7 @@ function WorkspaceTravelPage({route}: WorkspaceTravelPageProps) { route={route} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_TRAVEL} shouldShowOfflineIndicatorInWideScreen + shouldSkipVBBACall={false} > {(hasVBA, policyID) => ( diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index 5b17a4e26051..a008f89df50e 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -288,7 +288,6 @@ function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPagePr guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_WORKFLOWS} shouldShowOfflineIndicatorInWideScreen shouldShowNotFoundPage={!isPaidGroupPolicy || !isPolicyAdmin} - shouldSkipVBBACall isLoading={isLoading} shouldShowLoading={isLoading} shouldUseScrollView From 0d68d3db47d84df13d7d79e93df102550291b834 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 3 Jun 2024 11:49:54 +0200 Subject: [PATCH 031/109] fix sessionStorage not found for mobile --- src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx | 6 +++--- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx index 419dbef5d171..b4d57b5e4733 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx +++ b/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx @@ -8,10 +8,10 @@ function ActiveWorkspaceContextProvider({children}: ChildrenProps) { const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { updateActiveWorkspaceID(workspaceID); - if (workspaceID) { - sessionStorage.setItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID, workspaceID); + if (workspaceID && sessionStorage) { + sessionStorage?.setItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID, workspaceID); } else { - sessionStorage.removeItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); + sessionStorage?.removeItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); } }, []); diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 3e48b89ad5c0..30f0c2766251 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -7,6 +7,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -23,7 +24,8 @@ import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES, {Route} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; type PurposeForUsingExpensifyModalOnyxProps = { @@ -36,7 +38,8 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); - const activeWorkspaceID = sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); + const {activeWorkspaceID: contextActiveWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = sessionStorage ? sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID) : contextActiveWorkspaceID; useEffect(() => { const navigationState = navigation.getState() as State | undefined; From 6b063e83776bf2a66a45705dd2406dc6c6a0611b Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 3 Jun 2024 12:21:16 +0200 Subject: [PATCH 032/109] Fix sessionStorage not found error for native devices --- src/App.tsx | 2 +- .../ActiveWorkspaceProvider/index.tsx | 19 +++ .../index.website.tsx} | 2 +- .../BottomTabBar/index.tsx | 111 ++++++++++++++++++ .../index.website.tsx} | 0 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 src/components/ActiveWorkspaceProvider/index.tsx rename src/components/{ActiveWorkspace/ActiveWorkspaceProvider.tsx => ActiveWorkspaceProvider/index.website.tsx} (92%) create mode 100644 src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx rename src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/{BottomTabBar.tsx => BottomTabBar/index.website.tsx} (100%) diff --git a/src/App.tsx b/src/App.tsx index 6316fa80fba1..9eda57816e9d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,7 @@ import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; import ActiveElementRoleProvider from './components/ActiveElementRoleProvider'; -import ActiveWorkspaceContextProvider from './components/ActiveWorkspace/ActiveWorkspaceProvider'; +import ActiveWorkspaceContextProvider from './components/ActiveWorkspaceProvider'; import ColorSchemeWrapper from './components/ColorSchemeWrapper'; import ComposeProviders from './components/ComposeProviders'; import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackground'; diff --git a/src/components/ActiveWorkspaceProvider/index.tsx b/src/components/ActiveWorkspaceProvider/index.tsx new file mode 100644 index 000000000000..bc7260cdf10b --- /dev/null +++ b/src/components/ActiveWorkspaceProvider/index.tsx @@ -0,0 +1,19 @@ +import React, {useMemo, useState} from 'react'; +import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; + +function ActiveWorkspaceContextProvider({children}: ChildrenProps) { + const [activeWorkspaceID, setActiveWorkspaceID] = useState(undefined); + + const value = useMemo( + () => ({ + activeWorkspaceID, + setActiveWorkspaceID, + }), + [activeWorkspaceID, setActiveWorkspaceID], + ); + + return {children}; +} + +export default ActiveWorkspaceContextProvider; diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx b/src/components/ActiveWorkspaceProvider/index.website.tsx similarity index 92% rename from src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx rename to src/components/ActiveWorkspaceProvider/index.website.tsx index b4d57b5e4733..82e46d70f896 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx +++ b/src/components/ActiveWorkspaceProvider/index.website.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useMemo, useState} from 'react'; +import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; import CONST from '@src/CONST'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import ActiveWorkspaceContext from './ActiveWorkspaceContext'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { const [activeWorkspaceID, updateActiveWorkspaceID] = useState(undefined); diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx new file mode 100644 index 000000000000..ac4c0156f17c --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -0,0 +1,111 @@ +import {useNavigation, useNavigationState} from '@react-navigation/native'; +import React, {useCallback, useEffect} from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import {PressableWithFeedback} from '@components/Pressable'; +import Tooltip from '@components/Tooltip'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Session from '@libs/actions/Session'; +import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; +import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; +import Navigation from '@libs/Navigation/Navigation'; +import type {RootStackParamList, State} from '@libs/Navigation/types'; +import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; +import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; +import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; +import variables from '@styles/variables'; +import * as Welcome from '@userActions/Welcome'; +import CONST from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; + +type PurposeForUsingExpensifyModalOnyxProps = { + isLoadingApp: OnyxEntry; +}; +type PurposeForUsingExpensifyModalProps = PurposeForUsingExpensifyModalOnyxProps; + +function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const navigation = useNavigation(); + const {activeWorkspaceID} = useActiveWorkspace(); + + useEffect(() => { + const navigationState = navigation.getState() as State | undefined; + const routes = navigationState?.routes; + const currentRoute = routes?.[navigationState?.index ?? 0]; + // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method. + // To prevent this, the value of the bottomTabRoute?.name is checked here + if (Boolean(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || Session.isAnonymousUser()) { + return; + } + + Welcome.isOnboardingFlowCompleted({onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_ROOT)}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoadingApp]); + + // Parent navigator of the bottom tab bar is the root navigator. + const currentTabName = useNavigationState((state) => { + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(state); + + if (topmostCentralPaneRoute && topmostCentralPaneRoute.name === SCREENS.SEARCH.CENTRAL_PANE) { + return SCREENS.SEARCH.CENTRAL_PANE; + } + + const topmostBottomTabRoute = getTopmostBottomTabRoute(state); + return topmostBottomTabRoute?.name ?? SCREENS.HOME; + }); + + const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); + + const navigateToChats = useCallback(() => { + const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/r` as Route) : ROUTES.HOME; + Navigation.navigate(route); + }, [activeWorkspaceID]); + + return ( + + + + + + {chatTabBrickRoad && ( + + )} + + + + + + + ); +} + +BottomTabBar.displayName = 'BottomTabBar'; + +export default withOnyx({ + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, +})(BottomTabBar); diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx similarity index 100% rename from src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx rename to src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx From 99eebd81d885e3e77b89aad1a19b0744c43630b7 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 18:43:32 +0700 Subject: [PATCH 033/109] add new const --- src/CONST.ts | 4 ++++ src/components/MoneyReportHeader.tsx | 7 ++++--- src/components/ProcessMoneyReportHoldMenu.tsx | 6 ++++-- src/components/ReportActionItem/ReportPreview.tsx | 7 ++++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index de4e3305eddc..0c10b01c3d15 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1552,6 +1552,10 @@ const CONST = { CATEGORIZE: 'categorize', SHARE: 'share', }, + ACTION_TYPE: { + APPROVE: 'approve', + PAY: 'pay', + }, DEFAULT_AMOUNT: 0, TYPE: { SEND: 'send', diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index aad801a7c259..f60e2a3cb01b 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -19,6 +19,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; @@ -79,7 +80,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || ReportUtils.isTrackExpenseReport(transactionThreadReport)) && !isDeletedParentAction; const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); - const [requestType, setRequestType] = useState<'pay' | 'approve'>(); + const [requestType, setRequestType] = useState>(); const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy); const policyType = policy?.type; const isPayer = ReportUtils.isPayer(session, moneyRequestReport); @@ -124,7 +125,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea return; } setPaymentType(type); - setRequestType('pay'); + setRequestType(CONST.IOU.ACTION_TYPE.PAY); if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport) { @@ -133,7 +134,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea }; const confirmApproval = () => { - setRequestType('approve'); + setRequestType(CONST.IOU.ACTION_TYPE.APPROVE); if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { setIsHoldMenuVisible(true); } else { diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 6e81c9d57bc8..08a78f699916 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -4,9 +4,11 @@ import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import {isLinkedTransactionHeld} from '@libs/ReportActionsUtils'; import * as IOU from '@userActions/IOU'; +import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import DeepValueOf from '@src/types/utils/DeepValueOf'; import DecisionModal from './DecisionModal'; type ProcessMoneyReportHoldMenuProps = { @@ -35,7 +37,7 @@ type ProcessMoneyReportHoldMenuProps = { paymentType?: PaymentMethodType; /** Type of action handled */ - requestType?: 'pay' | 'approve'; + requestType?: DeepValueOf; }; function ProcessMoneyReportHoldMenu({ @@ -50,7 +52,7 @@ function ProcessMoneyReportHoldMenu({ moneyRequestReport, }: ProcessMoneyReportHoldMenuProps) { const {translate} = useLocalize(); - const isApprove = requestType === 'approve'; + const isApprove = requestType === CONST.IOU.ACTION_TYPE.APPROVE; const onSubmit = (full: boolean) => { if (isApprove) { diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 5fcf1c53a337..afb8beebfd33 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -35,6 +35,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations, UserWallet} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import DeepValueOf from '@src/types/utils/DeepValueOf'; import type {PendingMessageProps} from './MoneyRequestPreview/types'; import ReportActionItemImages from './ReportActionItemImages'; @@ -122,7 +123,7 @@ function ReportPreview({ ); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); - const [requestType, setRequestType] = useState<'pay' | 'approve'>(); + const [requestType, setRequestType] = useState>(); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy); const {isSmallScreenWidth} = useWindowDimensions(); const [paymentType, setPaymentType] = useState(); @@ -175,7 +176,7 @@ function ReportPreview({ return; } setPaymentType(type); - setRequestType('pay'); + setRequestType(CONST.IOU.ACTION_TYPE.PAY); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport) { @@ -185,7 +186,7 @@ function ReportPreview({ }; const confirmApproval = () => { - setRequestType('approve'); + setRequestType(CONST.IOU.ACTION_TYPE.APPROVE); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else { From 0091e82822ee70a60a32efa380738c8ffabf2160 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 20:37:21 +0700 Subject: [PATCH 034/109] fix lint --- src/components/MoneyReportHeader.tsx | 2 +- src/components/ProcessMoneyReportHoldMenu.tsx | 2 +- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index f60e2a3cb01b..789bcaac7355 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -19,7 +19,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import DeepValueOf from '@src/types/utils/DeepValueOf'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 08a78f699916..f3e7790a6b5f 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -8,7 +8,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import DeepValueOf from '@src/types/utils/DeepValueOf'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import DecisionModal from './DecisionModal'; type ProcessMoneyReportHoldMenuProps = { diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index afb8beebfd33..1fa16a73cff2 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -35,7 +35,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations, UserWallet} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import DeepValueOf from '@src/types/utils/DeepValueOf'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {PendingMessageProps} from './MoneyRequestPreview/types'; import ReportActionItemImages from './ReportActionItemImages'; From d67febbd775ae455c99dff200bf095a906da8ddd Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 3 Jun 2024 23:34:05 +0100 Subject: [PATCH 035/109] feat: calculate all tag required violations locally --- src/libs/Violations/ViolationsUtils.ts | 42 ++++++++++++++++++-------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 742b03c7554e..29516d3e560e 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -7,7 +7,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyCategories, PolicyTagList, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {PolicyCategories, PolicyTagList, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; /** * Calculates tag out of policy and missing tag violations for the given transaction @@ -52,15 +52,29 @@ function getTagViolationsForSingleLevelTags( * Calculates missing tag violations for policies with dependent tags, * by returning one per tag with its corresponding tagName in the data */ -function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[]) { - return [ - ...transactionViolations, - ...Object.values(policyTagList).map((tagList) => ({ - name: CONST.VIOLATIONS.MISSING_TAG, - type: CONST.VIOLATION_TYPES.VIOLATION, - data: {tagName: tagList.name}, - })), - ]; +function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], tagName: string | undefined) { + const tagViolations = [...transactionViolations]; + + if (!tagName) { + Object.values(policyTagList).forEach((tagList) => + tagViolations.push({ + name: CONST.VIOLATIONS.MISSING_TAG, + type: CONST.VIOLATION_TYPES.VIOLATION, + data: {tagName: tagList.name}, + }), + ); + } else { + const tags = TransactionUtils.getTagArrayFromName(tagName); + if (Object.keys(policyTagList).length !== tags.length || tags.includes('')) { + tagViolations.push({ + name: CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED, + type: CONST.VIOLATION_TYPES.VIOLATION, + data: undefined, + }); + } + } + + return tagViolations; } /** @@ -135,11 +149,13 @@ function getTagViolationsForMultiLevelTags( ): TransactionViolation[] { const filteredTransactionViolations = transactionViolations.filter( (violation) => - violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY && violation.name !== CONST.VIOLATIONS.MISSING_TAG, + !( + [CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, CONST.VIOLATIONS.TAG_OUT_OF_POLICY, CONST.VIOLATIONS.MISSING_TAG, CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED] as ViolationName[] + ).includes(violation.name), ); - if (hasDependentTags && !updatedTransaction.tag) { - return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations); + if (hasDependentTags) { + return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations, updatedTransaction.tag); } return getTagViolationForIndependentTags(policyTagList, filteredTransactionViolations, updatedTransaction); From d4e7d5d6f98614885aefade9cb335791934512c5 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 13:10:09 +0700 Subject: [PATCH 036/109] fix lint --- src/pages/home/report/ReportActionsList.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 6ad01455b543..940feb99bc27 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -210,9 +210,6 @@ function ReportActionsList({ [sortedReportActions, isOffline], ); - // whisper action doesn't affect lastVisibleActionCreated, so we should not take it into account while checking if there is the newest report action - // const newestVisibleReportAction = useMemo(() => sortedVisibleReportActions.find((item) => !ReportActionsUtils.isWhisperAction(item)) ?? null, [sortedVisibleReportActions]); - const lastActionIndex = sortedVisibleReportActions[0]?.reportActionID; const reportActionSize = useRef(sortedVisibleReportActions.length); const hasNewestReportAction = sortedVisibleReportActions[0]?.created === report.lastVisibleActionCreated; @@ -220,8 +217,6 @@ function ReportActionsList({ hasNewestReportActionRef.current = hasNewestReportAction; const previousLastIndex = useRef(lastActionIndex); - console.log(sortedVisibleReportActions[0].created, report.lastVisibleActionCreated) - const isLastPendingActionIsDelete = sortedReportActions?.[0]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; const linkedReportActionID = route.params?.reportActionID ?? ''; @@ -348,7 +343,6 @@ function ReportActionsList({ (isFromCurrentUser: boolean) => { // If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where // they are now in the list. - console.log(hasNewestReportActionRef.current); if (!isFromCurrentUser || !hasNewestReportActionRef.current) { return; } From 7392bd5bb7c192f0055f089cbcfeecc0941b971f Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 15:53:27 +0700 Subject: [PATCH 037/109] fix perf test --- src/components/ReportActionItem/ReportPreview.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 1fa16a73cff2..e97389b23ecc 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -171,7 +171,7 @@ function ReportPreview({ [chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled], ); - const confirmPayment = (type?: PaymentMethodType | undefined) => { + const confirmPayment = (type: PaymentMethodType | undefined) => { if (!type) { return; } @@ -400,7 +400,9 @@ function ReportPreview({ policyID={policyID} chatReportID={chatReportID} iouReport={iouReport} - onPress={confirmPayment} + onPress={(type) => { + confirmPayment(type); + }} confirmApproval={confirmApproval} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} From fd6d0f77d23ba1d839487e31b527e9bcda3f5e57 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 15:58:44 +0700 Subject: [PATCH 038/109] update suggestion --- src/components/ReportActionItem/ReportPreview.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index e97389b23ecc..944b846f29f5 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -179,9 +179,8 @@ function ReportPreview({ setRequestType(CONST.IOU.ACTION_TYPE.PAY); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); - } else if (chatReport) { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - IOU.payMoneyRequest(type, chatReport, iouReport as Report, false); + } else if (chatReport && iouReport) { + IOU.payMoneyRequest(type, chatReport, iouReport, false); } }; @@ -400,9 +399,7 @@ function ReportPreview({ policyID={policyID} chatReportID={chatReportID} iouReport={iouReport} - onPress={(type) => { - confirmPayment(type); - }} + onPress={confirmPayment} confirmApproval={confirmApproval} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} @@ -434,7 +431,7 @@ function ReportPreview({ - {isHoldMenuVisible && !!iouReport && requestType !== undefined && ( + {isHoldMenuVisible && iouReport && requestType !== undefined && ( Date: Tue, 4 Jun 2024 16:31:37 +0700 Subject: [PATCH 039/109] fix perf test --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 848376fb4b6d..d095defe8bcd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6399,8 +6399,8 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry } return [ - CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency ?? ''), - CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency ?? ''), + CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency ?? CONST.CURRENCY.USD), + CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency ?? CONST.CURRENCY.USD), ]; } From e7969dee6158575ffa6becde8f3ad893edcda0d1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 16:56:31 +0700 Subject: [PATCH 040/109] fix type --- src/CONST.ts | 4 ---- src/components/MoneyReportHeader.tsx | 8 ++++---- src/components/ProcessMoneyReportHoldMenu.tsx | 7 +++++-- src/components/ReportActionItem/ReportPreview.tsx | 8 ++++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 2e6ef61daa88..aa3ade14b040 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1552,10 +1552,6 @@ const CONST = { CATEGORIZE: 'categorize', SHARE: 'share', }, - ACTION_TYPE: { - APPROVE: 'approve', - PAY: 'pay', - }, DEFAULT_AMOUNT: 0, TYPE: { SEND: 'send', diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 8c866731d150..47ea32d7fa41 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -19,8 +19,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type {ActionHandledType} from './ProcessMoneyReportHoldMenu'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; @@ -80,7 +80,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || ReportUtils.isTrackExpenseReport(transactionThreadReport)) && !isDeletedParentAction; const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); - const [requestType, setRequestType] = useState>(); + const [requestType, setRequestType] = useState(); const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy); const policyType = policy?.type; const isPayer = ReportUtils.isPayer(session, moneyRequestReport); @@ -125,7 +125,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea return; } setPaymentType(type); - setRequestType(CONST.IOU.ACTION_TYPE.PAY); + setRequestType(CONST.IOU.REPORT_ACTION_TYPE.PAY); if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport) { @@ -134,7 +134,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea }; const confirmApproval = () => { - setRequestType(CONST.IOU.ACTION_TYPE.APPROVE); + setRequestType(CONST.IOU.REPORT_ACTION_TYPE.APPROVE); if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { setIsHoldMenuVisible(true); } else { diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index f3e7790a6b5f..01896fb0a3cb 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -11,6 +11,8 @@ import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import DecisionModal from './DecisionModal'; +type ActionHandledType = DeepValueOf; + type ProcessMoneyReportHoldMenuProps = { /** The chat report this report is linked to */ chatReport: OnyxEntry; @@ -37,7 +39,7 @@ type ProcessMoneyReportHoldMenuProps = { paymentType?: PaymentMethodType; /** Type of action handled */ - requestType?: DeepValueOf; + requestType?: ActionHandledType; }; function ProcessMoneyReportHoldMenu({ @@ -52,7 +54,7 @@ function ProcessMoneyReportHoldMenu({ moneyRequestReport, }: ProcessMoneyReportHoldMenuProps) { const {translate} = useLocalize(); - const isApprove = requestType === CONST.IOU.ACTION_TYPE.APPROVE; + const isApprove = requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE; const onSubmit = (full: boolean) => { if (isApprove) { @@ -84,3 +86,4 @@ function ProcessMoneyReportHoldMenu({ ProcessMoneyReportHoldMenu.displayName = 'ProcessMoneyReportHoldMenu'; export default ProcessMoneyReportHoldMenu; +export type {ActionHandledType}; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 944b846f29f5..2ac69fd01b0c 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -10,6 +10,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu'; +import type {ActionHandledType} from '@components/ProcessMoneyReportHoldMenu'; import SettlementButton from '@components/SettlementButton'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; @@ -35,7 +36,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations, UserWallet} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {PendingMessageProps} from './MoneyRequestPreview/types'; import ReportActionItemImages from './ReportActionItemImages'; @@ -123,7 +123,7 @@ function ReportPreview({ ); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); - const [requestType, setRequestType] = useState>(); + const [requestType, setRequestType] = useState(); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy); const {isSmallScreenWidth} = useWindowDimensions(); const [paymentType, setPaymentType] = useState(); @@ -176,7 +176,7 @@ function ReportPreview({ return; } setPaymentType(type); - setRequestType(CONST.IOU.ACTION_TYPE.PAY); + setRequestType(CONST.IOU.REPORT_ACTION_TYPE.PAY); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport && iouReport) { @@ -185,7 +185,7 @@ function ReportPreview({ }; const confirmApproval = () => { - setRequestType(CONST.IOU.ACTION_TYPE.APPROVE); + setRequestType(CONST.IOU.REPORT_ACTION_TYPE.APPROVE); if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else { From 7ccb8ffe1ec699de8a34997b33f8500c92825ea9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 16:58:36 +0700 Subject: [PATCH 041/109] refactor suggestion --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d095defe8bcd..99798c76d794 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6399,8 +6399,8 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry } return [ - CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency ?? CONST.CURRENCY.USD), - CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency ?? CONST.CURRENCY.USD), + CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * -1, iouReport?.currency), + CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * -1, iouReport?.currency), ]; } From 28220dd7e4e44522ffee29751db743d366f00982 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 17:09:20 +0700 Subject: [PATCH 042/109] run prettier --- src/components/MoneyReportHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 47ea32d7fa41..2e118f79f963 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -20,7 +20,6 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type {ActionHandledType} from './ProcessMoneyReportHoldMenu'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; @@ -28,6 +27,7 @@ import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; +import type {ActionHandledType} from './ProcessMoneyReportHoldMenu'; import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu'; import SettlementButton from './SettlementButton'; From 9b431fa629fca5e74af4e848ba7a3fe452bdec42 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 4 Jun 2024 15:19:01 +0200 Subject: [PATCH 043/109] navigate to home in native devices --- .../BottomTabBar/index.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx index ac4c0156f17c..c0679b120d0d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -1,5 +1,5 @@ import {useNavigation, useNavigationState} from '@react-navigation/native'; -import React, {useCallback, useEffect} from 'react'; +import React, {useEffect} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -24,7 +24,6 @@ import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -68,16 +67,13 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); - const navigateToChats = useCallback(() => { - const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/r` as Route) : ROUTES.HOME; - Navigation.navigate(route); - }, [activeWorkspaceID]); - return ( { + Navigation.navigate(ROUTES.HOME); + }} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.chats')} wrapperStyle={styles.flex1} From 7f647fbfd7c8d4bea13d3b4db815fc67ccce429d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 4 Jun 2024 15:30:16 +0200 Subject: [PATCH 044/109] refactoring --- .../BottomTabBar/index.tsx | 12 ++++++++---- .../BottomTabBar/index.website.tsx | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx index c0679b120d0d..e6c49f1411ab 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -1,5 +1,5 @@ import {useNavigation, useNavigationState} from '@react-navigation/native'; -import React, {useEffect} from 'react'; +import React, {useCallback, useEffect} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -24,6 +24,7 @@ import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -67,13 +68,16 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); + const navigateToChats = useCallback(() => { + const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/home` as Route) : ROUTES.HOME; + Navigation.navigate(route); + }, [activeWorkspaceID]); + return ( { - Navigation.navigate(ROUTES.HOME); - }} + onPress={navigateToChats} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.chats')} wrapperStyle={styles.flex1} diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 30f0c2766251..d989f3747f6b 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -70,7 +70,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID as string | undefined); const navigateToChats = useCallback(() => { - const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/r` as Route) : ROUTES.HOME; + const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/home` as Route) : ROUTES.HOME; Navigation.navigate(route); }, [activeWorkspaceID]); From 0dc7238a8a40007130fdde38de0a8b6b2387bff5 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 5 Jun 2024 02:06:21 +0700 Subject: [PATCH 045/109] fix: stale personal details while creating task with new user --- src/libs/ReportUtils.ts | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7e99c60cb618..1bf4a005876a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6043,18 +6043,27 @@ function getTaskAssigneeChatOnyxData( }, ); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${assigneeChatReportID}`, - value: { - pendingFields: { - createChat: null, + // BE will send different report's participants and assigneeAccountID. We clear the optimistic ones to avoid duplicated entries + successData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${assigneeChatReportID}`, + value: { + pendingFields: { + createChat: null, + }, + isOptimisticReport: false, + participants: {[assigneeAccountID]: null}, }, - isOptimisticReport: false, - // BE will send a different participant. We clear the optimistic one to avoid duplicated entries - participants: {[assigneeAccountID]: null}, }, - }); + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [assigneeAccountID]: null, + }, + }, + ); failureData.push( { From b29c8fd4c543b48b7a0d2e199917f01401791ce2 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Wed, 5 Jun 2024 01:49:00 +0530 Subject: [PATCH 046/109] fix: Distance rates - Default distance rate is Disabled instead of Enabled after creating new WS. Signed-off-by: Krishna Gupta --- src/CONST.ts | 2 +- src/libs/actions/Policy/Policy.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 32c500962d8a..23ac96f0e573 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1826,7 +1826,7 @@ const CONST = { NAME_DISTANCE: 'Distance', DISTANCE_UNIT_MILES: 'mi', DISTANCE_UNIT_KILOMETERS: 'km', - MILEAGE_IRS_RATE: 0.655, + MILEAGE_IRS_RATE: 0.67, DEFAULT_RATE: 'Default Rate', RATE_DECIMALS: 3, FAKE_P2P_ID: '_FAKE_P2P_ID_', diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 4d918352ba91..32c347925d84 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2008,6 +2008,7 @@ function buildOptimisticCustomUnits(): OptimisticCustomUnits { name: CONST.CUSTOM_UNITS.DEFAULT_RATE, rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, currency, + enabled: true, }, }, }, From dfad444c2dc7d3b1e97b9db130c4308739206b32 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 5 Jun 2024 14:51:09 +0700 Subject: [PATCH 047/109] resolve conflict --- .../ReportActionItem/ReportPreview.tsx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 7d0ff591795b..84e1691b3abe 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -180,7 +180,11 @@ function ReportPreview({ if (ReportUtils.hasHeldExpenses(iouReport?.reportID)) { setIsHoldMenuVisible(true); } else if (chatReport && iouReport) { - IOU.payMoneyRequest(type, chatReport, iouReport, false); + if (ReportUtils.isInvoiceReport(iouReport)) { + IOU.payInvoice(type, chatReport, iouReport); + } else { + IOU.payMoneyRequest(type, chatReport, iouReport); + } } }; @@ -311,17 +315,6 @@ function ReportPreview({ }; }, [formattedMerchant, formattedDescription, moneyRequestComment, translate, numberOfRequests, numberOfScanningReceipts, numberOfPendingRequests]); - const confirmPayment = (paymentMethodType?: PaymentMethodType) => { - if (!paymentMethodType || !chatReport || !iouReport) { - return; - } - if (ReportUtils.isInvoiceReport(iouReport)) { - IOU.payInvoice(paymentMethodType, chatReport, iouReport); - } else { - IOU.payMoneyRequest(paymentMethodType, chatReport, iouReport); - } - }; - return ( Date: Wed, 5 Jun 2024 15:09:40 +0700 Subject: [PATCH 048/109] move update to buildOnyxDataForMoneyRequest --- src/libs/actions/IOU.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1050fdb48779..fe3a5a8425dd 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -507,6 +507,7 @@ function buildOnyxDataForMoneyRequest( ...iouReport, lastMessageText: iouAction.message?.[0]?.text, lastMessageHtml: iouAction.message?.[0]?.html, + lastVisibleActionCreated: iouAction.created, pendingFields: { ...(shouldCreateNewMoneyRequestReport ? {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : {preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }, @@ -1991,10 +1992,6 @@ function getMoneyRequestInformation( iouReport.parentReportActionID = reportPreviewAction.reportActionID; } - if (iouAction) { - iouReport.lastVisibleActionCreated = iouAction.created; - } - const shouldCreateOptimisticPersonalDetails = isNewChatReport && !allPersonalDetails[payerAccountID]; // Add optimistic personal details for participant const optimisticPersonalDetailListAction = shouldCreateOptimisticPersonalDetails From 30189caf9c1239a176a45aa944d4550b23a5ba83 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 5 Jun 2024 11:07:15 +0100 Subject: [PATCH 049/109] refactor: apply pull request suggestions --- .../ReportActionItem/MoneyRequestView.tsx | 41 +++++++++++-------- src/hooks/useViolations.ts | 7 ++-- src/libs/Violations/ViolationsUtils.ts | 20 +++++---- src/types/onyx/TransactionViolation.ts | 1 - 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 434395e4095c..fbba403e8e0b 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -174,7 +174,8 @@ function MoneyRequestView({ const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback( - (field: ViolationField, data?: OnyxTypes.TransactionViolation['data']): boolean => !!canUseViolations && getViolationsForField(field, data).length > 0, + (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags?: boolean): boolean => + !!canUseViolations && getViolationsForField(field, data, policyHasDependentTags).length > 0, [canUseViolations, getViolationsForField], ); @@ -241,7 +242,7 @@ function MoneyRequestView({ const getPendingFieldAction = (fieldPath: TransactionPendingFieldsKey) => transaction?.pendingFields?.[fieldPath] ?? pendingAction; const getErrorForField = useCallback( - (field: ViolationField, data?: OnyxTypes.TransactionViolation['data']) => { + (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags?: boolean) => { // Checks applied when creating a new expense // NOTE: receipt field can return multiple violations, so we need to handle it separately const fieldChecks: Partial> = { @@ -267,14 +268,14 @@ function MoneyRequestView({ } // Return violations if there are any - if (canUseViolations && hasViolations(field, data)) { - const violations = getViolationsForField(field, data); + if (hasViolations(field, data, policyHasDependentTags)) { + const violations = getViolationsForField(field, data, policyHasDependentTags); return ViolationsUtils.getViolationTranslation(violations[0], translate); } return ''; }, - [transactionAmount, isSettled, isCancelled, isPolicyExpenseChat, isEmptyMerchant, transactionDate, hasErrors, canUseViolations, hasViolations, translate, getViolationsForField], + [transactionAmount, isSettled, isCancelled, isPolicyExpenseChat, isEmptyMerchant, transactionDate, hasErrors, hasViolations, translate, getViolationsForField], ); const distanceRequestFields = canUseP2PDistanceRequests ? ( @@ -497,21 +498,27 @@ function MoneyRequestView({ ) } brickRoadIndicator={ - getErrorForField('tag', { - tagListIndex: index, - tagListName: name, - tagListValue: TransactionUtils.getTagForDisplay(transaction, index), - policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), - }) + getErrorForField( + 'tag', + { + tagListIndex: index, + tagListName: name, + tagListValue: TransactionUtils.getTagForDisplay(transaction, index), + }, + PolicyUtils.hasDependentTags(policy, policyTagList), + ) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined } - errorText={getErrorForField('tag', { - tagListIndex: index, - tagListName: name, - tagListValue: TransactionUtils.getTagForDisplay(transaction, index), - policyHasDependentTags: PolicyUtils.hasDependentTags(policy, policyTagList), - })} + errorText={getErrorForField( + 'tag', + { + tagListIndex: index, + tagListName: name, + tagListValue: TransactionUtils.getTagForDisplay(transaction, index), + }, + PolicyUtils.hasDependentTags(policy, policyTagList), + )} /> ))} diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 7dad3be026fe..9120c0709d9a 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -62,7 +62,7 @@ function useViolations(violations: TransactionViolation[]) { }, [violations]); const getViolationsForField = useCallback( - (field: ViolationField, data?: TransactionViolation['data']) => { + (field: ViolationField, data?: TransactionViolation['data'], policyHasDependentTags?: boolean) => { const currentViolations = violationsByField.get(field) ?? []; // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation @@ -81,7 +81,7 @@ function useViolations(violations: TransactionViolation[]) { // missingTag has special logic for policies with dependent tags, because only one violation is returned for all tags // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here - if (data?.policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { + if (!!policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { return [ { ...currentViolations[0], @@ -98,7 +98,8 @@ function useViolations(violations: TransactionViolation[]) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } - // allTagLevelsRequired has special logic because we have to account for tags that are already filled + // allTagLevelsRequired has special logic because it is returned when one but not all the tags are set, + // so we need to return the violation for the tag fields without a tag set if (currentViolations[0]?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && data?.tagListValue) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 29516d3e560e..d433d123c903 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -9,6 +9,13 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyCategories, PolicyTagList, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; +const TRANSACTION_VIOLATIONS_FILTER = [ + CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, + CONST.VIOLATIONS.TAG_OUT_OF_POLICY, + CONST.VIOLATIONS.MISSING_TAG, + CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED, +] as ViolationName[]; + /** * Calculates tag out of policy and missing tag violations for the given transaction */ @@ -52,7 +59,7 @@ function getTagViolationsForSingleLevelTags( * Calculates missing tag violations for policies with dependent tags, * by returning one per tag with its corresponding tagName in the data */ -function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], tagName: string | undefined) { +function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], tagName: string) { const tagViolations = [...transactionViolations]; if (!tagName) { @@ -138,7 +145,7 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transac } /** - * Calculates some tag levels required and missing tag violations for the given transaction + * Calculates tag violations for a transaction on a policy with multi level tags */ function getTagViolationsForMultiLevelTags( updatedTransaction: Transaction, @@ -147,15 +154,10 @@ function getTagViolationsForMultiLevelTags( policyTagList: PolicyTagList, hasDependentTags: boolean, ): TransactionViolation[] { - const filteredTransactionViolations = transactionViolations.filter( - (violation) => - !( - [CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, CONST.VIOLATIONS.TAG_OUT_OF_POLICY, CONST.VIOLATIONS.MISSING_TAG, CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED] as ViolationName[] - ).includes(violation.name), - ); + const filteredTransactionViolations = transactionViolations.filter((violation) => !TRANSACTION_VIOLATIONS_FILTER.includes(violation.name)); if (hasDependentTags) { - return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations, updatedTransaction.tag); + return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations, updatedTransaction.tag ?? ''); } return getTagViolationForIndependentTags(policyTagList, filteredTransactionViolations, updatedTransaction); diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 99ce3d86dfe1..e81f7196209b 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -10,7 +10,6 @@ type TransactionViolation = { type: string; name: ViolationName; data?: { - policyHasDependentTags?: boolean; rejectedBy?: string; rejectReason?: string; formattedLimit?: string; From 7fcaeca565775b670ff86fbee4ab902fe1ef7480 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 5 Jun 2024 21:59:29 +0700 Subject: [PATCH 050/109] merge main --- src/libs/DateUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 50cb9a20dff6..032a8261bec9 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -380,7 +380,8 @@ function getDBTime(timestamp: string | number = ''): string { */ function getDBTimeWithSkew(timestamp: string | number = ''): string { if (networkTimeSkew > 0) { - return getDBTime(new Date(timestamp).valueOf() + networkTimeSkew); + const datetime = timestamp ? new Date(timestamp) : new Date(); + return getDBTime(datetime.valueOf() + networkTimeSkew); } return getDBTime(timestamp); } From 5a8f69ae5af682c807016cd51f3ac4bed82d7d4e Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 6 Jun 2024 11:50:00 +0700 Subject: [PATCH 051/109] fix lastVisibleActionCreated of task and split case --- ios/Podfile.lock | 2 +- src/libs/actions/IOU.ts | 1 + src/libs/actions/Task.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index de363c211cb3..eee5c82f82ae 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2606,7 +2606,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055 - Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 + Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 49c8cfe666a6..066ee1cd0c61 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3835,6 +3835,7 @@ function createSplitsAndOnyxData( splitChatReport.lastMessageText = splitIOUReportAction.message?.[0]?.text; splitChatReport.lastMessageHtml = splitIOUReportAction.message?.[0]?.html; splitChatReport.lastActorAccountID = currentUserAccountID; + splitChatReport.lastVisibleActionCreated = splitIOUReportAction.created; let splitChatReportNotificationPreference = splitChatReport.notificationPreference; if (splitChatReportNotificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 55d898d3d4f3..57b551510d58 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -131,10 +131,10 @@ function createTaskAndNavigate( const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assigneeAccountID, `task for ${title}`, parentReportID); optimisticTaskReport.parentReportActionID = optimisticAddCommentReport.reportAction.reportActionID; - const currentTime = DateUtils.getDBTime(); + const currentTime = DateUtils.getDBTimeWithSkew(); const lastCommentText = ReportUtils.formatReportLastMessageText(optimisticAddCommentReport?.reportAction?.message?.[0]?.text ?? ''); const optimisticParentReport = { - lastVisibleActionCreated: currentTime, + lastVisibleActionCreated: optimisticAddCommentReport.reportAction.created, lastMessageText: lastCommentText, lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, From a6cafa066a60702ab3c2c43006e5fc60fb8639e0 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 6 Jun 2024 11:34:26 -1000 Subject: [PATCH 052/109] Add logs for backend reachability and SequentialQueue --- src/libs/Network/SequentialQueue.ts | 24 ++++++++++++++++++++++-- src/libs/NetworkConnection.ts | 8 ++++---- src/libs/actions/Network.ts | 8 +++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index b94166c0249d..91949b7933df 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -26,6 +26,7 @@ let isQueuePaused = false; */ function pause() { if (isQueuePaused) { + console.debug('[SequentialQueue] Queue already paused'); return; } @@ -40,6 +41,7 @@ function flushOnyxUpdatesQueue() { // The only situation where the queue is paused is if we found a gap between the app current data state and our server's. If that happens, // we'll trigger async calls to make the client updated again. While we do that, we don't want to insert anything in Onyx. if (isQueuePaused) { + console.debug('[SequentialQueue] Queue already paused'); return; } QueuedOnyxUpdates.flushQueue(); @@ -56,11 +58,18 @@ function flushOnyxUpdatesQueue() { function process(): Promise { // When the queue is paused, return early. This prevents any new requests from happening. The queue will be flushed again when the queue is unpaused. if (isQueuePaused) { + console.debug('[SequentialQueue] Unable to process. Queue is paused.'); + return Promise.resolve(); + } + + if (NetworkStore.isOffline()) { + console.debug('[SequentialQueue] Unable to process. We are offline.'); return Promise.resolve(); } const persistedRequests = PersistedRequests.getAll(); - if (persistedRequests.length === 0 || NetworkStore.isOffline()) { + if (persistedRequests.length === 0) { + console.debug('[SequentialQueue] Unable to process. No requests to process.'); return Promise.resolve(); } @@ -72,6 +81,7 @@ function process(): Promise { // A response might indicate that the queue should be paused. This happens when a gap in onyx updates is detected between the client and the server and // that gap needs resolved before the queue can continue. if (response?.shouldPauseQueue) { + console.debug('[SequentialQueue] Handled \'shouldPauseQueue\' in response. Pausing the queue.'); pause(); } PersistedRequests.remove(requestToProcess); @@ -102,16 +112,24 @@ function process(): Promise { function flush() { // When the queue is paused, return early. This will keep an requests in the queue and they will get flushed again when the queue is unpaused if (isQueuePaused) { + console.debug('[SequentialQueue] Unable to flush. Queue is paused.'); + return; + } + + if (isSequentialQueueRunning) { + console.debug('[SequentialQueue] Unable to flush. Queue is already running.'); return; } - if (isSequentialQueueRunning || PersistedRequests.getAll().length === 0) { + if (PersistedRequests.getAll().length === 0) { + console.debug('[SequentialQueue] Unable to flush. No requests to process.'); return; } // ONYXKEYS.PERSISTED_REQUESTS is shared across clients, thus every client/tab will have a copy // It is very important to only process the queue from leader client otherwise requests will be duplicated. if (!ActiveClientManager.isClientTheLeader()) { + console.debug('[SequentialQueue] Unable to flush. Client is not the leader.'); return; } @@ -128,6 +146,7 @@ function flush() { callback: () => { Onyx.disconnect(connectionID); process().finally(() => { + console.debug('[SequentialQueue] Finished processing queue.'); isSequentialQueueRunning = false; resolveIsReadyPromise?.(); currentRequest = null; @@ -142,6 +161,7 @@ function flush() { */ function unpause() { if (!isQueuePaused) { + console.debug('[SequentialQueue] Unable to unpause queue. We are already processing.'); return; } diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index b3dd24fcd4ae..b6a1481224de 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -43,7 +43,7 @@ function setOfflineStatus(isCurrentlyOffline: boolean): void { // When reconnecting, ie, going from offline to online, all the reconnection callbacks // are triggered (this is usually Actions that need to re-download data from the server) if (isOffline && !isCurrentlyOffline) { - NetworkActions.setIsBackendReachable(true); + NetworkActions.setIsBackendReachable(true, 'moved from offline to online'); triggerReconnectionCallbacks('offline status changed'); } @@ -118,20 +118,20 @@ function subscribeToBackendAndInternetReachability(): () => void { }) .then((isBackendReachable: boolean) => { if (isBackendReachable) { - NetworkActions.setIsBackendReachable(true); + NetworkActions.setIsBackendReachable(true, 'successfully completed API request'); return; } checkInternetReachability().then((isInternetReachable: boolean) => { setOfflineStatus(!isInternetReachable); setNetWorkStatus(isInternetReachable); - NetworkActions.setIsBackendReachable(false); + NetworkActions.setIsBackendReachable(false, 'request succeeded, but internet reachability test failed'); }); }) .catch(() => { checkInternetReachability().then((isInternetReachable: boolean) => { setOfflineStatus(!isInternetReachable); setNetWorkStatus(isInternetReachable); - NetworkActions.setIsBackendReachable(false); + NetworkActions.setIsBackendReachable(false, 'request failed and internet reachability test failed'); }); }); }, CONST.NETWORK.BACKEND_CHECK_INTERVAL_MS); diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index 9c88403b0e98..2e5ef5b605f7 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -1,8 +1,14 @@ import Onyx from 'react-native-onyx'; import type {NetworkStatus} from '@libs/NetworkConnection'; import ONYXKEYS from '@src/ONYXKEYS'; +import Log from '@libs/Log'; -function setIsBackendReachable(isBackendReachable: boolean) { +function setIsBackendReachable(isBackendReachable: boolean, reason: string) { + if (isBackendReachable) { + Log.info(`[Network] Backend is reachable because: ${reason}`); + } else { + Log.info(`[Network] Backend is not reachable because: ${reason}`); + } Onyx.merge(ONYXKEYS.NETWORK, {isBackendReachable}); } From 0011a1cb078c59d917763bea7d46eca7ca3f6769 Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 7 Jun 2024 09:00:07 +0700 Subject: [PATCH 053/109] Fix: only call VBBA one time --- src/pages/workspace/WorkspacePageWithSections.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 738a0663b94b..3476f6748790 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -129,17 +129,20 @@ function WorkspacePageWithSections({ const firstRender = useRef(true); const isFocused = useIsFocused(); const prevPolicy = usePrevious(policy); + const isCalledVBBA = useRef(false); useEffect(() => { // Because isLoading is false before merging in Onyx, we need firstRender ref to display loading page as well before isLoading is change to true firstRender.current = false; }, []); - useFocusEffect( - useCallback(() => { - fetchData(policyID, shouldSkipVBBACall); - }, [policyID, shouldSkipVBBACall]), - ); + useEffect(() => { + if (isCalledVBBA.current) { + return; + } + isCalledVBBA.current = true; + fetchData(policyID, shouldSkipVBBACall); + }, [policyID, shouldSkipVBBACall]); const shouldShow = useMemo(() => { // If the policy object doesn't exist or contains only error data, we shouldn't display it. From 7951fcf44313156b4880274654f17c4e9e5404df Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 7 Jun 2024 09:05:46 +0700 Subject: [PATCH 054/109] fix rename ref --- src/pages/workspace/WorkspacePageWithSections.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 3476f6748790..dab029435923 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -129,7 +129,7 @@ function WorkspacePageWithSections({ const firstRender = useRef(true); const isFocused = useIsFocused(); const prevPolicy = usePrevious(policy); - const isCalledVBBA = useRef(false); + const isCalledVBBARef = useRef(false); useEffect(() => { // Because isLoading is false before merging in Onyx, we need firstRender ref to display loading page as well before isLoading is change to true @@ -137,10 +137,10 @@ function WorkspacePageWithSections({ }, []); useEffect(() => { - if (isCalledVBBA.current) { + if (isCalledVBBARef.current || !policyID) { return; } - isCalledVBBA.current = true; + isCalledVBBARef.current = true; fetchData(policyID, shouldSkipVBBACall); }, [policyID, shouldSkipVBBACall]); From 9a8a96b2de2cd69c68abaade4f7cf0a16ca4f697 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 7 Jun 2024 13:07:28 +0700 Subject: [PATCH 055/109] revert unrelated change --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index eee5c82f82ae..de363c211cb3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2606,7 +2606,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055 - Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 + Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b From 7481eb3a985328883717a6c09df066f7686a51bb Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 7 Jun 2024 17:07:21 +0800 Subject: [PATCH 056/109] only apply the fix for android --- src/components/MoneyRequestAmountInput.tsx | 3 ++- .../index.android.tsx | 5 +++++ src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx | 5 +++++ src/libs/shouldIgnoreSelectionWhenUpdatedManually/types.ts | 3 +++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx create mode 100644 src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx create mode 100644 src/libs/shouldIgnoreSelectionWhenUpdatedManually/types.ts diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index d97d6ef39af4..fb17297dc642 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -7,6 +7,7 @@ import * as Browser from '@libs/Browser'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import getOperatingSystem from '@libs/getOperatingSystem'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; +import shouldIgnoreSelectionWhenUpdatedManually from '@libs/shouldIgnoreSelectionWhenUpdatedManually'; import CONST from '@src/CONST'; import type {BaseTextInputRef} from './TextInput/BaseTextInput/types'; import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol'; @@ -298,7 +299,7 @@ function MoneyRequestAmountInput( selectedCurrencyCode={currency} selection={selection} onSelectionChange={(e: NativeSyntheticEvent) => { - if (willSelectionBeUpdatedManually.current) { + if (shouldIgnoreSelectionWhenUpdatedManually && willSelectionBeUpdatedManually.current) { willSelectionBeUpdatedManually.current = false; return; } diff --git a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx new file mode 100644 index 000000000000..d786760e8691 --- /dev/null +++ b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx @@ -0,0 +1,5 @@ +import type ShouldIgnoreSelectionWhenUpdatedManually from './types.ts'; + +const shouldIgnoreSelectionWhenUpdatedManually: ShouldIgnoreSelectionWhenUpdatedManually = true; + +export default shouldIgnoreSelectionWhenUpdatedManually; diff --git a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx new file mode 100644 index 000000000000..0e7c69b39386 --- /dev/null +++ b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx @@ -0,0 +1,5 @@ +import type ShouldIgnoreSelectionWhenUpdatedManually from './types.ts'; + +const shouldIgnoreSelectionWhenUpdatedManually: ShouldIgnoreSelectionWhenUpdatedManually = false; + +export default shouldIgnoreSelectionWhenUpdatedManually; diff --git a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/types.ts b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/types.ts new file mode 100644 index 000000000000..56394183ef7d --- /dev/null +++ b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/types.ts @@ -0,0 +1,3 @@ +type ShouldIgnoreSelectionWhenUpdatedManually = boolean; + +export default ShouldIgnoreSelectionWhenUpdatedManually; From 6d0630d35122717a484469b25e78fad35dbaa825 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 7 Jun 2024 17:17:35 +0800 Subject: [PATCH 057/109] lint --- src/libs/actions/Report.ts | 1 - .../shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx | 2 +- src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1262e8af7d44..01025f21aa78 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1027,7 +1027,6 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) */ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction: Partial = {}, parentReportID = '0') { if (childReportID !== '0') { - openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])]; diff --git a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx index d786760e8691..289c56ad69be 100644 --- a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx +++ b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.android.tsx @@ -1,4 +1,4 @@ -import type ShouldIgnoreSelectionWhenUpdatedManually from './types.ts'; +import type ShouldIgnoreSelectionWhenUpdatedManually from './types'; const shouldIgnoreSelectionWhenUpdatedManually: ShouldIgnoreSelectionWhenUpdatedManually = true; diff --git a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx index 0e7c69b39386..744a94aa1f32 100644 --- a/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx +++ b/src/libs/shouldIgnoreSelectionWhenUpdatedManually/index.tsx @@ -1,4 +1,4 @@ -import type ShouldIgnoreSelectionWhenUpdatedManually from './types.ts'; +import type ShouldIgnoreSelectionWhenUpdatedManually from './types'; const shouldIgnoreSelectionWhenUpdatedManually: ShouldIgnoreSelectionWhenUpdatedManually = false; From c3ab92d2d8c506fdff0bf6a261b885a340be003c Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 7 Jun 2024 17:48:24 +0800 Subject: [PATCH 058/109] remove dupe open report --- src/libs/actions/Report.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1262e8af7d44..01025f21aa78 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1027,7 +1027,6 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) */ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction: Partial = {}, parentReportID = '0') { if (childReportID !== '0') { - openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])]; From a11171a5dc51bb12a0be1a60e23612fa46373247 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Fri, 7 Jun 2024 14:22:41 +0200 Subject: [PATCH 059/109] add possibility of excluding styles in useMarkdownSyles --- src/components/Composer/index.native.tsx | 3 +- src/components/Composer/index.tsx | 3 +- src/components/Composer/types.ts | 3 ++ src/hooks/useMarkdownStyle.ts | 35 ++++++++++++++++--- .../ComposerWithSuggestions.tsx | 1 + 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 2fbd635ed42e..5e5f126da773 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -28,6 +28,7 @@ function Composer( // On Android the selection prop is required on the TextInput but this prop has issues on IOS selection, value, + isGroupPolicyReport = false, ...props }: ComposerProps, ref: ForwardedRef, @@ -36,7 +37,7 @@ function Composer( const {isFocused, shouldResetFocus} = useResetComposerFocus(textInput); const textContainsOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); const theme = useTheme(); - const markdownStyle = useMarkdownStyle(value); + const markdownStyle = useMarkdownStyle(value, !isGroupPolicyReport ? ['mentionReport'] : []); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 14762b2d4bc1..eae27440d175 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -71,6 +71,7 @@ function Composer( isReportActionCompose = false, isComposerFullSize = false, shouldContainScroll = false, + isGroupPolicyReport = false, ...props }: ComposerProps, ref: ForwardedRef, @@ -78,7 +79,7 @@ function Composer( const textContainsOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); const theme = useTheme(); const styles = useThemeStyles(); - const markdownStyle = useMarkdownStyle(value); + const markdownStyle = useMarkdownStyle(value, !isGroupPolicyReport ? ['mentionReport'] : []); const StyleUtils = useStyleUtils(); const textRef = useRef(null); const textInput = useRef(null); diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index 531bcd03f8bf..0ff91111bd07 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -70,6 +70,9 @@ type ComposerProps = TextInputProps & { /** Should make the input only scroll inside the element avoid scroll out to parent */ shouldContainScroll?: boolean; + + /** Indicates whether the composer is in a group policy report. Used for disabling report mentioning style in markdown input */ + isGroupPolicyReport?: boolean; }; export type {TextSelection, ComposerProps}; diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts index d1af33aa9da5..77ad5ce816f5 100644 --- a/src/hooks/useMarkdownStyle.ts +++ b/src/hooks/useMarkdownStyle.ts @@ -5,12 +5,25 @@ import FontUtils from '@styles/utils/FontUtils'; import variables from '@styles/variables'; import useTheme from './useTheme'; -function useMarkdownStyle(message: string | null = null): MarkdownStyle { +function useMarkdownStyle(message: string | null = null, excludeStyles: Array = []): MarkdownStyle { const theme = useTheme(); const emojiFontSize = containsOnlyEmojis(message ?? '') ? variables.fontSizeOnlyEmojis : variables.fontSizeNormal; - const markdownStyle = useMemo( + // this map is used to reset the styles that are not needed - passing undefined value can break the native side + const nonStylingDefaultValues: Record = useMemo( () => ({ + color: theme.text, + backgroundColor: 'transparent', + marginLeft: 0, + paddingLeft: 0, + borderColor: 'transparent', + borderWidth: 0, + }), + [theme], + ); + + const markdownStyle = useMemo(() => { + const styling = { syntax: { color: theme.syntax, }, @@ -59,9 +72,21 @@ function useMarkdownStyle(message: string | null = null): MarkdownStyle { color: theme.mentionText, backgroundColor: theme.mentionBG, }, - }), - [theme, emojiFontSize], - ); + }; + + if (excludeStyles.length) { + excludeStyles.forEach((key) => { + const style: Record = styling[key]; + if (style) { + Object.keys(style).forEach((styleKey) => { + style[styleKey] = nonStylingDefaultValues[styleKey] ?? style[styleKey]; + }); + } + }); + } + + return styling; + }, [theme, emojiFontSize, excludeStyles, nonStylingDefaultValues]); return markdownStyle; } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 0515ca011517..d7f4ee7f6c5c 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -775,6 +775,7 @@ function ComposerWithSuggestions( onLayout={onLayout} onScroll={hideSuggestionMenu} shouldContainScroll={Browser.isMobileSafari()} + isGroupPolicyReport={isGroupPolicyReport} /> From 5cf2c77fa6c88956c2f36002a701dafc49cf578c Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Fri, 7 Jun 2024 14:22:58 +0200 Subject: [PATCH 060/109] bump react native live markdown --- package-lock.json | 13 +++++++++---- package.json | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d972d33f7c27..9d54c06e46be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@dotlottie/react-player": "^1.6.3", - "@expensify/react-native-live-markdown": "0.1.70", + "@expensify/react-native-live-markdown": "0.1.82", "@expo/metro-runtime": "~3.1.1", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-listformat": "^7.2.2", @@ -3558,9 +3558,14 @@ } }, "node_modules/@expensify/react-native-live-markdown": { - "version": "0.1.70", - "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.70.tgz", - "integrity": "sha512-HyqBtZyvuJFB4gIUECKIMxWCnTPlPj+GPWmw80VzMBRFV9QiFRKUKRWefNEJ1cXV5hl8a6oOWDQla+dCnjCzOQ==", + "version": "0.1.82", + "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.82.tgz", + "integrity": "sha512-w/K2+0d1sAYvyLVpPv1ufDOTaj4y96Z362N3JDN+SDUmPQN2MvVGwsTL0ltzdw78yd62azFcQl6th7P6l62THQ==", + "workspaces": [ + "parser", + "example", + "WebExample" + ], "engines": { "node": ">= 18.0.0" }, diff --git a/package.json b/package.json index bfd035211038..34a825069341 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@dotlottie/react-player": "^1.6.3", - "@expensify/react-native-live-markdown": "0.1.70", + "@expensify/react-native-live-markdown": "0.1.82", "@expo/metro-runtime": "~3.1.1", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-listformat": "^7.2.2", From 95f9a526efc6f57eef879ddb108759e6fbc6bce0 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Fri, 7 Jun 2024 19:37:49 +0700 Subject: [PATCH 061/109] hide amount in submit button if skip confirmtion page --- src/pages/iou/MoneyRequestAmountForm.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 5bbc9d22a97f..afea2efd266c 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -228,19 +228,7 @@ function MoneyRequestAmountForm( ); const buttonText: string = useMemo(() => { - const currentAmount = moneyRequestAmountInput.current?.getAmount() ?? ''; if (skipConfirmation) { - if (currentAmount !== '') { - const currencyAmount = CurrencyUtils.convertToDisplayString(CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)), currency) ?? ''; - let text = translate('iou.submitAmount', {amount: currencyAmount}); - if (iouType === CONST.IOU.TYPE.SPLIT) { - text = translate('iou.splitAmount', {amount: currencyAmount}); - } else if (iouType === CONST.IOU.TYPE.TRACK) { - text = translate('iou.trackAmount', {amount: currencyAmount}); - } - return text[0].toUpperCase() + text.slice(1); - } - if (iouType === CONST.IOU.TYPE.SPLIT) { return translate('iou.splitExpense'); } From a1ad81a29e54d72aa194f34b4547a335270f9c09 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 7 Jun 2024 20:55:46 +0800 Subject: [PATCH 062/109] remove dupe openreport --- src/components/ReportActionItem/MoneyRequestAction.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 4f91b2084b45..6dbe123b2054 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -92,7 +92,6 @@ function MoneyRequestAction({ } const childReportID = action?.childReportID ?? '0'; - Report.openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); }; From f801dc83df490470c77be5dc54a35767900be680 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 7 Jun 2024 21:09:25 +0800 Subject: [PATCH 063/109] remove unused import --- src/components/ReportActionItem/MoneyRequestAction.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 6dbe123b2054..fd82e723c6b9 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -10,7 +10,6 @@ import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; From 1e95d49d6e8e7b536bc725b575a184f4e3011cb0 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Fri, 7 Jun 2024 21:39:06 +0700 Subject: [PATCH 064/109] remove dependency --- src/pages/iou/MoneyRequestAmountForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index afea2efd266c..ce1cddc91958 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -238,7 +238,7 @@ function MoneyRequestAmountForm( return translate('iou.submitExpense'); } return isEditing ? translate('common.save') : translate('common.next'); - }, [skipConfirmation, iouType, currency, isEditing, translate]); + }, [skipConfirmation, iouType, isEditing, translate]); const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); From 492a6726ef093c645e6800a8afe5f8a7e490e930 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 7 Jun 2024 16:39:03 +0100 Subject: [PATCH 065/109] refactor: apply suggestions --- .../ReportActionItem/MoneyRequestView.tsx | 14 ++++----- src/hooks/useViolations.ts | 6 ++-- src/libs/Violations/ViolationsUtils.ts | 31 ++++++++----------- src/types/onyx/TransactionViolation.ts | 1 - 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index f3b0b3e863ed..d1fc875832ec 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -171,8 +171,8 @@ function MoneyRequestView({ const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback( - (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags?: boolean): boolean => - !!canUseViolations && getViolationsForField(field, data, policyHasDependentTags).length > 0, + (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags = false, tagValue?: string): boolean => + !!canUseViolations && getViolationsForField(field, data, policyHasDependentTags, tagValue).length > 0, [canUseViolations, getViolationsForField], ); @@ -239,7 +239,7 @@ function MoneyRequestView({ const getPendingFieldAction = (fieldPath: TransactionPendingFieldsKey) => transaction?.pendingFields?.[fieldPath] ?? pendingAction; const getErrorForField = useCallback( - (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags?: boolean) => { + (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags = false, tagValue?: string) => { // Checks applied when creating a new expense // NOTE: receipt field can return multiple violations, so we need to handle it separately const fieldChecks: Partial> = { @@ -265,8 +265,8 @@ function MoneyRequestView({ } // Return violations if there are any - if (hasViolations(field, data, policyHasDependentTags)) { - const violations = getViolationsForField(field, data, policyHasDependentTags); + if (hasViolations(field, data, policyHasDependentTags, tagValue)) { + const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue); return ViolationsUtils.getViolationTranslation(violations[0], translate); } @@ -492,9 +492,9 @@ function MoneyRequestView({ { tagListIndex: index, tagListName: name, - tagListValue: TransactionUtils.getTagForDisplay(transaction, index), }, PolicyUtils.hasDependentTags(policy, policyTagList), + TransactionUtils.getTagForDisplay(transaction, index), ) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined @@ -504,9 +504,9 @@ function MoneyRequestView({ { tagListIndex: index, tagListName: name, - tagListValue: TransactionUtils.getTagForDisplay(transaction, index), }, PolicyUtils.hasDependentTags(policy, policyTagList), + TransactionUtils.getTagForDisplay(transaction, index), )} /> diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index 9120c0709d9a..83c725a48db0 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -62,7 +62,7 @@ function useViolations(violations: TransactionViolation[]) { }, [violations]); const getViolationsForField = useCallback( - (field: ViolationField, data?: TransactionViolation['data'], policyHasDependentTags?: boolean) => { + (field: ViolationField, data?: TransactionViolation['data'], policyHasDependentTags = false, tagValue?: string) => { const currentViolations = violationsByField.get(field) ?? []; // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation @@ -81,7 +81,7 @@ function useViolations(violations: TransactionViolation[]) { // missingTag has special logic for policies with dependent tags, because only one violation is returned for all tags // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here - if (!!policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { + if (policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { return [ { ...currentViolations[0], @@ -100,7 +100,7 @@ function useViolations(violations: TransactionViolation[]) { // allTagLevelsRequired has special logic because it is returned when one but not all the tags are set, // so we need to return the violation for the tag fields without a tag set - if (currentViolations[0]?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && data?.tagListValue) { + if (currentViolations[0]?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && tagValue) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 2786fdd6ad05..00de9a2175ae 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -9,13 +9,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyCategories, PolicyTagList, Transaction, TransactionViolation, ViolationName} from '@src/types/onyx'; -const TRANSACTION_VIOLATIONS_FILTER = [ - CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, - CONST.VIOLATIONS.TAG_OUT_OF_POLICY, - CONST.VIOLATIONS.MISSING_TAG, - CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED, -] as ViolationName[]; - /** * Calculates tag out of policy and missing tag violations for the given transaction */ @@ -76,7 +69,7 @@ function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transact tagViolations.push({ name: CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED, type: CONST.VIOLATION_TYPES.VIOLATION, - data: undefined, + data: {}, }); } } @@ -84,19 +77,19 @@ function getTagViolationsForDependentTags(policyTagList: PolicyTagList, transact return tagViolations; } -/** - * Calculates missing tag violations for policies with independent tags, - * by returning one per tag with its corresponding tagName in the data - */ +/** Calculates missing tag violations for policies with independent tags */ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transactionViolations: TransactionViolation[], transaction: Transaction) { const policyTagKeys = getSortedTagKeys(policyTagList); const selectedTags = transaction.tag?.split(CONST.COLON) ?? []; let newTransactionViolations = [...transactionViolations]; + newTransactionViolations = newTransactionViolations.filter( + (violation) => violation.name !== CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && violation.name !== CONST.VIOLATIONS.TAG_OUT_OF_POLICY, + ); + // We first get the errorIndexes for someTagLevelsRequired. If it's not empty, we puth SOME_TAG_LEVELS_REQUIRED in Onyx. // Otherwise, we put TAG_OUT_OF_POLICY in Onyx (when applicable) const errorIndexes = []; - for (let i = 0; i < policyTagKeys.length; i++) { const isTagRequired = policyTagList[policyTagKeys[i]].required ?? true; const isTagSelected = !!selectedTags[i]; @@ -104,7 +97,6 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transac errorIndexes.push(i); } } - if (errorIndexes.length !== 0) { newTransactionViolations.push({ name: CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, @@ -115,7 +107,6 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transac }); } else { let hasInvalidTag = false; - for (let i = 0; i < policyTagKeys.length; i++) { const selectedTag = selectedTags[i]; const tags = policyTagList[policyTagKeys[i]].tags; @@ -132,14 +123,12 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transac break; } } - if (!hasInvalidTag) { newTransactionViolations = reject(newTransactionViolations, { name: CONST.VIOLATIONS.TAG_OUT_OF_POLICY, }); } } - return newTransactionViolations; } @@ -153,7 +142,13 @@ function getTagViolationsForMultiLevelTags( policyTagList: PolicyTagList, hasDependentTags: boolean, ): TransactionViolation[] { - const filteredTransactionViolations = transactionViolations.filter((violation) => !TRANSACTION_VIOLATIONS_FILTER.includes(violation.name)); + const tagViolations = [ + CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED, + CONST.VIOLATIONS.TAG_OUT_OF_POLICY, + CONST.VIOLATIONS.MISSING_TAG, + CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED, + ] as ViolationName[]; + const filteredTransactionViolations = transactionViolations.filter((violation) => !tagViolations.includes(violation.name)); if (hasDependentTags) { return getTagViolationsForDependentTags(policyTagList, filteredTransactionViolations, updatedTransaction.tag ?? ''); diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index e81f7196209b..ab2037ff336a 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -28,7 +28,6 @@ type TransactionViolation = { taxName?: string; tagListIndex?: number; tagListName?: string; - tagListValue?: string; errorIndexes?: number[]; pendingPattern?: boolean; }; From df3326a8ee1faad996cb41cc7f10b0bd9b224478 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 7 Jun 2024 16:47:37 +0100 Subject: [PATCH 066/109] refactor: remove unused argument --- src/libs/Violations/ViolationsUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 00de9a2175ae..d03c2d0f4324 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -138,7 +138,6 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagList, transac function getTagViolationsForMultiLevelTags( updatedTransaction: Transaction, transactionViolations: TransactionViolation[], - policyRequiresTags: boolean, policyTagList: PolicyTagList, hasDependentTags: boolean, ): TransactionViolation[] { @@ -215,7 +214,7 @@ const ViolationsUtils = { newTransactionViolations = Object.keys(policyTagList).length === 1 ? getTagViolationsForSingleLevelTags(updatedTransaction, newTransactionViolations, policyRequiresTags, policyTagList) - : getTagViolationsForMultiLevelTags(updatedTransaction, newTransactionViolations, policyRequiresTags, policyTagList, hasDependentTags); + : getTagViolationsForMultiLevelTags(updatedTransaction, newTransactionViolations, policyTagList, hasDependentTags); } return { From e7f175fa239c1ccf8a893b640a7da6d789b07338 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 7 Jun 2024 17:39:48 +0100 Subject: [PATCH 067/109] refactor: apply suggestions --- .../ReportActionItem/MoneyRequestView.tsx | 74 ++++++++----------- src/types/onyx/PolicyTag.ts | 4 + 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index d1fc875832ec..4324b865cee0 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -334,6 +334,37 @@ function MoneyRequestView({ ...parentReportAction?.errors, }; + const TagList = policyTagLists.map(({name, orderWeight}, index) => { + const tagError = getErrorForField( + 'tag', + { + tagListIndex: index, + tagListName: name, + }, + PolicyUtils.hasDependentTags(policy, policyTagList), + TransactionUtils.getTagForDisplay(transaction, index), + ); + return ( + + + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.EDIT, iouType, orderWeight, transaction?.transactionID ?? '', report.reportID)) + } + brickRoadIndicator={tagError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + errorText={tagError} + /> + + ); + }); + return ( {shouldShowAnimatedBackground && } @@ -469,48 +500,7 @@ function MoneyRequestView({ /> )} - {shouldShowTag && - policyTagLists.map(({name, orderWeight}, index) => ( - - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.EDIT, iouType, orderWeight, transaction?.transactionID ?? '', report.reportID), - ) - } - brickRoadIndicator={ - getErrorForField( - 'tag', - { - tagListIndex: index, - tagListName: name, - }, - PolicyUtils.hasDependentTags(policy, policyTagList), - TransactionUtils.getTagForDisplay(transaction, index), - ) - ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR - : undefined - } - errorText={getErrorForField( - 'tag', - { - tagListIndex: index, - tagListName: name, - }, - PolicyUtils.hasDependentTags(policy, policyTagList), - TransactionUtils.getTagForDisplay(transaction, index), - )} - /> - - ))} + {shouldShowTag && TagList} {isCardTransaction && ( ; From df5e19f7bd5faab54cdbd605ed5de19fa6d88308 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Fri, 7 Jun 2024 18:12:55 +0100 Subject: [PATCH 068/109] refactor: apply suggestions --- src/components/ReportActionItem/MoneyRequestView.tsx | 4 ++-- src/libs/Violations/ViolationsUtils.ts | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 4324b865cee0..cc99a3f6e108 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -334,7 +334,7 @@ function MoneyRequestView({ ...parentReportAction?.errors, }; - const TagList = policyTagLists.map(({name, orderWeight}, index) => { + const tagList = policyTagLists.map(({name, orderWeight}, index) => { const tagError = getErrorForField( 'tag', { @@ -500,7 +500,7 @@ function MoneyRequestView({ /> )} - {shouldShowTag && TagList} + {shouldShowTag && tagList} {isCardTransaction && ( Date: Fri, 7 Jun 2024 12:44:39 -0600 Subject: [PATCH 069/109] Prettier --- src/libs/Network/SequentialQueue.ts | 2 +- src/libs/actions/Network.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index f27c89a47f72..43d9bc26e347 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -81,7 +81,7 @@ function process(): Promise { // A response might indicate that the queue should be paused. This happens when a gap in onyx updates is detected between the client and the server and // that gap needs resolved before the queue can continue. if (response?.shouldPauseQueue) { - console.debug('[SequentialQueue] Handled \'shouldPauseQueue\' in response. Pausing the queue.'); + console.debug("[SequentialQueue] Handled 'shouldPauseQueue' in response. Pausing the queue."); pause(); } PersistedRequests.remove(requestToProcess); diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index 2e5ef5b605f7..90d4649311d9 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; +import Log from '@libs/Log'; import type {NetworkStatus} from '@libs/NetworkConnection'; import ONYXKEYS from '@src/ONYXKEYS'; -import Log from '@libs/Log'; function setIsBackendReachable(isBackendReachable: boolean, reason: string) { if (isBackendReachable) { From 4f8ec01a36a821009c2c2b473669393aa1c7a2d8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 12:53:08 -0600 Subject: [PATCH 070/109] Change debug to logs --- src/libs/Network/SequentialQueue.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 43d9bc26e347..10df3c703c92 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -1,5 +1,6 @@ import Onyx from 'react-native-onyx'; import * as ActiveClientManager from '@libs/ActiveClientManager'; +import Log from '@libs/Log'; import * as Request from '@libs/Request'; import * as RequestThrottle from '@libs/RequestThrottle'; import * as PersistedRequests from '@userActions/PersistedRequests'; @@ -26,11 +27,11 @@ let isQueuePaused = false; */ function pause() { if (isQueuePaused) { - console.debug('[SequentialQueue] Queue already paused'); + Log.info('[SequentialQueue] Queue already paused'); return; } - console.debug('[SequentialQueue] Pausing the queue'); + Log.info('[SequentialQueue] Pausing the queue'); isQueuePaused = true; } @@ -41,7 +42,7 @@ function flushOnyxUpdatesQueue() { // The only situation where the queue is paused is if we found a gap between the app current data state and our server's. If that happens, // we'll trigger async calls to make the client updated again. While we do that, we don't want to insert anything in Onyx. if (isQueuePaused) { - console.debug('[SequentialQueue] Queue already paused'); + Log.info('[SequentialQueue] Queue already paused'); return; } QueuedOnyxUpdates.flushQueue(); @@ -58,18 +59,18 @@ function flushOnyxUpdatesQueue() { function process(): Promise { // When the queue is paused, return early. This prevents any new requests from happening. The queue will be flushed again when the queue is unpaused. if (isQueuePaused) { - console.debug('[SequentialQueue] Unable to process. Queue is paused.'); + Log.info('[SequentialQueue] Unable to process. Queue is paused.'); return Promise.resolve(); } if (NetworkStore.isOffline()) { - console.debug('[SequentialQueue] Unable to process. We are offline.'); + Log.info('[SequentialQueue] Unable to process. We are offline.'); return Promise.resolve(); } const persistedRequests = PersistedRequests.getAll(); if (persistedRequests.length === 0) { - console.debug('[SequentialQueue] Unable to process. No requests to process.'); + Log.info('[SequentialQueue] Unable to process. No requests to process.'); return Promise.resolve(); } @@ -81,7 +82,7 @@ function process(): Promise { // A response might indicate that the queue should be paused. This happens when a gap in onyx updates is detected between the client and the server and // that gap needs resolved before the queue can continue. if (response?.shouldPauseQueue) { - console.debug("[SequentialQueue] Handled 'shouldPauseQueue' in response. Pausing the queue."); + Log.info("[SequentialQueue] Handled 'shouldPauseQueue' in response. Pausing the queue."); pause(); } PersistedRequests.remove(requestToProcess); @@ -112,24 +113,24 @@ function process(): Promise { function flush() { // When the queue is paused, return early. This will keep an requests in the queue and they will get flushed again when the queue is unpaused if (isQueuePaused) { - console.debug('[SequentialQueue] Unable to flush. Queue is paused.'); + Log.info('[SequentialQueue] Unable to flush. Queue is paused.'); return; } if (isSequentialQueueRunning) { - console.debug('[SequentialQueue] Unable to flush. Queue is already running.'); + Log.info('[SequentialQueue] Unable to flush. Queue is already running.'); return; } if (PersistedRequests.getAll().length === 0) { - console.debug('[SequentialQueue] Unable to flush. No requests to process.'); + Log.info('[SequentialQueue] Unable to flush. No requests to process.'); return; } // ONYXKEYS.PERSISTED_REQUESTS is shared across clients, thus every client/tab will have a copy // It is very important to only process the queue from leader client otherwise requests will be duplicated. if (!ActiveClientManager.isClientTheLeader()) { - console.debug('[SequentialQueue] Unable to flush. Client is not the leader.'); + Log.info('[SequentialQueue] Unable to flush. Client is not the leader.'); return; } @@ -146,7 +147,7 @@ function flush() { callback: () => { Onyx.disconnect(connectionID); process().finally(() => { - console.debug('[SequentialQueue] Finished processing queue.'); + Log.info('[SequentialQueue] Finished processing queue.'); isSequentialQueueRunning = false; if (NetworkStore.isOffline() || PersistedRequests.getAll().length === 0) { resolveIsReadyPromise?.(); @@ -163,7 +164,7 @@ function flush() { */ function unpause() { if (!isQueuePaused) { - console.debug('[SequentialQueue] Unable to unpause queue. We are already processing.'); + Log.info('[SequentialQueue] Unable to unpause queue. We are already processing.'); return; } From ed85fecf451ea05ba647bfae8134a6d7e273f111 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 12:53:19 -0600 Subject: [PATCH 071/109] Move check outside of promise --- src/libs/NetworkConnection.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 0aa3f224858d..897b947e6815 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -125,17 +125,17 @@ function subscribeToBackendAndInternetReachability(): () => void { NetworkActions.setIsBackendReachable(true, 'successfully completed API request'); return; } + NetworkActions.setIsBackendReachable(false, 'request succeeded, but internet reachability test failed'); checkInternetReachability().then((isInternetReachable: boolean) => { setOfflineStatus(!isInternetReachable); setNetWorkStatus(isInternetReachable); - NetworkActions.setIsBackendReachable(false, 'request succeeded, but internet reachability test failed'); }); }) .catch(() => { + NetworkActions.setIsBackendReachable(false, 'request failed and internet reachability test failed'); checkInternetReachability().then((isInternetReachable: boolean) => { setOfflineStatus(!isInternetReachable); setNetWorkStatus(isInternetReachable); - NetworkActions.setIsBackendReachable(false, 'request failed and internet reachability test failed'); }); }); }, CONST.NETWORK.BACKEND_CHECK_INTERVAL_MS); From 98e2e5bf8f931ea82e2f2b2e2c3bf7397dc5cddc Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 13:00:34 -0600 Subject: [PATCH 072/109] Add more logs for when going offline --- src/libs/NetworkConnection.ts | 14 +++++++------- src/libs/actions/Network.ts | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 897b947e6815..60f838759ef9 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -37,8 +37,8 @@ const triggerReconnectionCallbacks = throttle( * Called when the offline status of the app changes and if the network is "reconnecting" (going from offline to online) * then all of the reconnection callbacks are triggered */ -function setOfflineStatus(isCurrentlyOffline: boolean): void { - NetworkActions.setIsOffline(isCurrentlyOffline); +function setOfflineStatus(isCurrentlyOffline: boolean, reason: string): void { + NetworkActions.setIsOffline(isCurrentlyOffline, reason); // When reconnecting, ie, going from offline to online, all the reconnection callbacks // are triggered (this is usually Actions that need to re-download data from the server) @@ -74,13 +74,13 @@ Onyx.connect({ } shouldForceOffline = currentShouldForceOffline; if (shouldForceOffline) { - setOfflineStatus(true); + setOfflineStatus(true, 'shouldForceOffline was detected in the Onyx data'); Log.info(`[NetworkStatus] Setting "offlineStatus" to "true" because user is under force offline`); } else { // If we are no longer forcing offline fetch the NetInfo to set isOffline appropriately NetInfo.fetch().then((state) => { const isInternetReachable = !!state.isInternetReachable; - setOfflineStatus(isInternetReachable); + setOfflineStatus(isInternetReachable, 'NetInfo checked if the internet is reachable'); Log.info( `[NetworkStatus] The force-offline mode was turned off. Getting the device network status from NetInfo. Network state: ${JSON.stringify( state, @@ -127,14 +127,14 @@ function subscribeToBackendAndInternetReachability(): () => void { } NetworkActions.setIsBackendReachable(false, 'request succeeded, but internet reachability test failed'); checkInternetReachability().then((isInternetReachable: boolean) => { - setOfflineStatus(!isInternetReachable); + setOfflineStatus(!isInternetReachable, 'checkInternetReachability was called after api/ping returned a non-200 jsonCode'); setNetWorkStatus(isInternetReachable); }); }) .catch(() => { NetworkActions.setIsBackendReachable(false, 'request failed and internet reachability test failed'); checkInternetReachability().then((isInternetReachable: boolean) => { - setOfflineStatus(!isInternetReachable); + setOfflineStatus(!isInternetReachable, 'checkInternetReachability was called after api/ping request failed'); setNetWorkStatus(isInternetReachable); }); }); @@ -163,7 +163,7 @@ function subscribeToNetworkStatus(): () => void { Log.info('[NetworkConnection] Not setting offline status because shouldForceOffline = true'); return; } - setOfflineStatus(state.isInternetReachable === false); + setOfflineStatus(state.isInternetReachable === false, 'NetInfo received a state change event'); Log.info(`[NetworkStatus] NetInfo.addEventListener event coming, setting "offlineStatus" to ${!!state.isInternetReachable} with network state: ${JSON.stringify(state)}`); setNetWorkStatus(state.isInternetReachable); }); diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index 90d4649311d9..6bb0b9454012 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -12,7 +12,12 @@ function setIsBackendReachable(isBackendReachable: boolean, reason: string) { Onyx.merge(ONYXKEYS.NETWORK, {isBackendReachable}); } -function setIsOffline(isOffline: boolean) { +function setIsOffline(isOffline: boolean, reason: string) { + if (isOffline) { + Log.info(`[Network] Client is entering offline mode because: ${reason}`); + } else { + Log.info(`[Network] Client is back online because: ${reason}`); + } Onyx.merge(ONYXKEYS.NETWORK, {isOffline}); } From e404d1c9803a7d73fe748a981e81da5055a6b07f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 13:12:09 -0600 Subject: [PATCH 073/109] Default reason to a blank screen --- src/libs/actions/Network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index 6bb0b9454012..5f9600818b2d 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -12,7 +12,7 @@ function setIsBackendReachable(isBackendReachable: boolean, reason: string) { Onyx.merge(ONYXKEYS.NETWORK, {isBackendReachable}); } -function setIsOffline(isOffline: boolean, reason: string) { +function setIsOffline(isOffline: boolean, reason = '') { if (isOffline) { Log.info(`[Network] Client is entering offline mode because: ${reason}`); } else { From cfb586c61b16b5c81a2d6aeeaf3e1ea5b0c94e8e Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 13:13:48 -0600 Subject: [PATCH 074/109] Restructure logic for optional reason --- src/libs/actions/Network.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Network.ts b/src/libs/actions/Network.ts index 5f9600818b2d..883e336d6c90 100644 --- a/src/libs/actions/Network.ts +++ b/src/libs/actions/Network.ts @@ -13,10 +13,11 @@ function setIsBackendReachable(isBackendReachable: boolean, reason: string) { } function setIsOffline(isOffline: boolean, reason = '') { - if (isOffline) { - Log.info(`[Network] Client is entering offline mode because: ${reason}`); - } else { - Log.info(`[Network] Client is back online because: ${reason}`); + if (reason) { + let textToLog = '[Network] Client is'; + textToLog += isOffline ? ' entering offline mode' : ' back online'; + textToLog += ` because: ${reason}`; + Log.info(textToLog); } Onyx.merge(ONYXKEYS.NETWORK, {isOffline}); } From 41ff6a2f873ef7b7648d01cdd4d2ae9221622aa1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 7 Jun 2024 13:30:58 -0600 Subject: [PATCH 075/109] Fix another default variable --- src/libs/NetworkConnection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/NetworkConnection.ts b/src/libs/NetworkConnection.ts index 60f838759ef9..de064441047a 100644 --- a/src/libs/NetworkConnection.ts +++ b/src/libs/NetworkConnection.ts @@ -37,7 +37,7 @@ const triggerReconnectionCallbacks = throttle( * Called when the offline status of the app changes and if the network is "reconnecting" (going from offline to online) * then all of the reconnection callbacks are triggered */ -function setOfflineStatus(isCurrentlyOffline: boolean, reason: string): void { +function setOfflineStatus(isCurrentlyOffline: boolean, reason = ''): void { NetworkActions.setIsOffline(isCurrentlyOffline, reason); // When reconnecting, ie, going from offline to online, all the reconnection callbacks From 6b8c4b2e62ffc262671616467d42c4e44adc8fee Mon Sep 17 00:00:00 2001 From: truph01 Date: Sat, 8 Jun 2024 08:02:32 +0700 Subject: [PATCH 076/109] Fix: Remove isCalledVBBARef --- src/pages/workspace/WorkspacePageWithSections.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index dab029435923..738a0663b94b 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -129,20 +129,17 @@ function WorkspacePageWithSections({ const firstRender = useRef(true); const isFocused = useIsFocused(); const prevPolicy = usePrevious(policy); - const isCalledVBBARef = useRef(false); useEffect(() => { // Because isLoading is false before merging in Onyx, we need firstRender ref to display loading page as well before isLoading is change to true firstRender.current = false; }, []); - useEffect(() => { - if (isCalledVBBARef.current || !policyID) { - return; - } - isCalledVBBARef.current = true; - fetchData(policyID, shouldSkipVBBACall); - }, [policyID, shouldSkipVBBACall]); + useFocusEffect( + useCallback(() => { + fetchData(policyID, shouldSkipVBBACall); + }, [policyID, shouldSkipVBBACall]), + ); const shouldShow = useMemo(() => { // If the policy object doesn't exist or contains only error data, we shouldn't display it. From f302598a454996abbb4f1ff34ddb55b92aed52d0 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sun, 9 Jun 2024 16:38:57 +0200 Subject: [PATCH 077/109] Add missing js doc --- src/types/onyx/UserMetadata.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/onyx/UserMetadata.ts b/src/types/onyx/UserMetadata.ts index 14181b33c4b4..a8b34cb29401 100644 --- a/src/types/onyx/UserMetadata.ts +++ b/src/types/onyx/UserMetadata.ts @@ -11,6 +11,8 @@ type UserMetadata = { /** User's account ID */ accountID?: number; + + /** Type of environment the user is using (staging or production) */ environment?: string; }; From f1fc716d207902ce588b3d892a9ea8d22a2502af Mon Sep 17 00:00:00 2001 From: badeggg Date: Sun, 9 Jun 2024 23:22:32 +0800 Subject: [PATCH 078/109] try fix 42916 loop crash --- ios/Podfile.lock | 2 +- src/hooks/useAutoFocusInput.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c138d1b27f61..f489ec290755 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2606,7 +2606,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055 - Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 + Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b diff --git a/src/hooks/useAutoFocusInput.ts b/src/hooks/useAutoFocusInput.ts index c1f58901cee0..2e2d9a086ab1 100644 --- a/src/hooks/useAutoFocusInput.ts +++ b/src/hooks/useAutoFocusInput.ts @@ -49,6 +49,9 @@ export default function useAutoFocusInput(): UseAutoFocusInput { const inputCallbackRef = (ref: TextInput | null) => { inputRef.current = ref; + if (isInputInitialized) { + return; + } setIsInputInitialized(true); }; From 21059739d373370f3b584cb2cbc4c8382b2dfd50 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:26:10 +0530 Subject: [PATCH 079/109] Bump live-markdown to 0.1.83 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index aaef29c6fad9..bd2bc7197320 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@dotlottie/react-player": "^1.6.3", - "@expensify/react-native-live-markdown": "0.1.82", + "@expensify/react-native-live-markdown": "0.1.83", "@expo/metro-runtime": "~3.1.1", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-listformat": "^7.2.2", @@ -3558,9 +3558,9 @@ } }, "node_modules/@expensify/react-native-live-markdown": { - "version": "0.1.82", - "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.82.tgz", - "integrity": "sha512-w/K2+0d1sAYvyLVpPv1ufDOTaj4y96Z362N3JDN+SDUmPQN2MvVGwsTL0ltzdw78yd62azFcQl6th7P6l62THQ==", + "version": "0.1.83", + "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.83.tgz", + "integrity": "sha512-xGn1P9FbFVueEF8BNKJJ4dQb0wPtsAvrrxND9pwVQT35ZL5cu1KZ4o6nzCqtesISPRB8Dw9Zx0ftIZy2uCQyzA==", "workspaces": [ "parser", "example", diff --git a/package.json b/package.json index ee1ad32a0067..e70c8c8e853e 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@dotlottie/react-player": "^1.6.3", - "@expensify/react-native-live-markdown": "0.1.82", + "@expensify/react-native-live-markdown": "0.1.83", "@expo/metro-runtime": "~3.1.1", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-listformat": "^7.2.2", From 1e373b79a8af637110782a5d2b915c13221c379a Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:31:49 +0530 Subject: [PATCH 080/109] Update podfile --- ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c138d1b27f61..aca46d6b18ed 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1852,7 +1852,7 @@ PODS: - RNGoogleSignin (10.0.1): - GoogleSignIn (~> 7.0) - React-Core - - RNLiveMarkdown (0.1.82): + - RNLiveMarkdown (0.1.83): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -1870,9 +1870,9 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNLiveMarkdown/common (= 0.1.82) + - RNLiveMarkdown/common (= 0.1.83) - Yoga - - RNLiveMarkdown/common (0.1.82): + - RNLiveMarkdown/common (0.1.83): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -2589,7 +2589,7 @@ SPEC CHECKSUMS: RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 74b7b3d06d667ba0bbf41da7718f2607ae0dfe8f RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0 - RNLiveMarkdown: d160a948e52282067439585c89a3962582c082ce + RNLiveMarkdown: 88030b7d9a31f5f6e67743df48ad952d64513b4a RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 rnmapbox-maps: df8fe93dbd251f25022f4023d31bc04160d4d65c RNPermissions: 0b61d30d21acbeafe25baaa47d9bae40a0c65216 From f2b7460d0bde8fab0a191f02bd941da8fa6022ba Mon Sep 17 00:00:00 2001 From: OSBotify Date: Sun, 9 Jun 2024 21:03:44 +0000 Subject: [PATCH 081/109] Update version to 1.4.80-17 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9343423c9f14..03328cb1e19f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048016 - versionName "1.4.80-16" + versionCode 1001048017 + versionName "1.4.80-17" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ad1d743d778d..17f238555fb9 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.80.16 + 1.4.80.17 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 23bdf74a3648..bab29a9f7bf2 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.80.16 + 1.4.80.17 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 9f8cc7b1612e..fa2b2c45f34b 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.80 CFBundleVersion - 1.4.80.16 + 1.4.80.17 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index bd2bc7197320..31f216228117 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.80-16", + "version": "1.4.80-17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.80-16", + "version": "1.4.80-17", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e70c8c8e853e..55202a6280ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.80-16", + "version": "1.4.80-17", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 092c86a93c08fe4aef7e4a43350cc350cad36e61 Mon Sep 17 00:00:00 2001 From: badeggg Date: Mon, 10 Jun 2024 14:41:46 +0800 Subject: [PATCH 082/109] remote unnecessary change in ios/Podfile.lock --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f489ec290755..c138d1b27f61 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2606,7 +2606,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055 - Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 + Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b From 902b5605399eb1c0494bf4fd95b012018cb3c277 Mon Sep 17 00:00:00 2001 From: Tienifr <113963320+tienifr@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:32:44 +0700 Subject: [PATCH 083/109] fix add comment to TEXTBOX Co-authored-by: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> --- src/CONST.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index 02de81e61aa4..6a2b9a72279f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3387,6 +3387,9 @@ const CONST = { */ IMAGE: 'image', + /** + * @deprecated Please stop using the accessibilityRole prop and use the role prop instead. + */ TEXTBOX: 'textbox', }, /** From b5693a6a44c6ab33aedb62e2009c6f24e7eec53a Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 07:45:31 +0000 Subject: [PATCH 084/109] Update version to 1.4.81-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- ios/NotificationServiceExtension/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 03328cb1e19f..386c96587c7b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048017 - versionName "1.4.80-17" + versionCode 1001048100 + versionName "1.4.81-0" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 17f238555fb9..03e11f677fbd 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.80 + 1.4.81 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.80.17 + 1.4.81.0 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index bab29a9f7bf2..661f10f6defc 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.80 + 1.4.81 CFBundleSignature ???? CFBundleVersion - 1.4.80.17 + 1.4.81.0 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index fa2b2c45f34b..f1d3734a4f94 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.4.80 + 1.4.81 CFBundleVersion - 1.4.80.17 + 1.4.81.0 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 31f216228117..972ab4d261fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.80-17", + "version": "1.4.81-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.80-17", + "version": "1.4.81-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 55202a6280ac..811342a5d265 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.80-17", + "version": "1.4.81-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 3a7cfe61c6f86a0a9b532f53e8fc8637eca2489c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 08:03:49 +0000 Subject: [PATCH 085/109] Update version to 1.4.81-1 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 386c96587c7b..cc26412f0c0f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048100 - versionName "1.4.81-0" + versionCode 1001048101 + versionName "1.4.81-1" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 03e11f677fbd..71fb74c7dc8a 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.0 + 1.4.81.1 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 661f10f6defc..6037678d5b9b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.0 + 1.4.81.1 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index f1d3734a4f94..8bbe7f95850c 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.0 + 1.4.81.1 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 972ab4d261fd..79f4e6053de1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-0", + "version": "1.4.81-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-0", + "version": "1.4.81-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 811342a5d265..fe69f4594efd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-0", + "version": "1.4.81-1", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From d9485372fce8c1864ccb44155d4760315794346d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 10 Jun 2024 11:40:54 +0200 Subject: [PATCH 086/109] merging into main --- .../BottomTabBar/index.website.tsx | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index d989f3747f6b..ed4ddc4c7d92 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -47,7 +47,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const currentRoute = routes?.[navigationState?.index ?? 0]; // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method. // To prevent this, the value of the bottomTabRoute?.name is checked here - if (Boolean(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || Session.isAnonymousUser()) { + if (!!(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || Session.isAnonymousUser()) { return; } @@ -76,17 +76,17 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return ( - + - + + { + Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); + }} + role={CONST.ROLE.BUTTON} + accessibilityLabel={translate('common.search')} + wrapperStyle={styles.flex1} + style={styles.bottomTabBarItem} + > + + + + + + + + ); } From 40d9c24d70fedc02e718923def345e57b0c624a1 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 10 Jun 2024 11:48:30 +0200 Subject: [PATCH 087/109] minor change --- .../BottomTabBar/index.website.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index ed4ddc4c7d92..ad42ef31e1e9 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -39,7 +39,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const {translate} = useLocalize(); const navigation = useNavigation(); const {activeWorkspaceID: contextActiveWorkspaceID} = useActiveWorkspace(); - const activeWorkspaceID = sessionStorage ? sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID) : contextActiveWorkspaceID; + const activeWorkspaceID = sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID) ?? contextActiveWorkspaceID; useEffect(() => { const navigationState = navigation.getState() as State | undefined; From 57d77e3b7ed0bc9dcc546f5080da12c8fcdb8eea Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 10 Jun 2024 11:56:49 +0200 Subject: [PATCH 088/109] eslint fix --- .../BottomTabBar/index.website.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index ad42ef31e1e9..bc813f70f081 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -67,7 +67,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return topmostBottomTabRoute?.name ?? SCREENS.HOME; }); - const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID as string | undefined); + const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID); const navigateToChats = useCallback(() => { const route = activeWorkspaceID ? (`/w/${activeWorkspaceID}/home` as Route) : ROUTES.HOME; From 9be952ebc413e11b694cd07c052e0f3816e71182 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 10 Jun 2024 19:50:22 +0800 Subject: [PATCH 089/109] fix flashlist doesn't rerender correctly when chaning priority mode --- src/components/LHNOptionsList/LHNOptionsList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 6405e3026b1a..7a2650ab5bd6 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -165,8 +165,8 @@ function LHNOptionsList({ ); const extraData = useMemo( - () => [reportActions, reports, policy, personalDetails, data.length, draftComments], - [reportActions, reports, policy, personalDetails, data.length, draftComments], + () => [reportActions, reports, policy, personalDetails, data.length, draftComments, optionMode], + [reportActions, reports, policy, personalDetails, data.length, draftComments, optionMode], ); const previousOptionMode = usePrevious(optionMode); From 02d3efd66d200533660d3a755bd8e1d9bf0c5bb8 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 6 Jun 2024 16:08:36 +0200 Subject: [PATCH 090/109] improve WorkspaceSwitcherButton --- src/components/WorkspaceSwitcherButton.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/WorkspaceSwitcherButton.tsx b/src/components/WorkspaceSwitcherButton.tsx index 7f7715f6f827..f12b60ec9228 100644 --- a/src/components/WorkspaceSwitcherButton.tsx +++ b/src/components/WorkspaceSwitcherButton.tsx @@ -1,4 +1,4 @@ -import React, {useMemo, useRef} from 'react'; +import React, {memo, useMemo, useRef} from 'react'; import type {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; @@ -26,7 +26,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) { const pressableRef = useRef(null); - const {source, name, type, id} = useMemo(() => { + const mainAvatar = useMemo(() => { if (!policy) { return {source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR}; } @@ -56,7 +56,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) { > {({hovered}) => ( Date: Mon, 10 Jun 2024 20:08:23 +0700 Subject: [PATCH 091/109] remove language translation --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 468f06f6b702..4382c05c48e9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -688,7 +688,6 @@ export default { finished: 'Finished', sendInvoice: ({amount}: RequestAmountParams) => `Send ${amount} invoice`, submitAmount: ({amount}: RequestAmountParams) => `submit ${amount}`, - trackAmount: ({amount}: RequestAmountParams) => `track ${amount}`, submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `submitted ${formattedAmount}${comment ? ` for ${comment}` : ''}`, trackedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `tracking ${formattedAmount}${comment ? ` for ${comment}` : ''}`, splitAmount: ({amount}: SplitAmountParams) => `split ${amount}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 2d4500b823b7..53a431792ab8 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -682,7 +682,6 @@ export default { finished: 'Finalizado', sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`, submitAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`, - trackAmount: ({amount}: RequestAmountParams) => `seguimiento de ${amount}`, submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicitó ${formattedAmount}${comment ? ` para ${comment}` : ''}`, trackedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `realizó un seguimiento de ${formattedAmount}${comment ? ` para ${comment}` : ''}`, splitAmount: ({amount}: SplitAmountParams) => `dividir ${amount}`, From c8ae97bd6dc2d7d61db41c8e28d9dadf29609c1c Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Mon, 10 Jun 2024 16:52:51 +0200 Subject: [PATCH 092/109] add proper defualt props --- src/pages/Search/SearchPageBottomTab.tsx | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index c201047033e6..663a01ba02b8 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useMemo} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; @@ -7,20 +8,42 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; +import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {SearchQuery} from '@src/types/onyx/SearchResults'; import SearchFilters from './SearchFilters'; +type SearchPageProps = StackScreenProps; + +const defaultSearchProps = { + query: '' as SearchQuery, + policyIDs: undefined, + sortBy: CONST.SEARCH_TABLE_COLUMNS.DATE, + sortOrder: CONST.SORT_ORDER.DESC, +}; function SearchPageBottomTab() { const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const activeRoute = useActiveRoute(); const styles = useThemeStyles(); - const currentQuery = activeRoute?.params && 'query' in activeRoute.params ? activeRoute?.params?.query : ''; - const policyIDs = activeRoute?.params && 'policyIDs' in activeRoute.params ? (activeRoute?.params?.policyIDs as string) : undefined; - const query = currentQuery as SearchQuery; + + const { + query: rawQuery, + policyIDs, + sortBy, + sortOrder, + } = useMemo(() => { + if (activeRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE || !activeRoute.params) { + return defaultSearchProps; + } + return {...defaultSearchProps, ...activeRoute.params} as SearchPageProps['route']['params']; + }, [activeRoute]); + + const query = rawQuery as SearchQuery; + const isValidQuery = Object.values(CONST.TAB_SEARCH).includes(query); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); @@ -45,6 +68,8 @@ function SearchPageBottomTab() { )} From 34f0aa93233ec0db5e0e5ff9de1dd8991859ed93 Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Mon, 10 Jun 2024 11:00:22 -0400 Subject: [PATCH 093/109] Remove redundant enabled flag --- src/libs/actions/Policy/Policy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index fdc2df185537..0e168f973078 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1409,7 +1409,6 @@ function buildOptimisticCustomUnits(): OptimisticCustomUnits { rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, enabled: true, currency, - enabled: true, }, }, }, From dfcde582fc798d057fb4767ee3de9b3df373f9ce Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 10 Jun 2024 23:27:47 +0800 Subject: [PATCH 094/109] get the cleaned tag name to display --- .../SelectionList/Search/TransactionListItemRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index 50a34be86f61..c23ae815f472 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -181,14 +181,14 @@ function TagCell({isLargeScreenWidth, showTooltip, transactionItem}: Transaction return isLargeScreenWidth ? ( ) : ( ); } From 01d001f8683da9f505205304af1a550e4f57c8eb Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 10 Jun 2024 17:28:30 +0200 Subject: [PATCH 095/109] Prevent navigating to Search when user is anonymous --- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 25bec235ed45..29b46df54e23 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Session from '@libs/actions/Session'; +import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; @@ -96,7 +97,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps { - Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); + interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL))); }} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.search')} From 169109ef9f69200d71b83cb4e05ee5b7cec2d91c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 15:51:36 +0000 Subject: [PATCH 096/109] Update version to 1.4.81-2 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index cc26412f0c0f..76ed848c95de 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048101 - versionName "1.4.81-1" + versionCode 1001048102 + versionName "1.4.81-2" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 71fb74c7dc8a..fcf3fd78d476 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.1 + 1.4.81.2 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6037678d5b9b..1f6276364287 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.1 + 1.4.81.2 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 8bbe7f95850c..944a919d7ed7 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.1 + 1.4.81.2 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 79f4e6053de1..2ff0a013f064 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-1", + "version": "1.4.81-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-1", + "version": "1.4.81-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fe69f4594efd..c689ccf39d87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-1", + "version": "1.4.81-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From e1e0f7c5e29d78f3ba2437d62cf580ce4ed314c7 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 16:01:55 +0000 Subject: [PATCH 097/109] Update version to 1.4.81-3 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 76ed848c95de..a133140caaca 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048102 - versionName "1.4.81-2" + versionCode 1001048103 + versionName "1.4.81-3" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index fcf3fd78d476..170a624bd058 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.2 + 1.4.81.3 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 1f6276364287..7c638c077dde 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.2 + 1.4.81.3 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 944a919d7ed7..855e74ed1c0b 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.2 + 1.4.81.3 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 2ff0a013f064..00fbcbdd362c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-2", + "version": "1.4.81-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-2", + "version": "1.4.81-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c689ccf39d87..3c0495da37cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-2", + "version": "1.4.81-3", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 763047d99281702f495afd56e67aa670d700c147 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 16:30:49 +0000 Subject: [PATCH 098/109] Update version to 1.4.81-4 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a133140caaca..3c78c0b5e5a3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048103 - versionName "1.4.81-3" + versionCode 1001048104 + versionName "1.4.81-4" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 170a624bd058..72750512d714 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.3 + 1.4.81.4 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 7c638c077dde..9e1488df6595 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.3 + 1.4.81.4 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 855e74ed1c0b..fd28fa7dc5bc 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.3 + 1.4.81.4 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 00fbcbdd362c..7094b58b5962 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-3", + "version": "1.4.81-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-3", + "version": "1.4.81-4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3c0495da37cc..fc72f146c384 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-3", + "version": "1.4.81-4", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 1178c0a63a06dacd4913437dc20851a3cdefe394 Mon Sep 17 00:00:00 2001 From: Danny McClain Date: Mon, 10 Jun 2024 11:51:26 -0500 Subject: [PATCH 099/109] Added new connections illustration and updated both hubs to use it --- docs/_data/_routes.yml | 4 +-- ...imple-illustration__monitor-remotesync.svg | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 docs/assets/images/simple-illustration__monitor-remotesync.svg diff --git a/docs/_data/_routes.yml b/docs/_data/_routes.yml index ad738e44ab44..5fd65532c021 100644 --- a/docs/_data/_routes.yml +++ b/docs/_data/_routes.yml @@ -71,7 +71,7 @@ platforms: - href: integrations title: Integrations - icon: /assets/images/workflow.svg + icon: /assets/images/simple-illustration__monitor-remotesync.svg description: Integrate with accounting or HR software to streamline expense approvals. - href: spending-insights @@ -131,7 +131,7 @@ platforms: - href: connections title: Connections - icon: /assets/images/workflow.svg + icon: /assets/images/simple-illustration__monitor-remotesync.svg description: Connect to accounting software to streamline expense approvals. - href: settings diff --git a/docs/assets/images/simple-illustration__monitor-remotesync.svg b/docs/assets/images/simple-illustration__monitor-remotesync.svg new file mode 100644 index 000000000000..e4ed84a35851 --- /dev/null +++ b/docs/assets/images/simple-illustration__monitor-remotesync.svg @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file From bca071f8d7434e60fd7aefaa5ef5898e395a2ce9 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 10 Jun 2024 12:26:15 -0600 Subject: [PATCH 100/109] fix lint --- src/types/onyx/PolicyTag.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index ef3146c27c75..467ba3271981 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -18,6 +18,7 @@ type PolicyTag = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** A list of errors keyed by microtime */ errors?: OnyxCommon.Errors | null; + /** The rules applied to the policy tag */ rules?: { /** * String representation of regex to match against parent tag. Eg, if San Francisco is a child tag of California From 191f197c6b970ca511510fa5e738709d0f3143af Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 10 Jun 2024 20:50:24 +0200 Subject: [PATCH 101/109] merge to main --- .../BottomTabBar/index.website.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index bc813f70f081..4fecfdcb0e3e 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Session from '@libs/actions/Session'; +import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; @@ -100,7 +101,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps { - Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); + interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL))); }} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.search')} From 0fb9222754d90ded9d965a2d656f6bca316e7408 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 10 Jun 2024 13:24:35 -0600 Subject: [PATCH 102/109] update receipt background color --- src/components/ReceiptImage.tsx | 5 - .../SelectionList/Search/ReportListItem.tsx | 101 +++++++++--------- .../Search/TransactionListItem.tsx | 17 ++- .../Search/TransactionListItemRow.tsx | 11 +- src/components/ThumbnailImage.tsx | 8 +- 5 files changed, 60 insertions(+), 82 deletions(-) diff --git a/src/components/ReceiptImage.tsx b/src/components/ReceiptImage.tsx index 65eb9b82ecdc..946856ecec37 100644 --- a/src/components/ReceiptImage.tsx +++ b/src/components/ReceiptImage.tsx @@ -76,9 +76,6 @@ type ReceiptImageProps = ( /** The colod of the fallback icon */ fallbackIconColor?: string; - - /** Whether the component is hovered */ - isHovered?: boolean; }; function ReceiptImage({ @@ -96,7 +93,6 @@ function ReceiptImage({ fallbackIconSize, shouldUseInitialObjectPosition = false, fallbackIconColor, - isHovered = false, }: ReceiptImageProps) { const styles = useThemeStyles(); @@ -134,7 +130,6 @@ function ReceiptImage({ fallbackIconSize={fallbackIconSize} fallbackIconColor={fallbackIconColor} objectPosition={shouldUseInitialObjectPosition ? CONST.IMAGE_OBJECT_POSITION.INITIAL : CONST.IMAGE_OBJECT_POSITION.TOP} - isHovered={isHovered} /> ); } diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index bc5a84c33b90..9adff46395e6 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -146,64 +146,61 @@ function ReportListItem({ shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} > - {(hovered?: boolean) => ( - - {!isLargeScreenWidth && ( - - )} - - - - - {reportItem?.reportName} - {`${reportItem.transactions.length} ${translate('search.groupedExpenses')}`} - + + {!isLargeScreenWidth && ( + + )} + + + + + {reportItem?.reportName} + {`${reportItem.transactions.length} ${translate('search.groupedExpenses')}`} - - + + + + + {isLargeScreenWidth && ( + <> + {/** We add an empty view with type style to align the total with the table header */} + + + - - {isLargeScreenWidth && ( - <> - {/** We add an empty view with type style to align the total with the table header */} - - - - - - )} - - - {reportItem.transactions.map((transaction) => ( - { - openReportInRHP(transaction); - }} - showItemHeaderOnNarrowLayout={false} - containerStyle={styles.mt3} - isHovered={hovered} - isChildListItem - /> - ))} + + )} - )} + + {reportItem.transactions.map((transaction) => ( + { + openReportInRHP(transaction); + }} + showItemHeaderOnNarrowLayout={false} + containerStyle={styles.mt3} + isChildListItem + /> + ))} + ); } diff --git a/src/components/SelectionList/Search/TransactionListItem.tsx b/src/components/SelectionList/Search/TransactionListItem.tsx index b222631b7273..ecf9264301c2 100644 --- a/src/components/SelectionList/Search/TransactionListItem.tsx +++ b/src/components/SelectionList/Search/TransactionListItem.tsx @@ -50,16 +50,13 @@ function TransactionListItem({ shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} > - {(hovered?: boolean) => ( - { - onSelectRow(item); - }} - isHovered={hovered} - /> - )} + { + onSelectRow(item); + }} + /> ); } diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index c23ae815f472..c2ad559511ff 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -33,10 +33,6 @@ type TransactionCellProps = { transactionItem: TransactionListItemType; } & CellProps; -type ReceiptCellProps = { - isHovered?: boolean; -} & TransactionCellProps; - type ActionCellProps = { onButtonPress: () => void; } & CellProps; @@ -51,7 +47,6 @@ type TransactionListItemRowProps = { onButtonPress: () => void; showItemHeaderOnNarrowLayout?: boolean; containerStyle?: StyleProp; - isHovered?: boolean; isChildListItem?: boolean; }; @@ -68,7 +63,7 @@ const getTypeIcon = (type?: SearchTransactionType) => { } }; -function ReceiptCell({transactionItem, isHovered = false}: ReceiptCellProps) { +function ReceiptCell({transactionItem}: TransactionCellProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -85,7 +80,6 @@ function ReceiptCell({transactionItem, isHovered = false}: ReceiptCellProps) { fallbackIconSize={20} fallbackIconColor={theme.icon} iconSize="x-small" - isHovered={isHovered} /> ); @@ -215,7 +209,6 @@ function TransactionListItemRow({ onButtonPress, showItemHeaderOnNarrowLayout = true, containerStyle, - isHovered = false, isChildListItem = false, }: TransactionListItemRowProps) { const styles = useThemeStyles(); @@ -242,7 +235,6 @@ function TransactionListItemRow({ transactionItem={item} isLargeScreenWidth={false} showTooltip={false} - isHovered={isHovered} /> diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index d4a45f3d93b3..269c94bc761d 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -5,6 +5,7 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useThumbnailDimensions from '@hooks/useThumbnailDimensions'; +import useStyleUtils from '@hooks/useStyleUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -48,9 +49,6 @@ type ThumbnailImageProps = { /** The object position of image */ objectPosition?: ImageObjectPosition; - - /** Whether the component is hovered */ - isHovered?: boolean; }; type UpdateImageSizeParams = { @@ -69,7 +67,6 @@ function ThumbnailImage({ fallbackIconSize = variables.iconSizeSuperLarge, fallbackIconColor, objectPosition = CONST.IMAGE_OBJECT_POSITION.INITIAL, - isHovered = false, }: ThumbnailImageProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -78,6 +75,7 @@ function ThumbnailImage({ const cachedDimensions = shouldDynamicallyResize && typeof previewSourceURL === 'string' ? thumbnailDimensionsCache.get(previewSourceURL) : null; const [imageDimensions, setImageDimensions] = useState({width: cachedDimensions?.width ?? imageWidth, height: cachedDimensions?.height ?? imageHeight}); const {thumbnailDimensionsStyles} = useThumbnailDimensions(imageDimensions.width, imageDimensions.height); + const StyleUtils = useStyleUtils(); useEffect(() => { setFailedToLoad(false); @@ -110,7 +108,7 @@ function ThumbnailImage({ if (failedToLoad || previewSourceURL === '') { return ( - + Date: Mon, 10 Jun 2024 13:43:51 -0600 Subject: [PATCH 103/109] fix topbar text overflow --- src/components/Breadcrumbs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index e5eb09691eba..e641a0c2218a 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -68,7 +68,7 @@ function Breadcrumbs({breadcrumbs, style}: BreadcrumbsProps) { / {secondaryBreadcrumb.text} From ea6652471da0b22c86a7d5cc07956f40b599fa05 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 20:09:16 +0000 Subject: [PATCH 104/109] Update version to 1.4.81-5 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 3c78c0b5e5a3..2e89bbc7e26a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048104 - versionName "1.4.81-4" + versionCode 1001048105 + versionName "1.4.81-5" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 72750512d714..f0c5755901cc 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.4 + 1.4.81.5 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 9e1488df6595..94700c2c07ef 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.4 + 1.4.81.5 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index fd28fa7dc5bc..c26dd25e1c96 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.4 + 1.4.81.5 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 7094b58b5962..598784cfc378 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-4", + "version": "1.4.81-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-4", + "version": "1.4.81-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fc72f146c384..a74743a1d88f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-4", + "version": "1.4.81-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 628c5a79e6b18b3d7ecedb4a29962bc3014d7d6e Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 10 Jun 2024 14:48:45 -0600 Subject: [PATCH 105/109] Revert "chore: remove all selection ranges during navigation" --- src/libs/Navigation/NavigationRoot.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 9e1eb348451b..06a3dce8d59a 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -135,11 +135,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N // We want to clean saved scroll offsets for screens that aren't anymore in the state. cleanStaleScrollOffsets(state); - - // clear all window selection on navigation - // this is to prevent the selection from persisting when navigating to a new page in web - // using "?" to avoid crash in native - window?.getSelection?.()?.removeAllRanges?.(); }; return ( From 89c64835daab4c01f65defb664a8d10566539cb9 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 10 Jun 2024 15:09:27 -0600 Subject: [PATCH 106/109] fix lint --- .../SelectionList/Search/TransactionListItemRow.tsx | 9 +-------- src/components/ThumbnailImage.tsx | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index c2ad559511ff..d17d923a54e1 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -203,14 +203,7 @@ function TaxCell({transactionItem, showTooltip}: TransactionCellProps) { ); } -function TransactionListItemRow({ - item, - showTooltip, - onButtonPress, - showItemHeaderOnNarrowLayout = true, - containerStyle, - isChildListItem = false, -}: TransactionListItemRowProps) { +function TransactionListItemRow({item, showTooltip, onButtonPress, showItemHeaderOnNarrowLayout = true, containerStyle, isChildListItem = false}: TransactionListItemRowProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isLargeScreenWidth} = useWindowDimensions(); diff --git a/src/components/ThumbnailImage.tsx b/src/components/ThumbnailImage.tsx index 269c94bc761d..3b89b7c3a7ad 100644 --- a/src/components/ThumbnailImage.tsx +++ b/src/components/ThumbnailImage.tsx @@ -2,10 +2,10 @@ import React, {useCallback, useEffect, useState} from 'react'; import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useThumbnailDimensions from '@hooks/useThumbnailDimensions'; -import useStyleUtils from '@hooks/useStyleUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; From 061a7fdcc0900cc3e60da7bf0c2d00d7e7f05003 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 10 Jun 2024 21:13:58 +0000 Subject: [PATCH 107/109] Update version to 1.4.81-6 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2e89bbc7e26a..1aa1369a556a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048105 - versionName "1.4.81-5" + versionCode 1001048106 + versionName "1.4.81-6" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index f0c5755901cc..08b0e985cf5f 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.5 + 1.4.81.6 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 94700c2c07ef..ac9882f31c38 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.5 + 1.4.81.6 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index c26dd25e1c96..9af1d6896871 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.5 + 1.4.81.6 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 598784cfc378..b2a5b377b281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-5", + "version": "1.4.81-6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-5", + "version": "1.4.81-6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a74743a1d88f..058406cf369d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-5", + "version": "1.4.81-6", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From ce6c1125ee2641ccbe3115dffcf98283957cf671 Mon Sep 17 00:00:00 2001 From: chiragsalian Date: Mon, 10 Jun 2024 16:46:19 -0700 Subject: [PATCH 108/109] removing check for shouldShowHoldMessage --- .../MoneyRequestPreview/MoneyRequestPreviewContent.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index b6d6915279cb..e03ed59ffb79 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -163,11 +163,7 @@ function MoneyRequestPreviewContent({ const isTooLong = violationsCount > 1 || violationMessage.length > 15; const hasViolationsAndFieldErrors = violationsCount > 0 && hasFieldErrors; - message += ` ${CONST.DOT_SEPARATOR} ${isTooLong || hasViolationsAndFieldErrors ? translate('violations.reviewRequired') : violationMessage}`; - if (shouldShowHoldMessage) { - message += ` ${CONST.DOT_SEPARATOR} ${translate('iou.hold')}`; - } - return message; + return `${message} ${CONST.DOT_SEPARATOR} ${isTooLong || hasViolationsAndFieldErrors ? translate('violations.reviewRequired') : violationMessage}`; } const isMerchantMissing = TransactionUtils.isMerchantMissing(transaction); From 60442419ab6434f1f22729399637c8c67c0167a3 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Tue, 11 Jun 2024 00:07:59 +0000 Subject: [PATCH 109/109] Update version to 1.4.81-7 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1aa1369a556a..ba41291038f3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001048106 - versionName "1.4.81-6" + versionCode 1001048107 + versionName "1.4.81-7" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 08b0e985cf5f..e782155beee7 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.81.6 + 1.4.81.7 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index ac9882f31c38..c18015ff0858 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.81.6 + 1.4.81.7 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 9af1d6896871..2feda22f8a36 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.81 CFBundleVersion - 1.4.81.6 + 1.4.81.7 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index b2a5b377b281..6fb017197b28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.81-6", + "version": "1.4.81-7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.81-6", + "version": "1.4.81-7", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 058406cf369d..dcd8fd60863c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.81-6", + "version": "1.4.81-7", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",