Skip to content

Commit ee9ae8b

Browse files
authored
Merge pull request Expensify#59887 from callstack-internal/feat/59364-expensify-feed-selector
[Internal QA] Expensify Card feed selector
2 parents ea14b5b + aca943d commit ee9ae8b

24 files changed

Lines changed: 352 additions & 125 deletions

src/ONYXKEYS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,9 @@ const ONYXKEYS = {
590590
/** Currently displaying feed */
591591
LAST_SELECTED_FEED: 'lastSelectedFeed_',
592592

593+
/** Currently displaying Expensify Card feed */
594+
LAST_SELECTED_EXPENSIFY_CARD_FEED: 'lastSelectedExpensifyCardFeed_',
595+
593596
/** Whether the bank account chosen for Expensify Card in on verification waitlist */
594597
NVP_EXPENSIFY_ON_CARD_WAITLIST: 'nvp_expensify_onCardWaitlist_',
595598

@@ -947,6 +950,7 @@ type OnyxCollectionValuesMapping = {
947950
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName;
948951
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean;
949952
[ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CompanyCardFeed;
953+
[ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED]: OnyxTypes.FundID;
950954
[ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST]: OnyxTypes.CardOnWaitlist;
951955
[ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD]: OnyxTypes.IssueNewCard;
952956
};

src/ROUTES.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,10 @@ const ROUTES = {
16531653
route: 'settings/workspaces/:policyID/expensify-card/settings/account',
16541654
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/settings/account`, backTo),
16551655
},
1656+
WORKSPACE_EXPENSIFY_CARD_SELECT_FEED: {
1657+
route: 'settings/workspaces/:policyID/expensify-card/select-feed',
1658+
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/select-feed`, backTo),
1659+
},
16561660
WORKSPACE_EXPENSIFY_CARD_SETTINGS_FREQUENCY: {
16571661
route: 'settings/workspaces/:policyID/expensify-card/settings/frequency',
16581662
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings/frequency` as const,

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ const SCREENS = {
507507
EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit',
508508
EXPENSIFY_CARD_ISSUE_NEW: 'Workspace_ExpensifyCard_New',
509509
EXPENSIFY_CARD_NAME: 'Workspace_ExpensifyCard_Name',
510+
EXPENSIFY_CARD_SELECT_FEED: 'Workspace_ExpensifyCard_Select_Feed',
510511
EXPENSIFY_CARD_LIMIT_TYPE: 'Workspace_ExpensifyCard_LimitType',
511512
EXPENSIFY_CARD_BANK_ACCOUNT: 'Workspace_ExpensifyCard_BankAccount',
512513
EXPENSIFY_CARD_SETTINGS: 'Workspace_ExpensifyCard_Settings',

src/components/FeedSelector.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import useTheme from '@hooks/useTheme';
4+
import useThemeStyles from '@hooks/useThemeStyles';
5+
import variables from '@styles/variables';
6+
import type IconAsset from '@src/types/utils/IconAsset';
7+
import CaretWrapper from './CaretWrapper';
8+
import Icon from './Icon';
9+
import * as Expensicons from './Icon/Expensicons';
10+
import {PressableWithFeedback} from './Pressable';
11+
import Text from './Text';
12+
13+
type Props = {
14+
/** Function to call when the feed is selected */
15+
onFeedSelect: () => void;
16+
17+
/** Icon for the card */
18+
cardIcon: IconAsset;
19+
20+
/** Whether to show assign card button */
21+
shouldChangeLayout?: boolean;
22+
23+
/** Feed name */
24+
feedName?: string;
25+
26+
/** Supporting text */
27+
supportingText?: string;
28+
29+
/** Whether the RBR indicator should be shown */
30+
shouldShowRBR?: boolean;
31+
};
32+
33+
function FeedSelector({onFeedSelect, cardIcon, shouldChangeLayout, feedName, supportingText, shouldShowRBR = false}: Props) {
34+
const styles = useThemeStyles();
35+
const theme = useTheme();
36+
37+
return (
38+
<PressableWithFeedback
39+
onPress={onFeedSelect}
40+
style={[styles.flexRow, styles.alignItemsCenter, styles.gap3, shouldChangeLayout && styles.mb3]}
41+
accessibilityLabel={feedName ?? ''}
42+
>
43+
<Icon
44+
src={cardIcon}
45+
height={variables.cardIconHeight}
46+
width={variables.cardIconWidth}
47+
additionalStyles={styles.cardIcon}
48+
/>
49+
<View style={styles.flex1}>
50+
<View style={[styles.flexRow, styles.gap1]}>
51+
<CaretWrapper style={styles.flex1}>
52+
<Text style={[styles.textStrong, styles.flexShrink1]}>{feedName}</Text>
53+
</CaretWrapper>
54+
{shouldShowRBR && (
55+
<Icon
56+
src={Expensicons.DotIndicator}
57+
fill={theme.danger}
58+
/>
59+
)}
60+
</View>
61+
<Text style={styles.textLabelSupporting}>{supportingText}</Text>
62+
</View>
63+
</PressableWithFeedback>
64+
);
65+
}
66+
67+
export default FeedSelector;

src/hooks/useDefaultFundID.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {useOnyx} from 'react-native-onyx';
2+
import {getFundIdFromSettingsKey} from '@libs/CardUtils';
3+
import CONST from '@src/CONST';
4+
import ONYXKEYS from '@src/ONYXKEYS';
5+
import useWorkspaceAccountID from './useWorkspaceAccountID';
6+
7+
/**
8+
* Hook to get the default fundID for a given policyID. This is used to get the settings and cards for each of the feeds.
9+
* It will always return lastSelectedExpensifyCardFeed if it exists or fallback to the workspaceAccountID or domainFundID.
10+
*/
11+
function useDefaultFundID(policyID: string | undefined) {
12+
const workspaceAccountID = useWorkspaceAccountID(policyID);
13+
const [lastSelectedExpensifyCardFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED}${policyID}`);
14+
15+
const [domainFundID] = useOnyx(ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS, {
16+
selector: (cardSettings) => {
17+
const matchingKey = Object.entries(cardSettings ?? {}).find(
18+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
19+
([key, settings]) => settings?.preferredPolicy && settings.preferredPolicy === policyID && !key.includes(workspaceAccountID.toString()),
20+
);
21+
22+
return getFundIdFromSettingsKey(matchingKey?.[0] ?? '');
23+
},
24+
});
25+
26+
if (lastSelectedExpensifyCardFeed) {
27+
return lastSelectedExpensifyCardFeed;
28+
}
29+
30+
if (workspaceAccountID) {
31+
return workspaceAccountID;
32+
}
33+
34+
return domainFundID ?? CONST.DEFAULT_NUMBER_ID;
35+
}
36+
37+
export default useDefaultFundID;

