-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[Home Page] Time Sensitive - Add Card Setup Items #80975
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ac997db
82b82f4
92fde3d
ecbf6ac
8e89d27
5361d7e
5d48ae8
c3bad00
f0493a6
43706bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import {useMemo} from 'react'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
| import {isCard, isCardPendingActivate, isCardPendingIssue} from '@libs/CardUtils'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type {Card} from '@src/types/onyx'; | ||
|
|
||
| function useTimeSensitiveCards() { | ||
| const [cardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true}); | ||
|
|
||
| const {cardsNeedingShippingAddress, cardsNeedingActivation} = useMemo<{cardsNeedingShippingAddress: Card[]; cardsNeedingActivation: Card[]}>(() => { | ||
| const cards = Object.values(cardList ?? {}).filter(isCard); | ||
| const isPhysicalExpensifyCard = (card: Card) => card.bank === CONST.EXPENSIFY_CARD.BANK && !card.nameValuePairs?.isVirtual; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor issue |
||
|
|
||
| return cards.reduce<{cardsNeedingShippingAddress: Card[]; cardsNeedingActivation: Card[]}>( | ||
| (acc, card) => { | ||
| if (!isPhysicalExpensifyCard(card)) { | ||
| return acc; | ||
| } | ||
|
|
||
| if (isCardPendingIssue(card)) { | ||
| acc.cardsNeedingShippingAddress.push(card); | ||
| } | ||
|
|
||
| if (isCardPendingActivate(card)) { | ||
| acc.cardsNeedingActivation.push(card); | ||
| } | ||
|
|
||
| return acc; | ||
| }, | ||
| {cardsNeedingShippingAddress: [], cardsNeedingActivation: []}, | ||
| ); | ||
| }, [cardList]); | ||
|
|
||
| const shouldShowAddShippingAddress = cardsNeedingShippingAddress.length > 0; | ||
| const shouldShowActivateCard = cardsNeedingActivation.length > 0; | ||
|
|
||
| return { | ||
| shouldShowAddShippingAddress, | ||
| shouldShowActivateCard, | ||
| cardsNeedingShippingAddress, | ||
| cardsNeedingActivation, | ||
| }; | ||
| } | ||
|
|
||
| export default useTimeSensitiveCards; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
| import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; | ||
| import {getEarlyDiscountInfo, shouldShowDiscountBanner} from '@libs/SubscriptionUtils'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
|
|
||
| function useTimeSensitiveOffers() { | ||
| const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL, {canBeMissing: true}); | ||
| const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL, {canBeMissing: true}); | ||
| const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID, {canBeMissing: true}); | ||
| const hasTeam2025Pricing = useHasTeam2025Pricing(); | ||
| const subscriptionPlan = useSubscriptionPlan(); | ||
|
|
||
| // Use the same logic as the subscription page to determine if discount banner should be shown | ||
| const shouldShowDiscount = shouldShowDiscountBanner(hasTeam2025Pricing, subscriptionPlan, firstDayFreeTrial, lastDayFreeTrial, userBillingFundID); | ||
| const discountInfo = getEarlyDiscountInfo(firstDayFreeTrial); | ||
|
|
||
| // Determine which offer to show based on discount type (they are mutually exclusive) | ||
| const shouldShow50off = shouldShowDiscount && discountInfo?.discountType === 50; | ||
| const shouldShow25off = shouldShowDiscount && discountInfo?.discountType === 25; | ||
|
|
||
| return { | ||
| shouldShow50off, | ||
| shouldShow25off, | ||
| firstDayFreeTrial, | ||
| discountInfo, | ||
| }; | ||
| } | ||
|
|
||
| export default useTimeSensitiveOffers; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import React from 'react'; | ||
| import ExpensifyCardIcon from '@assets/images/expensify-card-icon.svg'; | ||
| import BaseWidgetItem from '@components/BaseWidgetItem'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useTheme from '@hooks/useTheme'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import ROUTES from '@src/ROUTES'; | ||
| import type {Card} from '@src/types/onyx'; | ||
|
|
||
| type ActivateCardProps = { | ||
| card: Card; | ||
| }; | ||
|
|
||
| function ActivateCard({card}: ActivateCardProps) { | ||
adamgrzybowski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const theme = useTheme(); | ||
| const {translate} = useLocalize(); | ||
|
|
||
| return ( | ||
| <BaseWidgetItem | ||
| icon={ExpensifyCardIcon} | ||
| iconBackgroundColor={theme.widgetIconBG} | ||
| iconFill={theme.widgetIconFill} | ||
| title={translate('homePage.timeSensitiveSection.activateCard.title')} | ||
| subtitle={translate('homePage.timeSensitiveSection.activateCard.subtitle')} | ||
| ctaText={translate('homePage.timeSensitiveSection.activateCard.cta')} | ||
| onCtaPress={() => Navigation.navigate(ROUTES.SETTINGS_WALLET_CARD_ACTIVATE.getRoute(String(card.cardID)))} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export default ActivateCard; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import React from 'react'; | ||
| import ExpensifyCardIcon from '@assets/images/expensify-card-icon.svg'; | ||
| import BaseWidgetItem from '@components/BaseWidgetItem'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useTheme from '@hooks/useTheme'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import ROUTES from '@src/ROUTES'; | ||
| import type {Card} from '@src/types/onyx'; | ||
|
|
||
| type AddShippingAddressProps = { | ||
| card: Card; | ||
| }; | ||
|
|
||
| function AddShippingAddress({card}: AddShippingAddressProps) { | ||
| const theme = useTheme(); | ||
| const {translate} = useLocalize(); | ||
|
|
||
| return ( | ||
| <BaseWidgetItem | ||
| icon={ExpensifyCardIcon} | ||
| iconBackgroundColor={theme.widgetIconBG} | ||
| iconFill={theme.widgetIconFill} | ||
| title={translate('homePage.timeSensitiveSection.addShippingAddress.title')} | ||
| subtitle={translate('homePage.timeSensitiveSection.addShippingAddress.subtitle')} | ||
| ctaText={translate('homePage.timeSensitiveSection.addShippingAddress.cta')} | ||
| onCtaPress={() => Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAIN_CARD.getRoute(String(card.cardID)))} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export default AddShippingAddress; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,14 @@ const filterOutPersonalCards = (cards: OnyxEntry<CardList>): CardList => { | |
| return filterObject(cards ?? {}, (key, card) => !isPersonalCard(card)); | ||
| }; | ||
|
|
||
| /** | ||
| * Filter to keep only personal cards from the card list. | ||
| * Personal cards have fundID === '0' or no fundID. | ||
| */ | ||
| const filterPersonalCards = (cards: OnyxEntry<CardList>): CardList => { | ||
| return filterObject(cards ?? {}, (key, card) => isPersonalCard(card)); | ||
| }; | ||
|
Comment on lines
+31
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gonna remove this in my PR
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing here #81058 |
||
|
|
||
| /** | ||
| * Selects the Expensify Card feed from the card list and returns the first one. | ||
| */ | ||
|
|
@@ -41,4 +49,4 @@ const defaultExpensifyCardSelector = (allCards: OnyxEntry<NonPersonalAndWorkspac | |
| */ | ||
| const cardByIdSelector = (cardID: string) => (cardList: OnyxEntry<CardList>) => cardList?.[cardID]; | ||
|
|
||
| export {filterCardsHiddenFromSearch, filterOutPersonalCards, defaultExpensifyCardSelector, cardByIdSelector}; | ||
| export {filterCardsHiddenFromSearch, filterOutPersonalCards, filterPersonalCards, defaultExpensifyCardSelector, cardByIdSelector}; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will refactor this to a selector
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring here #81058