Skip to content
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

Checkout: Convert useCreateExistingCards to TypeScript #50723

Merged
merged 7 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -50,7 +50,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 >(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems general purpose enough to warrant being put in a less specific place, but I'm not sure where that would be.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, same. There's a few things in this directory and lib that fall into a general utility category but I figure we can always move them later.

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