Skip to content

Commit

Permalink
Merge pull request #47483 from hungvu193/fix-44148
Browse files Browse the repository at this point in the history
Fix GBR and error briefly show after connecting to QBO, Xero.
  • Loading branch information
arosiclair authored Sep 2, 2024
2 parents e60b821 + e1d9d43 commit 73bcfea
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 53 deletions.
12 changes: 10 additions & 2 deletions src/components/Indicator.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand All @@ -55,7 +57,13 @@ 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),
Expand Down
8 changes: 4 additions & 4 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ function hasPolicyCategoriesError(policyCategories: OnyxEntry<PolicyCategories>)
/**
* Checks if the policy had a sync error.
*/
function hasSyncError(policy: OnyxEntry<Policy>): boolean {
return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, false));
function hasSyncError(policy: OnyxEntry<Policy>, isSyncInProgress: boolean): boolean {
return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, isSyncInProgress));
}

/**
Expand Down Expand Up @@ -159,8 +159,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<Policy>): ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined {
if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy)) {
function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>, isConnectionInProgress: boolean): ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined {
if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy, isConnectionInProgress)) {
return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
}
return undefined;
Expand Down
32 changes: 7 additions & 25 deletions src/libs/WorkspacesSettingsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,14 +19,6 @@ type CheckingMethod = () => boolean;

type BrickRoad = ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;

let allPolicies: OnyxCollection<Policy>;

Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
});

let reimbursementAccount: OnyxEntry<ReimbursementAccount>;

Onyx.connect({
Expand Down Expand Up @@ -100,7 +93,7 @@ const getBrickRoadForPolicy = (report: Report, altReportActions?: OnyxCollection
return shouldShowGreenDotIndicator ? CONST.BRICK_ROAD_INDICATOR_STATUS.INFO : undefined;
};

function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection<Policy>) {
function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection<Policy>, allConnectionProgresses: OnyxCollection<PolicyConnectionSyncProgress>) {
// 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));
Expand All @@ -110,7 +103,10 @@ function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection<Policy>) {
() => 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,
];

Expand Down Expand Up @@ -154,19 +150,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
*/
Expand Down Expand Up @@ -318,7 +301,6 @@ export {
getWorkspacesBrickRoads,
getWorkspacesUnreadStatuses,
hasGlobalWorkspaceSettingsRBR,
checkIfWorkspaceSettingsTabHasRBR,
hasWorkspaceSettingsRBR,
getChatTabBrickRoad,
getUnitTranslationKey,
Expand Down
21 changes: 19 additions & 2 deletions src/libs/actions/connections/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

Expand Down Expand Up @@ -383,7 +384,8 @@ function getSynchronizationErrorMessage(policy: OnyxEntry<Policy>, connectionNam
}

const connection = policy?.connections?.[connectionName];
if (isSyncInProgress || isEmptyObject(connection?.lastSync) || connection?.lastSync?.isSuccessful) {

if (isSyncInProgress || isEmptyObject(connection?.lastSync) || connection?.lastSync?.isSuccessful !== false || !connection?.lastSync?.errorDate) {
return;
}
return `${syncError} ("${connection?.lastSync?.errorMessage}")`;
Expand Down Expand Up @@ -448,6 +450,20 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID:
);
}

function isConnectionInProgress(connectionSyncProgress: OnyxEntry<PolicyConnectionSyncProgress>, policy?: OnyxEntry<Policy>): boolean {
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,
Expand All @@ -458,4 +474,5 @@ export {
syncConnection,
copyExistingPolicyConnection,
isConnectionUnverified,
isConnectionInProgress,
};
5 changes: 3 additions & 2 deletions src/pages/home/sidebar/AllSettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
? [
Expand Down Expand Up @@ -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 (
<ScreenWrapper
Expand Down
5 changes: 3 additions & 2 deletions src/pages/settings/InitialSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms
const {translate} = useLocalize();
const activeCentralPaneRoute = useActiveCentralPaneRoute();
const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? '';
const [allConnectionSyncProgresses] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}`);

const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION);
const subscriptionPlan = useSubscriptionPlan();
Expand Down Expand Up @@ -195,7 +196,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms
translationKey: 'common.workspaces',
icon: Expensicons.Building,
routeName: ROUTES.SETTINGS_WORKSPACES,
brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies, allConnectionSyncProgresses) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
},
{
translationKey: 'allSettingsScreen.domains',
Expand Down Expand Up @@ -225,7 +226,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms
sectionTranslationKey: 'common.workspaces',
items,
};
}, [policies, privateSubscription?.errors, styles.badgeSuccess, styles.workspaceSettingsSectionContainer, subscriptionPlan, translate]);
}, [policies, privateSubscription?.errors, styles.badgeSuccess, styles.workspaceSettingsSectionContainer, subscriptionPlan, translate, allConnectionSyncProgresses]);

/**
* Retuns a list of menu items data for general section
Expand Down
6 changes: 4 additions & 2 deletions src/pages/workspace/WorkspaceInitialPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -21,6 +21,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';
Expand Down Expand Up @@ -95,7 +96,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);
Expand Down
13 changes: 10 additions & 3 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -25,6 +25,7 @@ import useNetwork from '@hooks/useNetwork';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {isConnectionInProgress} from '@libs/actions/connections';
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -122,6 +123,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();

Expand Down Expand Up @@ -337,7 +339,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),
Expand All @@ -355,7 +362,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
};
})
.sort((a, b) => localeCompare(a.title, b.title));
}, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms, session?.email]);
}, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms, session?.email, allConnectionSyncProgresses]);

const getHeaderButton = () => (
<Button
Expand Down
17 changes: 6 additions & 11 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -23,7 +22,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {getSynchronizationErrorMessage, isAuthenticationError, isConnectionUnverified, removePolicyConnection, syncConnection} from '@libs/actions/connections';
import {getSynchronizationErrorMessage, isAuthenticationError, isConnectionInProgress, isConnectionUnverified, removePolicyConnection, syncConnection} from '@libs/actions/connections';
import {
areSettingsInErrorFields,
findCurrentXeroOrganization,
Expand Down Expand Up @@ -61,12 +60,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
const {canUseWorkspaceFeeds} = usePermissions();
const {startIntegrationFlow, popoverAnchorRefs} = useAccountingContext();

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;
Expand Down Expand Up @@ -291,9 +285,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 ? (
<ActivityIndicator
style={[styles.popoverMenuIcon]}
Expand Down

0 comments on commit 73bcfea

Please sign in to comment.