Skip to content

Commit

Permalink
Checkout: Convert useCreateExistingCards to TypeScript (#50723)
Browse files Browse the repository at this point in the history
* Move useCreatePaymentMethods to a directory

* Update all imports to use-create-payment-methods

* Move useMemoCompare hook to own file

* Update PaymentMethod type to make nodes optional

They are already optional

* Move useCreateExistingCards to own file

* Fix payment-methods imports

* Only pass string to activePayButtonText
  • Loading branch information
sirbrillig authored Mar 3, 2021
1 parent e47c0eb commit f4baf0e
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 70 deletions.
2 changes: 1 addition & 1 deletion client/me/purchases/add-new-payment-method/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import Column from 'calypso/components/layout/column';
import PaymentMethodSidebar from 'calypso/me/purchases/components/payment-method-sidebar';
import { isEnabled } from '@automattic/calypso-config';
import PaymentMethodSelector from 'calypso/me/purchases/manage-purchase/payment-method-selector';
import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods';
import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods';
import PaymentMethodLoader from 'calypso/me/purchases/components/payment-method-loader';

function AddNewPaymentMethod() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
useCreateCreditCard,
useCreateExistingCards,
useCreatePayPal,
} from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods';
} from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods';
import { translateCheckoutPaymentMethodToWpcomPaymentMethod } from 'calypso/my-sites/checkout/composite-checkout/lib/translate-payment-method-names';
import doesValueExist from 'calypso/my-sites/checkout/composite-checkout/lib/does-value-exist';
import useFetchAvailablePaymentMethods from './use-fetch-available-payment-methods';
Expand Down Expand Up @@ -52,7 +52,7 @@ export default function useCreateAssignablePaymentMethods(
const storedCards = useSelector( getStoredCards );
const existingCardMethods = useCreateExistingCards( {
storedCards,
activePayButtonText: translate( 'Use this card' ),
activePayButtonText: String( translate( 'Use this card' ) ),
} );

const paymentMethods = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import useIsApplePayAvailable from './hooks/use-is-apple-pay-available';
import filterAppropriatePaymentMethods from './lib/filter-appropriate-payment-methods';
import useStoredCards from './hooks/use-stored-cards';
import usePrepareProductsForCart from './hooks/use-prepare-products-for-cart';
import useCreatePaymentMethods from './use-create-payment-methods';
import useCreatePaymentMethods from './hooks/use-create-payment-methods';
import {
applePayProcessor,
freePurchaseProcessor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { useMemo, useRef, useEffect } from 'react';
import { useMemo } from 'react';
import {
createPayPalMethod,
createAlipayMethod,
Expand All @@ -19,33 +19,35 @@ import {
createEpsMethod,
createEpsPaymentMethodStore,
createApplePayMethod,
createExistingCardMethod,
} from '@automattic/composite-checkout';
import { useShoppingCart } from '@automattic/shopping-cart';

/**
* Internal dependencies
*/
import { createWeChatMethod, createWeChatPaymentMethodStore } from './payment-methods/wechat';
import { createWeChatMethod, createWeChatPaymentMethodStore } from '../../payment-methods/wechat';
import {
createCreditCardPaymentMethodStore,
createCreditCardMethod,
} from './payment-methods/credit-card';
} from '../../payment-methods/credit-card';
import {
createEbanxTefPaymentMethodStore,
createEbanxTefMethod,
} from './payment-methods/ebanx-tef';
} from '../../payment-methods/ebanx-tef';
import {
createIdWalletPaymentMethodStore,
createIdWalletMethod,
} from './payment-methods/id-wallet';
} from '../../payment-methods/id-wallet';
import {
createNetBankingPaymentMethodStore,
createNetBankingMethod,
} from './payment-methods/netbanking';
import { createFullCreditsMethod } from './payment-methods/full-credits';
import { createFreePaymentMethod } from './payment-methods/free-purchase';
} from '../../payment-methods/netbanking';
import { createFullCreditsMethod } from '../../payment-methods/full-credits';
import { createFreePaymentMethod } from '../../payment-methods/free-purchase';
import { translateCheckoutPaymentMethodToWpcomPaymentMethod } from 'calypso/my-sites/checkout/composite-checkout/lib/translate-payment-method-names';
import useCreateExistingCards from './use-create-existing-cards';

export { useCreateExistingCards };

