Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
[Claim] Rejected/failed/errors in claim flow (#2169)
Browse files Browse the repository at this point in the history
* useErrorMessage hook

* extend SwapCallbackError to be more generic

* add useErrorMessage to claim index & InvestOption

* add useErrorMessage to SwapMod

* [Claim] Reject tx modals (#2193)

* change hook name and add new hooks

* add new modal enum

* add hooks in app

* useMemo in useErrorMessageAndModal hook

* remove testing fn
  • Loading branch information
W3stside authored Jan 18, 2022
1 parent 7029923 commit 7099b6f
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 10 deletions.
28 changes: 24 additions & 4 deletions src/components/swap/styleds.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { loadingOpacityMixin } from 'components/Loader/styled'
import { TooltipContainer } from 'components/Tooltip'
import { transparentize } from 'polished'
import { ReactNode } from 'react'
import { MouseEventHandler, ReactNode } from 'react'
import { AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import styled, { css } from 'styled-components/macro'
Expand Down Expand Up @@ -89,9 +89,10 @@ export const Dots = styled.span`
}
`

const SwapCallbackErrorInner = styled.div`
const SwapCallbackErrorInner = styled.div<{ $css?: string }>`
background-color: ${({ theme }) => transparentize(0.9, theme.red1)};
border-radius: 1rem;
position: relative;
display: flex;
align-items: center;
font-size: 0.825rem;
Expand All @@ -105,6 +106,8 @@ const SwapCallbackErrorInner = styled.div`
margin: 0;
font-weight: 500;
}
${({ $css }) => $css}
`

const SwapCallbackErrorInnerAlertTriangle = styled.div`
Expand All @@ -118,9 +121,26 @@ const SwapCallbackErrorInnerAlertTriangle = styled.div`
height: 48px;
`

export function SwapCallbackError({ error }: { error: ReactNode }) {
const Closer = styled.div`
position: absolute;
right: 0;
top: 0;
padding: 7px 10px;
font-weight: bold;
cursor: pointer;
`

export type ErrorMessageProps = {
error?: ReactNode
handleClose?: MouseEventHandler<HTMLDivElement>
showClose?: boolean
$css?: string
}

export function SwapCallbackError({ error, handleClose, showClose, ...styleProps }: ErrorMessageProps) {
return (
<SwapCallbackErrorInner>
<SwapCallbackErrorInner {...styleProps}>
{showClose && <Closer onClick={handleClose}>X</Closer>}
<SwapCallbackErrorInnerAlertTriangle>
<AlertTriangle size={24} />
</SwapCallbackErrorInnerAlertTriangle>
Expand Down
57 changes: 57 additions & 0 deletions src/custom/hooks/useErrorMessageAndModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useMemo, useState } from 'react'
import { ErrorMessageProps, SwapCallbackError } from 'components/swap/styleds'
import useTransactionErrorModal from './useTransactionErrorModal'

/**
* @description hook for getting CowSwap error and handling them visually
* @description ErrorMessage component accepts an error message to override exported error state, and a close option
* @returns returns object: { error, setError, ErrorMessage } => error message, error message setter, and our ErrorMessage component
*/
export function useErrorMessage() {
const [internalError, setError] = useState<string | undefined>()

return useMemo(() => {
const handleCloseError = () => setError(undefined)

return {
error: internalError,
handleSetError: setError,
ErrorMessage: ({
error = internalError,
showClose = false,
...rest
}: Pick<ErrorMessageProps, 'error' | 'showClose' | '$css'>) =>
error ? (
<SwapCallbackError showClose={showClose} handleClose={handleCloseError} error={error} {...rest} />
) : null,
}
}, [internalError])
}

export function useErrorModal() {
// Any async bc errors
const [internalError, setError] = useState<string | undefined>()
const { openModal, closeModal, TransactionErrorModal } = useTransactionErrorModal()

return useMemo(() => {
const handleCloseError = () => {
closeModal()
setError(undefined)
}
const handleSetError = (error: string | undefined) => {
setError(error)

// IF error, open modal
error && openModal()
}

return {
error: internalError,
handleCloseError,
handleSetError,
ErrorModal: ({ message = internalError }: { message?: string }) => (
<TransactionErrorModal onDismiss={handleCloseError} message={message} />
),
}
}, [TransactionErrorModal, closeModal, internalError, openModal])
}
24 changes: 24 additions & 0 deletions src/custom/hooks/useTransactionErrorModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useCallback } from 'react'
import { ApplicationModal } from 'state/application/reducer'
import { TransactionErrorContent } from 'components/TransactionConfirmationModal'
import { useOpenModal, useCloseModals, useModalOpen } from 'state/application/hooks'
import { GpModal } from '../components/Modal'

export default function useTransactionErrorModal() {
const openModal = useOpenModal(ApplicationModal.TRANSACTION_ERROR)
const closeModal = useCloseModals()
const showTransactionErrorModal = useModalOpen(ApplicationModal.TRANSACTION_ERROR)

return {
openModal,
closeModal,
TransactionErrorModal: useCallback(
({ message, onDismiss }: { message?: string; onDismiss: () => void }) => (
<GpModal isOpen={showTransactionErrorModal} onDismiss={closeModal}>
<TransactionErrorContent onDismiss={onDismiss} message={message} />
</GpModal>
),
[closeModal, showTransactionErrorModal]
),
}
}
14 changes: 10 additions & 4 deletions src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useClaimDispatchers, useClaimState } from 'state/claim/hooks'
import { ButtonConfirmed } from 'components/Button'
import { ButtonSize } from 'theme'
import Loader from 'components/Loader'
import { useErrorModal } from 'hooks/useErrorMessageAndModal'

const RangeSteps = styled.div`
display: flex;
Expand Down Expand Up @@ -49,18 +50,18 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
const { updateInvestAmount } = useClaimDispatchers()
const { investFlowData } = useClaimState()

const { handleSetError, ErrorModal } = useErrorModal()

const investedAmount = useMemo(() => investFlowData[optionIndex].investedAmount, [investFlowData, optionIndex])

const [percentage, setPercentage] = useState<string>(INVESTMENT_STEPS[0])

const { account } = useActiveWeb3React()

const token = currencyAmount?.currency

const decimals = token?.decimals
const balance = useCurrencyBalance(account || undefined, token)

const decimals = balance?.currency?.decimals

const handleStepChange = useCallback(
(value: string) => {
if (!maxCost || !balance) {
Expand Down Expand Up @@ -92,6 +93,7 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
// Save "local" approving state (pre-BC) for rendering spinners etc
const [approving, setApproving] = useState(false)
const handleApprove = useCallback(async () => {
handleSetError(undefined)
if (!approveCallback) return

try {
Expand All @@ -100,10 +102,11 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
await approveCallback({ transactionSummary: `Approve ${token?.symbol || 'token'} for investing in vCOW` })
} catch (error) {
console.error('[InvestOption]: Issue approving.', error)
handleSetError(error?.message)
} finally {
setApproving(false)
}
}, [approveCallback, token?.symbol])
}, [approveCallback, handleSetError, token?.symbol])

const vCowAmount = useMemo(() => {
if (!token || !price || !investedAmount) {
Expand Down Expand Up @@ -195,6 +198,9 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
</div>
</span>
</InvestSummary>
{/* Error modal */}
<ErrorModal />
{/* Investment inputs */}
<InvestInput>
<div>
<span>
Expand Down
10 changes: 10 additions & 0 deletions src/custom/pages/Claim/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import useTransactionConfirmationModal from 'hooks/useTransactionConfirmationMod

import { GNO, USDC_BY_CHAIN } from 'constants/tokens'
import { isSupportedChain } from 'utils/supportedChainId'
import { useErrorModal } from 'hooks/useErrorMessageAndModal'
import { EnhancedUserClaimData } from './types'

const GNO_CLAIM_APPROVE_MESSAGE = 'Approving GNO for investing in vCOW'
Expand Down Expand Up @@ -73,11 +74,14 @@ export default function Claim() {
resetClaimUi,
} = useClaimDispatchers()

// addresses
const { address: resolvedAddress, name: resolvedENS } = useENS(inputAddress)
const isInputAddressValid = useMemo(() => isAddress(resolvedAddress || ''), [resolvedAddress])

// toggle wallet when disconnected
const toggleWalletModal = useWalletModalToggle()
// error handling modals
const { handleSetError, ErrorModal } = useErrorModal()

// get user claim data
const userClaimData = useUserEnhancedClaimData(activeClaimAccount)
Expand Down Expand Up @@ -127,6 +131,9 @@ export default function Claim() {
// TODO: useCallback
// handle submit claim
const handleSubmitClaim = () => {
// Reset error handling
handleSetError(undefined)

// just to be sure
if (!activeClaimAccount) return

Expand All @@ -143,6 +150,7 @@ export default function Claim() {
.catch((error) => {
setClaimStatus(ClaimStatus.DEFAULT)
console.log(error)
handleSetError(error?.message)
})
}

Expand Down Expand Up @@ -207,6 +215,8 @@ export default function Claim() {
<PageWrapper>
{/* Approve confirmation modal */}
<TransactionConfirmationModal />
{/* Error modal */}
<ErrorModal />
{/* If claim is confirmed > trigger confetti effect */}
<Confetti start={claimStatus === ClaimStatus.CONFIRMED} />

Expand Down
7 changes: 5 additions & 2 deletions src/custom/pages/Swap/SwapMod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { /* Row, */ AutoRow /*, RowFixed */ } from 'components/Row'
// import BetterTradeLink from 'components/swap/BetterTradeLink'
import confirmPriceImpactWithoutFee from 'components/swap/confirmPriceImpactWithoutFee'
import ConfirmSwapModal from 'components/swap/ConfirmSwapModal'
import { /* ArrowWrapper, Dots, */ SwapCallbackError, Wrapper } from 'components/swap/styleds'
import { /* ArrowWrapper, Dots, */ /* SwapCallbackError, */ Wrapper } from 'components/swap/styleds'
import SwapHeader from 'components/swap/SwapHeader'
// import TradePrice from 'components/swap/TradePrice'
// import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
Expand Down Expand Up @@ -89,6 +89,7 @@ import { ApplicationModal } from 'state/application/reducer'
import TransactionConfirmationModal, { OperationType } from 'components/TransactionConfirmationModal'
import AffiliateStatusCheck from 'components/AffiliateStatusCheck'
import usePriceImpact from 'hooks/usePriceImpact'
import { useErrorMessage } from 'hooks/useErrorMessageAndModal'

// MOD - exported in ./styleds to avoid circ dep
// export const StyledInfo = styled(Info)`
Expand Down Expand Up @@ -507,6 +508,8 @@ export default function Swap({
}
}

const { ErrorMessage } = useErrorMessage()

return (
<>
<TokenWarningModal
Expand Down Expand Up @@ -960,7 +963,7 @@ export default function Swap({
</Text> */}
</ButtonError>
)}
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
{isExpertMode ? <ErrorMessage error={swapErrorMessage} /> : null}
</BottomGrouping>
</Wrapper>
</StyledAppBody>
Expand Down
1 change: 1 addition & 0 deletions src/state/application/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum ApplicationModal {
PRIVACY_POLICY,
// ----------------- MOD: CowSwap specific modals --------------------
TRANSACTION_CONFIRMATION,
TRANSACTION_ERROR,
ARBITRUM_OPTIONS,
// ------------------------------------------------------------------------------
}
Expand Down

0 comments on commit 7099b6f

Please sign in to comment.