Skip to content

Commit

Permalink
[PAY-1951][PAY-1966] Improve the Stripe drawer experience on mobile (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
schottra authored Oct 9, 2023
1 parent 2d3f098 commit f94c45d
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 45 deletions.
16 changes: 14 additions & 2 deletions packages/mobile/src/components/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -250,6 +255,7 @@ type DrawerComponent = {
(props: Omit<DrawerProps, 'titleIcon' | 'titleImage'>): React.ReactElement
}
export const Drawer: DrawerComponent = ({
blockClose = false,
isOpen,
children,
onClose,
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -661,10 +673,10 @@ export const Drawer: DrawerComponent = ({
{...edgeProps}
>
{CustomDrawerHeader ? (
<CustomDrawerHeader onClose={onClose} />
<CustomDrawerHeader onClose={handlePressClose} />
) : (
<DrawerHeader
onClose={onClose}
onClose={handlePressClose}
title={title}
titleIcon={titleIcon}
titleImage={titleImage}
Expand Down
11 changes: 2 additions & 9 deletions packages/mobile/src/components/drawer/NativeDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { DrawerProps } from './Drawer'
import Drawer from './Drawer'

type NativeDrawerProps = SetOptional<DrawerProps, 'isOpen' | 'onClose'> & {
blockClose?: boolean
drawerName: DrawerName
}

Expand All @@ -18,20 +17,14 @@ type NativeDrawerProps = SetOptional<DrawerProps, 'isOpen' | 'onClose'> & {
* 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,16 @@ const useStyles = makeStyles(({ spacing, typography, palette }) => ({
paddingTop: spacing(4),
paddingHorizontal: spacing(4),
paddingBottom: spacing(6),
columnGap: spacing(4)
gap: spacing(4)
},
headerContainer: {
borderBottomWidth: 1,
borderBottomColor: palette.neutralLight8,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
padding: spacing(4)
height: spacing(10),
paddingHorizontal: spacing(4)
},
titleContainer: {
...flexRowCentered(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -30,31 +40,44 @@ const useStyles = makeStyles(({ spacing }) => ({
}
}))

const StripeOnrampDrawerHeader = ({ onClose }: { onClose: () => void }) => {
const styles = useStyles()
const { neutralLight4 } = useThemeColors()
return (
<View style={styles.headerContainer}>
<TouchableOpacity activeOpacity={0.7} onPress={onClose}>
<IconCloseAlt
width={spacing(6)}
height={spacing(6)}
fill={neutralLight4}
/>
</TouchableOpacity>
</View>
)
}

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 (
<AppDrawer
blockClose={stripeSessionStatus === 'fulfillment_processing'}
drawerHeader={StripeOnrampDrawerHeader}
zIndex={zIndex.STRIPE_ONRAMP_DRAWER}
modalName={MODAL_NAME}
drawerStyle={styles.root}
isGestureSupported={false}
isFullscreen
onClose={handleClose}
>
<View style={styles.exitContainer}>
<IconRemove
fill={neutralLight4}
width={spacing(6)}
height={spacing(6)}
onPress={handleClose}
/>
<View style={styles.contentContainer}>
<StripeOnrampEmbed />
</View>
<StripeOnrampEmbed />
</AppDrawer>
)
}
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'
}
Expand All @@ -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]
Expand All @@ -54,6 +67,15 @@ export const StripeOnrampEmbed = () => {
[dispatch]
)

const renderLoadingSpinner = useCallback(
() => (
<View style={styles.spinnerContainer}>
<LoadingSpinner />
</View>
),
[styles]
)

const html = `
<!DOCTYPE html>
<html lang="en">
Expand All @@ -69,7 +91,7 @@ export const StripeOnrampEmbed = () => {
<div id="onramp-element" />
<script type="text/javascript">
const handleSessionUpdate = (event) => {
window.ReactNativeWebView.postMessage(event)
window.ReactNativeWebView.postMessage(JSON.stringify(event.payload.session))
}
try {
const onramp = new window.StripeOnramp("${STRIPE_PUBLISHABLE_KEY}")
Expand All @@ -96,14 +118,14 @@ export const StripeOnrampEmbed = () => {
{clientSecret ? (
<WebView
source={{ html }}
startInLoadingState={true}
renderLoading={renderLoadingSpinner}
scrollEnabled={false}
onError={handleError}
onMessage={handleSessionUpdate}
/>
) : (
<View style={styles.spinnerContainer}>
<LoadingSpinner />
</View>
renderLoadingSpinner()
)}
</View>
)
Expand Down

0 comments on commit f94c45d

Please sign in to comment.