Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the logic for TransactionModal #408

Merged
merged 15 commits into from
May 15, 2024
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
5 changes: 3 additions & 2 deletions dapp/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from "react"
import React, { useState } from "react"
import { AnimatePresence, motion, Variants } from "framer-motion"
import { useState } from "react"
import { useLocation, useOutlet } from "react-router-dom"
import DocsDrawer from "./DocsDrawer"
import Header from "./Header"
import Sidebar from "./Sidebar"
import ModalRoot from "./ModalRoot"

const wrapperVariants: Variants = {
in: { opacity: 0, y: 48 },
Expand Down Expand Up @@ -39,6 +39,7 @@ function Layout() {
</AnimatePresence>
<Sidebar />
<DocsDrawer />
<ModalRoot />
</>
)
}
Expand Down
19 changes: 19 additions & 0 deletions dapp/src/components/ModalRoot/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { ElementType } from "react"
import { useModal } from "#/hooks"
import { ModalType } from "#/types"
import TransactionModal from "../TransactionModal"

const MODALS: Record<ModalType, ElementType> = {
STAKE: TransactionModal,
UNSTAKE: TransactionModal,
} as const

export default function ModalRoot() {
const { modalType, modalProps, closeModal } = useModal()

if (!modalType) {
return null
}
const SpecificModal = MODALS[modalType]
return <SpecificModal closeModal={closeModal} {...modalProps} />
}
29 changes: 29 additions & 0 deletions dapp/src/components/ModalRoot/withBaseModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { ComponentType } from "react"
import { Modal, ModalContent, ModalOverlay } from "@chakra-ui/react"
import { BaseModalProps } from "#/types"

export const MODAL_BASE_SIZE = "lg"

function withBaseModal<T extends BaseModalProps>(
WrappedModalContent: ComponentType<T>,
) {
return function ModalBase(props: T) {
const { closeModal } = props
return (
<Modal
isOpen
onClose={closeModal}
scrollBehavior="inside"
closeOnOverlayClick={false}
size={MODAL_BASE_SIZE}
>
<ModalOverlay mt="header_height" />
<ModalContent mt="modal_shift">
<WrappedModalContent {...props} />
</ModalContent>
</Modal>
)
}
}

export default withBaseModal
13 changes: 5 additions & 8 deletions dapp/src/components/TransactionModal/ActionFormModal.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import React, { ReactNode, useCallback, useState } from "react"
import { Box, ModalBody, ModalCloseButton, ModalHeader } from "@chakra-ui/react"
import {
useStakeFlowContext,
useTransactionContext,
useWalletContext,
} from "#/hooks"
import { useAppDispatch, useStakeFlowContext, useWalletContext } from "#/hooks"
import { ACTION_FLOW_TYPES, ActionFlowType, BaseFormProps } from "#/types"
import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase"
import { logPromiseFailure } from "#/utils"
import { setTokenAmount } from "#/store/action-flow"
import StakeFormModal from "./ActiveStakingStep/StakeFormModal"
import UnstakeFormModal from "./ActiveUnstakingStep/UnstakeFormModal"

Expand All @@ -30,8 +27,8 @@ const FORM_DATA: Record<

function ActionFormModal({ type }: { type: ActionFlowType }) {
const { btcAccount, ethAccount } = useWalletContext()
const { setTokenAmount } = useTransactionContext()
const { initStake } = useStakeFlowContext()
const dispatch = useAppDispatch()

const [isLoading, setIsLoading] = useState(false)

Expand All @@ -55,14 +52,14 @@ function ActionFormModal({ type }: { type: ActionFlowType }) {
// TODO: Init unstake flow
if (type === ACTION_FLOW_TYPES.STAKE) await handleInitStake()

setTokenAmount({ amount: values.amount, currency: "bitcoin" })
dispatch(setTokenAmount({ amount: values.amount, currency: "bitcoin" }))
} catch (error) {
console.error(error)
} finally {
setIsLoading(false)
}
},
[handleInitStake, setTokenAmount, type],
[dispatch, handleInitStake, type],
)

