From 0b6e8a84dd98325b768e770e987d68c09508f3ea Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:24:51 +0700 Subject: [PATCH 1/9] handle offline mode for bank connection --- .../addNew/BankConnection/index.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index e7e71d1c3785..da41d2cbe789 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -2,12 +2,14 @@ import React, {useCallback, useEffect, useMemo} from 'react'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import BlockingView from '@components/BlockingViews/BlockingView'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; @@ -36,6 +38,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); const {isNewFeedConnected, newFeed} = useMemo(() => CardUtils.checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); + const {isOffline} = useNetwork(); const url = getCompanyCardBankConnection(policyID, bankName); @@ -67,7 +70,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { ); useEffect(() => { - if (!url) { + if (!url || isOffline) { return; } if (isNewFeedConnected) { @@ -79,7 +82,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { return; } customWindow = openBankConnection(url); - }, [isNewFeedConnected, newFeed, policyID, url]); + }, [isNewFeedConnected, isOffline, newFeed, policyID, url]); return ( @@ -87,16 +90,18 @@ function BankConnection({policyID}: BankConnectionStepProps) { title={translate('workspace.companyCards.addCards')} onBackButtonPress={handleBackButtonPress} /> - + + + ); } From 9cfd06bdca4c6a97d2bb84518cba667b43b3879d Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:54:03 +0700 Subject: [PATCH 2/9] fix eslint --- .../addNew/BankConnection/index.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index da41d2cbe789..009533bc8e79 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -12,11 +12,11 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CardUtils from '@libs/CardUtils'; +import {checkIfNewFeedConnected} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as Card from '@userActions/Card'; -import * as CompanyCards from '@userActions/CompanyCards'; +import {getWorkspaceAccountID} from '@libs/PolicyUtils'; +import {updateSelectedFeed} from '@userActions/Card'; +import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import getCompanyCardBankConnection from '@userActions/getCompanyCardBankConnection'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -34,10 +34,10 @@ function BankConnection({policyID}: BankConnectionStepProps) { const {translate} = useLocalize(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); - const {isNewFeedConnected, newFeed} = useMemo(() => CardUtils.checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); + const {isNewFeedConnected, newFeed} = useMemo(() => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); const {isOffline} = useNetwork(); const url = getCompanyCardBankConnection(policyID, bankName); @@ -52,14 +52,14 @@ function BankConnection({policyID}: BankConnectionStepProps) { const handleBackButtonPress = () => { customWindow?.close(); if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); return; } if (bankName === CONST.COMPANY_CARDS.BANKS.AMEX) { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED}); return; } - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); }; const CustomSubtitle = ( @@ -76,7 +76,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { if (isNewFeedConnected) { customWindow?.close(); if (newFeed) { - Card.updateSelectedFeed(newFeed, policyID); + updateSelectedFeed(newFeed, policyID); } Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); return; From 86490d86a1283e1a3475009337c34865ce839511 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:46:16 +0700 Subject: [PATCH 3/9] Disable assign card button when offline --- .../companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx index 590aa2e78459..1cba02f22277 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx @@ -4,6 +4,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import CardRowSkeleton from '@components/Skeletons/CardRowSkeleton'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import colors from '@styles/theme/colors'; import CONST from '@src/CONST'; @@ -19,6 +20,7 @@ type WorkspaceCompanyCardsFeedAddedEmptyPageProps = { function WorkspaceCompanyCardsFeedAddedEmptyPage({handleAssignCard, isDisabledAssignCardButton}: WorkspaceCompanyCardsFeedAddedEmptyPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const {isOffline} = useNetwork(); return ( From f115b9b7dbe526158c7e8d50529cfed5a73202f5 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:56:00 +0700 Subject: [PATCH 4/9] Update dependencies --- .../workspace/companyCards/addNew/BankConnection/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index ee05a373fdcc..e59477a96bf8 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -86,7 +86,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { if (!shouldBlockWindowOpen) { customWindow = openBankConnection(url); } - }, [isNewFeedConnected, shouldBlockWindowOpen, newFeed, policyID, url]); + }, [isNewFeedConnected, shouldBlockWindowOpen, newFeed, policyID, url, isOffline]); return ( From 7ce7b8398fb90823fbd93c7f40624685d4d2fc2a Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:37:18 +0700 Subject: [PATCH 5/9] Update condition to disable button --- .../companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx index 1cba02f22277..9fedc7905fe3 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx @@ -38,7 +38,7 @@ function WorkspaceCompanyCardsFeedAddedEmptyPage({handleAssignCard, isDisabledAs buttonAction: handleAssignCard, icon: Expensicons.Plus, success: true, - isDisabled: isDisabledAssignCardButton ?? isOffline, + isDisabled: isOffline || isDisabledAssignCardButton, }, ]} /> From da198cc77298c1ef9ebe3749a24c3c63a8671eaa Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 00:11:02 +0700 Subject: [PATCH 6/9] add offline alert modal for direct feed --- ...orkspaceCompanyCardsFeedAddedEmptyPage.tsx | 4 +--- .../WorkspaceCompanyCardsPage.tsx | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx index 9fedc7905fe3..590aa2e78459 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage.tsx @@ -4,7 +4,6 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import CardRowSkeleton from '@components/Skeletons/CardRowSkeleton'; import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import colors from '@styles/theme/colors'; import CONST from '@src/CONST'; @@ -20,7 +19,6 @@ type WorkspaceCompanyCardsFeedAddedEmptyPageProps = { function WorkspaceCompanyCardsFeedAddedEmptyPage({handleAssignCard, isDisabledAssignCardButton}: WorkspaceCompanyCardsFeedAddedEmptyPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const {isOffline} = useNetwork(); return ( diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 5fef9d0dba38..372a6b4c64d8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -2,13 +2,14 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useState} from 'react'; import {ActivityIndicator} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import ConfirmModal from '@components/ConfirmModal'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import * as Illustrations from '@components/Icon/Illustrations'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getFilteredCardList, getSelectedFeed, hasOnlyOneCardToAssign, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getFilteredCardList, getSelectedFeed, hasOnlyOneCardToAssign, isCustomFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; @@ -56,6 +57,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const isFeedAdded = !isPending && !isNoFeed; const isFeedExpired = isSelectedFeedExpired(selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(cards); + const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); const fetchCompanyCards = useCallback(() => { openPolicyCompanyCardsPage(policyID, workspaceAccountID); @@ -82,6 +84,16 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { if (!selectedFeed) { return; } + + const isCommercialFeed = isCustomFeed(selectedFeed); + + // If the feed is a direct feed (not a commercial feed) and the user is offline, + // show the offline alert modal to inform them of the connectivity issue. + if (!isCommercialFeed && isOffline) { + setShouldShowOfflineModal(true); + return; + } + const data: Partial = { bankName: selectedFeed, }; @@ -159,6 +171,15 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { isNoDelegateAccessMenuVisible={isNoDelegateAccessMenuVisible} onClose={() => setIsNoDelegateAccessMenuVisible(false)} /> + setShouldShowOfflineModal(false)} + onCancel={() => setShouldShowOfflineModal(false)} + confirmText={translate('common.buttonConfirm')} + prompt={translate('common.offlinePrompt')} + shouldShowCancelButton={false} + /> ); } From fb84a5054fa859d758bafcf9e84eacf6011bdc92 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 11:16:30 +0700 Subject: [PATCH 7/9] Update download button and remove unused actions --- src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 372a6b4c64d8..6e439027642f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -175,10 +175,10 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { title={translate('common.youAppearToBeOffline')} isVisible={shouldShowOfflineModal} onConfirm={() => setShouldShowOfflineModal(false)} - onCancel={() => setShouldShowOfflineModal(false)} confirmText={translate('common.buttonConfirm')} prompt={translate('common.offlinePrompt')} shouldShowCancelButton={false} + success={false} /> ); From adf01fb8e2a23699cb773df95b31128d0785daaa Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:27:39 +0700 Subject: [PATCH 8/9] change to DecisionModal --- .../companyCards/WorkspaceCompanyCardsPage.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 6e439027642f..fdba1ca5481b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -2,11 +2,12 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useState} from 'react'; import {ActivityIndicator} from 'react-native'; import {useOnyx} from 'react-native-onyx'; -import ConfirmModal from '@components/ConfirmModal'; +import DecisionModal from '@components/DecisionModal'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import * as Illustrations from '@components/Icon/Illustrations'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getFilteredCardList, getSelectedFeed, hasOnlyOneCardToAssign, isCustomFeed, isSelectedFeedExpired} from '@libs/CardUtils'; @@ -58,7 +59,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const isFeedExpired = isSelectedFeedExpired(selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(cards); const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); - + const {isSmallScreenWidth} = useResponsiveLayout(); const fetchCompanyCards = useCallback(() => { openPolicyCompanyCardsPage(policyID, workspaceAccountID); }, [policyID, workspaceAccountID]); @@ -171,14 +172,15 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { isNoDelegateAccessMenuVisible={isNoDelegateAccessMenuVisible} onClose={() => setIsNoDelegateAccessMenuVisible(false)} /> - setShouldShowOfflineModal(false)} - confirmText={translate('common.buttonConfirm')} prompt={translate('common.offlinePrompt')} - shouldShowCancelButton={false} - success={false} + isSmallScreenWidth={isSmallScreenWidth} + onSecondOptionSubmit={() => setShouldShowOfflineModal(false)} + secondOptionText={translate('common.buttonConfirm')} + isVisible={shouldShowOfflineModal} + onClose={() => setShouldShowOfflineModal(false)} /> ); From 82aafc132074b515d89c7ac117f89bca9219e31e Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:31:03 +0700 Subject: [PATCH 9/9] fix lint --- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index fdba1ca5481b..6450bd058565 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -59,7 +59,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const isFeedExpired = isSelectedFeedExpired(selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(cards); const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); - const {isSmallScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const fetchCompanyCards = useCallback(() => { openPolicyCompanyCardsPage(policyID, workspaceAccountID); }, [policyID, workspaceAccountID]); @@ -176,7 +176,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { setShouldShowOfflineModal(false)} secondOptionText={translate('common.buttonConfirm')} isVisible={shouldShowOfflineModal}