Skip to content

Commit

Permalink
Show error notifications (#313)
Browse files Browse the repository at this point in the history
## What has been done

This PR adds support for displaying notifications to the user. The
notifications will be displayed as toast. According to the
[styleguide](https://www.figma.com/file/OUdSfHsmgV8EJpWxAzXjl5/ACRE?node-id=5217%3A34321&mode=dev),
there are 3 different notification styles, so it was decided to update
the already existing styles. The reason for this change is also that the
Toast component is based on the Alert theme. However, to avoid repeating
the implemented logic, the previous styles are included in the
`CardAlert` component.

### `useToast` hook 

This hook is created to make it easier to work with the toast component.
It is a
[wrapper](chakra-ui/chakra-ui#3429 (comment))
for hook (`useToast`) coming from the Chakra UI library. Thanks to this,
we injected the basic common settings for all components. In addition,
the `open` function has been added to simplify the logic of opening only
one toast for a given id.

## UI

- CardAlert
<img width="442" alt="Screenshot 2024-03-13 at 13 51 38"
src="https://github.com/thesis/acre/assets/23117945/3b0b9686-726e-467c-9df2-2f7c1177a409">
<img width="443" alt="Screenshot 2024-03-13 at 13 56 10"
src="https://github.com/thesis/acre/assets/23117945/f210055b-0e10-446b-84e5-c3c58d4764cc">

- ReceiveSTBTCAlert
<img width="441" alt="Screenshot 2024-03-13 at 13 51 22"
src="https://github.com/thesis/acre/assets/23117945/9c0f7dcc-5709-472e-8306-55c76a9e028c">

- Toast
<img width="537" alt="Screenshot 2024-03-13 at 13 51 16"
src="https://github.com/thesis/acre/assets/23117945/ee2a04d1-2076-475f-9625-2f03af0ace28">



## Testing

**Missing accounts**
- [ ] Show notifications about not connected accounts after the app has
loaded.
- [ ] Remove notification when a user connects an account.
- [ ] The notifications are only shown once.

**Message signing and deposit transaction**

- [ ] Show a notification when something goes wrong with message
signing.
- [ ] Show a notification when something goes wrong with signing a
deposit transaction.
- [ ] Only one notification can be visible on the screen.
- [ ] Close the notification when the user moves to the next step.
  • Loading branch information
r-czajkowski authored Apr 16, 2024
2 parents 1a2dbab + 8190785 commit cde70d9
Show file tree
Hide file tree
Showing 47 changed files with 614 additions and 193 deletions.
24 changes: 0 additions & 24 deletions dapp/src/assets/icons/AlertInfo.tsx

This file was deleted.

1 change: 0 additions & 1 deletion dapp/src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export * from "./ArrowUpRight"
export * from "./ArrowLeft"
export * from "./ArrowRight"
export * from "./AcreLogo"
export * from "./AlertInfo"
export * from "./stBTC"
export * from "./BTC"
export * from "./ShieldPlus"
Expand Down
13 changes: 5 additions & 8 deletions dapp/src/components/Header/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import React from "react"
import { Button, HStack, Icon, Tooltip } from "@chakra-ui/react"
import {
useRequestBitcoinAccount,
useRequestEthereumAccount,
useWalletContext,
} from "#/hooks"
import { useWallet } from "#/hooks"
import { CurrencyBalance } from "#/components/shared/CurrencyBalance"
import { TextMd } from "#/components/shared/Typography"
import { BitcoinIcon, EthereumIcon } from "#/assets/icons"
Expand All @@ -30,9 +26,10 @@ const getCustomDataByAccount = (
}

export default function ConnectWallet() {
const { requestAccount: requestBitcoinAccount } = useRequestBitcoinAccount()
const { requestAccount: requestEthereumAccount } = useRequestEthereumAccount()
const { btcAccount, ethAccount } = useWalletContext()
const {
bitcoin: { account: btcAccount, requestAccount: requestBitcoinAccount },
ethereum: { account: ethAccount, requestAccount: requestEthereumAccount },
} = useWallet()

const customDataBtcAccount = getCustomDataByAccount(btcAccount)
const customDataEthAccount = getCustomDataByAccount(ethAccount)
Expand Down
12 changes: 6 additions & 6 deletions dapp/src/components/LiquidStakingTokenPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
import { SizeType } from "#/types"
import { useDocsDrawer, useSharesBalance, useWalletContext } from "#/hooks"
import { TextMd, TextSm } from "./shared/Typography"
import Alert from "./shared/Alert"
import { CurrencyBalance } from "./shared/CurrencyBalance"
import { CardAlert } from "./shared/alerts"

type LiquidStakingTokenPopoverProps = PopoverProps & { popoverSize: SizeType }

Expand Down Expand Up @@ -55,18 +55,18 @@ export function LiquidStakingTokenPopover({
variant="greater-balance-xl"
currency="stbtc"
/>
<Alert
<CardAlert
mt={5}
status="info"
withAlertIcon={false}
withActionIcon
onclick={openDocsDrawer}
withIcon={false}
withLink
onClick={openDocsDrawer}
>
<TextSm>
Your tokens are this Ethereum address once the staking transaction
is finalized.
</TextSm>
</Alert>
</CardAlert>
</PopoverBody>
</PopoverContent>
</Popover>
Expand Down
10 changes: 7 additions & 3 deletions dapp/src/components/TransactionHistory/Table/utils/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { StakeHistory } from "#/types"
import { capitalize, truncateAddress } from "#/utils"
import { capitalizeFirstLetter, truncateAddress } from "#/utils"
import CustomCell from "../Cell/Custom"
import Cell from "../Cell"
import SimpleText from "../Cell/components/SimpleText"
Expand Down Expand Up @@ -32,10 +32,14 @@ export const COLUMNS: ColumnDef<StakeHistory, any>[] = [
cell: ({ row: { original } }) => (
<Cell
firstField={
<SimpleText>{capitalize(original.callTx.action)}</SimpleText>
<SimpleText>
{capitalizeFirstLetter(original.callTx.action)}
</SimpleText>
}
secondField={
<SimpleText>{capitalize(original.receiptTx.action)}</SimpleText>
<SimpleText>
{capitalizeFirstLetter(original.receiptTx.action)}
</SimpleText>
}
/>
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
import React, { useCallback } from "react"
import React, { useCallback, useState } from "react"
import {
useDepositBTCTransaction,
useDepositTelemetry,
useExecuteFunction,
useModalFlowContext,
useStakeFlowContext,
useToast,
useTransactionContext,
useWalletContext,
} from "#/hooks"
import Alert from "#/components/shared/Alert"
import { TextMd } from "#/components/shared/Typography"
import { logPromiseFailure } from "#/utils"
import { PROCESS_STATUSES } from "#/types"
import { CardAlert } from "#/components/shared/alerts"
import { TOASTS, TOAST_IDS } from "#/types/toast"
import StakingStepsModalContent from "./StakingStepsModalContent"

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 { btcAddress, depositReceipt, stake } = useStakeFlowContext()
const depositTelemetry = useDepositTelemetry()
const { closeToast, openToast } = useToast()

const onStakeBTCSuccess = useCallback(() => {
setStatus(PROCESS_STATUSES.SUCCEEDED)
}, [setStatus])
const [isLoading, setIsLoading] = useState(false)
const [buttonText, setButtonText] = useState("Deposit BTC")

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

const onStakeBTCError = useCallback(() => {
setStatus(PROCESS_STATUSES.FAILED)
Expand All @@ -36,34 +46,51 @@ export default function DepositBTCModal() {
)

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

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

const { sendBitcoinTransaction } =
useDepositBTCTransaction(onDepositBTCSuccess)
const showError = useCallback(() => {
openToast({
id: TOAST_ID,
render: TOAST,
})
setButtonText("Try again")
}, [openToast])

const onDepositBTCError = useCallback(() => showError(), [showError])

const { sendBitcoinTransaction } = useDepositBTCTransaction(
onDepositBTCSuccess,
onDepositBTCError,
)

const handledDepositBTC = useCallback(async () => {
if (!tokenAmount?.amount || !btcAddress || !depositReceipt || !ethAccount)
return

setIsLoading(true)
const response = await depositTelemetry(
depositReceipt,
btcAddress,
ethAccount.address,
)
setIsLoading(false)

// TODO: Display the correct message for the user
if (response.verificationStatus !== "valid") return

logPromiseFailure(sendBitcoinTransaction(tokenAmount?.amount, btcAddress))
if (response.verificationStatus === "valid") {
logPromiseFailure(sendBitcoinTransaction(tokenAmount?.amount, btcAddress))
} else {
showError()
}
}, [
btcAddress,
depositReceipt,
depositTelemetry,
ethAccount,
sendBitcoinTransaction,
showError,
tokenAmount?.amount,
])

Expand All @@ -73,15 +100,16 @@ export default function DepositBTCModal() {

return (
<StakingStepsModalContent
buttonText="Deposit BTC"
buttonText={buttonText}
activeStep={1}
isLoading={isLoading}
onClick={handledDepositBTCWrapper}
>
<Alert>
<CardAlert status="error">
<TextMd>
Make a Bitcoin transaction to deposit and stake your BTC.
</TextMd>
</Alert>
</CardAlert>
</StakingStepsModalContent>
)
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,58 @@
import React, { useCallback, useEffect } from "react"
import React, { useCallback, useEffect, useState } from "react"
import {
useExecuteFunction,
useModalFlowContext,
useStakeFlowContext,
useToast,
} from "#/hooks"
import { logPromiseFailure } from "#/utils"
import AlertReceiveSTBTC from "#/components/shared/AlertReceiveSTBTC"
import { PROCESS_STATUSES } from "#/types"
import { PROCESS_STATUSES, TOASTS, TOAST_IDS } from "#/types"
import { ReceiveSTBTCAlert } from "#/components/shared/alerts"
import StakingStepsModalContent from "./StakingStepsModalContent"

const TOAST_ID = TOAST_IDS.SIGNING_ERROR
const TOAST = TOASTS[TOAST_ID]

export default function SignMessageModal() {
const { goNext, setStatus } = useModalFlowContext()
const { signMessage } = useStakeFlowContext()
const handleSignMessage = useExecuteFunction(signMessage, goNext)

const handleSignMessageWrapper = useCallback(() => {
logPromiseFailure(handleSignMessage())
}, [handleSignMessage])
const [buttonText, setButtonText] = useState("Sign now")
const { closeToast, openToast } = useToast()

useEffect(() => {
setStatus(PROCESS_STATUSES.PENDING)
}, [setStatus])

const onSignMessageSuccess = useCallback(() => {
closeToast(TOAST_ID)
goNext()
}, [closeToast, goNext])

const onSignMessageError = useCallback(() => {
openToast({
id: TOAST_ID,
render: TOAST,
})
setButtonText("Try again")
}, [openToast])

const handleSignMessage = useExecuteFunction(
signMessage,
onSignMessageSuccess,
onSignMessageError,
)

const handleSignMessageWrapper = useCallback(() => {
logPromiseFailure(handleSignMessage())
}, [handleSignMessage])

return (
<StakingStepsModalContent
buttonText="Sign now"
buttonText={buttonText}
activeStep={0}
onClick={handleSignMessageWrapper}
>
<AlertReceiveSTBTC />
<ReceiveSTBTCAlert />
</StakingStepsModalContent>
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import React from "react"
import {
Button,
HStack,
ModalBody,
ModalFooter,
ModalHeader,
} from "@chakra-ui/react"
import { HStack, ModalBody, ModalFooter, ModalHeader } from "@chakra-ui/react"
import { TextLg, TextMd } from "#/components/shared/Typography"
import StepperBase, { StepBase } from "#/components/shared/StepperBase"
import Spinner from "#/components/shared/Spinner"
import { LoadingButton } from "#/components/shared/LoadingButton"

export function Title({ children }: { children: React.ReactNode }) {
return <TextLg fontWeight="bold">{children}</TextLg>
Expand Down Expand Up @@ -43,12 +38,14 @@ const STEPS: StepBase[] = [

export default function StakingStepsModalContent({
buttonText,
isLoading,
activeStep,
onClick,
children,
}: {
buttonText: string
activeStep: number
isLoading?: boolean
onClick: () => void
children: React.ReactNode
}) {
Expand All @@ -68,9 +65,14 @@ export default function StakingStepsModalContent({
{children}
</ModalBody>
<ModalFooter>
<Button size="lg" width="100%" onClick={onClick}>
<LoadingButton
size="lg"
width="100%"
onClick={onClick}
isLoading={isLoading}
>
{buttonText}
</Button>
</LoadingButton>
</ModalFooter>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useExecuteFunction, useModalFlowContext } from "#/hooks"
import { PROCESS_STATUSES } from "#/types"
import { Button, ModalBody, ModalFooter, ModalHeader } from "@chakra-ui/react"
import { TextMd } from "#/components/shared/Typography"
import AlertReceiveSTBTC from "#/components/shared/AlertReceiveSTBTC"
import { logPromiseFailure } from "#/utils"
import { ReceiveSTBTCAlert } from "#/components/shared/alerts"

export default function SignMessageModal() {
const { setStatus } = useModalFlowContext()
Expand Down Expand Up @@ -46,7 +46,7 @@ export default function SignMessageModal() {
You will sign a gas-free Ethereum message to indicate the address
where you&apos;d like to get your stBTC liquid staking token.
</TextMd>
<AlertReceiveSTBTC />
<ReceiveSTBTCAlert />
</ModalBody>
<ModalFooter pt={10}>
<Button size="lg" width="100%" onClick={handleSignMessageWrapper}>
Expand Down
Loading

0 comments on commit cde70d9

Please sign in to comment.