From f94c45d926ee52107897d0417ef9835ba2c399c7 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:47:05 -0400 Subject: [PATCH] [PAY-1951][PAY-1966] Improve the Stripe drawer experience on mobile (#6274) --- .../mobile/src/components/drawer/Drawer.tsx | 16 ++++- .../src/components/drawer/NativeDrawer.tsx | 11 +-- .../PremiumTrackPurchaseDrawer.tsx | 5 +- .../StripeOnrampDrawer.tsx | 67 +++++++++++++------ .../StripeOnrampEmbed.tsx | 42 +++++++++--- 5 files changed, 96 insertions(+), 45 deletions(-) diff --git a/packages/mobile/src/components/drawer/Drawer.tsx b/packages/mobile/src/components/drawer/Drawer.tsx index 5c91515e5f0..d85bcbe6f62 100644 --- a/packages/mobile/src/components/drawer/Drawer.tsx +++ b/packages/mobile/src/components/drawer/Drawer.tsx @@ -87,6 +87,11 @@ export enum DrawerAnimationStyle { } export type DrawerProps = { + /** Disable close behavior. Useful if the drawer is processing an operation that + * must complete before closing. Only supported for fullscreen drawers with gesture + * support disabled. + */ + blockClose?: boolean /** * Whether or not the drawer is open */ @@ -250,6 +255,7 @@ type DrawerComponent = { (props: Omit): React.ReactElement } export const Drawer: DrawerComponent = ({ + blockClose = false, isOpen, children, onClose, @@ -613,6 +619,12 @@ export const Drawer: DrawerComponent = ({ ] ) + const handlePressClose = useCallback(() => { + if (!blockClose) { + onClose() + } + }, [blockClose, onClose]) + // NOTE: sk - Need to interpolate the border radius bc of a funky // issue with border radius under 1 in ios const interpolatedBorderRadius = borderRadiusAnim.current.interpolate({ @@ -661,10 +673,10 @@ export const Drawer: DrawerComponent = ({ {...edgeProps} > {CustomDrawerHeader ? ( - + ) : ( & { - blockClose?: boolean drawerName: DrawerName } @@ -18,20 +17,14 @@ type NativeDrawerProps = SetOptional & { * opening and closing. */ export const NativeDrawer = (props: NativeDrawerProps) => { - const { - blockClose = false, - drawerName, - onClose: onCloseProp, - ...other - } = props + const { drawerName, onClose: onCloseProp, ...other } = props const { isOpen, onClose, onClosed, visibleState } = useDrawer(drawerName) const handleClose = useCallback(() => { - if (blockClose) return onCloseProp?.() onClose() - }, [blockClose, onCloseProp, onClose]) + }, [onCloseProp, onClose]) if (visibleState === false) return null diff --git a/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx b/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx index bb726951b58..21e9e1185e6 100644 --- a/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx +++ b/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx @@ -81,7 +81,7 @@ const useStyles = makeStyles(({ spacing, typography, palette }) => ({ paddingTop: spacing(4), paddingHorizontal: spacing(4), paddingBottom: spacing(6), - columnGap: spacing(4) + gap: spacing(4) }, headerContainer: { borderBottomWidth: 1, @@ -89,7 +89,8 @@ const useStyles = makeStyles(({ spacing, typography, palette }) => ({ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', - padding: spacing(4) + height: spacing(10), + paddingHorizontal: spacing(4) }, titleContainer: { ...flexRowCentered(), diff --git a/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampDrawer.tsx b/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampDrawer.tsx index 2a9426e7a47..e6514bc1025 100644 --- a/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampDrawer.tsx +++ b/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampDrawer.tsx @@ -1,27 +1,37 @@ import { useCallback } from 'react' -import { modalsActions, purchaseContentActions } from '@audius/common' -import { View } from 'react-native' -import { useDispatch } from 'react-redux' +import { stripeModalUIActions, stripeModalUISelectors } from '@audius/common' +import { TouchableOpacity, View } from 'react-native' +import { useDispatch, useSelector } from 'react-redux' -import IconRemove from 'app/assets/images/iconRemove.svg' +import IconCloseAlt from 'app/assets/images/iconCloseAlt.svg' import { makeStyles } from 'app/styles' import { spacing } from 'app/styles/spacing' -import { useColor } from 'app/utils/theme' +import { useThemeColors } from 'app/utils/theme' import { zIndex } from 'app/utils/zIndex' import { AppDrawer } from '../drawer/AppDrawer' import { StripeOnrampEmbed } from './StripeOnrampEmbed' -const { setVisibility } = modalsActions -const { cleanup } = purchaseContentActions +const { cancelStripeOnramp } = stripeModalUIActions +const { getStripeModalState } = stripeModalUISelectors export const MODAL_NAME = 'StripeOnRamp' -const useStyles = makeStyles(({ spacing }) => ({ - root: { - paddingTop: spacing(4) +const useStyles = makeStyles(({ spacing, palette }) => ({ + headerContainer: { + borderBottomWidth: 1, + borderBottomColor: palette.neutralLight8, + height: spacing(12), + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-start', + paddingHorizontal: spacing(4) + }, + contentContainer: { + paddingTop: spacing(6), + flex: 1 }, exitContainer: { justifyContent: 'flex-start', @@ -30,31 +40,44 @@ const useStyles = makeStyles(({ spacing }) => ({ } })) +const StripeOnrampDrawerHeader = ({ onClose }: { onClose: () => void }) => { + const styles = useStyles() + const { neutralLight4 } = useThemeColors() + return ( + + + + + + ) +} + export const StripeOnrampDrawer = () => { const styles = useStyles() - const neutralLight4 = useColor('neutralLight4') const dispatch = useDispatch() + const { stripeSessionStatus } = useSelector(getStripeModalState) const handleClose = useCallback(() => { - dispatch(setVisibility({ modal: MODAL_NAME, visible: 'closing' })) - dispatch(cleanup()) + dispatch(cancelStripeOnramp()) }, [dispatch]) return ( - - + + - ) } diff --git a/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampEmbed.tsx b/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampEmbed.tsx index ea242ddd15b..2fbb111685a 100644 --- a/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampEmbed.tsx +++ b/packages/mobile/src/components/stripe-onramp-drawer/StripeOnrampEmbed.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { stripeModalUISelectors, stripeModalUIActions } from '@audius/common' +import type { OnrampSessionResult } from '@stripe/crypto' import { View } from 'react-native' import { WebView } from 'react-native-webview' import { useDispatch, useSelector } from 'react-redux' @@ -19,10 +20,11 @@ const STRIPE_PUBLISHABLE_KEY = env.REACT_APP_STRIPE_CLIENT_PUBLISHABLE_KEY const useStyles = makeStyles(() => ({ root: { display: 'flex', - height: '85%' + flex: 1, + height: '100%' }, spinnerContainer: { - height: '85%', + height: '100%', justifyContent: 'center', alignItems: 'center' } @@ -36,10 +38,21 @@ export const StripeOnrampEmbed = () => { const handleSessionUpdate = useCallback( (event) => { - if (event?.payload?.session?.status) { - dispatch( - stripeSessionStatusChanged({ status: event.payload.session.status }) - ) + try { + const { status } = JSON.parse( + event.nativeEvent.data + ) as OnrampSessionResult + + if (status) { + if (status === 'error') { + console.error('Received Stripe session error') + dispatch(cancelStripeOnramp()) + } else { + dispatch(stripeSessionStatusChanged({ status })) + } + } + } catch (e) { + console.error(`Failed to parse Stripe session update: ${e}`) } }, [dispatch] @@ -54,6 +67,15 @@ export const StripeOnrampEmbed = () => { [dispatch] ) + const renderLoadingSpinner = useCallback( + () => ( + + + + ), + [styles] + ) + const html = ` @@ -69,7 +91,7 @@ export const StripeOnrampEmbed = () => {