src/hooks/useDomainFundID.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/hooks/useExpensifyCardFeeds.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {useOnyx} from 'react-native-onyx';
2+
import ONYXKEYS from '@src/ONYXKEYS';
3+
4+
function useExpensifyCardFeeds(policyID: string | undefined) {
5+
const [allExpensifyCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS, {
6+
selector: (cardSettings) => {
7+
const matchingEntries = Object.entries(cardSettings ?? {}).filter(
8+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
9+
([_, settings]) => settings?.preferredPolicy && settings.preferredPolicy === policyID,
10+
);
11+
12+
return Object.fromEntries(matchingEntries);
13+
},
14+
});
15+
16+
return allExpensifyCardFeeds;
17+
}
18+
19+
export default useExpensifyCardFeeds;

src/libs/CardUtils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,17 @@ function isExpensifyCardFullySetUp(policy?: OnyxEntry<Policy>, cardSettings?: On
597597
return !!(policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID);
598598
}
599599

600+
function getFundIdFromSettingsKey(key: string) {
601+
const prefix = ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS;
602+
if (!key?.startsWith(prefix)) {
603+
return CONST.DEFAULT_NUMBER_ID;
604+
}
605+
const fundIDStr = key.substring(prefix.length);
606+
607+
const fundID = Number(fundIDStr);
608+
return Number.isNaN(fundID) ? CONST.DEFAULT_NUMBER_ID : fundID;
609+
}
610+
600611
export {
601612
isExpensifyCard,
602613
isCorporateCard,
@@ -641,4 +652,5 @@ export {
641652
hasCardListObject,
642653
isExpensifyCardFullySetUp,
643654
filterInactiveCards,
655+
getFundIdFromSettingsKey,
644656
};

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
567567
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default,
568568
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default,
569569
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_FREQUENCY]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage').default,
570+
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SELECT_FEED]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardSelectorPage').default,
570571
[SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts').default,
571572
[SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage').default,
572573
[SCREENS.WORKSPACE.EXPENSIFY_CARD_NAME]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceEditCardNamePage').default,

src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ const WORKSPACE_TO_RHP: Partial<Record<keyof WorkspaceSplitNavigatorParamList, s
239239
SCREENS.WORKSPACE.EXPENSIFY_CARD_NAME,
240240
SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT,
241241
SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE,
242+
SCREENS.WORKSPACE.EXPENSIFY_CARD_SELECT_FEED,
242243
],
243244
[SCREENS.WORKSPACE.RULES]: [
244245
SCREENS.WORKSPACE.RULES_CUSTOM_NAME,

0 commit comments

Comments
 (0)