Skip to content
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
106 changes: 78 additions & 28 deletions react/lib/components/Widget/AltpaymentWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, useEffect, useRef, useState } from 'react'
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react'
import { TextField, Select, MenuItem, InputLabel, FormControl, Box, CircularProgress } from '@mui/material'
import { styled } from '@mui/material/styles'
import { QRCodeSVG } from 'qrcode.react'
Expand Down Expand Up @@ -329,6 +329,8 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
setCoinPair(undefined)
setAltpaymentError(undefined)
setAltpaymentShift(undefined)
setLoadingPair(false)
setLoadingShift(false)
setPairAmount(undefined)
setPairAmountFixedDecimals(undefined)
setShiftCompleted(false)
Expand Down Expand Up @@ -377,38 +379,30 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
}
}

const SideshiftCtn = styled('div')({
const SideshiftCtn = useMemo(() => styled('div')({
alignItems: 'center',
justifyContent: 'flex-start',
display: 'flex',
flexDirection: 'column',
boxSizing: 'border-box',
position: 'absolute',
zIndex: 9,
top: 0,
left: 0,
right: 0,
bottom: 0,
position: 'relative',
background: '#f5f5f7',
overflowY: 'auto',
overflowX: 'hidden',
width: '100%',
padding: '16px',
'@media (min-width: 760px)': {
padding: '24px',
},
})
}), [])

const LoadingCenter = styled('div')({
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '16px',
textAlign: 'center',
width: 'max-content',
width: '100%',
minHeight: '300px',
maxWidth: '100%',
})

Expand All @@ -418,12 +412,20 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
'& img': { width: '150px', marginTop: '10px' },
})

