Skip to content
167 changes: 139 additions & 28 deletions src/Components/SavedMethods.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
module SavedCardsElement = {
@react.component
let make = (
~cardsArr: array<PaymentType.customerMethods>,
~setPaymentToken,
~paymentTokenVal,
~savedCardlength,
~cvcProps,
~setRequiredFieldsBody,
) => {
open CardUtils

cardsArr
->Array.mapWithIndex((obj, i) =>
<SavedCardItem
key={i->Int.toString}
setPaymentToken
isActive={paymentTokenVal == obj.paymentToken}
paymentItem=obj
brandIcon={obj->getPaymentMethodBrand}
index=i
savedCardlength
cvcProps
setRequiredFieldsBody
/>
)
->React.array
}
}

module ClickToPayElement = {
@react.component
let make = (
~savedMethods,
~isClickToPayAuthenticateError,
~setIsClickToPayAuthenticateError,
~setPaymentToken,
~paymentTokenVal,
~cvcProps,
~getVisaCards,
~setIsClickToPayRememberMe,
~closeComponentIfSavedMethodsAreEmpty,
) => {
let clickToPayConfig = Recoil.useRecoilValueFromAtom(RecoilAtoms.clickToPayConfig)
let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)

<RenderIf condition={clickToPayConfig.isReady == Some(true)}>
<ClickToPayAuthenticate
loggerState
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</RenderIf>
}
}

