diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index a4e6d9e1c09d4..67837e662aa38 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -2,13 +2,15 @@ import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list'; import {FlashList} from '@shopify/flash-list'; import React, {useRef} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {PressableWithFeedback} from '@components/Pressable'; import SearchBar from '@components/SearchBar'; +import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton'; import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; +import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -29,7 +31,8 @@ import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage'; import WorkspaceCompanyCardsListRow from './WorkspaceCompanyCardsListRow'; @@ -37,9 +40,6 @@ type WorkspaceCompanyCardsListProps = { /** Selected feed */ selectedFeed: CompanyCardFeedWithDomainID; - /** List of company cards */ - cardsList: OnyxEntry; - /** Current policy id */ policyID: string; @@ -53,17 +53,24 @@ type WorkspaceCompanyCardsListProps = { shouldShowGBDisclaimer?: boolean; }; -function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) { +function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) { const styles = useThemeStyles(); + const {isOffline} = useNetwork(); const {translate, localeCompare} = useLocalize(); const listRef = useRef>(null); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); + const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); + const isLoadingCardsList = !isOffline && isLoadingOnyxValue(cardsListMetadata); + const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); + const isLoadingPersonalDetails = !isOffline && isLoadingOnyxValue(personalDetailsMetadata); + const isLoadingCardsTableData = isLoadingCardsList || isLoadingPersonalDetails; + const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true}); const policy = usePolicy(policyID); - const {cardList, ...assignedCards} = cardsList ?? {}; + const cardListTyped: Record | undefined = (cardsList as {cardList?: Record})?.cardList ?? {}; + const assignedCards = Object.fromEntries(Object.entries(cardsList ?? {}).filter(([key]) => key !== 'cardList')) as Record; const [cardFeeds] = useCardFeeds(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); @@ -89,7 +96,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC const renderItem = ({item: cardName, index}: ListRenderItemInfo) => { const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName); - const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID]; + const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined; const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; @@ -101,7 +108,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC if (isPlaid) { cardIdentifier = cardName; } else if (isCommercial) { - const cardValue = cardList?.[cardName] ?? cardName; + const cardValue = cardListTyped?.[cardName] ?? cardName; const digitsOnly = cardValue.replaceAll(/\D/g, ''); if (digitsOnly.length >= 10) { const first6 = digitsOnly.substring(0, 6); @@ -111,7 +118,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC cardIdentifier = cardValue; } } else { - cardIdentifier = cardList?.[cardName] ?? cardName; + cardIdentifier = cardListTyped?.[cardName] ?? cardName; } } @@ -149,7 +156,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC > {({hovered}) => ( : undefined} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled" contentContainerStyle={styles.flexGrow1} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index e7076d407867d..b0bf0bd30452b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import ActivityIndicator from '@components/ActivityIndicator'; import DecisionModal from '@components/DecisionModal'; import useAssignCard from '@hooks/useAssignCard'; @@ -20,7 +20,7 @@ import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage} from '@userActio import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList'; @@ -39,10 +39,10 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); - const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); + const [cardFeeds] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; - const [cardsList] = useCardsList(selectedFeed); + const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0; @@ -53,18 +53,17 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const isFeedAdded = !isPending && !isNoFeed; const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); - const fetchCompanyCards = useCallback(() => { - openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); - }, [policyID, domainOrWorkspaceAccountID]); - const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && (!cardFeeds || (!!defaultFeed?.isLoading && isEmptyObject(cardsList))); + const {isOffline} = useNetwork({ + onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID), + }); + const isLoading = !isOffline && (!cardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata))); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); useEffect(() => { - fetchCompanyCards(); - }, [fetchCompanyCards]); + openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); + }, [policyID, domainOrWorkspaceAccountID]); useEffect(() => { if (isLoading || !feed || isPending) { @@ -113,7 +112,6 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { {isFeedAdded && !isPending && (