const handleSubmitFormWrapper = useCallback(
Expand Down
11 changes: 7 additions & 4 deletions dapp/src/components/TransactionModal/ActiveFlowStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { ReactElement, useEffect } from "react"
import { useModalFlowContext } from "#/hooks"
import { useActionFlowActiveStep, useActionFlowType, useModal } from "#/hooks"
import {
ACTION_FLOW_STEPS_TYPES,
ActionFlowType,
Expand All @@ -18,14 +18,17 @@ const FLOW: Record<ActionFlowType, (activeStep: number) => ReactElement> = {
}

export function ActiveFlowStep() {
const { activeStep, type, onClose } = useModalFlowContext()
const { closeModal } = useModal()
const activeStep = useActionFlowActiveStep()
const type = useActionFlowType()

const numberOfSteps = Object.keys(ACTION_FLOW_STEPS_TYPES[type]).length

useEffect(() => {
if (activeStep > numberOfSteps) {
onClose()
closeModal()
}
}, [activeStep, numberOfSteps, onClose])
}, [activeStep, closeModal, numberOfSteps])

return FLOW[type](activeStep)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useCallback } from "react"
import {
useActionFlowTokenAmount,
useAppDispatch,
useDepositBTCTransaction,
useDepositTelemetry,
useExecuteFunction,
useModalFlowContext,
useStakeFlowContext,
useToast,
useTransactionContext,
useWalletContext,
} from "#/hooks"
import { logPromiseFailure } from "#/utils"
Expand All @@ -17,27 +17,28 @@ import Spinner from "#/components/shared/Spinner"
import { TextMd } from "#/components/shared/Typography"
import { CardAlert } from "#/components/shared/alerts"
import { ONE_SEC_IN_MILLISECONDS } from "#/constants"
import { setStatus } from "#/store/action-flow"

const DELAY = ONE_SEC_IN_MILLISECONDS * 2
const DELAY = ONE_SEC_IN_MILLISECONDS
const TOAST_ID = TOAST_IDS.DEPOSIT_TRANSACTION_ERROR
const TOAST = TOASTS[TOAST_ID]

export default function DepositBTCModal() {
const { ethAccount } = useWalletContext()
const { tokenAmount } = useTransactionContext()
const { setStatus } = useModalFlowContext()
const tokenAmount = useActionFlowTokenAmount()
const { btcAddress, depositReceipt, stake } = useStakeFlowContext()
const depositTelemetry = useDepositTelemetry()
const { closeToast, openToast } = useToast()
const dispatch = useAppDispatch()

const onStakeBTCSuccess = useCallback(
() => setStatus(PROCESS_STATUSES.SUCCEEDED),
[setStatus],
() => dispatch(setStatus(PROCESS_STATUSES.SUCCEEDED)),
[dispatch],
)

const onStakeBTCError = useCallback(() => {
setStatus(PROCESS_STATUSES.FAILED)
}, [setStatus])
dispatch(setStatus(PROCESS_STATUSES.FAILED))
}, [dispatch])

const handleStake = useExecuteFunction(
stake,
Expand All @@ -47,10 +48,10 @@ export default function DepositBTCModal() {

const onDepositBTCSuccess = useCallback(() => {
closeToast(TOAST_ID)
setStatus(PROCESS_STATUSES.LOADING)
dispatch(setStatus(PROCESS_STATUSES.LOADING))

logPromiseFailure(handleStake())
}, [closeToast, setStatus, handleStake])
}, [closeToast, dispatch, handleStake])