@react.component
let make = (
~paymentToken: RecoilAtomTypes.paymentToken,
Expand Down Expand Up @@ -37,9 +100,12 @@ let make = (
loggerState.setLogError(~value=message, ~eventName=INVALID_FORMAT)
}
let (isSaveCardsChecked, setIsSaveCardsChecked) = React.useState(_ => false)
let {displaySavedPaymentMethodsCheckbox, readOnly} = Recoil.useRecoilValueFromAtom(
let (showMore, setShowMore) = React.useState(_ => true)
let {displaySavedPaymentMethodsCheckbox, readOnly, layout} = Recoil.useRecoilValueFromAtom(
RecoilAtoms.optionAtom,
)
let layoutClass = CardUtils.getLayoutClass(layout)
let displayMergedSavedMethods = layoutClass.savedMethodsLayout.displayMergedSavedMethods
let isGuestCustomer = useIsGuestCustomer()

let {iframeId, clientSecret} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
Expand All @@ -66,38 +132,72 @@ let make = (
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)
let {paymentToken: paymentTokenVal, customerId} = paymentToken

let bottomElement = {
let (cardOptionDetails, dropDownOptionsDetails) = React.useMemo(() => {
(
savedMethods->Array.slice(~start=0, ~end=layoutClass.savedMethodsLayout.maxSavedItems),
savedMethods->Array.sliceToEnd(~start=layoutClass.savedMethodsLayout.maxSavedItems),
)
}, (savedMethods, layoutClass))

let mergedViewBottomElement = {
<div
className="PickerItemContainer" tabIndex={0} role="region" ariaLabel="Saved payment methods">
{savedMethods
->Array.mapWithIndex((obj, i) =>
<SavedCardItem
key={i->Int.toString}
<SavedCardsElement
cardsArr=cardOptionDetails
setPaymentToken
paymentTokenVal
savedCardlength
cvcProps
setRequiredFieldsBody
/>
<RenderIf condition={!showMore}>
<SavedCardsElement
cardsArr=dropDownOptionsDetails
setPaymentToken
isActive={paymentTokenVal == obj.paymentToken}
paymentItem=obj
brandIcon={obj->getPaymentMethodBrand}
index=i
paymentTokenVal
savedCardlength
cvcProps
setRequiredFieldsBody
/>
)
->React.array}
<RenderIf condition={clickToPayConfig.isReady == Some(true)}>
<ClickToPayAuthenticate
loggerState
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
</RenderIf>
<ClickToPayElement
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</div>
}

let bottomElement = {
<div
className="PickerItemContainer" tabIndex={0} role="region" ariaLabel="Saved payment methods">
<RenderIf condition={!displayMergedSavedMethods}>
<SavedCardsElement
cardsArr=savedMethods
setPaymentToken
paymentTokenVal
savedCardlength
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
setRequiredFieldsBody
/>
</RenderIf>
<ClickToPayElement
savedMethods
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
setPaymentToken
paymentTokenVal
cvcProps
getVisaCards
setIsClickToPayRememberMe
closeComponentIfSavedMethodsAreEmpty
/>
</div>
}

Expand Down Expand Up @@ -336,15 +436,23 @@ let make = (
let enableSavedPaymentShimmer = React.useMemo(() => {
savedCardlength === 0 &&
!showPaymentMethodsScreen &&
(loadSavedCards === PaymentType.LoadingSavedCards || clickToPayConfig.isReady->Option.isNone)
}, (savedCardlength, loadSavedCards, showPaymentMethodsScreen, clickToPayConfig.isReady))
(loadSavedCards === PaymentType.LoadingSavedCards || clickToPayConfig.isReady->Option.isNone) &&
!displayMergedSavedMethods
}, (
savedCardlength,
loadSavedCards,
showPaymentMethodsScreen,
clickToPayConfig.isReady,
displayMergedSavedMethods,
))
Comment on lines 436 to +447
Copy link
Contributor

Choose a reason for hiding this comment

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

Try to simplify this condition for better readability.


<div className="flex flex-col overflow-auto h-auto no-scrollbar animate-slowShow">
{if enableSavedPaymentShimmer {
<RenderIf condition={enableSavedPaymentShimmer}>
<PaymentElementShimmer.SavedPaymentCardShimmer />
} else {
<RenderIf condition={!showPaymentMethodsScreen}> {bottomElement} </RenderIf>
}}
</RenderIf>
<RenderIf condition={!enableSavedPaymentShimmer}>
{displayMergedSavedMethods ? mergedViewBottomElement : bottomElement}
</RenderIf>
<RenderIf condition={conditionsForShowingSaveCardCheckbox}>
<div className="pt-4 pb-2 flex items-center justify-start">
<SaveDetailsCheckbox isChecked=isSaveCardsChecked setIsChecked=setIsSaveCardsChecked />
Expand All @@ -360,7 +468,7 @@ let make = (
}
/>
</RenderIf>
<RenderIf condition={!enableSavedPaymentShimmer}>
<RenderIf condition={!enableSavedPaymentShimmer && !displayMergedSavedMethods}>
<div
className="Label flex flex-row gap-3 items-end cursor-pointer mt-4"
style={
Expand All @@ -386,5 +494,8 @@ let make = (
{React.string(localeString.morePaymentMethods)}
</div>
</RenderIf>
<RenderIf condition={displayMergedSavedMethods && dropDownOptionsDetails->Array.length > 0}>
<ShowMoreToggle showMore setShowMore />
</RenderIf>
</div>
}
16 changes: 16 additions & 0 deletions src/Components/ShowMoreToggle.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@react.component
let make = (~showMore, ~setShowMore) => {
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)

<div
className="Label flex flex-row gap-1 items-end cursor-pointer mt-3 text-[14px] font-medium float-left w-fit"
style={
color: themeObj.colorPrimary,
}
onClick={_ => setShowMore(_ => !showMore)}>
{showMore ? React.string("Show more") : React.string("Show Less")}
<div className="m-1">
{showMore ? <Icon name="arrow-down" size=10 /> : <Icon name="arrow-up" size=10 />}
</div>
</div>
}
61 changes: 51 additions & 10 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}, (clickToPayConfig, isClickToPayAuthenticateError))

let layoutClass = CardUtils.getLayoutClass(layout)
let displayMergedSavedMethods = layoutClass.savedMethodsLayout.displayMergedSavedMethods

let (getVisaCards, closeComponentIfSavedMethodsAreEmpty) = ClickToPayHook.useClickToPay(
~areClickToPayUIScriptsLoaded,
Expand Down Expand Up @@ -278,6 +279,9 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}, [selectedOption])
useSubmitPaymentData(submitCallback)
React.useEffect(() => {
if displayMergedSavedMethods {
setShowPaymentMethodsScreen(_ => selectedOption != "saved_methods")
}
setSelectedOption(prev =>
selectedOption !== ""
? prev
Expand Down Expand Up @@ -402,6 +406,19 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</RenderIf>
}}
</SessionPaymentWrapper>
| SavedMethods =>
<SavedMethods
paymentToken
setPaymentToken
savedMethods
loadSavedCards
cvcProps
sessions
isClickToPayAuthenticateError
setIsClickToPayAuthenticateError
getVisaCards
closeComponentIfSavedMethodsAreEmpty
/>
| _ =>
<ReusableReactSuspense loaderComponent={loader()} componentName="PaymentMethodsWrapperLazy">
<PaymentMethodsWrapperLazy paymentMethodName=selectedOption />
Expand Down Expand Up @@ -440,6 +457,32 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
None
}, (paymentMethodList, customerPaymentMethods))

