From d9d70ae7b01fdc2a23d383ff333567f8f48bdc09 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 6 Dec 2023 11:54:10 +0100 Subject: [PATCH] Initialization of the staking flow --- dapp/src/DApp.tsx | 14 ++-- dapp/src/components/Header/index.tsx | 4 +- dapp/src/components/ModalOverlay/index.tsx | 41 +++++++++++ dapp/src/components/Modals/BaseModal.tsx | 23 ++++++ .../components/Modals/ConnectWalletModal.tsx | 44 ++++++++++++ .../Modals/StakingOverviewModal.tsx | 67 ++++++++++++++++++ .../components/Overview/PositionDetails.tsx | 49 +++++++------ dapp/src/components/Staking/index.tsx | 28 ++++++++ dapp/src/contexts/StakingFlowContext.tsx | 42 +++++++++++ dapp/src/contexts/WalletContext.tsx | 11 +-- dapp/src/contexts/index.tsx | 1 + dapp/src/hooks/index.ts | 1 + dapp/src/hooks/useRequestBitcoinAccount.ts | 6 +- dapp/src/hooks/useRequestEthereumAccount.ts | 6 +- dapp/src/hooks/useStakingFlowContext.ts | 14 ++++ dapp/src/static/images/ConnectBTCAccount.png | Bin 0 -> 5287 bytes dapp/src/types/index.ts | 1 + dapp/src/types/staking.ts | 1 + 18 files changed, 317 insertions(+), 36 deletions(-) create mode 100644 dapp/src/components/ModalOverlay/index.tsx create mode 100644 dapp/src/components/Modals/BaseModal.tsx create mode 100644 dapp/src/components/Modals/ConnectWalletModal.tsx create mode 100644 dapp/src/components/Modals/StakingOverviewModal.tsx create mode 100644 dapp/src/components/Staking/index.tsx create mode 100644 dapp/src/contexts/StakingFlowContext.tsx create mode 100644 dapp/src/hooks/useStakingFlowContext.ts create mode 100644 dapp/src/static/images/ConnectBTCAccount.png create mode 100644 dapp/src/types/staking.ts diff --git a/dapp/src/DApp.tsx b/dapp/src/DApp.tsx index 085b6fbe8..d1d38a8bd 100644 --- a/dapp/src/DApp.tsx +++ b/dapp/src/DApp.tsx @@ -2,7 +2,11 @@ import React from "react" import { ChakraProvider } from "@chakra-ui/react" import { useDetectThemeMode } from "./hooks" import theme from "./theme" -import { LedgerWalletAPIProvider, WalletContextProvider } from "./contexts" +import { + LedgerWalletAPIProvider, + StakingFlowProvider, + WalletContextProvider, +} from "./contexts" import Header from "./components/Header" import Overview from "./components/Overview" @@ -23,9 +27,11 @@ function DAppProviders() { return ( - - - + + + + + ) diff --git a/dapp/src/components/Header/index.tsx b/dapp/src/components/Header/index.tsx index 838ed37b2..d78d97ef8 100644 --- a/dapp/src/components/Header/index.tsx +++ b/dapp/src/components/Header/index.tsx @@ -2,9 +2,11 @@ import React from "react" import { Flex } from "@chakra-ui/react" import ConnectWallet from "./ConnectWallet" +export const HEADER_HEIGHT = 24 + export default function Header() { return ( - + ) diff --git a/dapp/src/components/ModalOverlay/index.tsx b/dapp/src/components/ModalOverlay/index.tsx new file mode 100644 index 000000000..9ca30d1f1 --- /dev/null +++ b/dapp/src/components/ModalOverlay/index.tsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react" +import { Box, useColorModeValue } from "@chakra-ui/react" +import { useStakingFlowContext } from "../../hooks" + +const DELAY = 300 + +export default function ModalOverlay({ marginTop }: { marginTop?: number }) { + const { modalType } = useStakingFlowContext() + const [showOverlay, setShowOverlay] = useState(!modalType) + + useEffect(() => { + const timeout = setTimeout( + () => { + setShowOverlay(!!modalType) + }, + // When the modal opens, we should show the sidebar without delay. + // However, when the modal disappears, the overlay should disappear with some delay. + modalType ? 0 : DELAY, + ) + return () => clearTimeout(timeout) + }, [modalType]) + + return ( + + ) +} diff --git a/dapp/src/components/Modals/BaseModal.tsx b/dapp/src/components/Modals/BaseModal.tsx new file mode 100644 index 000000000..1f98f4345 --- /dev/null +++ b/dapp/src/components/Modals/BaseModal.tsx @@ -0,0 +1,23 @@ +import React from "react" +import { Modal, ModalCloseButton, ModalContent } from "@chakra-ui/react" +import { useStakingFlowContext } from "../../hooks" +import { HEADER_HEIGHT } from "../Header" + +export default function BaseModal({ children }: { children: React.ReactNode }) { + const { closeModal } = useStakingFlowContext() + + return ( + + + + {children} + + + ) +} diff --git a/dapp/src/components/Modals/ConnectWalletModal.tsx b/dapp/src/components/Modals/ConnectWalletModal.tsx new file mode 100644 index 000000000..5ca953960 --- /dev/null +++ b/dapp/src/components/Modals/ConnectWalletModal.tsx @@ -0,0 +1,44 @@ +import React from "react" +import { + Button, + Image, + ModalBody, + ModalFooter, + ModalHeader, + VStack, + Text, +} from "@chakra-ui/react" +import { useRequestBitcoinAccount } from "../../hooks" +import BaseModal from "./BaseModal" +import ConnectBTCAccount from "../../static/images/ConnectBTCAccount.png" + +export default function ConnectWalletModal() { + const { requestAccount } = useRequestBitcoinAccount() + + return ( + + + Bitcoin account not installed + + + + + + Bitcoin account is required to make transactions for depositing and + staking your BTC. + + + + + + + + ) +} diff --git a/dapp/src/components/Modals/StakingOverviewModal.tsx b/dapp/src/components/Modals/StakingOverviewModal.tsx new file mode 100644 index 000000000..e7cbcddb2 --- /dev/null +++ b/dapp/src/components/Modals/StakingOverviewModal.tsx @@ -0,0 +1,67 @@ +import React from "react" +import { + Button, + Flex, + ModalBody, + ModalFooter, + ModalHeader, + Step, + StepDescription, + StepIndicator, + StepNumber, + StepSeparator, + StepTitle, + Stepper, +} from "@chakra-ui/react" +import { useStakingFlowContext } from "../../hooks" +import BaseModal from "./BaseModal" + +const STEPS = [ + { + title: "Sign message", + description: + "You will sign a gas-free Ethereum message to indicate the address where you'd like to get your stBTC liquid staking token.", + }, + { + title: "Deposit BTC", + description: + "You will make a Bitcoin transaction to deposit and stake your BTC.", + }, +] + +export default function StakingOverviewModal() { + const { closeModal } = useStakingFlowContext() + + return ( + + Staking overview + + + {STEPS.map((step) => ( + + + + + + {step.title} + {step.description} + + + + ))} + + + + + + + ) +} diff --git a/dapp/src/components/Overview/PositionDetails.tsx b/dapp/src/components/Overview/PositionDetails.tsx index 972db661f..cfe51119c 100644 --- a/dapp/src/components/Overview/PositionDetails.tsx +++ b/dapp/src/components/Overview/PositionDetails.tsx @@ -10,34 +10,41 @@ import { } from "@chakra-ui/react" import { BITCOIN, USD } from "../../constants" import { Info } from "../../static/icons" +import Staking from "../Staking" +import { useStakingFlowContext } from "../../hooks" export default function PositionDetails() { + const { setModalType } = useStakingFlowContext() + return ( - - - Your positions - - - 34.75 - {BITCOIN.symbol} - - + <> + + + Your positions + - 1.245.148,1 - {USD.symbol} + 34.75 + {BITCOIN.symbol} - {/* TODO: Add correct text for tooltip */} - - - + + + 1.245.148,1 + {USD.symbol} + + {/* TODO: Add correct text for tooltip */} + + + + + + {/* TODO: Handle click actions */} + + + - - {/* TODO: Handle click actions */} - - - - + + ) } diff --git a/dapp/src/components/Staking/index.tsx b/dapp/src/components/Staking/index.tsx new file mode 100644 index 000000000..b4e56103b --- /dev/null +++ b/dapp/src/components/Staking/index.tsx @@ -0,0 +1,28 @@ +import React from "react" +import ConnectWalletModal from "../Modals/ConnectWalletModal" +import StakingOverviewModal from "../Modals/StakingOverviewModal" +import { useStakingFlowContext, useWalletContext } from "../../hooks" +import ModalOverlay from "../ModalOverlay" +import { HEADER_HEIGHT } from "../Header" + +function Modal() { + const { modalType } = useStakingFlowContext() + const { btcAccount } = useWalletContext() + + if (!modalType) return null + + if (!btcAccount) return + + if (modalType === "overview") return +} + +export default function Staking() { + return ( + <> + + {/* The user has several modals in a flow. + Let's use our own modal overlay to prevent the background flickering effect. */} + + + ) +} diff --git a/dapp/src/contexts/StakingFlowContext.tsx b/dapp/src/contexts/StakingFlowContext.tsx new file mode 100644 index 000000000..a58446787 --- /dev/null +++ b/dapp/src/contexts/StakingFlowContext.tsx @@ -0,0 +1,42 @@ +import React, { createContext, useCallback, useMemo, useState } from "react" +import { ModalType } from "../types" + +type StakingFlowContextValue = { + modalType: ModalType | undefined + closeModal: () => void + setModalType: React.Dispatch> +} + +export const StakingFlowContext = createContext({ + modalType: undefined, + setModalType: () => {}, + closeModal: () => {}, +}) + +export function StakingFlowProvider({ + children, +}: { + children: React.ReactNode +}): React.ReactElement { + const [modalType, setModalType] = useState(undefined) + + const closeModal = useCallback(() => { + setModalType(undefined) + }, []) + + const contextValue: StakingFlowContextValue = + useMemo( + () => ({ + modalType, + closeModal, + setModalType, + }), + [modalType, closeModal], + ) + + return ( + + {children} + + ) +} diff --git a/dapp/src/contexts/WalletContext.tsx b/dapp/src/contexts/WalletContext.tsx index f327ce0bd..63fb3faba 100644 --- a/dapp/src/contexts/WalletContext.tsx +++ b/dapp/src/contexts/WalletContext.tsx @@ -8,9 +8,12 @@ type WalletContextValue = { setEthAccount: React.Dispatch> } -export const WalletContext = createContext( - undefined, -) +export const WalletContext = createContext({ + ethAccount: undefined, + btcAccount: undefined, + setEthAccount: () => {}, + setBtcAccount: () => {}, +}) export function WalletContextProvider({ children, @@ -27,7 +30,7 @@ export function WalletContextProvider({ ethAccount, setEthAccount, }), - [btcAccount, setBtcAccount, ethAccount, setEthAccount], + [btcAccount, ethAccount], ) return ( diff --git a/dapp/src/contexts/index.tsx b/dapp/src/contexts/index.tsx index 3ea3058ed..9271773c0 100644 --- a/dapp/src/contexts/index.tsx +++ b/dapp/src/contexts/index.tsx @@ -1,2 +1,3 @@ export * from "./WalletContext" export * from "./LedgerWalletAPIProvider" +export * from "./StakingFlowContext" diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 038388476..b12025abb 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -2,3 +2,4 @@ export * from "./useDetectThemeMode" export * from "./useRequestBitcoinAccount" export * from "./useRequestEthereumAccount" export * from "./useWalletContext" +export * from "./useStakingFlowContext" diff --git a/dapp/src/hooks/useRequestBitcoinAccount.ts b/dapp/src/hooks/useRequestBitcoinAccount.ts index 971a3f9ef..b9cce07c2 100644 --- a/dapp/src/hooks/useRequestBitcoinAccount.ts +++ b/dapp/src/hooks/useRequestBitcoinAccount.ts @@ -5,12 +5,12 @@ import { UseRequestAccountReturn } from "../types" import { WalletContext } from "../contexts" export function useRequestBitcoinAccount(): UseRequestAccountReturn { - const walletContext = useContext(WalletContext) + const { setBtcAccount } = useContext(WalletContext) const { account, requestAccount } = useRequestAccount() useEffect(() => { - walletContext?.setBtcAccount(account || undefined) - }, [account, walletContext]) + setBtcAccount(account || undefined) + }, [account, setBtcAccount]) const requestBitcoinAccount = useCallback(async () => { await requestAccount({ currencyIds: [CURRENCY_ID_BITCOIN] }) diff --git a/dapp/src/hooks/useRequestEthereumAccount.ts b/dapp/src/hooks/useRequestEthereumAccount.ts index ebeb1f268..c780aea20 100644 --- a/dapp/src/hooks/useRequestEthereumAccount.ts +++ b/dapp/src/hooks/useRequestEthereumAccount.ts @@ -5,12 +5,12 @@ import { UseRequestAccountReturn } from "../types" import { WalletContext } from "../contexts" export function useRequestEthereumAccount(): UseRequestAccountReturn { - const walletContext = useContext(WalletContext) + const { setEthAccount } = useContext(WalletContext) const { account, requestAccount } = useRequestAccount() useEffect(() => { - walletContext?.setEthAccount(account || undefined) - }, [account, walletContext]) + setEthAccount(account || undefined) + }, [account, setEthAccount]) const requestEthereumAccount = useCallback(async () => { await requestAccount({ currencyIds: [CURRENCY_ID_ETHEREUM] }) diff --git a/dapp/src/hooks/useStakingFlowContext.ts b/dapp/src/hooks/useStakingFlowContext.ts new file mode 100644 index 000000000..870e15f71 --- /dev/null +++ b/dapp/src/hooks/useStakingFlowContext.ts @@ -0,0 +1,14 @@ +import { useContext } from "react" +import { StakingFlowContext } from "../contexts" + +export function useStakingFlowContext() { + const context = useContext(StakingFlowContext) + + if (!context) { + throw new Error( + "StakingFlowContext used outside of StakingFlowContext component", + ) + } + + return context +} diff --git a/dapp/src/static/images/ConnectBTCAccount.png b/dapp/src/static/images/ConnectBTCAccount.png new file mode 100644 index 0000000000000000000000000000000000000000..deff3c0cc0415a029d766cf59af2a38af1490a55 GIT binary patch literal 5287 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 literal 0 HcmV?d00001 diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index 1e77e81e7..32b259c7f 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -1,2 +1,3 @@ export * from "./ledger-live-app" export * from "./currency" +export * from "./staking" diff --git a/dapp/src/types/staking.ts b/dapp/src/types/staking.ts new file mode 100644 index 000000000..38c5d8098 --- /dev/null +++ b/dapp/src/types/staking.ts @@ -0,0 +1 @@ +export type ModalType = "overview" | "stake"