const BackLink = styled('div')({
fontSize: '14px', marginTop: '20px', cursor: 'pointer',
const BackLink = styled('button')({
fontSize: '14px', cursor: 'pointer',
fontFamily: 'inherit', background: 'transparent', color: 'inherit',
border: '1px solid #000', opacity: '0.7', padding: '2px 20px',
borderRadius: '3px', '&:hover': { opacity: '1' },
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const BackRow = styled('div')({
width: '100%',
display: 'flex',
justifyContent: 'center',
marginTop: '30px',
})

const ShiftReady = styled('div')({
width: '100%', display: 'flex', flexDirection: 'column',
minWidth: 0,
Expand Down Expand Up @@ -479,8 +481,16 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
})

const AmountError = styled('p')({
position: 'absolute', bottom: '10px', textAlign: 'center',
background: '#00000014', padding: '10px', borderRadius: '5px'
margin: '12px auto 0',
textAlign: 'center',
background: '#00000014',
padding: '10px',
borderRadius: '5px',
minHeight: '28px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'opacity 120ms ease',
})

const ErrorMsg = styled('p')({
Expand Down Expand Up @@ -699,6 +709,13 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props

const isAutoStart = Boolean(preselectedCoin)
const isAutoStartLoading = isAutoStart && !altpaymentShift && !altpaymentError
const showManualAmountBackButton = altpaymentEditable
const amountValidationMessage =
pairAmount && isAboveMinimumAltpaymentAmount === false
? 'Amount is below minimum.'
: pairAmount && isBelowMaximumAltpaymentAmount === false
? 'Amount is above maximum.'
: ''
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const renderLoading = (message: string) => (
<LoadingCenter>
Expand All @@ -717,12 +734,33 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
</LoadingCenter>
)

const backToWidget = () => {
resetTrade()
setUseAltpayment(false)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const backToRateSelection = () => {
autoQuoteRequestedRef.current = false
setAltpaymentError(undefined)
setAltpaymentShift(undefined)
setLoadingShift(false)
setCoinPair(undefined)
}

return (
<SideshiftCtn>
{altpaymentError ? (
<Fragment>
<ErrorMsg>Error: {altpaymentError.errorMessage}</ErrorMsg>
<BackLink onClick={resetTrade}>Back</BackLink>
{showManualAmountBackButton ? (
<BackRow>
<BackLink type="button" onClick={resetTrade}>Back</BackLink>
</BackRow>
) : (
<BackRow>
<BackLink type="button" onClick={backToWidget}>Back</BackLink>
</BackRow>
)}
</Fragment>
) : isAutoStartLoading ? (
renderLoading('Loading SideShift...')
Expand Down Expand Up @@ -822,6 +860,11 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
</CopyCtn>
</ShiftReadyMain>
</ShiftReadyBody>
{showManualAmountBackButton ? (
<BackRow>
<BackLink type="button" onClick={resetTrade}>Back</BackLink>
</BackRow>
) : null}
</ShiftReady>
)
) : loadingShift ? (
Expand Down Expand Up @@ -870,12 +913,17 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
animation={animation}
/>
</div>
{pairAmount && !isAboveMinimumAltpaymentAmount && (
<AmountError>Amount is below minimum.</AmountError>
)}
{pairAmount && !isBelowMaximumAltpaymentAmount && (
<AmountError>Amount is above maximum.</AmountError>
)}
<AmountError
aria-live="polite"
style={amountValidationMessage ? { opacity: 1, visibility: 'visible' } : { opacity: 0, visibility: 'hidden' }}
>
{amountValidationMessage || '\u00A0'}
</AmountError>
{showManualAmountBackButton ? (
<BackRow>
<BackLink type="button" onClick={backToRateSelection}>Back</BackLink>
</BackRow>
) : null}
</Fragment>
) : (
<Fragment>
Expand Down Expand Up @@ -961,7 +1009,9 @@ export const AltpaymentWidget: React.FunctionComponent<AltpaymentProps> = props
animation={animation}
/>
)}
<BackLink onClick={() => {setUseAltpayment(false)}}>Back</BackLink>
<BackRow>
<BackLink type="button" onClick={backToWidget}>Back</BackLink>
</BackRow>
</Fragment>
)
// END: Altpayment region
Expand Down
4 changes: 2 additions & 2 deletions react/lib/components/Widget/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
...(thisUseAltpayment ? {
minWidth: 320,
width: thisAltpaymentShift ? 'min(94vw, 600px)' : 'min(92vw, 420px)',
minHeight: thisAltpaymentShift ? 640 : 520,
minHeight: 420,
} : {}),
}}
pt={0}
Expand Down Expand Up @@ -1183,7 +1183,7 @@ export const Widget: React.FunctionComponent<WidgetProps> = props => {
px={thisUseAltpayment ? 0 : 3}
pt={thisUseAltpayment ? 0 : 2}
position="relative"
sx={thisUseAltpayment ? { minHeight: 480, flex: 1, alignSelf: 'stretch', width: '100%' } : undefined}
sx={thisUseAltpayment ? { flex: 1, alignSelf: 'stretch', width: '100%' } : undefined}
>
{thisUseAltpayment ? (
<AltpaymentWidget
Expand Down
46 changes: 46 additions & 0 deletions react/lib/tests/components/AltpaymentWidget.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const coins = [
coin: 'BTC',
name: 'Bitcoin',
networks: ['bitcoin'],
hasMemo: false,
fixedOnly: false,
variableOnly: false,
tokenDetails: {},
},
]

Expand Down Expand Up @@ -116,4 +120,46 @@ describe('AltpaymentWidget copy feedback', () => {
expect(screen.getByText('Click to copy')).toBeTruthy()
})
})

test('non-editable errors provide a button that resets state before exiting altpayment', () => {
render(
<AltpaymentWidget
{...baseProps}
altpaymentError={{ errorMessage: 'Quote failed' } as any}
/>,
)

const backButton = screen.getByRole('button', { name: 'Back' })
expect(backButton.getAttribute('type')).toBe('button')

fireEvent.click(backButton)

expect(baseProps.setCoinPair).toHaveBeenCalledWith(undefined)
expect(baseProps.setAltpaymentError).toHaveBeenCalledWith(undefined)
expect(baseProps.setAltpaymentShift).toHaveBeenCalledWith(undefined)
expect(baseProps.setLoadingPair).toHaveBeenCalledWith(false)
expect(baseProps.setLoadingShift).toHaveBeenCalledWith(false)
expect(baseProps.setShiftCompleted).toHaveBeenCalledWith(false)
expect(baseProps.setUseAltpayment).toHaveBeenCalledWith(false)
expect(baseProps.setAltpaymentShift.mock.invocationCallOrder[0]).toBeLessThan(
baseProps.setUseAltpayment.mock.invocationCallOrder[0],
)
})

test('editable errors reset the trade without exiting altpayment', () => {
render(
<AltpaymentWidget
{...baseProps}
altpaymentEditable
altpaymentError={{ errorMessage: 'Quote failed' } as any}
/>,
)

fireEvent.click(screen.getByRole('button', { name: 'Back' }))

expect(baseProps.setAltpaymentError).toHaveBeenCalledWith(undefined)
expect(baseProps.setLoadingPair).toHaveBeenCalledWith(false)
expect(baseProps.setLoadingShift).toHaveBeenCalledWith(false)
expect(baseProps.setUseAltpayment).not.toHaveBeenCalled()
})
})
Loading