let enablePaymentElementShimmer = React.useMemo(() => {
(!displaySavedPaymentMethods ||
(displayMergedSavedMethods && savedMethods->Array.length == 0)) &&
paymentOptions->Array.length == 0 &&
walletOptions->Array.length == 0
}, (
displaySavedPaymentMethods,
displayMergedSavedMethods,
savedMethods,
paymentOptions,
walletOptions,
))

let isShowUseExistingPaymentMethods = React.useMemo(() => {
((displaySavedPaymentMethods && savedMethods->Array.length > 0) ||
isShowPaymentMethodsDependingOnClickToPay) &&
showPaymentMethodsScreen &&
!displayMergedSavedMethods
}, (
displaySavedPaymentMethods,
isShowPaymentMethodsDependingOnClickToPay,
displayMergedSavedMethods,
savedMethods,
showPaymentMethodsScreen,
))

<>
<RenderIf condition={paymentLabel->Option.isSome}>
<div
Expand All @@ -450,15 +493,18 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
</div>
</RenderIf>
{if clickToPayConfig.isReady->Option.isNone {
if areClickToPayUIScriptsLoaded {
if displayMergedSavedMethods {
<PaymentElementShimmer />
} else if areClickToPayUIScriptsLoaded {
<ClickToPayHelpers.SrcLoader />
} else {
<PaymentElementShimmer.SavedPaymentCardShimmer />
}
} else {
<RenderIf
condition={!showPaymentMethodsScreen &&
(displaySavedPaymentMethods || isShowPaymentMethodsDependingOnClickToPay)}>
(displaySavedPaymentMethods || isShowPaymentMethodsDependingOnClickToPay) &&
!displayMergedSavedMethods}>
<SavedMethods
paymentToken
setPaymentToken
Expand All @@ -475,7 +521,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}}
<RenderIf
condition={(paymentOptions->Array.length > 0 || walletOptions->Array.length > 0) &&
showPaymentMethodsScreen &&
(showPaymentMethodsScreen || displayMergedSavedMethods) &&
clickToPayConfig.isReady->Option.isSome}>
<div
className="flex flex-col place-items-center"
Expand Down Expand Up @@ -508,9 +554,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
}}
</div>
</RenderIf>
<RenderIf
condition={((displaySavedPaymentMethods && savedMethods->Array.length > 0) ||
isShowPaymentMethodsDependingOnClickToPay) && showPaymentMethodsScreen}>
<RenderIf condition=isShowUseExistingPaymentMethods>
<div
className="Label flex flex-row gap-3 items-end cursor-pointer mt-4"
style={
Expand Down Expand Up @@ -541,10 +585,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
<ErrorBoundary.ErrorTextAndImage divRef level={Top} />
</RenderIf>
| _ =>
<RenderIf
condition={!displaySavedPaymentMethods &&
paymentOptions->Array.length == 0 &&
walletOptions->Array.length == 0}>
<RenderIf condition=enablePaymentElementShimmer>
<PaymentElementShimmer />
</RenderIf>
}}
Expand Down
1 change: 1 addition & 0 deletions src/PaymentElementV2.res
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
| Boleto
| PayPal
| EFT
| SavedMethods
| Unknown => React.null
}}
</ErrorBoundary>
Expand Down
7 changes: 7 additions & 0 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ let getPaymentMethodsFields = (~localeString: LocaleStringTypes.localeStrings) =
displayName: localeString.payment_methods_card,
miniIcon: None,
},
{
paymentMethodName: "saved_methods",
icon: Some(icon("default-card", ~size=19)),
fields: [],
displayName: "Saved",
miniIcon: None,
},
{
paymentMethodName: "klarna",
icon: Some(icon("klarna", ~size=19)),
Expand Down
Loading
Loading