From a23976a681df6e600287bb73bb9bacf058a29eb2 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 10 Aug 2024 14:42:18 +0800 Subject: [PATCH 001/432] optimistically remove hold when paying --- src/libs/actions/IOU.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ef5f6d6d61c0..c1d55a6afd1a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6705,6 +6705,29 @@ function getPayMoneyRequestParams( }); } + if (full) { + for (const transaction of TransactionUtils.getAllReportTransactions(iouReport.reportID)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, + value: { + comment: { + hold: null, + }, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, + value: { + comment: { + hold: transaction.comment?.hold, + }, + }, + }); + } + } + let optimisticHoldReportID; let optimisticHoldActionID; let optimisticHoldReportExpenseActionIDs; From b43f7c04e6913ade3960877892e84eb44f781668 Mon Sep 17 00:00:00 2001 From: Wiktor Gut Date: Tue, 13 Aug 2024 12:58:22 +0200 Subject: [PATCH 002/432] pulling from, to filters --- src/pages/Search/AdvancedSearchFilters.tsx | 5 +++ src/pages/Search/SearchFiltersInPage.tsx | 42 +++++++++++++++++++++ src/types/form/SearchAdvancedFiltersForm.ts | 2 + 3 files changed, 49 insertions(+) create mode 100644 src/pages/Search/SearchFiltersInPage.tsx diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index 644ae64466f7..9a7688fdc268 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -190,6 +190,11 @@ function AdvancedSearchFilters() { description: 'common.to' as const, route: ROUTES.SEARCH_ADVANCED_FILTERS_TO, }, + { + title: getFilterParticipantDisplayTitle(searchAdvancedFilters.in ?? [], personalDetails), + description: 'common.to' as const, + route: ROUTES.SEARCH_ADVANCED_FILTERS_TO, + }, ], [searchAdvancedFilters, translate, cardList, taxRates, personalDetails], ); diff --git a/src/pages/Search/SearchFiltersInPage.tsx b/src/pages/Search/SearchFiltersInPage.tsx new file mode 100644 index 000000000000..1bcf1ea66248 --- /dev/null +++ b/src/pages/Search/SearchFiltersInPage.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SearchFiltersParticipantsSelector from '@components/Search/SearchFiltersParticipantsSelector'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as SearchActions from '@userActions/Search'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function SearchFiltersStatusPage() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM); + + return ( + + + + { + SearchActions.updateAdvancedFilters({ + from: selectedAccountIDs, + }); + }} + /> + + + ); +} + +SearchFiltersStatusPage.displayName = 'SearchFiltersStatusPage'; + +export default SearchFiltersStatusPage; diff --git a/src/types/form/SearchAdvancedFiltersForm.ts b/src/types/form/SearchAdvancedFiltersForm.ts index f2643c0c987d..2afc63b7a2d3 100644 --- a/src/types/form/SearchAdvancedFiltersForm.ts +++ b/src/types/form/SearchAdvancedFiltersForm.ts @@ -17,6 +17,7 @@ const FILTER_KEYS = { KEYWORD: 'keyword', FROM: 'from', TO: 'to', + IN: 'in', } as const; type InputID = ValueOf; @@ -39,6 +40,7 @@ type SearchAdvancedFiltersForm = Form< [FILTER_KEYS.TAG]: string[]; [FILTER_KEYS.FROM]: string[]; [FILTER_KEYS.TO]: string[]; + [FILTER_KEYS.IN]: string[]; } >; From 30c56d1121f849ae855b3e465d82a9d643a6f554 Mon Sep 17 00:00:00 2001 From: daledah Date: Tue, 13 Aug 2024 20:29:12 +0700 Subject: [PATCH 003/432] fix: maintain tax assign when tax code changes --- src/libs/actions/TaxRate.ts | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts index 0e7245a93f7b..869c14017607 100644 --- a/src/libs/actions/TaxRate.ts +++ b/src/libs/actions/TaxRate.ts @@ -21,6 +21,7 @@ import INPUT_IDS from '@src/types/form/WorkspaceNewTaxForm'; import {default as INPUT_IDS_TAX_CODE} from '@src/types/form/WorkspaceTaxCodeForm'; import type {Policy, TaxRate, TaxRates} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import type {CustomUnit, Rate} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; let allPolicies: OnyxCollection; @@ -486,6 +487,63 @@ function renamePolicyTax(policyID: string, taxID: string, newName: string) { function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: string) { const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const originalTaxRate = {...policy?.taxRates?.taxes[oldTaxCode]}; + const customUnits = Object.values(policy?.customUnits ?? {}); + const optimisticCustomUnit = { + customUnits: { + ...policy?.customUnits, + ...customUnits.reduce((units, customUnit) => { + // eslint-disable-next-line no-param-reassign + units[customUnit.customUnitID] = { + ...customUnit, + rates: { + ...customUnit.rates, + ...Object.keys(customUnit.rates).reduce((rates, rateID) => { + if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) { + // eslint-disable-next-line no-param-reassign + rates[rateID] = { + ...customUnit.rates[rateID], + attributes: { + ...customUnit.rates[rateID].attributes, + taxRateExternalID: newTaxCode, + }, + }; + } + return rates; + }, {} as Record), + } as Record, + }; + return units; + }, {} as Record), + }, + }; + const failureCustomUnit = { + customUnits: { + ...policy?.customUnits, + ...customUnits.reduce((units, customUnit) => { + // eslint-disable-next-line no-param-reassign + units[customUnit.customUnitID] = { + ...customUnit, + rates: { + ...customUnit.rates, + ...Object.keys(customUnit.rates).reduce((rates, rateID) => { + if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) { + // eslint-disable-next-line no-param-reassign + rates[rateID] = { + ...customUnit.rates[rateID], + attributes: { + ...customUnit.rates[rateID].attributes, + taxRateExternalID: oldTaxCode, + }, + }; + } + return rates; + }, {} as Record), + } as Record, + }; + return units; + }, {} as Record), + }, + }; const onyxData: OnyxData = { optimisticData: [ { @@ -504,6 +562,7 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri }, }, }, + ...(!!customUnits && optimisticCustomUnit), }, }, ], @@ -544,6 +603,7 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri }, }, }, + ...(!!customUnits && failureCustomUnit), }, }, ], From 2aef27d8d1b5533993249eacc7d974a51b0424fa Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 14 Aug 2024 14:50:06 +0700 Subject: [PATCH 004/432] refactor: simplify optimistic and failture data --- src/libs/actions/TaxRate.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts index 869c14017607..decb74c1a642 100644 --- a/src/libs/actions/TaxRate.ts +++ b/src/libs/actions/TaxRate.ts @@ -490,58 +490,48 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri const customUnits = Object.values(policy?.customUnits ?? {}); const optimisticCustomUnit = { customUnits: { - ...policy?.customUnits, ...customUnits.reduce((units, customUnit) => { // eslint-disable-next-line no-param-reassign units[customUnit.customUnitID] = { - ...customUnit, rates: { - ...customUnit.rates, ...Object.keys(customUnit.rates).reduce((rates, rateID) => { if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) { // eslint-disable-next-line no-param-reassign rates[rateID] = { - ...customUnit.rates[rateID], attributes: { - ...customUnit.rates[rateID].attributes, taxRateExternalID: newTaxCode, }, }; } return rates; }, {} as Record), - } as Record, + }, }; return units; - }, {} as Record), + }, {} as Record>), }, }; const failureCustomUnit = { customUnits: { - ...policy?.customUnits, ...customUnits.reduce((units, customUnit) => { // eslint-disable-next-line no-param-reassign units[customUnit.customUnitID] = { - ...customUnit, rates: { - ...customUnit.rates, ...Object.keys(customUnit.rates).reduce((rates, rateID) => { if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) { // eslint-disable-next-line no-param-reassign rates[rateID] = { - ...customUnit.rates[rateID], attributes: { - ...customUnit.rates[rateID].attributes, taxRateExternalID: oldTaxCode, }, }; } return rates; }, {} as Record), - } as Record, + }, }; return units; - }, {} as Record), + }, {} as Record>), }, }; const onyxData: OnyxData = { From d43e7a87b6f821aee884eb307bcb4227448a7461 Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 14 Aug 2024 17:44:43 +0700 Subject: [PATCH 005/432] refactor: simplify failureCustomUnits --- src/libs/actions/TaxRate.ts | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts index decb74c1a642..29aa3c2fadff 100644 --- a/src/libs/actions/TaxRate.ts +++ b/src/libs/actions/TaxRate.ts @@ -512,27 +512,7 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri }, }; const failureCustomUnit = { - customUnits: { - ...customUnits.reduce((units, customUnit) => { - // eslint-disable-next-line no-param-reassign - units[customUnit.customUnitID] = { - rates: { - ...Object.keys(customUnit.rates).reduce((rates, rateID) => { - if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) { - // eslint-disable-next-line no-param-reassign - rates[rateID] = { - attributes: { - taxRateExternalID: oldTaxCode, - }, - }; - } - return rates; - }, {} as Record), - }, - }; - return units; - }, {} as Record>), - }, + customUnits: policy?.customUnits, }; const onyxData: OnyxData = { optimisticData: [ From 5ebdf23fa6229b39e311d055300ed841e5cb39d2 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 15 Aug 2024 11:10:15 +0700 Subject: [PATCH 006/432] fix: Message and Notification Swapping in IOU --- src/libs/Network/SequentialQueue.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 5646cb8956dd..9f418e36b30b 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -176,7 +176,9 @@ function unpause() { const numberOfPersistedRequests = PersistedRequests.getAll().length || 0; console.debug(`[SequentialQueue] Unpausing the queue and flushing ${numberOfPersistedRequests} requests`); isQueuePaused = false; - flushOnyxUpdatesQueue(); + if (PersistedRequests.getAll().length === 0) { + flushOnyxUpdatesQueue(); + } flush(); } From d7b8835d364b91337eff276cac7eba254d597b85 Mon Sep 17 00:00:00 2001 From: Hans Date: Thu, 15 Aug 2024 14:22:31 +0700 Subject: [PATCH 007/432] only show error message if isSuccessful is false --- src/libs/actions/connections/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index c47c57d57466..cfce7442efed 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -379,7 +379,8 @@ function getSynchronizationErrorMessage(policy: OnyxEntry, connectionNam } const connection = policy?.connections?.[connectionName]; - if (isSyncInProgress || connection?.lastSync?.isSuccessful) { + + if (isSyncInProgress || connection?.lastSync?.isSuccessful !== false) { return; } return `${syncError} ("${connection?.lastSync?.errorMessage}")`; From cd5b1fb244834c2073d5a8ad7f241bdfc0bf065a Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sat, 10 Aug 2024 12:55:46 -0400 Subject: [PATCH 008/432] Fix maintainVisibleContentPosition in Safari --- src/CONST.ts | 2 +- src/components/FlatList/index.tsx | 43 +++++++++++++++++++------------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index f1e4a3bb46d5..7f50e5b8bd5d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4475,7 +4475,7 @@ const CONST = { REPORT_FIELD_TITLE_FIELD_ID: 'text_title', MOBILE_PAGINATION_SIZE: 15, - WEB_PAGINATION_SIZE: 50, + WEB_PAGINATION_SIZE: 30, /** Dimensions for illustration shown in Confirmation Modal */ CONFIRM_CONTENT_SVG_SIZE: { diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index 75b5c54cb5d7..231740dd1d0f 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -96,7 +96,7 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const firstVisibleView = firstVisibleViewRef.current; const prevFirstVisibleOffset = prevFirstVisibleOffsetRef.current; - if (firstVisibleView == null || prevFirstVisibleOffset == null) { + if (firstVisibleView == null || !firstVisibleView.isConnected || prevFirstVisibleOffset == null) { return; } @@ -120,28 +120,39 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false mutationObserverRef.current?.disconnect(); - const mutationObserver = new MutationObserver(() => { + const mutationObserver = new MutationObserver((mutations) => { + // Check if the first visible view is removed and re-calculate it + // if needed. + mutations.forEach((mutation) => { + mutation.removedNodes.forEach((node) => { + if (node !== firstVisibleViewRef.current) { + return; + } + firstVisibleViewRef.current = null; + }); + }); + + if (firstVisibleViewRef.current == null) { + prepareForMaintainVisibleContentPosition(); + } + // When the list is hidden, the size will be 0. // Ignore the callback if the list is hidden because scrollOffset will always be 0. if (!getScrollableNode(scrollRef.current)?.clientHeight) { return; } - // This needs to execute after scroll events are dispatched, but - // in the same tick to avoid flickering. rAF provides the right timing. - requestAnimationFrame(() => { - // Chrome adjusts scroll position when elements are added at the top of the - // view. We want to have the same behavior as react-native / Safari so we - // reset the scroll position to the last value we got from an event. - const lastScrollOffset = lastScrollOffsetRef.current; - const scrollOffset = getScrollOffset(); - if (lastScrollOffset !== scrollOffset) { - scrollToOffset(lastScrollOffset, false); - } + // Chrome adjusts scroll position when elements are added at the top of the + // view. We want to have the same behavior as react-native / Safari so we + // reset the scroll position to the last value we got from an event. + const lastScrollOffset = lastScrollOffsetRef.current; + const scrollOffset = getScrollOffset(); + if (lastScrollOffset !== scrollOffset) { + scrollToOffset(lastScrollOffset, false); + } - adjustForMaintainVisibleContentPosition(); - prepareForMaintainVisibleContentPosition(); - }); + adjustForMaintainVisibleContentPosition(); + prepareForMaintainVisibleContentPosition(); }); mutationObserver.observe(contentView, { attributes: true, From 517585c4cf2c9847b12b73eafb06becf0a86bbdf Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 15 Aug 2024 12:35:43 -0600 Subject: [PATCH 009/432] create status skeleton --- src/components/Search/index.tsx | 4 +- .../Skeletons/SearchStatusSkeleton.tsx | 82 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/components/Skeletons/SearchStatusSkeleton.tsx diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 0fb2daede842..f2039de83cfd 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -9,6 +9,7 @@ import SearchTableHeader from '@components/SelectionList/SearchTableHeader'; import type {ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import SelectionListWithModal from '@components/SelectionListWithModal'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; +import SearchStatusSkeleton from '@components/Skeletons/SearchStatusSkeleton'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; @@ -191,7 +192,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { turnOffMobileSelectionMode(); }, [isSearchResultsEmpty, prevIsSearchResultEmpty]); - if (shouldShowLoadingState) { + if (true) { return ( <> + ); diff --git a/src/components/Skeletons/SearchStatusSkeleton.tsx b/src/components/Skeletons/SearchStatusSkeleton.tsx new file mode 100644 index 000000000000..151267c669c6 --- /dev/null +++ b/src/components/Skeletons/SearchStatusSkeleton.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import {View} from 'react-native'; +import {Path, Rect} from 'react-native-svg'; +import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type SearchStatusSkeletonProps = { + shouldAnimate?: boolean; +}; + +function SearchStatusSkeleton({shouldAnimate = true}: SearchStatusSkeletonProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + + return ( + + + + + + + + + + + + + + ); +} + +SearchStatusSkeleton.displayName = 'SearchStatusSkeleton'; + +export default SearchStatusSkeleton; From e7ef910917a56ba9f06b7c3b26683e1cef0ea510 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 15 Aug 2024 13:09:25 -0600 Subject: [PATCH 010/432] fix style --- .../Skeletons/SearchStatusSkeleton.tsx | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/Skeletons/SearchStatusSkeleton.tsx b/src/components/Skeletons/SearchStatusSkeleton.tsx index 151267c669c6..43fe9f7b13d2 100644 --- a/src/components/Skeletons/SearchStatusSkeleton.tsx +++ b/src/components/Skeletons/SearchStatusSkeleton.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import {Path, Rect} from 'react-native-svg'; +import {Rect} from 'react-native-svg'; import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -21,20 +21,13 @@ function SearchStatusSkeleton({shouldAnimate = true}: SearchStatusSkeletonProps) backgroundColor={theme.skeletonLHNIn} foregroundColor={theme.skeletonLHNOut} > - - + + + + + + ); } From 2961eb8cdb9e26b2a767ce74439d3d89e48148f6 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Thu, 15 Aug 2024 13:11:34 -0600 Subject: [PATCH 011/432] rm hardcoded value --- src/components/Search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index f2039de83cfd..f4c77445bec7 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -192,7 +192,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { turnOffMobileSelectionMode(); }, [isSearchResultsEmpty, prevIsSearchResultEmpty]); - if (true) { + if (shouldShowLoadingState) { return ( <> Date: Thu, 15 Aug 2024 15:32:16 -0600 Subject: [PATCH 012/432] add width do fix bug on ios --- src/components/Search/index.tsx | 2 +- src/components/Skeletons/SearchStatusSkeleton.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index f4c77445bec7..f2039de83cfd 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -192,7 +192,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { turnOffMobileSelectionMode(); }, [isSearchResultsEmpty, prevIsSearchResultEmpty]); - if (shouldShowLoadingState) { + if (true) { return ( <> - + Date: Thu, 15 Aug 2024 15:41:08 -0600 Subject: [PATCH 013/432] rm hardcoded value --- src/components/Search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index f2039de83cfd..f4c77445bec7 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -192,7 +192,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { turnOffMobileSelectionMode(); }, [isSearchResultsEmpty, prevIsSearchResultEmpty]); - if (true) { + if (shouldShowLoadingState) { return ( <> Date: Fri, 16 Aug 2024 17:58:28 +0700 Subject: [PATCH 014/432] update solution --- src/libs/Network/SequentialQueue.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 9f418e36b30b..8dbbce8fa832 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -158,7 +158,10 @@ function flush() { resolveIsReadyPromise?.(); } currentRequest = null; - flushOnyxUpdatesQueue(); + // The queue can be paused when we sync the data with backend so we should only update the Onyx data when the queue is empty + if (PersistedRequests.getAll().length === 0) { + flushOnyxUpdatesQueue(); + } }); }, }); @@ -176,9 +179,6 @@ function unpause() { const numberOfPersistedRequests = PersistedRequests.getAll().length || 0; console.debug(`[SequentialQueue] Unpausing the queue and flushing ${numberOfPersistedRequests} requests`); isQueuePaused = false; - if (PersistedRequests.getAll().length === 0) { - flushOnyxUpdatesQueue(); - } flush(); } From fa4dcf098f5563ddb69e20a94153c384ef898e6f Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 16 Aug 2024 16:15:40 -0400 Subject: [PATCH 015/432] Tentative mobile safari fix --- src/components/FlatList/index.tsx | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index 231740dd1d0f..f13c16008837 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -3,6 +3,9 @@ import type {ForwardedRef, MutableRefObject} from 'react'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; import {FlatList} from 'react-native'; +import {isMobileSafari} from '@libs/Browser'; + +const IS_MOBILE_SAFARI = isMobileSafari(); function mergeRefs(...args: Array | ForwardedRef | null>) { return function forwardRef(node: FlatList) { @@ -57,9 +60,20 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const getContentView = useCallback(() => getScrollableNode(scrollRef.current)?.childNodes[0], []); const scrollToOffset = useCallback( - (offset: number, animated: boolean) => { + (offset: number, animated: boolean, interrupt: boolean) => { const behavior = animated ? 'smooth' : 'instant'; - getScrollableNode(scrollRef.current)?.scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior}); + const node = getScrollableNode(scrollRef.current); + if (node == null) { + return; + } + + if (IS_MOBILE_SAFARI && interrupt) { + node.style.overflowY = 'hidden'; + } + node.scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior}); + if (IS_MOBILE_SAFARI && interrupt) { + node.style.overflowY = 'scroll'; + } }, [horizontal], ); @@ -102,12 +116,12 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const firstVisibleViewOffset = horizontal ? firstVisibleView.offsetLeft : firstVisibleView.offsetTop; const delta = firstVisibleViewOffset - prevFirstVisibleOffset; - if (Math.abs(delta) > 0.5) { + if (Math.abs(delta) > (IS_MOBILE_SAFARI ? 100 : 0.5)) { const scrollOffset = getScrollOffset(); prevFirstVisibleOffsetRef.current = firstVisibleViewOffset; - scrollToOffset(scrollOffset + delta, false); + scrollToOffset(scrollOffset + delta, false, true); if (mvcpAutoscrollToTopThresholdRef.current != null && scrollOffset <= mvcpAutoscrollToTopThresholdRef.current) { - scrollToOffset(0, true); + scrollToOffset(0, true, false); } } }, [getScrollOffset, scrollToOffset, mvcpMinIndexForVisible, horizontal]); @@ -147,8 +161,8 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false // reset the scroll position to the last value we got from an event. const lastScrollOffset = lastScrollOffsetRef.current; const scrollOffset = getScrollOffset(); - if (lastScrollOffset !== scrollOffset) { - scrollToOffset(lastScrollOffset, false); + if (!IS_MOBILE_SAFARI && lastScrollOffset !== scrollOffset) { + scrollToOffset(lastScrollOffset, false, false); } adjustForMaintainVisibleContentPosition(); From ae54131f1b5164fee28f447450dfb476c6097dfc Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sat, 17 Aug 2024 14:48:51 -0400 Subject: [PATCH 016/432] Add commments --- src/components/FlatList/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index f13c16008837..d6e636d4ef53 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -5,6 +5,8 @@ import type {FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react import {FlatList} from 'react-native'; import {isMobileSafari} from '@libs/Browser'; +// Changing the scroll position during a momentum scroll does not work on mobile Safari. +// We do a best effort to avoid content jumping by using some hacks on mobile Safari only. const IS_MOBILE_SAFARI = isMobileSafari(); function mergeRefs(...args: Array | ForwardedRef | null>) { @@ -67,6 +69,8 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false return; } + // Stop momentum scrolling on mobile Safari otherwise the scroll position update + // will not work. if (IS_MOBILE_SAFARI && interrupt) { node.style.overflowY = 'hidden'; } From 316e144c929640c856315889431348be346ba9d4 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sat, 17 Aug 2024 14:50:45 -0400 Subject: [PATCH 017/432] Support horizontal --- src/components/FlatList/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index d6e636d4ef53..67624e7e99d1 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -69,14 +69,15 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false return; } + const overflowProp = horizontal ? 'overflowX' : 'overflowY'; // Stop momentum scrolling on mobile Safari otherwise the scroll position update // will not work. if (IS_MOBILE_SAFARI && interrupt) { - node.style.overflowY = 'hidden'; + node.style[overflowProp] = 'hidden'; } node.scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior}); if (IS_MOBILE_SAFARI && interrupt) { - node.style.overflowY = 'scroll'; + node.style[overflowProp] = 'scroll'; } }, [horizontal], From f70dce711b76bd9fef408469c531fcda02cb9948 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sat, 17 Aug 2024 15:55:05 -0400 Subject: [PATCH 018/432] Last fixes --- src/components/FlatList/index.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index 67624e7e99d1..9bca23efb384 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -122,14 +122,14 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const firstVisibleViewOffset = horizontal ? firstVisibleView.offsetLeft : firstVisibleView.offsetTop; const delta = firstVisibleViewOffset - prevFirstVisibleOffset; if (Math.abs(delta) > (IS_MOBILE_SAFARI ? 100 : 0.5)) { - const scrollOffset = getScrollOffset(); + const scrollOffset = lastScrollOffsetRef.current; prevFirstVisibleOffsetRef.current = firstVisibleViewOffset; scrollToOffset(scrollOffset + delta, false, true); if (mvcpAutoscrollToTopThresholdRef.current != null && scrollOffset <= mvcpAutoscrollToTopThresholdRef.current) { scrollToOffset(0, true, false); } } - }, [getScrollOffset, scrollToOffset, mvcpMinIndexForVisible, horizontal]); + }, [scrollToOffset, mvcpMinIndexForVisible, horizontal]); const setupMutationObserver = useCallback(() => { const contentView = getContentView(); @@ -161,15 +161,6 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false return; } - // Chrome adjusts scroll position when elements are added at the top of the - // view. We want to have the same behavior as react-native / Safari so we - // reset the scroll position to the last value we got from an event. - const lastScrollOffset = lastScrollOffsetRef.current; - const scrollOffset = getScrollOffset(); - if (!IS_MOBILE_SAFARI && lastScrollOffset !== scrollOffset) { - scrollToOffset(lastScrollOffset, false, false); - } - adjustForMaintainVisibleContentPosition(); prepareForMaintainVisibleContentPosition(); }); @@ -180,7 +171,7 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false }); mutationObserverRef.current = mutationObserver; - }, [adjustForMaintainVisibleContentPosition, prepareForMaintainVisibleContentPosition, getContentView, getScrollOffset, scrollToOffset]); + }, [adjustForMaintainVisibleContentPosition, prepareForMaintainVisibleContentPosition, getContentView]); useEffect(() => { if (!isListRenderedRef.current) { From 5a9bf323e844fcc026a69cf28e5fb332d53142ec Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 17:53:19 +0700 Subject: [PATCH 019/432] introduce allConnectionProgreses Onyx data --- src/components/Indicator.tsx | 11 +++++++++-- src/libs/WorkspacesSettingsUtils.ts | 11 ++++++++--- src/libs/actions/connections/index.ts | 18 +++++++++++++++++- src/pages/home/sidebar/AllSettingsScreen.tsx | 5 +++-- src/pages/settings/InitialSettingsPage.tsx | 5 +++-- .../accounting/PolicyAccountingPage.tsx | 9 ++------- 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/components/Indicator.tsx b/src/components/Indicator.tsx index 5ae227d1de70..e0d8ec95738f 100644 --- a/src/components/Indicator.tsx +++ b/src/components/Indicator.tsx @@ -1,9 +1,10 @@ import React from 'react'; import {StyleSheet, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isConnectionInProgress} from '@libs/actions/connections'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -41,6 +42,7 @@ type IndicatorProps = IndicatorOnyxProps; function Indicator({reimbursementAccount, policies, bankAccountList, fundList, userWallet, walletTerms, loginList}: IndicatorOnyxProps) { const theme = useTheme(); const styles = useThemeStyles(); + const [allConnectionSyncProgresses] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}`); // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and // those should be cleaned out before doing any error checking @@ -55,7 +57,12 @@ function Indicator({reimbursementAccount, policies, bankAccountList, fundList, u () => Object.values(cleanPolicies).some(PolicyUtils.hasPolicyError), () => Object.values(cleanPolicies).some(PolicyUtils.hasCustomUnitsError), () => Object.values(cleanPolicies).some(PolicyUtils.hasEmployeeListError), - () => Object.values(cleanPolicies).some(PolicyUtils.hasSyncError), + () => + Object.values(cleanPolicies).some( + (cleanPolicy) => + PolicyUtils.hasSyncError(cleanPolicy) && + !isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}}`], cleanPolicy), + ), () => SubscriptionUtils.hasSubscriptionRedDotError(), () => Object.keys(reimbursementAccount?.errors ?? {}).length > 0, () => !!loginList && UserUtils.hasLoginListError(loginList), diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 9b96c8404bf6..7224b5ae2f9c 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -5,7 +5,8 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, ReimbursementAccount, Report, ReportAction, ReportActions, TransactionViolations} from '@src/types/onyx'; -import type {Unit} from '@src/types/onyx/Policy'; +import type {PolicyConnectionSyncProgress, Unit} from '@src/types/onyx/Policy'; +import {isConnectionInProgress} from './actions/connections'; import * as CurrencyUtils from './CurrencyUtils'; import type {Phrase, PhraseParameters} from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; @@ -100,7 +101,7 @@ const getBrickRoadForPolicy = (report: Report, altReportActions?: OnyxCollection return shouldShowGreenDotIndicator ? CONST.BRICK_ROAD_INDICATOR_STATUS.INFO : undefined; }; -function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection) { +function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection, allConnectionProgresses: OnyxCollection) { // When attempting to open a policy with an invalid policyID, the policy collection is updated to include policy objects with error information. // Only policies displayed on the policy list page should be verified. Otherwise, the user will encounter an RBR unrelated to any policies on the list. const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)); @@ -110,7 +111,11 @@ function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection) { () => Object.values(cleanPolicies).some(hasCustomUnitsError), () => Object.values(cleanPolicies).some(hasTaxRateError), () => Object.values(cleanPolicies).some(hasEmployeeListError), - () => Object.values(cleanPolicies).some(hasSyncError), + () => + Object.values(cleanPolicies).some( + (cleanPolicy) => + hasSyncError(cleanPolicy) && !isConnectionInProgress(allConnectionProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy), + ), () => Object.keys(reimbursementAccount?.errors ?? {}).length > 0, ]; diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index cfce7442efed..1d13fe90d349 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -1,3 +1,4 @@ +import {differenceInMinutes, isValid, parseISO} from 'date-fns'; import isObject from 'lodash/isObject'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; @@ -9,7 +10,7 @@ import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; -import type {ConnectionName, Connections, PolicyConnectionName} from '@src/types/onyx/Policy'; +import type {ConnectionName, Connections, PolicyConnectionName, PolicyConnectionSyncProgress} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; type ConnectionNameExceptNetSuite = Exclude; @@ -431,6 +432,20 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID: ); } +function isConnectionInProgress(connectionSyncProgress?: OnyxEntry, policy?: OnyxEntry): boolean | undefined { + if (!policy || !connectionSyncProgress) { + return false; + } + + const lastSyncProgressDate = parseISO(connectionSyncProgress?.timestamp ?? ''); + return ( + !!connectionSyncProgress?.stageInProgress && + (connectionSyncProgress.stageInProgress !== CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.JOB_DONE || !policy?.connections?.[connectionSyncProgress.connectionName]) && + isValid(lastSyncProgressDate) && + differenceInMinutes(new Date(), lastSyncProgressDate) < CONST.POLICY.CONNECTIONS.SYNC_STAGE_TIMEOUT_MINUTES + ); +} + export { removePolicyConnection, updatePolicyConnectionConfig, @@ -440,4 +455,5 @@ export { syncConnection, copyExistingPolicyConnection, isConnectionUnverified, + isConnectionInProgress, }; diff --git a/src/pages/home/sidebar/AllSettingsScreen.tsx b/src/pages/home/sidebar/AllSettingsScreen.tsx index dc969cb98622..c0322fc0fcf7 100644 --- a/src/pages/home/sidebar/AllSettingsScreen.tsx +++ b/src/pages/home/sidebar/AllSettingsScreen.tsx @@ -30,6 +30,7 @@ function AllSettingsScreen({policies}: AllSettingsScreenProps) { const waitForNavigate = useWaitForNavigation(); const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const [allConnectionSyncProgresses] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}`); const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION); @@ -48,7 +49,7 @@ function AllSettingsScreen({policies}: AllSettingsScreenProps) { })(); }, focused: !shouldUseNarrowLayout, - brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies, allConnectionSyncProgresses) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }, ...(privateSubscription ? [ @@ -90,7 +91,7 @@ function AllSettingsScreen({policies}: AllSettingsScreenProps) { hoverAndPressStyle: styles.hoveredComponentBG, brickRoadIndicator: item.brickRoadIndicator, })); - }, [shouldUseNarrowLayout, policies, privateSubscription, waitForNavigate, translate, styles]); + }, [shouldUseNarrowLayout, policies, privateSubscription, waitForNavigate, translate, styles, allConnectionSyncProgresses]); return ( (null); const {canUseWorkspaceFeeds} = usePermissions(); - const lastSyncProgressDate = parseISO(connectionSyncProgress?.timestamp ?? ''); - const isSyncInProgress = - !!connectionSyncProgress?.stageInProgress && - (connectionSyncProgress.stageInProgress !== CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.JOB_DONE || !policy.connections?.[connectionSyncProgress.connectionName]) && - isValid(lastSyncProgressDate) && - differenceInMinutes(new Date(), lastSyncProgressDate) < CONST.POLICY.CONNECTIONS.SYNC_STAGE_TIMEOUT_MINUTES; + const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy); const accountingIntegrations = Object.values(CONST.POLICY.CONNECTIONS.NAME); const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName; From c1c7d634de100933ca8566ffc28d2dd01bfee2e3 Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 18:03:33 +0700 Subject: [PATCH 020/432] fix linting --- src/languages/en.ts | 4 ++-- src/languages/es.ts | 4 ++-- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 1684ed3057da..a9c1dd2305f9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3189,9 +3189,9 @@ export default { : 'this integration'; return `Are you sure you want to disconnect ${integrationName}?`; }, - connectPrompt: (integrationToConnect: ConnectionName): string => + connectPrompt: (integrationToConnect?: ConnectionName): string => `Are you sure you want to connect ${ - CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'this accounting integration' + integrationToConnect ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] : 'this accounting integration' }? This will remove any existing acounting connections.`, enterCredentials: 'Enter your credentials', connections: { diff --git a/src/languages/es.ts b/src/languages/es.ts index b4d071ba4a08..f7ddcb8e954c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3171,9 +3171,9 @@ export default { currentIntegration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] : 'integración'; return `¿Estás seguro de que quieres desconectar ${integrationName}?`; }, - connectPrompt: (integrationToConnect: ConnectionName): string => + connectPrompt: (integrationToConnect?: ConnectionName): string => `¿Estás seguro de que quieres conectar a ${ - CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'esta integración contable' + integrationToConnect ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] : 'esta integración contable' }? Esto eliminará cualquier conexión contable existente.`, enterCredentials: 'Ingresa tus credenciales', connections: { diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 909d86c185e0..d7ea0f486e41 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -344,7 +344,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { if (!connectedIntegration) { return []; } - const synchronizationError = getSynchronizationErrorMessage(policy, connectedIntegration, isSyncInProgress); + const synchronizationError = getSynchronizationErrorMessage(policy, connectedIntegration, !!isSyncInProgress); const shouldShowSynchronizationError = !!synchronizationError; const shouldHideConfigurationOptions = isConnectionUnverified(policy, connectedIntegration); const integrationData = accountingIntegrationData(connectedIntegration, policyID, translate, undefined, undefined, policy); @@ -406,7 +406,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { errorTextStyle: [styles.mt5], shouldShowRedDotIndicator: true, description: isSyncInProgress - ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) + ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress?.stageInProgress) : translate('workspace.accounting.lastSync', datetimeToRelative), rightComponent: isSyncInProgress ? ( Date: Mon, 19 Aug 2024 13:06:19 +0200 Subject: [PATCH 021/432] Update UpdatePolicyConnectionConfiguration to be 1:1:1 Part-3 --- src/libs/API/types.ts | 10 + .../actions/connections/QuickbooksOnline.ts | 389 +++++++++++++++++- ...NonReimbursableDefaultVendorSelectPage.tsx | 4 +- .../qbo/import/QuickbooksClassesPage.tsx | 6 +- .../qbo/import/QuickbooksCustomersPage.tsx | 6 +- .../qbo/import/QuickbooksLocationsPage.tsx | 6 +- .../qbo/import/QuickbooksTaxesPage.tsx | 4 +- 7 files changed, 407 insertions(+), 18 deletions(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index ef39e9bf005b..62efc0c4ad32 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -210,6 +210,11 @@ const WRITE_COMMANDS = { UPDATE_QUICKBOOKS_ONLINE_ENABLE_NEW_CATEGORIES: 'UpdateQuickbooksOnlineEnableNewCategories', UPDATE_QUICKBOOKS_ONLINE_AUTO_CREATE_VENDOR: 'UpdateQuickbooksOnlineAutoCreateVendor', UPDATE_QUICKBOOKS_ONLINE_REIMBURSABLE_EXPENSES_ACCOUNT: 'UpdateQuickbooksOnlineReimbursableExpensesAccount', + UPDATE_QUICKBOOKS_ONLINE_SYNC_TAX: 'UpdateQuickbooksOnlineSyncTax', + UPDATE_QUICKBOOKS_ONLINE_SYNC_LOCATIONS: 'UpdateQuickbooksOnlineSyncLocations', + UPDATE_QUICKBOOKS_ONLINE_SYNC_CUSTOMERS: 'UpdateQuickbooksOnlineSyncCustomers', + UPDATE_QUICKBOOKS_ONLINE_SYNC_CLASSES: 'UpdateQuickbooksOnlineSyncClasses', + UPDATE_QUICKBOOKS_ONLINE_NON_REIMBURSABLE_BILL_DEFAULT_VENDOR: 'UpdateQuickbooksOnlineNonReimbursableBillDefaultVendor', UPDATE_MANY_POLICY_CONNECTION_CONFIGS: 'UpdateManyPolicyConnectionConfigurations', REMOVE_POLICY_CONNECTION: 'RemovePolicyConnection', SET_POLICY_TAXES_ENABLED: 'SetPolicyTaxesEnabled', @@ -551,6 +556,11 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CLEAR_OUTSTANDING_BALANCE]: null; [WRITE_COMMANDS.CANCEL_BILLING_SUBSCRIPTION]: Parameters.CancelBillingSubscriptionParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_ENABLE_NEW_CATEGORIES]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_SYNC_TAX]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_SYNC_LOCATIONS]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_SYNC_CUSTOMERS]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_SYNC_CLASSES]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_NON_REIMBURSABLE_BILL_DEFAULT_VENDOR]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_AUTO_CREATE_VENDOR]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_REIMBURSABLE_EXPENSES_ACCOUNT]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; [WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG]: Parameters.UpdatePolicyConnectionConfigParams; diff --git a/src/libs/actions/connections/QuickbooksOnline.ts b/src/libs/actions/connections/QuickbooksOnline.ts index a3216378add3..b7646f848667 100644 --- a/src/libs/actions/connections/QuickbooksOnline.ts +++ b/src/libs/actions/connections/QuickbooksOnline.ts @@ -8,7 +8,7 @@ import {getCommandURL} from '@libs/ApiUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ConnectionName, Connections} from '@src/types/onyx/Policy'; +import type {ConnectionName, Connections, IntegrationEntityMap} from '@src/types/onyx/Policy'; type ConnectionNameExceptNetSuite = Exclude; @@ -249,4 +249,389 @@ function updateQuickbooksOnlineReimbursableExpensesAccount { if (row.value !== nonReimbursableBillDefaultVendor) { - Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.QUICK_BOOKS_CONFIG.NON_REIMBURSABLE_BILL_DEFAULT_VENDOR, row.value); + QuickbooksOnline.updateQuickbooksOnlineNonReimbursableBillDefaultVendor(policyID, row.value); } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT.getRoute(policyID)); }, diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx index 023c676d2018..af2d8da42216 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx @@ -9,7 +9,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Connections from '@libs/actions/connections'; +import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -48,10 +48,8 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { accessibilityLabel={translate('workspace.qbo.classes')} isOn={isSwitchOn} onToggle={() => - Connections.updatePolicyConnectionConfig( + QuickbooksOnline.updateQuickbooksOnlineSyncClasses( policyID, - CONST.POLICY.CONNECTIONS.NAME.QBO, - CONST.QUICK_BOOKS_CONFIG.SYNC_CLASSES, isSwitchOn ? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE : CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG, ) } diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx index 6d4f8c8c9f1e..949978492dc9 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx @@ -9,7 +9,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Connections from '@libs/actions/connections'; +import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -47,10 +47,8 @@ function QuickbooksCustomersPage({policy}: WithPolicyProps) { accessibilityLabel={translate('workspace.qbo.customers')} isOn={isSwitchOn} onToggle={() => - Connections.updatePolicyConnectionConfig( + QuickbooksOnline.updateQuickbooksOnlineSyncCustomers( policyID, - CONST.POLICY.CONNECTIONS.NAME.QBO, - CONST.QUICK_BOOKS_CONFIG.SYNC_CUSTOMERS, isSwitchOn ? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE : CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG, ) } diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx index e1fd7ed0d160..5368d2ff8fb2 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx @@ -9,7 +9,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Connections from '@libs/actions/connections'; +import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -52,10 +52,8 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) { accessibilityLabel={translate('workspace.qbo.locations')} isOn={isSwitchOn} onToggle={() => - Connections.updatePolicyConnectionConfig( + QuickbooksOnline.updateQuickbooksOnlineSyncLocations( policyID, - CONST.POLICY.CONNECTIONS.NAME.QBO, - CONST.QUICK_BOOKS_CONFIG.SYNC_LOCATIONS, isSwitchOn ? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE : CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG, ) } diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx index 30680d2c935a..ecc1eaf7af5a 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksTaxesPage.tsx @@ -8,7 +8,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Connections from '@libs/actions/connections'; +import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -45,7 +45,7 @@ function QuickbooksTaxesPage({policy}: WithPolicyProps) { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.QUICK_BOOKS_CONFIG.SYNC_TAX, !syncTax)} + onToggle={() => QuickbooksOnline.updateQuickbooksOnlineSyncTax(policyID, !syncTax)} disabled={!syncTax && isJournalExportEntity} /> From e353d5a1f3fe596e97d975fc03ca0d066da0983d Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 18:14:21 +0700 Subject: [PATCH 022/432] fix lint --- src/languages/en.ts | 4 ++-- src/languages/es.ts | 4 ++-- src/libs/actions/connections/index.ts | 2 +- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index a9c1dd2305f9..1684ed3057da 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3189,9 +3189,9 @@ export default { : 'this integration'; return `Are you sure you want to disconnect ${integrationName}?`; }, - connectPrompt: (integrationToConnect?: ConnectionName): string => + connectPrompt: (integrationToConnect: ConnectionName): string => `Are you sure you want to connect ${ - integrationToConnect ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] : 'this accounting integration' + CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'this accounting integration' }? This will remove any existing acounting connections.`, enterCredentials: 'Enter your credentials', connections: { diff --git a/src/languages/es.ts b/src/languages/es.ts index f7ddcb8e954c..b4d071ba4a08 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3171,9 +3171,9 @@ export default { currentIntegration && CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[currentIntegration] : 'integración'; return `¿Estás seguro de que quieres desconectar ${integrationName}?`; }, - connectPrompt: (integrationToConnect?: ConnectionName): string => + connectPrompt: (integrationToConnect: ConnectionName): string => `¿Estás seguro de que quieres conectar a ${ - integrationToConnect ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] : 'esta integración contable' + CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integrationToConnect] ?? 'esta integración contable' }? Esto eliminará cualquier conexión contable existente.`, enterCredentials: 'Ingresa tus credenciales', connections: { diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 1d13fe90d349..8cb7afbd1144 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -432,7 +432,7 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID: ); } -function isConnectionInProgress(connectionSyncProgress?: OnyxEntry, policy?: OnyxEntry): boolean | undefined { +function isConnectionInProgress(connectionSyncProgress: OnyxEntry, policy?: OnyxEntry): boolean | undefined { if (!policy || !connectionSyncProgress) { return false; } diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index d7ea0f486e41..6ffd57f982c3 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -405,9 +405,10 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { errorText: synchronizationError, errorTextStyle: [styles.mt5], shouldShowRedDotIndicator: true, - description: isSyncInProgress - ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress?.stageInProgress) - : translate('workspace.accounting.lastSync', datetimeToRelative), + description: + isSyncInProgress && connectionSyncProgress?.stageInProgress + ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) + : translate('workspace.accounting.lastSync', datetimeToRelative), rightComponent: isSyncInProgress ? ( Date: Mon, 19 Aug 2024 18:14:53 +0700 Subject: [PATCH 023/432] remove unused import --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 6ffd57f982c3..07e1ba4d1b0a 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -1,4 +1,3 @@ -import {differenceInMinutes, isValid, parseISO} from 'date-fns'; import React, {useEffect, useMemo, useRef, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; From 5cf0bfd612b1e5f278510605261ecf13ea8f9709 Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 18:17:33 +0700 Subject: [PATCH 024/432] update return value --- src/libs/actions/connections/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 8cb7afbd1144..c782f659fd05 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -432,7 +432,7 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID: ); } -function isConnectionInProgress(connectionSyncProgress: OnyxEntry, policy?: OnyxEntry): boolean | undefined { +function isConnectionInProgress(connectionSyncProgress: OnyxEntry, policy?: OnyxEntry): boolean { if (!policy || !connectionSyncProgress) { return false; } From c1e7e9c8e8a3925473a5435fa89c5ca05519d354 Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 18:22:47 +0700 Subject: [PATCH 025/432] update accounting page --- src/pages/workspace/WorkspaceInitialPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 115a24691838..1937e757666f 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -3,7 +3,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ConfirmModal from '@components/ConfirmModal'; @@ -41,6 +41,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; +import { isConnectionInProgress } from '@libs/actions/connections'; type WorkspaceMenuItem = { translationKey: TranslationPaths; @@ -93,7 +94,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc const policy = policyDraft?.id ? policyDraft : policyProp; const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && !isEmptyObject(policy.errors)); - const hasSyncError = PolicyUtils.hasSyncError(policy); + const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`); + const hasSyncError = PolicyUtils.hasSyncError(policy) && !isConnectionInProgress(connectionSyncProgress, policy); const waitForNavigate = useWaitForNavigation(); const {singleExecution, isExecuting} = useSingleExecution(); const activeRoute = useNavigationState(getTopmostRouteName); From 5613c04a3ce9c5094126d08ab2e42ae6f3098afc Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 21:55:46 +0700 Subject: [PATCH 026/432] refactor hasSync logic --- src/components/Indicator.tsx | 10 ++++++---- src/libs/PolicyUtils.ts | 8 ++++---- src/libs/WorkspacesSettingsUtils.ts | 19 ++----------------- src/pages/workspace/WorkspaceInitialPage.tsx | 7 ++++--- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/components/Indicator.tsx b/src/components/Indicator.tsx index e0d8ec95738f..a051399681f5 100644 --- a/src/components/Indicator.tsx +++ b/src/components/Indicator.tsx @@ -12,6 +12,7 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, FundList, LoginList, Policy, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx'; + type CheckingMethod = () => boolean; type IndicatorOnyxProps = { @@ -58,10 +59,11 @@ function Indicator({reimbursementAccount, policies, bankAccountList, fundList, u () => Object.values(cleanPolicies).some(PolicyUtils.hasCustomUnitsError), () => Object.values(cleanPolicies).some(PolicyUtils.hasEmployeeListError), () => - Object.values(cleanPolicies).some( - (cleanPolicy) => - PolicyUtils.hasSyncError(cleanPolicy) && - !isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}}`], cleanPolicy), + Object.values(cleanPolicies).some((cleanPolicy) => + PolicyUtils.hasSyncError( + cleanPolicy, + isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}}`], cleanPolicy), + ), ), () => SubscriptionUtils.hasSubscriptionRedDotError(), () => Object.keys(reimbursementAccount?.errors ?? {}).length > 0, diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 3f3a2a96a1e1..a240dfe01f66 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -89,8 +89,8 @@ function hasPolicyCategoriesError(policyCategories: OnyxEntry) /** * Checks if the policy had a sync error. */ -function hasSyncError(policy: OnyxEntry): boolean { - return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, false)); +function hasSyncError(policy: OnyxEntry, isSyncInProgress: boolean): boolean { + return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, isSyncInProgress)); } /** @@ -152,8 +152,8 @@ function getUnitRateValue(toLocaleDigit: (arg: string) => string, customUnitRate /** * Get the brick road indicator status for a policy. The policy has an error status if there is a policy member error, a custom unit error or a field error. */ -function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf | undefined { - if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy)) { +function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry, isConnectionInProgress: boolean): ValueOf | undefined { + if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy, isConnectionInProgress)) { return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } return undefined; diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 7224b5ae2f9c..9c1ace6cc8c9 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -112,9 +112,8 @@ function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection, allConn () => Object.values(cleanPolicies).some(hasTaxRateError), () => Object.values(cleanPolicies).some(hasEmployeeListError), () => - Object.values(cleanPolicies).some( - (cleanPolicy) => - hasSyncError(cleanPolicy) && !isConnectionInProgress(allConnectionProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy), + Object.values(cleanPolicies).some((cleanPolicy) => + hasSyncError(cleanPolicy, isConnectionInProgress(allConnectionProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)), ), () => Object.keys(reimbursementAccount?.errors ?? {}).length > 0, ]; @@ -159,19 +158,6 @@ function getChatTabBrickRoad(policyID?: string): BrickRoad | undefined { return undefined; } -function checkIfWorkspaceSettingsTabHasRBR(policyID?: string) { - if (!policyID) { - return hasGlobalWorkspaceSettingsRBR(allPolicies); - } - const policy = allPolicies ? allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] : null; - - if (!policy) { - return false; - } - - return hasWorkspaceSettingsRBR(policy); -} - /** * @returns a map where the keys are policyIDs and the values are BrickRoads for each policy */ @@ -323,7 +309,6 @@ export { getWorkspacesBrickRoads, getWorkspacesUnreadStatuses, hasGlobalWorkspaceSettingsRBR, - checkIfWorkspaceSettingsTabHasRBR, hasWorkspaceSettingsRBR, getChatTabBrickRoad, getUnitTranslationKey, diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 1937e757666f..044cea2bbbfa 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -20,6 +20,7 @@ import usePrevious from '@hooks/usePrevious'; import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; +import {isConnectionInProgress} from '@libs/actions/connections'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -41,7 +42,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; -import { isConnectionInProgress } from '@libs/actions/connections'; + type WorkspaceMenuItem = { translationKey: TranslationPaths; @@ -95,7 +96,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && !isEmptyObject(policy.errors)); const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`); - const hasSyncError = PolicyUtils.hasSyncError(policy) && !isConnectionInProgress(connectionSyncProgress, policy); + const hasSyncError = PolicyUtils.hasSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy)); const waitForNavigate = useWaitForNavigation(); const {singleExecution, isExecuting} = useSingleExecution(); const activeRoute = useNavigationState(getTopmostRouteName); @@ -468,4 +469,4 @@ export default withPolicyAndFullscreenLoading( key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params?.policyID ?? '-1'}`, }, })(WorkspaceInitialPage), -); +); \ No newline at end of file From 6abc5e9e1f50b3a17c7fc2e3ebaba908e21572c3 Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 22:07:51 +0700 Subject: [PATCH 027/432] update the workspacelist logic --- src/components/Indicator.tsx | 1 - src/pages/workspace/WorkspacesListPage.tsx | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/Indicator.tsx b/src/components/Indicator.tsx index a051399681f5..edf1cb231291 100644 --- a/src/components/Indicator.tsx +++ b/src/components/Indicator.tsx @@ -12,7 +12,6 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, FundList, LoginList, Policy, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx'; - type CheckingMethod = () => boolean; type IndicatorOnyxProps = { diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 94fb454c4a2d..6d4aa82f3931 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -1,6 +1,6 @@ import React, {useCallback, useMemo, useState} from 'react'; import {FlatList, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; @@ -42,6 +42,7 @@ import type {Policy as PolicyType, ReimbursementAccount, Report, Session as Sess import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import WorkspacesListRow from './WorkspacesListRow'; +import { isConnectionInProgress } from '@libs/actions/connections'; type WorkspaceItem = Required> & Pick & @@ -123,6 +124,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: const {translate} = useLocalize(); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); + const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS); const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace(); @@ -338,7 +340,12 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: title: policy.name, icon: policy.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), action: () => Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policy.id)), - brickRoadIndicator: reimbursementAccountBrickRoadIndicator ?? PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy), + brickRoadIndicator: + reimbursementAccountBrickRoadIndicator ?? + PolicyUtils.getPolicyBrickRoadIndicatorStatus( + policy, + isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy), + ), pendingAction: policy.pendingAction, errors: policy.errors, dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), From 5bf86d5fc46e0c44fcfcb73ea447d34e039a336a Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 22:19:28 +0700 Subject: [PATCH 028/432] fix linting --- src/pages/workspace/WorkspacesListPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 6d4aa82f3931..a388fd73269d 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -42,7 +42,6 @@ import type {Policy as PolicyType, ReimbursementAccount, Report, Session as Sess import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import WorkspacesListRow from './WorkspacesListRow'; -import { isConnectionInProgress } from '@libs/actions/connections'; type WorkspaceItem = Required> & Pick & From 266526d0b5c178992b7820fbb7b020fe88b3495e Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 22:26:37 +0700 Subject: [PATCH 029/432] add import connection for list page --- src/pages/workspace/WorkspacesListPage.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index a388fd73269d..751acab415ac 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -24,6 +24,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; +import {isConnectionInProgress} from '@libs/actions/connections'; import useThemeStyles from '@hooks/useThemeStyles'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import localeCompare from '@libs/LocaleCompare'; @@ -43,6 +44,7 @@ import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import WorkspacesListRow from './WorkspacesListRow'; + type WorkspaceItem = Required> & Pick & Pick & From 2f0d0fa8fdc8be833e90617287e485931104af0a Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 19 Aug 2024 23:08:04 +0700 Subject: [PATCH 030/432] fix lint --- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 751acab415ac..7540b41bb021 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -363,7 +363,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: }; }) .sort((a, b) => localeCompare(a.title, b.title)); - }, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms]); + }, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms, allConnectionSyncProgresses]); const getHeaderButton = () => ( )} - + ) : ( )} - + ) : (