From 35b09975d6d849ff264ed6c8fe13bc213ba16f31 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Thu, 19 Mar 2026 10:36:34 -0300 Subject: [PATCH 1/2] Revert "Fix : Assign new cards - Assign new cards tab with infinite spinner" --- .../BankConnection/useBankConnection.ts | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts b/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts index 2ba807c7b703e..2482a48e04bd7 100644 --- a/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts +++ b/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {useCallback, useEffect, useMemo, useRef} from 'react'; import useCardFeeds from '@hooks/useCardFeeds'; import useImportPlaidAccounts from '@hooks/useImportPlaidAccounts'; import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; @@ -51,7 +51,7 @@ export default function useBankConnection({ const {isBlockedToAddNewFeeds, isAllFeedsResultLoading} = useIsBlockedToAddFeed(policyID); const {isFeedConnectionBroken} = useUpdateFeedBrokenConnection({policyID, feed}); const shouldBlockWindowOpen = useRef(false); - const [refreshSuccessHandled, setRefreshSuccessHandled] = useState(false); + const refreshSuccessHandled = useRef(false); const addNewCardData = addNewCard?.data; const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCardData?.plaidConnectedFeed ?? addNewCardData?.selectedBank); @@ -73,8 +73,8 @@ export default function useBankConnection({ if (!isRefreshConnectionFlow || !feed || !hasConnectionSource || shouldWaitForData) { return false; } - return !isFeedExpired && !isFeedConnectionBroken && (!!prevIsFeedExpired || refreshSuccessHandled); - }, [isRefreshConnectionFlow, feed, hasConnectionSource, shouldWaitForData, prevIsFeedExpired, isFeedExpired, isFeedConnectionBroken, refreshSuccessHandled]); + return !!prevIsFeedExpired && !isFeedExpired && !isFeedConnectionBroken; + }, [isRefreshConnectionFlow, feed, hasConnectionSource, shouldWaitForData, prevIsFeedExpired, isFeedExpired, isFeedConnectionBroken]); const fallbackNavigation = useCallback(() => { Navigation.goBack(policyID ? ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID) : undefined); @@ -114,12 +114,12 @@ export default function useBankConnection({ }, [shouldOpenWindow, onBackButtonPress]); useEffect(() => { - if (!isRefreshComplete || refreshSuccessHandled) { + if (!isRefreshComplete || refreshSuccessHandled.current) { return; } - setRefreshSuccessHandled(true); + refreshSuccessHandled.current = true; onSuccess?.(); - }, [isRefreshComplete, onSuccess, refreshSuccessHandled]); + }, [isRefreshComplete, onSuccess]); useEffect(() => { if (!policyID || !isBlockedToAddNewFeeds || feed) { @@ -147,11 +147,6 @@ export default function useBankConnection({ } if (!isRefreshConnectionFlow) { handleSuccess(); - return; - } - - if (!refreshSuccessHandled) { - setRefreshSuccessHandled(true); } return; } @@ -196,7 +191,6 @@ export default function useBankConnection({ handleFailure, shouldOpenWindow, isRefreshConnectionFlow, - refreshSuccessHandled, ]); return { From 7bf1e2cab3b157ea0e8fe4f241347066934da276 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Thu, 19 Mar 2026 10:51:53 -0300 Subject: [PATCH 2/2] Revert "Merge pull request #84313 from fedirjh/sync-card-feed-on-demand" This reverts commit 926a0753f556524babad3a919d908317d62cb87a, reversing changes made to 5814d5bce3ee4b9a9e62afd9ea84ac6bbf8c749b. --- src/ROUTES.ts | 4 - src/SCREENS.ts | 1 - src/languages/de.ts | 4 - src/languages/en.ts | 4 - src/languages/es.ts | 4 - src/languages/fr.ts | 4 - src/languages/it.ts | 4 - src/languages/ja.ts | 4 - src/languages/nl.ts | 4 - src/languages/pl.ts | 4 - src/languages/pt-BR.ts | 4 - src/languages/zh-hans.ts | 4 - .../ModalStackNavigators/index.tsx | 1 - src/libs/Navigation/linkingConfig/config.ts | 3 - src/libs/Navigation/types.ts | 4 - .../BankConnection/index.native.tsx | 214 +++++++++------ .../companyCards/BankConnection/index.tsx | 205 ++++++++++---- .../openBankConnection/index.native.ts | 6 - .../BankConnection/useBankConnection.ts | 209 -------------- .../BrokenCardFeedConnectionPage.tsx | 37 +-- .../RefreshCardFeedConnectionPage.tsx | 66 ----- .../WorkspaceCompanyCardsSettingsPage.tsx | 52 +--- .../companyCards/addNew/AddNewCardPage.tsx | 40 +-- .../addNew/PlaidConnectionStep.tsx | 14 +- src/pages/workspace/withPolicy.tsx | 1 - tests/ui/RefreshCardFeedConnection.tsx | 258 ------------------ 26 files changed, 301 insertions(+), 854 deletions(-) delete mode 100644 src/pages/workspace/companyCards/BankConnection/openBankConnection/index.native.ts delete mode 100644 src/pages/workspace/companyCards/BankConnection/useBankConnection.ts delete mode 100644 src/pages/workspace/companyCards/RefreshCardFeedConnectionPage.tsx delete mode 100644 tests/ui/RefreshCardFeedConnection.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index cbfd864cf5308..d5f42d95b1d6a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2449,10 +2449,6 @@ const ROUTES = { route: 'workspaces/:policyID/company-cards/:feed/broken-card-feed-connection', getRoute: (policyID: string, feed: CompanyCardFeedWithDomainID) => `workspaces/${policyID}/company-cards/${encodeURIComponent(feed)}/broken-card-feed-connection` as const, }, - WORKSPACE_COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION: { - route: 'workspaces/:policyID/company-cards/:feed/refresh-card-feed-connection', - getRoute: (policyID: string, feed: CompanyCardFeedWithDomainID) => `workspaces/${policyID}/company-cards/${encodeURIComponent(feed)}/refresh-card-feed-connection` as const, - }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/:cardID/assignee', getRoute: (params: WorkspaceCompanyCardsAssignCardParams, backTo?: string) => diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 916f2e3bcee25..961e910964233 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -636,7 +636,6 @@ const SCREENS = { PROFILE: 'Workspace_Overview', COMPANY_CARDS: 'Workspace_CompanyCards', COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_BrokenCardFeedConnection', - COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_RefreshCardFeedConnection', COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: 'Workspace_CompanyCards_AssignCard_Assignee', COMPANY_CARDS_ASSIGN_CARD_BANK_CONNECTION: 'Workspace_CompanyCards_AssignCard_Bank_Connection', COMPANY_CARDS_ASSIGN_CARD_PLAID_CONNECTION: 'Workspace_CompanyCards_AssignCard_Plaid_Connection', diff --git a/src/languages/de.ts b/src/languages/de.ts index b59898c5d137f..484c07db12e8a 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -5387,10 +5387,6 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU removeCardFeed: 'Kartenfeed entfernen', removeCardFeedTitle: (feedName: string) => `${feedName}-Feed entfernen`, removeCardFeedDescription: 'Möchtest du diesen Kartenfeed wirklich entfernen? Dadurch werden alle Karten zugewiesen.', - assignNewCards: 'Neue Karten zuweisen', - assignNewCardsDescription: 'Die neuesten Karten von deiner Bank zum Zuweisen abrufen', - refreshConnectionSuccess: 'Verbindung aktualisiert', - refreshConnectionSuccessDescription: 'Deine Bankverbindung wurde erfolgreich erneut authentifiziert. Du kannst jetzt neue Karten zuweisen.', error: { feedNameRequired: 'Name des Kartenfeeds ist erforderlich', statementCloseDateRequired: 'Bitte wählen Sie ein Abrechnungsenddatum aus.', diff --git a/src/languages/en.ts b/src/languages/en.ts index de90f6203c0e5..b0c9e30aca2bb 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5372,10 +5372,6 @@ const translations = { removeCardFeed: 'Remove card feed', removeCardFeedTitle: (feedName: string) => `Remove ${feedName} feed`, removeCardFeedDescription: 'Are you sure you want to remove this card feed? This will unassign all cards.', - assignNewCards: 'Assign new cards', - assignNewCardsDescription: 'Get the latest cards to assign from your bank', - refreshConnectionSuccess: 'Connection refreshed', - refreshConnectionSuccessDescription: 'Your bank connection has been re-authenticated successfully. You can now assign new cards.', error: { feedNameRequired: 'Card feed name is required', statementCloseDateRequired: 'Please select a statement close date.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 4469d5c529724..81221f1bed1e5 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5262,10 +5262,6 @@ ${amount} para ${merchant} - ${date}`, removeCardFeed: 'Quitar la alimentación de tarjetas', removeCardFeedTitle: (feedName) => `Eliminar el feed de ${feedName}`, removeCardFeedDescription: '¿Estás seguro de que deseas eliminar esta fuente de tarjetas? Esto anulará la asignación de todas las tarjetas.', - assignNewCards: 'Asignar nuevas tarjetas', - assignNewCardsDescription: 'Obtén las últimas tarjetas de tu banco para asignar', - refreshConnectionSuccess: 'Conexión actualizada', - refreshConnectionSuccessDescription: 'La conexión con tu banco ha sido reautenticada exitosamente. Ahora puedes asignar nuevas tarjetas.', error: { feedNameRequired: 'Se requiere el nombre de la fuente de la tarjeta', statementCloseDateRequired: 'Por favor, selecciona una fecha de cierre del estado de cuenta.', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index e4cb2e2588c98..6e58a125d3d1b 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -5408,10 +5408,6 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. removeCardFeed: 'Supprimer le flux de cartes', removeCardFeedTitle: (feedName: string) => `Supprimer le flux ${feedName}`, removeCardFeedDescription: "Voulez-vous vraiment supprimer ce flux de cartes ? Cela retirera l'assignation de toutes les cartes.", - assignNewCards: 'Attribuer de nouvelles cartes', - assignNewCardsDescription: 'Obtenez les dernières cartes à attribuer depuis votre banque', - refreshConnectionSuccess: 'Connexion actualisée', - refreshConnectionSuccessDescription: 'Votre connexion bancaire a été ré-authentifiée avec succès. Vous pouvez maintenant attribuer de nouvelles cartes.', error: { feedNameRequired: 'Le nom du flux de carte est obligatoire', statementCloseDateRequired: 'Veuillez sélectionner une date de clôture de relevé.', diff --git a/src/languages/it.ts b/src/languages/it.ts index cf8fe55a70e16..81f87d08a66f9 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -5378,10 +5378,6 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. removeCardFeed: 'Rimuovi flusso carta', removeCardFeedTitle: (feedName: string) => `Rimuovi feed ${feedName}`, removeCardFeedDescription: 'Sei sicuro di voler rimuovere questo flusso di carte? Questo rimuoverà l’assegnazione di tutte le carte.', - assignNewCards: 'Assegna nuove carte', - assignNewCardsDescription: 'Ottieni le ultime carte da assegnare dalla tua banca', - refreshConnectionSuccess: 'Connessione aggiornata', - refreshConnectionSuccessDescription: 'La connessione bancaria è stata riautenticata con successo. Ora puoi assegnare nuove carte.', error: { feedNameRequired: 'Il nome del feed della carta è obbligatorio', statementCloseDateRequired: 'Seleziona una data di chiusura dell’estratto conto.', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 588ebc0a170d7..cc8b702bc7a5a 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -5322,10 +5322,6 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO removeCardFeed: 'カードフィードを削除', removeCardFeedTitle: (feedName: string) => `${feedName}フィードを削除`, removeCardFeedDescription: 'このカードフィードを削除してもよろしいですか?すべてのカードの割り当てが解除されます。', - assignNewCards: '新しいカードを割り当てる', - assignNewCardsDescription: '銀行から最新のカードを取得して割り当てます', - refreshConnectionSuccess: '接続が更新されました', - refreshConnectionSuccessDescription: '銀行接続の再認証が正常に完了しました。新しいカードを割り当てることができます。', error: { feedNameRequired: 'カードフィード名は必須です', statementCloseDateRequired: '明細書の締め日を選択してください。', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index d41f7c2680d4e..abe453d051132 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -5359,10 +5359,6 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_ removeCardFeed: 'Kaartfeed verwijderen', removeCardFeedTitle: (feedName: string) => `Feed ${feedName} verwijderen`, removeCardFeedDescription: 'Weet je zeker dat je deze kaartfeed wilt verwijderen? Hierdoor worden alle kaarten losgekoppeld.', - assignNewCards: 'Nieuwe kaarten toewijzen', - assignNewCardsDescription: 'Haal de nieuwste kaarten op van je bank om toe te wijzen', - refreshConnectionSuccess: 'Verbinding vernieuwd', - refreshConnectionSuccessDescription: 'Je bankverbinding is succesvol opnieuw geverifieerd. Je kunt nu nieuwe kaarten toewijzen.', error: { feedNameRequired: 'Naam van kaartfeed is vereist', statementCloseDateRequired: 'Selecteer een afsluitdatum voor het afschrift.', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index af05e0ebac4ba..294fd53bd81e2 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -5354,10 +5354,6 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy removeCardFeed: 'Usuń źródło karty', removeCardFeedTitle: (feedName: string) => `Usuń strumień ${feedName}`, removeCardFeedDescription: 'Na pewno chcesz usunąć ten kanał kart? Spowoduje to odłączenie wszystkich kart.', - assignNewCards: 'Przypisz nowe karty', - assignNewCardsDescription: 'Pobierz najnowsze karty z banku do przypisania', - refreshConnectionSuccess: 'Połączenie odświeżone', - refreshConnectionSuccessDescription: 'Połączenie z bankiem zostało pomyślnie ponownie uwierzytelnione. Możesz teraz przypisać nowe karty.', error: { feedNameRequired: 'Nazwa źródła karty jest wymagana', statementCloseDateRequired: 'Wybierz datę zamknięcia wyciągu.', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 7891f5ccdb174..0b95faa376237 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -5359,10 +5359,6 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS removeCardFeed: 'Remover feed do cartão', removeCardFeedTitle: (feedName: string) => `Remover feed ${feedName}`, removeCardFeedDescription: 'Tem certeza de que deseja remover este feed de cartão? Isso removerá a atribuição de todos os cartões.', - assignNewCards: 'Atribuir novos cartões', - assignNewCardsDescription: 'Obtenha os cartões mais recentes do seu banco para atribuir', - refreshConnectionSuccess: 'Conexão atualizada', - refreshConnectionSuccessDescription: 'Sua conexão bancária foi reautenticada com sucesso. Agora você pode atribuir novos cartões.', error: { feedNameRequired: 'O nome do feed do cartão é obrigatório', statementCloseDateRequired: 'Selecione uma data de fechamento do extrato.', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index c15caf416694e..49e48de160ded 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -5236,10 +5236,6 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM removeCardFeed: '移除卡片流水', removeCardFeedTitle: (feedName: string) => `移除 ${feedName} 数据源`, removeCardFeedDescription: '确定要移除此卡片数据源吗?这将取消分配所有卡片。', - assignNewCards: '分配新卡片', - assignNewCardsDescription: '从银行获取最新的卡片进行分配', - refreshConnectionSuccess: '连接已刷新', - refreshConnectionSuccessDescription: '您的银行连接已成功重新验证。您现在可以分配新卡片。', error: { feedNameRequired: '必须填写卡片流水名称', statementCloseDateRequired: '请选择账单结算日期。', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 6f97fdc83917c..1f3cb4fe362e9 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -790,7 +790,6 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default, [SCREENS.WORKSPACE.INVOICES_VERIFY_ACCOUNT]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicesVerifyAccountPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION]: () => require('../../../../pages/workspace/companyCards/BrokenCardFeedConnectionPage').default, - [SCREENS.WORKSPACE.COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION]: () => require('../../../../pages/workspace/companyCards/RefreshCardFeedConnectionPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: () => require('../../../../pages/workspace/companyCards/assignCard/AssigneeStep').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CARD_SELECTION]: () => require('../../../../pages/workspace/companyCards/assignCard/CardSelectionStep').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE]: () => diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f8ffe8c72688b..6f1c1752ec391 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -868,9 +868,6 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION.route, }, - [SCREENS.WORKSPACE.COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION]: { - path: ROUTES.WORKSPACE_COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION.route, - }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 0282075054d63..82f78383a616d 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1294,10 +1294,6 @@ type SettingsNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; - [SCREENS.WORKSPACE.COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION]: { - policyID: string; - feed: CompanyCardFeedWithDomainID; - }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { policyID: string; feed: CompanyCardFeedWithDomainID; diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index a5aeea42d803b..870bc4928d326 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -1,27 +1,35 @@ -import React, {useRef, useState} from 'react'; +import React, {useEffect, useMemo, useRef, useState} from 'react'; import type {WebViewNavigation} from 'react-native-webview'; import {WebView} from 'react-native-webview'; import ActivityIndicator from '@components/ActivityIndicator'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; -import ConfirmationPage from '@components/ConfirmationPage'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import useCardFeeds from '@hooks/useCardFeeds'; +import useImportPlaidAccounts from '@hooks/useImportPlaidAccounts'; +import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; +import {updateSelectedFeed} from '@libs/actions/Card'; +import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; +import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import getUAForWebView from '@libs/getUAForWebView'; import Navigation from '@libs/Navigation/Navigation'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import WorkspaceCompanyCardsErrorConfirmation from '@pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation'; +import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; +import {getCompanyCardBankConnection} from '@userActions/getCompanyCardBankConnection'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; -import useBankConnection from './useBankConnection'; type BankConnectionProps = { /** ID of the policy */ @@ -32,43 +40,37 @@ type BankConnectionProps = { /** Route params for add new card flow */ route?: PlatformStackRouteProp; - - /** Whether this is a refresh card list flow */ - isRefreshConnectionFlow?: boolean; - - /** Called when the assign flow succeeds */ - onSuccess?: () => void; - - /** Called when the assign flow fails due to broken connection */ - onFailure?: () => void; - - /** Called when the back button is pressed */ - onBackButtonPress?: () => void; }; -function BankConnection({policyID: policyIDFromProps, feed, route, isRefreshConnectionFlow, onSuccess, onFailure, onBackButtonPress}: BankConnectionProps) { +function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnectionProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const webViewRef = useRef(null); const [session] = useOnyx(ONYXKEYS.SESSION); + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const authToken = session?.authToken ?? null; + const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const selectedBank = addNewCard?.data?.selectedBank; const {feed: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; + const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.cardToAssign?.plaidAccessToken; + const isPlaid = !!plaidToken; + const url = getCompanyCardBankConnection(policyID, bankName); + const [cardFeeds] = useCardFeeds(policyID); const [isConnectionCompleted, setConnectionCompleted] = useState(false); - - const {handleBackButtonPress, url, isPlaid, isNewFeedHasError, newFeed, isAllFeedsResultLoading, isBlockedToAddNewFeeds, isRefreshComplete} = useBankConnection({ - policyID, - feed, - bankNameFromRoute, - onSuccess, - onFailure, - onBackButtonPress, - isRefreshConnectionFlow, - shouldOpenWindow: false, - }); - + const prevFeedsData = usePrevious(cardFeeds); + const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.[feed]) : false; + const {isNewFeedConnected, newFeed} = useMemo( + () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), + [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], + ); const headerTitleAddCards = !backTo ? translate('workspace.companyCards.addCards') : undefined; - const headerTitle = feed ? translate(isRefreshConnectionFlow ? 'workspace.moreFeatures.companyCards.assignNewCards' : 'workspace.companyCards.assignCard') : headerTitleAddCards; + const headerTitle = feed ? translate('workspace.companyCards.assignCard') : headerTitleAddCards; + const onImportPlaidAccounts = useImportPlaidAccounts(policyID); + const {updateBrokenConnection, isFeedConnectionBroken} = useUpdateFeedBrokenConnection({policyID, feed}); + const isNewFeedHasError = !!(newFeed && cardFeeds?.[newFeed]?.errors); + const {isBlockedToAddNewFeeds, isAllFeedsResultLoading} = useIsBlockedToAddFeed(policyID); const fullscreenReasonAttributes: SkeletonSpanReasonAttributes = { context: 'BankConnection', @@ -82,63 +84,93 @@ function BankConnection({policyID: policyIDFromProps, feed, route, isRefreshConn }; const renderLoading = () => ; - const checkIfConnectionCompleted = (navState: WebViewNavigation) => { - if (!navState.url.includes(ROUTES.BANK_CONNECTION_COMPLETE)) { + useEffect(() => { + if (!policyID || !isBlockedToAddNewFeeds || feed) { return; } - setConnectionCompleted(true); + Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.companyCards.alias, ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)), { + forceReplace: true, + }); + }, [isBlockedToAddNewFeeds, policyID, feed]); + + const handleBackButtonPress = () => { + // Handle assign card flow + if (feed) { + Navigation.goBack(); + return; + } + + // Handle add new card flow + if (backTo) { + Navigation.goBack(backTo); + return; + } + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); }; - const getContent = () => { - if (isRefreshComplete) { - return ( - Navigation.dismissModal()} - /> - ); + useEffect(() => { + if ((!url && !isPlaid) || isNewFeedHasError) { + return; } - if (isNewFeedHasError) { - return ( - - ); + + // Handle assign card flow + if (feed && !isFeedExpired) { + if (isFeedConnectionBroken) { + updateBrokenConnection(); + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); + return; + } + setAssignCardStepAndData({ + currentStep: assignCard?.cardToAssign?.dateOption ? CONST.COMPANY_CARD.STEP.CONFIRMATION : CONST.COMPANY_CARD.STEP.ASSIGNEE, + isEditing: false, + }); + return; } - if (!!url && !isConnectionCompleted && !isPlaid && !isAllFeedsResultLoading && (!isBlockedToAddNewFeeds || !!feed)) { - return ( - - ); + + // Handle add new card flow + if (isNewFeedConnected) { + if (newFeed) { + updateSelectedFeed(newFeed, policyID); + } + + // Direct feeds (except those added via Plaid) are created with default statement period end date. + // Redirect the user to set a custom date. + if (policyID && !isPlaid) { + setAddNewCompanyCardStepAndData({ + step: CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE, + }); + } else { + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); + } } - return ( - - ); + if (isPlaid) { + onImportPlaidAccounts(); + } + }, [ + isNewFeedConnected, + newFeed, + policyID, + url, + feed, + isFeedExpired, + assignCard?.cardToAssign?.dateOption, + isPlaid, + onImportPlaidAccounts, + isFeedConnectionBroken, + updateBrokenConnection, + isNewFeedHasError, + ]); + + const checkIfConnectionCompleted = (navState: WebViewNavigation) => { + if (!navState.url.includes(ROUTES.BANK_CONNECTION_COMPLETE)) { + return; + } + setConnectionCompleted(true); }; return ( - {getContent()} + + {!!url && !isConnectionCompleted && !isPlaid && !isNewFeedHasError && !isAllFeedsResultLoading && (!isBlockedToAddNewFeeds || !!feed) && ( + + )} + {(isAllFeedsResultLoading || (isBlockedToAddNewFeeds && !feed) || isConnectionCompleted || isPlaid) && !isNewFeedHasError && ( + + )} + {isNewFeedHasError && ( + + )} + ); } diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index 7803c8b0c4234..79e91521b3ca0 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -1,24 +1,39 @@ -import React from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import ActivityIndicator from '@components/ActivityIndicator'; import BlockingView from '@components/BlockingViews/BlockingView'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; -import ConfirmationPage from '@components/ConfirmationPage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; +import useCardFeeds from '@hooks/useCardFeeds'; +import useImportPlaidAccounts from '@hooks/useImportPlaidAccounts'; +import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import useOnyx from '@hooks/useOnyx'; +import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; +import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; +import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import WorkspaceCompanyCardsErrorConfirmation from '@pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation'; +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'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; -import useBankConnection from './useBankConnection'; +import openBankConnection from './openBankConnection'; + +let customWindow: Window | null = null; type BankConnectionProps = { /** ID of the policy */ @@ -29,70 +44,156 @@ type BankConnectionProps = { /** Route params for add new card flow */ route?: PlatformStackRouteProp; - - /** Whether this is a refresh card list flow */ - isRefreshConnectionFlow?: boolean; - - /** Called when the assign flow succeeds */ - onSuccess?: () => void; - - /** Called when the assign flow fails due to broken connection */ - onFailure?: () => void; - - /** Called when the back button is pressed */ - onBackButtonPress?: () => void; }; -function BankConnection({policyID: policyIDFromProps, feed, route, isRefreshConnectionFlow, onSuccess, onFailure, onBackButtonPress}: BankConnectionProps) { +function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnectionProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const illustrations = useMemoizedLazyIllustrations(['PendingBank']); + const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const {feed: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; + const [cardFeeds] = useCardFeeds(policyID); + const prevFeedsData = usePrevious(cardFeeds); + const illustrations = useMemoizedLazyIllustrations(['PendingBank']); + const [shouldBlockWindowOpen, setShouldBlockWindowOpen] = useState(false); + const selectedBank = addNewCard?.data?.selectedBank; + const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const {isNewFeedConnected, newFeed} = useMemo( + () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), + [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], + ); + const {isOffline} = useNetwork(); + const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.cardToAssign?.plaidAccessToken; + const {updateBrokenConnection, isFeedConnectionBroken} = useUpdateFeedBrokenConnection({policyID, feed}); + const isPlaid = !!plaidToken; - const { - onOpenBankConnectionFlow, - handleBackButtonPress, - bankName, - bankDisplayName, - isPlaid, - isNewFeedHasError, - newFeed, - isAllFeedsResultLoading, - isBlockedToAddNewFeeds, - isRefreshComplete, - } = useBankConnection({ - policyID, - feed, - bankNameFromRoute, - onSuccess, - onFailure, - onBackButtonPress, - isRefreshConnectionFlow, - }); + const url = getCompanyCardBankConnection(policyID, bankName); + const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.[feed]) : false; + const headerTitleAddCards = !backTo ? translate('workspace.companyCards.addCards') : undefined; + const headerTitle = feed ? translate('workspace.companyCards.assignCard') : headerTitleAddCards; + const isNewFeedHasError = !!(newFeed && cardFeeds?.[newFeed]?.errors); + const onImportPlaidAccounts = useImportPlaidAccounts(policyID); + const {isBlockedToAddNewFeeds, isAllFeedsResultLoading} = useIsBlockedToAddFeed(policyID); - const headerTitleAddCards = !backTo ? translate(isRefreshConnectionFlow ? 'workspace.moreFeatures.companyCards.assignNewCards' : 'workspace.companyCards.addCards') : undefined; - const headerTitle = feed ? translate(isRefreshConnectionFlow ? 'workspace.moreFeatures.companyCards.assignNewCards' : 'workspace.companyCards.assignCard') : headerTitleAddCards; + const onOpenBankConnectionFlow = useCallback(() => { + if (!url) { + return; + } + customWindow = openBankConnection(url); + }, [url]); + + useEffect(() => { + if (!policyID || !isBlockedToAddNewFeeds || feed) { + return; + } + Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.companyCards.alias, ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)), { + forceReplace: true, + }); + }, [isBlockedToAddNewFeeds, policyID, feed]); + + const handleBackButtonPress = () => { + customWindow?.close(); + + // Handle assign card flow + if (feed) { + Navigation.goBack(); + return; + } + + // Handle add new card flow + if (backTo) { + Navigation.goBack(backTo); + return; + } + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); + }; const CustomSubtitle = ( - {bankName && translate(`workspace.moreFeatures.companyCards.pendingBankDescription`, bankDisplayName ?? bankName)} + {bankName && translate(`workspace.moreFeatures.companyCards.pendingBankDescription`, addNewCard?.data?.plaidConnectedFeedName ?? bankName)} {translate('workspace.moreFeatures.companyCards.pendingBankLink')}. ); - const getContent = () => { - if (isRefreshComplete) { - return ( - Navigation.dismissModal()} - /> - ); + useEffect(() => { + if ((!url && !isPlaid) || isOffline || isNewFeedHasError || isAllFeedsResultLoading || (isBlockedToAddNewFeeds && !feed)) { + return; + } + + // Handle assign card flow + if (feed) { + if (!isFeedExpired) { + customWindow?.close(); + if (isFeedConnectionBroken) { + updateBrokenConnection(); + Navigation.closeRHPFlow(); + return; + } + setAssignCardStepAndData({ + currentStep: assignCard?.cardToAssign?.dateOption ? CONST.COMPANY_CARD.STEP.CONFIRMATION : CONST.COMPANY_CARD.STEP.ASSIGNEE, + isEditing: false, + }); + return; + } + if (isPlaid) { + return; + } + if (url) { + customWindow = openBankConnection(url); + return; + } + } + + // Handle add new card flow + if (isNewFeedConnected) { + setShouldBlockWindowOpen(true); + customWindow?.close(); + if (newFeed) { + updateSelectedFeed(newFeed, policyID); + } + + // Direct feeds (except those added via Plaid) are created with default statement period end date. + // Redirect the user to set a custom date. + if (policyID && !isPlaid) { + setAddNewCompanyCardStepAndData({ + step: CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE, + }); + } else { + Navigation.closeRHPFlow(); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); + } + return; } + if (!shouldBlockWindowOpen) { + if (isPlaid) { + onImportPlaidAccounts(); + return; + } + if (url) { + customWindow = openBankConnection(url); + } + } + }, [ + isNewFeedConnected, + isAllFeedsResultLoading, + shouldBlockWindowOpen, + isBlockedToAddNewFeeds, + newFeed, + policyID, + url, + feed, + isFeedExpired, + isOffline, + assignCard?.cardToAssign?.dateOption, + isPlaid, + onImportPlaidAccounts, + isFeedConnectionBroken, + updateBrokenConnection, + isNewFeedHasError, + ]); + + const getContent = () => { if (isNewFeedHasError) { return ( { - // No-op for native - return null; -}; - -export default handleOpenBankConnectionFlow; diff --git a/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts b/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts deleted file mode 100644 index 2482a48e04bd7..0000000000000 --- a/src/pages/workspace/companyCards/BankConnection/useBankConnection.ts +++ /dev/null @@ -1,209 +0,0 @@ -import {useCallback, useEffect, useMemo, useRef} from 'react'; -import useCardFeeds from '@hooks/useCardFeeds'; -import useImportPlaidAccounts from '@hooks/useImportPlaidAccounts'; -import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; -import useNetwork from '@hooks/useNetwork'; -import useOnyx from '@hooks/useOnyx'; -import usePrevious from '@hooks/usePrevious'; -import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; -import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import {getCompanyCardBankConnection} from '@userActions/getCompanyCardBankConnection'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; -import openBankConnection from './openBankConnection'; - -type UseBankConnectionProps = { - policyID?: string; - feed?: CompanyCardFeedWithDomainID; - bankNameFromRoute?: string | null; - onSuccess?: (newFeed?: CompanyCardFeedWithDomainID) => void; - onFailure?: () => void; - onBackButtonPress?: () => void; - isRefreshConnectionFlow?: boolean; - shouldOpenWindow?: boolean; -}; - -let customWindow: Window | null = null; - -function closeCustomWindow() { - customWindow?.close(); -} - -export default function useBankConnection({ - policyID, - feed, - bankNameFromRoute, - onSuccess, - onFailure, - onBackButtonPress, - isRefreshConnectionFlow, - shouldOpenWindow = true, -}: UseBankConnectionProps) { - const {isOffline} = useNetwork(); - const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); - const [cardFeeds] = useCardFeeds(policyID); - const prevFeedsData = usePrevious(cardFeeds); - const onImportPlaidAccounts = useImportPlaidAccounts(policyID); - const {isBlockedToAddNewFeeds, isAllFeedsResultLoading} = useIsBlockedToAddFeed(policyID); - const {isFeedConnectionBroken} = useUpdateFeedBrokenConnection({policyID, feed}); - const shouldBlockWindowOpen = useRef(false); - const refreshSuccessHandled = useRef(false); - - const addNewCardData = addNewCard?.data; - const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCardData?.plaidConnectedFeed ?? addNewCardData?.selectedBank); - const bankDisplayName = addNewCardData?.plaidConnectedFeedName ?? bankName; - const plaidToken = addNewCardData?.publicToken ?? assignCard?.cardToAssign?.plaidAccessToken; - const isPlaid = !!plaidToken; - const url = getCompanyCardBankConnection(policyID, bankName); - const isFeedExpired = feed ? !!isSelectedFeedExpired(cardFeeds?.[feed]) : false; - const prevIsFeedExpired = usePrevious(isFeedExpired); - const {isNewFeedConnected, newFeed} = useMemo( - () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCardData?.plaidConnectedFeed), - [addNewCardData?.plaidConnectedFeed, cardFeeds, prevFeedsData], - ); - const isNewFeedHasError = !!(newFeed && cardFeeds?.[newFeed]?.errors); - const hasConnectionSource = !!url || isPlaid; - const shouldWaitForData = isOffline || isNewFeedHasError || isAllFeedsResultLoading || (isBlockedToAddNewFeeds && !feed); - - const isRefreshComplete = useMemo(() => { - if (!isRefreshConnectionFlow || !feed || !hasConnectionSource || shouldWaitForData) { - return false; - } - return !!prevIsFeedExpired && !isFeedExpired && !isFeedConnectionBroken; - }, [isRefreshConnectionFlow, feed, hasConnectionSource, shouldWaitForData, prevIsFeedExpired, isFeedExpired, isFeedConnectionBroken]); - - const fallbackNavigation = useCallback(() => { - Navigation.goBack(policyID ? ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID) : undefined); - }, [policyID]); - - const handleSuccess = useCallback( - (connectedFeed?: CompanyCardFeedWithDomainID) => { - if (onSuccess) { - onSuccess(connectedFeed); - return; - } - fallbackNavigation(); - }, - [onSuccess, fallbackNavigation], - ); - - const handleFailure = useMemo(() => onFailure ?? fallbackNavigation, [onFailure, fallbackNavigation]); - - const onOpenBankConnectionFlow = useCallback(() => { - if (!url || !shouldOpenWindow) { - return; - } - customWindow = openBankConnection(url); - }, [url, shouldOpenWindow]); - - const handleBackButtonPress = useCallback(() => { - if (shouldOpenWindow) { - closeCustomWindow(); - } - - if (onBackButtonPress) { - onBackButtonPress(); - return; - } - - Navigation.goBack(); - }, [shouldOpenWindow, onBackButtonPress]); - - useEffect(() => { - if (!isRefreshComplete || refreshSuccessHandled.current) { - return; - } - refreshSuccessHandled.current = true; - onSuccess?.(); - }, [isRefreshComplete, onSuccess]); - - useEffect(() => { - if (!policyID || !isBlockedToAddNewFeeds || feed) { - return; - } - Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.companyCards.alias, ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)), { - forceReplace: true, - }); - }, [isBlockedToAddNewFeeds, policyID, feed]); - - useEffect(() => { - if (!hasConnectionSource || shouldWaitForData) { - return; - } - - // Handle existing feed flow - if (feed) { - if (!isFeedExpired) { - if (shouldOpenWindow) { - closeCustomWindow(); - } - if (isFeedConnectionBroken) { - handleFailure(); - return; - } - if (!isRefreshConnectionFlow) { - handleSuccess(); - } - return; - } - if (!isPlaid && url && shouldOpenWindow) { - customWindow = openBankConnection(url); - } - return; - } - - // Handle new feed flow - if (isNewFeedConnected) { - shouldBlockWindowOpen.current = true; - if (shouldOpenWindow) { - closeCustomWindow(); - } - handleSuccess(newFeed); - return; - } - - if (!shouldBlockWindowOpen.current) { - if (isPlaid) { - onImportPlaidAccounts(); - return; - } - if (url && shouldOpenWindow) { - customWindow = openBankConnection(url); - } - } - }, [ - hasConnectionSource, - shouldWaitForData, - isNewFeedConnected, - newFeed, - policyID, - url, - feed, - isFeedExpired, - isPlaid, - onImportPlaidAccounts, - isFeedConnectionBroken, - handleSuccess, - handleFailure, - shouldOpenWindow, - isRefreshConnectionFlow, - ]); - - return { - onOpenBankConnectionFlow, - handleBackButtonPress, - bankName, - bankDisplayName, - url, - isPlaid, - isNewFeedHasError, - newFeed, - isAllFeedsResultLoading, - isBlockedToAddNewFeeds, - isRefreshComplete, - }; -} diff --git a/src/pages/workspace/companyCards/BrokenCardFeedConnectionPage.tsx b/src/pages/workspace/companyCards/BrokenCardFeedConnectionPage.tsx index f65402860deae..9f0521319dbb7 100644 --- a/src/pages/workspace/companyCards/BrokenCardFeedConnectionPage.tsx +++ b/src/pages/workspace/companyCards/BrokenCardFeedConnectionPage.tsx @@ -1,18 +1,14 @@ -import React, {useCallback, useEffect} from 'react'; +import React, {useEffect} from 'react'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; -import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import LoadingPage from '@pages/LoadingPage'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearAssignCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; +import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import PlaidConnectionStep from './addNew/PlaidConnectionStep'; import BankConnection from './BankConnection'; @@ -25,11 +21,9 @@ function BrokenCardFeedConnectionPage({route, policy}: BrokenCardFeedConnectionP const policyID = policy?.id; const {translate} = useLocalize(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const currentStep = assignCard?.currentStep; - const {updateBrokenConnection} = useUpdateFeedBrokenConnection({policyID, feed}); useEffect(() => { return () => { @@ -37,39 +31,12 @@ function BrokenCardFeedConnectionPage({route, policy}: BrokenCardFeedConnectionP }; }, []); - const handleAssignSuccess = useCallback(() => { - setAssignCardStepAndData({ - currentStep: assignCard?.cardToAssign?.dateOption ? CONST.COMPANY_CARD.STEP.CONFIRMATION : CONST.COMPANY_CARD.STEP.ASSIGNEE, - isEditing: false, - }); - }, [assignCard?.cardToAssign?.dateOption]); - - const handleAssignFailure = useCallback(() => { - updateBrokenConnection(); - if (shouldUseNarrowLayout) { - Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); - return; - } - Navigation.closeRHPFlow(); - }, [policyID, shouldUseNarrowLayout, updateBrokenConnection]); - - const handleBackButtonPress = useCallback(() => { - if (shouldUseNarrowLayout) { - Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); - return; - } - Navigation.closeRHPFlow(); - }, [policyID, shouldUseNarrowLayout]); - switch (currentStep) { case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: return ( ); case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: diff --git a/src/pages/workspace/companyCards/RefreshCardFeedConnectionPage.tsx b/src/pages/workspace/companyCards/RefreshCardFeedConnectionPage.tsx deleted file mode 100644 index 4823433a6e5e1..0000000000000 --- a/src/pages/workspace/companyCards/RefreshCardFeedConnectionPage.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, {useCallback, useEffect} from 'react'; -import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; -import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {SettingsNavigatorParamList} from '@navigation/types'; -import LoadingPage from '@pages/LoadingPage'; -import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; -import PlaidConnectionStep from './addNew/PlaidConnectionStep'; -import BankConnection from './BankConnection'; - -type RefreshCardFeedConnectionPageProps = PlatformStackScreenProps & - WithPolicyAndFullscreenLoadingProps; - -function RefreshCardFeedConnectionPage({route, policy}: RefreshCardFeedConnectionPageProps) { - const feed = route.params?.feed; - const policyID = policy?.id; - - const {translate} = useLocalize(); - - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); - const currentStep = assignCard?.currentStep; - const {updateBrokenConnection} = useUpdateFeedBrokenConnection({policyID, feed}); - - useEffect(() => { - return () => { - clearAssignCardStepAndData(); - }; - }, []); - - const navigateToFeedSettings = useCallback(() => { - Navigation.goBack(policyID ? ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID) : undefined); - }, [policyID]); - - switch (currentStep) { - case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: - return ( - - ); - case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: - return ( - - ); - default: - return ; - } -} - -export default withPolicyAndFullscreenLoading(RefreshCardFeedConnectionPage); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 5a8d42a4ab811..655090ea74fd9 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -10,29 +10,18 @@ import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; -import {useCurrencyListState} from '@hooks/useCurrencyList'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {deleteWorkspaceCompanyCardFeed, setAssignCardStepAndData, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; -import { - getCompanyCardFeed, - getCompanyFeeds, - getCustomOrFormattedFeedName, - getDomainOrWorkspaceAccountID, - getPlaidCountry, - getPlaidInstitutionId, - getSelectedFeed, - isDirectFeed, -} from '@libs/CardUtils'; +import {deleteWorkspaceCompanyCardFeed, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; +import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -59,7 +48,7 @@ function WorkspaceCompanyCardsSettingsPage({ const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); - const icons = useMemoizedLazyExpensifyIcons(['Sync', 'Trashcan'] as const); + const icons = useMemoizedLazyExpensifyIcons(['Trashcan'] as const); const feedName = selectedFeed ? getCustomOrFormattedFeedName(translate, feed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -67,9 +56,6 @@ function WorkspaceCompanyCardsSettingsPage({ const isPersonal = liabilityType === CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); const isPending = !!selectedFeedData?.pending; - const {currencyList} = useCurrencyListState(); - const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY); - const isDirectCardFeed = isDirectFeed(feed); const statementCloseDate = useMemo(() => { if (!selectedFeedData?.statementPeriodEndDay) { return undefined; @@ -106,30 +92,6 @@ function WorkspaceCompanyCardsSettingsPage({ } }; - const openBankConnectionFlow = () => { - if (!selectedFeed) { - return; - } - - const institutionId = getPlaidInstitutionId(selectedFeed); - const initialStep = institutionId ? CONST.COMPANY_CARD.STEP.PLAID_CONNECTION : CONST.COMPANY_CARD.STEP.BANK_CONNECTION; - - if (institutionId) { - const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); - setAddNewCompanyCardStepAndData({ - data: { - selectedCountry: country, - }, - }); - } - - setAssignCardStepAndData({currentStep: initialStep}); - - Navigation.setNavigationActionToMicrotaskQueue(() => { - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION.getRoute(policyID, selectedFeed)); - }); - }; - const onToggleLiability = (isOn: boolean) => { if (!feed) { return; @@ -187,14 +149,6 @@ function WorkspaceCompanyCardsSettingsPage({ /> {translate('workspace.moreFeatures.companyCards.setTransactionLiabilityDescription')} - {isDirectCardFeed && ( - - )} { - if (newFeed) { - updateSelectedFeed(newFeed, policyID); - } - - const isPlaid = !!addNewCardFeed?.data?.publicToken; - - // Direct feeds (except those added via Plaid) are created with default statement period end date. - // Redirect the user to set a custom date. - if (policyID && !isPlaid) { - setAddNewCompanyCardStepAndData({ - step: CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE, - }); - } else { - Navigation.closeRHPFlow(); - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); - } - }, - [addNewCardFeed?.data?.publicToken, policyID], - ); - - const handleBackButtonPress = useCallback(() => { - setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); - }, []); - if (isAddCardFeedLoading || isAllFeedsResultLoading || isBlockedToAddNewFeeds) { const reasonAttributes: SkeletonSpanReasonAttributes = { context: 'AddNewCardPage', @@ -136,13 +108,7 @@ function AddNewCardPage({policy}: WithPolicyAndFullscreenLoadingProps) { CurrentStep = ; break; case CONST.COMPANY_CARDS.STEP.BANK_CONNECTION: - CurrentStep = ( - - ); + CurrentStep = ; break; case CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS: CurrentStep = ; diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 05c3c8fae6acd..c6b9762af0879 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -27,17 +27,7 @@ import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import type {CardFeedWithNumber} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -function PlaidConnectionStep({ - feed, - policyID, - onExit, - isRefreshConnectionFlow, -}: { - feed?: CompanyCardFeedWithDomainID; - policyID?: string; - onExit?: () => void; - isRefreshConnectionFlow?: boolean; -}) { +function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeedWithDomainID; policyID?: string; onExit?: () => void}) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); @@ -228,7 +218,7 @@ function PlaidConnectionStep({ shouldEnableMaxHeight > {isPlaidDisabled ? ( diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index 4a2e610b4e1df..f4ef4b7212a5b 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -53,7 +53,6 @@ type PolicyRouteName = | typeof SCREENS.WORKSPACE.RULES | typeof SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW | typeof SCREENS.WORKSPACE.COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION - | typeof SCREENS.WORKSPACE.COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION | typeof SCREENS.WORKSPACE.ACCOUNTING.CLAIM_OFFER | typeof SCREENS.WORKSPACE.TIME_TRACKING; diff --git a/tests/ui/RefreshCardFeedConnection.tsx b/tests/ui/RefreshCardFeedConnection.tsx deleted file mode 100644 index a4fa47484c1fc..0000000000000 --- a/tests/ui/RefreshCardFeedConnection.tsx +++ /dev/null @@ -1,258 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {PortalProvider} from '@gorhom/portal'; -import {NavigationContainer} from '@react-navigation/native'; -import {act, render, screen, waitFor} from '@testing-library/react-native'; -import React from 'react'; -import Onyx from 'react-native-onyx'; -import ComposeProviders from '@components/ComposeProviders'; -import {LocaleContextProvider} from '@components/LocaleContextProvider'; -import OnyxListItemProvider from '@components/OnyxListItemProvider'; -import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID'; -import * as useResponsiveLayoutModule from '@hooks/useResponsiveLayout'; -import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types'; -import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; -import type {SettingsNavigatorParamList} from '@navigation/types'; -import RefreshCardFeedConnectionPage from '@pages/workspace/companyCards/RefreshCardFeedConnectionPage'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import type {CompanyCardFeedWithDomainID} from '@src/types/onyx/CardFeeds'; -import * as LHNTestUtils from '../utils/LHNTestUtils'; -import * as TestHelper from '../utils/TestHelper'; -import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; - -const WORKSPACE_ACCOUNT_ID = 5678; -const POLICY_ID = LHNTestUtils.getFakePolicy().id; -const DIRECT_FEED = `oauth.chase.com#${WORKSPACE_ACCOUNT_ID}` as CompanyCardFeedWithDomainID; - -TestHelper.setupGlobalFetchMock(); - -jest.mock('@hooks/useNetwork', () => - jest.fn(() => ({ - isOffline: false, - })), -); - -jest.mock('react-native-permissions', () => ({ - RESULTS: {UNAVAILABLE: 'unavailable', BLOCKED: 'blocked', DENIED: 'denied', GRANTED: 'granted', LIMITED: 'limited'}, - check: jest.fn(() => Promise.resolve('granted')), - request: jest.fn(() => Promise.resolve('granted')), - PERMISSIONS: {IOS: {CONTACTS: 'ios.permission.CONTACTS'}, ANDROID: {READ_CONTACTS: 'android.permission.READ_CONTACTS'}}, -})); - -jest.mock('@rnmapbox/maps', () => ({default: jest.fn(), MarkerView: jest.fn(), setAccessToken: jest.fn()})); - -jest.mock('react-native-plaid-link-sdk', () => ({dismissLink: jest.fn(), openLink: jest.fn(), usePlaidEmitter: jest.fn()})); - -jest.mock('@libs/Navigation/Navigation', () => ({ - navigate: jest.fn(), - goBack: jest.fn(), - closeRHPFlow: jest.fn(), - dismissModal: jest.fn(), - setNavigationActionToMicrotaskQueue: jest.fn((callback: () => void) => callback?.()), - getTopmostReportId: jest.fn(), -})); - -const mockClearAssignCardStepAndData = jest.fn(); - -jest.mock('@userActions/CompanyCards', () => ({ - setAssignCardStepAndData: jest.fn(), - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - clearAssignCardStepAndData: (...args: unknown[]) => mockClearAssignCardStepAndData(...args), - setAddNewCompanyCardStepAndData: jest.fn(), -})); - -let capturedProps: {isRefreshConnectionFlow?: boolean; onFailure?: () => void; onBackButtonPress?: () => void} = {}; -jest.mock('@pages/workspace/companyCards/BankConnection', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const {View, Text} = require('react-native'); - return { - __esModule: true, - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - default: (props: {isRefreshConnectionFlow?: boolean; onFailure?: () => void; onBackButtonPress?: () => void}) => { - capturedProps = props; - return ( - - {props.isRefreshConnectionFlow ? 'refresh-flow' : 'normal-flow'} - - ); - }, - }; -}); - -jest.mock('@pages/workspace/companyCards/addNew/PlaidConnectionStep', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const {View} = require('react-native'); - return { - __esModule: true, - default: () => , - }; -}); - -const Stack = createPlatformStackNavigator(); - -const renderRefreshPage = () => { - return render( - - - - - - - - - , - ); -}; - -describe('RefreshCardFeedConnection', () => { - beforeAll(() => { - Onyx.init({keys: ONYXKEYS}); - }); - - beforeEach(() => { - capturedProps = {}; - jest.spyOn(useResponsiveLayoutModule, 'default').mockReturnValue({ - isSmallScreenWidth: false, - shouldUseNarrowLayout: false, - } as ResponsiveLayoutResult); - }); - - afterEach(async () => { - await act(async () => { - await Onyx.clear(); - }); - jest.clearAllMocks(); - }); - - describe('Step rendering', () => { - it('should show loading page when no step is set', async () => { - await TestHelper.signInWithTestUser(); - - const policy = {...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, workspaceAccountID: WORKSPACE_ACCOUNT_ID}; - - await act(async () => { - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - }); - - const {unmount} = renderRefreshPage(); - await waitForBatchedUpdatesWithAct(); - - await waitFor(() => { - expect(screen.getByText('Assign new cards')).toBeOnTheScreen(); - }); - - unmount(); - await waitForBatchedUpdatesWithAct(); - }); - - it('should render BankConnection when step is BANK_CONNECTION', async () => { - await TestHelper.signInWithTestUser(); - - const policy = {...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, workspaceAccountID: WORKSPACE_ACCOUNT_ID}; - - await act(async () => { - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - await Onyx.merge(ONYXKEYS.ASSIGN_CARD, {currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION}); - }); - - const {unmount} = renderRefreshPage(); - await waitForBatchedUpdatesWithAct(); - - await waitFor(() => { - expect(screen.getByTestId('BankConnection')).toBeOnTheScreen(); - expect(screen.getByText('refresh-flow')).toBeOnTheScreen(); - }); - - unmount(); - await waitForBatchedUpdatesWithAct(); - }); - - it('should render PlaidConnectionStep when step is PLAID_CONNECTION', async () => { - await TestHelper.signInWithTestUser(); - - const policy = {...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, workspaceAccountID: WORKSPACE_ACCOUNT_ID}; - - await act(async () => { - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - await Onyx.merge(ONYXKEYS.ASSIGN_CARD, {currentStep: CONST.COMPANY_CARD.STEP.PLAID_CONNECTION}); - }); - - const {unmount} = renderRefreshPage(); - await waitForBatchedUpdatesWithAct(); - - await waitFor(() => { - expect(screen.getByTestId('PlaidConnectionStep')).toBeOnTheScreen(); - }); - - unmount(); - await waitForBatchedUpdatesWithAct(); - }); - }); - - describe('BankConnection props', () => { - it('should pass onFailure and onBackButtonPress callbacks', async () => { - await TestHelper.signInWithTestUser(); - - const policy = {...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, workspaceAccountID: WORKSPACE_ACCOUNT_ID}; - - await act(async () => { - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - await Onyx.merge(ONYXKEYS.ASSIGN_CARD, {currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION}); - }); - - const {unmount} = renderRefreshPage(); - await waitForBatchedUpdatesWithAct(); - - await waitFor(() => { - expect(screen.getByTestId('BankConnection')).toBeOnTheScreen(); - }); - - expect(capturedProps.onFailure).toBeDefined(); - expect(capturedProps.onBackButtonPress).toBeDefined(); - - unmount(); - await waitForBatchedUpdatesWithAct(); - }); - }); - - describe('Cleanup', () => { - it('should call clearAssignCardStepAndData on unmount', async () => { - await TestHelper.signInWithTestUser(); - - const policy = {...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, workspaceAccountID: WORKSPACE_ACCOUNT_ID}; - - await act(async () => { - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - }); - - const {unmount} = renderRefreshPage(); - await waitForBatchedUpdatesWithAct(); - - unmount(); - await waitForBatchedUpdatesWithAct(); - - expect(mockClearAssignCardStepAndData).toHaveBeenCalled(); - }); - }); - - describe('Route generation', () => { - it('should generate correct route for refresh card feed connection', () => { - const route = ROUTES.WORKSPACE_COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION.getRoute(POLICY_ID, DIRECT_FEED); - expect(route).toBe(`workspaces/${POLICY_ID}/company-cards/${encodeURIComponent(DIRECT_FEED)}/refresh-card-feed-connection`); - }); - }); -});