export function useCreatePayPal( { labelText = null } = {} ) {
const paypalMethod = useMemo( () => createPayPalMethod( { labelText } ), [ labelText ] );
Expand Down Expand Up @@ -288,59 +290,6 @@ function useCreateApplePay( {
return applePayMethod;
}

// See https://usehooks.com/useMemoCompare/
function useMemoCompare( next, compare ) {
// Ref for storing previous value
const previousRef = useRef();
const previous = previousRef.current;

// Pass previous and next value to compare function
// to determine whether to consider them equal.
const isEqual = compare( previous, next );

// If not equal update previousRef to next value.
// We only update if not equal so that this hook continues to return
// the same old value if compare keeps returning true.
useEffect( () => {
if ( ! isEqual ) {
previousRef.current = next;
}
} );

// Finally, if equal then return the previous value
return isEqual ? previous : next;
}

export function useCreateExistingCards( { storedCards, activePayButtonText = undefined } ) {
// Memoize the cards by comparing their stored_details_id values, in case the
// objects are recreated on each render.
const memoizedStoredCards = useMemoCompare( storedCards, ( prev, next ) => {
const prevIds = prev?.map( ( card ) => card.stored_details_id ) ?? [];
const nextIds = next?.map( ( card ) => card.stored_details_id ) ?? [];
return (
prevIds.length === nextIds.length && prevIds.every( ( id, index ) => id === nextIds[ index ] )
);
} );
const existingCardMethods = useMemo( () => {
return (
memoizedStoredCards?.map( ( storedDetails ) =>
createExistingCardMethod( {
id: `existingCard-${ storedDetails.stored_details_id }`,
cardholderName: storedDetails.name,
cardExpiry: storedDetails.expiry,
brand: storedDetails.card_type,
last4: storedDetails.card,
storedDetailsId: storedDetails.stored_details_id,
paymentMethodToken: storedDetails.mp_ref,
paymentPartnerProcessorId: storedDetails.payment_partner,
activePayButtonText,
} )
) ?? []
);
}, [ memoizedStoredCards, activePayButtonText ] );
return existingCardMethods;
}

export default function useCreatePaymentMethods( {
isStripeLoading,
stripeLoadingError,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* External dependencies
*/
import { useMemo } from 'react';
import { createExistingCardMethod } from '@automattic/composite-checkout';
import type { PaymentMethod } from '@automattic/composite-checkout';

/**
* Internal dependencies
*/
import useMemoCompare from '../use-memo-compare';
import type { StoredCard } from '../../types/stored-cards';

export default function useCreateExistingCards( {
storedCards,
activePayButtonText = undefined,
}: {
storedCards: StoredCard[];
activePayButtonText?: string;
} ): PaymentMethod[] {
// Memoize the cards by comparing their stored_details_id values, in case the
// objects themselves are recreated on each render.
const memoizedStoredCards: StoredCard[] | undefined = useMemoCompare(
storedCards,
( prev: undefined | StoredCard[], next: undefined | StoredCard[] ) => {
const prevIds = prev?.map( ( card ) => card.stored_details_id ) ?? [];
const nextIds = next?.map( ( card ) => card.stored_details_id ) ?? [];
return (
prevIds.length === nextIds.length &&
prevIds.every( ( id, index ) => id === nextIds[ index ] )
);
}
);

const existingCardMethods = useMemo( () => {
return (
memoizedStoredCards?.map( ( storedDetails ) =>
createExistingCardMethod( {
id: `existingCard-${ storedDetails.stored_details_id }`,
cardholderName: storedDetails.name,
cardExpiry: storedDetails.expiry,
brand: storedDetails.card_type,
last4: storedDetails.card,
storedDetailsId: storedDetails.stored_details_id,
paymentMethodToken: storedDetails.mp_ref,
paymentPartnerProcessorId: storedDetails.payment_partner,
activePayButtonText,
} )
) ?? []
);
}, [ memoizedStoredCards, activePayButtonText ] );
return existingCardMethods;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useRef, useEffect } from 'react';

// See https://usehooks.com/useMemoCompare/
export default function useMemoCompare< A, B >(
next: B,
compare: ( previous: A | B | undefined, next: B ) => boolean
): A | B | undefined {
// Ref for storing previous value
const previousRef = useRef< undefined | A | B >();
const previous = previousRef.current;

// Pass previous and next value to compare function
// to determine whether to consider them equal.
const isEqual = compare( previous, next );

// If not equal update previousRef to next value.
// We only update if not equal so that this hook continues to return
// the same old value if compare keeps returning true.
useEffect( () => {
if ( ! isEqual ) {
previousRef.current = next;
}
} );

// Finally, if equal then return the previous value
return isEqual ? previous : next;
}
2 changes: 1 addition & 1 deletion client/my-sites/purchases/payment-methods/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Layout from 'calypso/components/layout';
import Column from 'calypso/components/layout/column';
import PaymentMethodSidebar from 'calypso/me/purchases/components/payment-method-sidebar';
import PaymentMethodSelector from 'calypso/me/purchases/manage-purchase/payment-method-selector';
import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods';
import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods';
import PaymentMethodLoader from 'calypso/me/purchases/components/payment-method-loader';
import doesValueExist from 'calypso/my-sites/checkout/composite-checkout/lib/does-value-exist';

Expand Down
6 changes: 3 additions & 3 deletions packages/composite-checkout/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export interface OrderSummaryData {

export interface PaymentMethod {
id: string;
label: React.ReactNode;
activeContent: React.ReactNode;
inactiveContent: React.ReactNode;
label?: React.ReactNode;
activeContent?: React.ReactNode;
inactiveContent?: React.ReactNode;
submitButton: ReactElement;
getAriaLabel: ( localize: ( value: string ) => string ) => string;
}
Expand Down

0 comments on commit f4baf0e

Please sign in to comment.