const showError = useCallback(() => {
openToast({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { CableWithPlugIcon, Info } from "#/assets/icons"
import { TextMd } from "#/components/shared/Typography"
import { EXTERNAL_HREF } from "#/constants"
import IconWrapper from "#/components/shared/IconWrapper"
import { MODAL_BASE_SIZE } from "#/components/shared/ModalBase"
import { MODAL_BASE_SIZE } from "#/components/ModalRoot/withBaseModal"
import {
IconBrandDiscordFilled,
IconReload,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import React, { useCallback, useState } from "react"
import {
useAppDispatch,
useExecuteFunction,
useModalFlowContext,
useStakeFlowContext,
} from "#/hooks"
import { PROCESS_STATUSES } from "#/types"
import { logPromiseFailure } from "#/utils"
import { setStatus } from "#/store/action-flow"
import ServerErrorModal from "./ServerErrorModal"
import RetryModal from "./RetryModal"
import LoadingModal from "../../LoadingModal"

export default function StakingErrorModal() {
const { setStatus } = useModalFlowContext()
const { stake } = useStakeFlowContext()
const dispatch = useAppDispatch()

const [isLoading, setIsLoading] = useState(false)
const [isServerError, setIsServerError] = useState(false)

const onStakeBTCSuccess = useCallback(
() => setStatus(PROCESS_STATUSES.SUCCEEDED),
[setStatus],
() => dispatch(setStatus(PROCESS_STATUSES.SUCCEEDED)),
[dispatch],
)

const onStakeBTCError = useCallback(() => setIsServerError(true), [])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React, { useCallback } from "react"
import { useExecuteFunction, useModalFlowContext } from "#/hooks"
import { useAppDispatch, useExecuteFunction } from "#/hooks"
import { PROCESS_STATUSES } from "#/types"
import { Button, ModalBody, ModalFooter, ModalHeader } from "@chakra-ui/react"
import { TextMd } from "#/components/shared/Typography"
import { logPromiseFailure } from "#/utils"
import { setStatus } from "#/store/action-flow"

export default function SignMessageModal() {
const { setStatus } = useModalFlowContext()
const dispatch = useAppDispatch()

const onSignMessageSuccess = useCallback(() => {
setStatus(PROCESS_STATUSES.SUCCEEDED)
}, [setStatus])
dispatch(setStatus(PROCESS_STATUSES.SUCCEEDED))
}, [dispatch])

// TODO: After a failed attempt, we should display the message
const onSignMessageError = useCallback(() => {
setStatus(PROCESS_STATUSES.FAILED)
}, [setStatus])
dispatch(setStatus(PROCESS_STATUSES.FAILED))
}, [dispatch])

const handleSignMessage = useExecuteFunction(
// TODO: Use a correct function from the SDK
Expand All @@ -25,13 +26,13 @@ export default function SignMessageModal() {
)

const handleSignMessageWrapper = useCallback(() => {
setStatus(PROCESS_STATUSES.LOADING)
dispatch(setStatus(PROCESS_STATUSES.LOADING))

// TODO: Remove when SDK is ready
setTimeout(() => {
logPromiseFailure(handleSignMessage())
}, 5000)
}, [setStatus, handleSignMessage])
}, [dispatch, handleSignMessage])

return (
<>
Expand Down
10 changes: 6 additions & 4 deletions dapp/src/components/TransactionModal/ModalContentWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from "react"
import {
useModalFlowContext,
useActionFlowStatus,
useActionFlowTokenAmount,
useActionFlowType,
useRequestBitcoinAccount,
useRequestEthereumAccount,
useTransactionContext,
useWalletContext,
} from "#/hooks"
import { BitcoinIcon, EthereumIcon } from "#/assets/icons"
Expand All @@ -23,8 +24,9 @@ export default function ModalContentWrapper({
const { btcAccount, ethAccount } = useWalletContext()
const { requestAccount: requestBitcoinAccount } = useRequestBitcoinAccount()
const { requestAccount: requestEthereumAccount } = useRequestEthereumAccount()
const { type, status } = useModalFlowContext()
const { tokenAmount } = useTransactionContext()
const status = useActionFlowStatus()
const type = useActionFlowType()
const tokenAmount = useActionFlowTokenAmount()

if (!btcAccount || !isSupportedBTCAddressType(btcAccount.address))
return (
Expand Down
6 changes: 3 additions & 3 deletions dapp/src/components/TransactionModal/SuccessModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
VStack,
} from "@chakra-ui/react"
import { LoadingSpinnerSuccessIcon } from "#/assets/icons"
import { useModalFlowContext } from "#/hooks"
import { useModal } from "#/hooks"
import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion"
import { ACTION_FLOW_TYPES, ActionFlowType, TokenAmount } from "#/types"
import { TextMd } from "../shared/Typography"
Expand Down Expand Up @@ -66,7 +66,7 @@ type SuccessModalProps = {
}

export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) {
const { onClose } = useModalFlowContext()
const { closeModal } = useModal()

const { header, footer, renderBody } = CONTENT[type]

Expand All @@ -80,7 +80,7 @@ export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) {
</VStack>
</ModalBody>
<ModalFooter pt={0}>
<Button size="lg" width="100%" variant="outline" onClick={onClose}>
<Button size="lg" width="100%" variant="outline" onClick={closeModal}>
Go to dashboard
</Button>
<HStack spacing={2}>
Expand Down
Loading
Loading