Skip to content

Commit

Permalink
dApp: Port improvements from the Ledger Demo Testnet branch (#431)
Browse files Browse the repository at this point in the history
This PR ports improvement from the Ledger Demo Testnet
[branch](https://github.com/thesis/acre/tree/testnet-ledger-demo) to the
main. What has been changed:

- Redirect user to dashboard from modal success window 
- Redirect to block explorer for transaction from the modal success
window
-  Sort activities by timestamp
  • Loading branch information
kpyszkowski authored May 29, 2024
2 parents c699247 + fb41652 commit ae598d1
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from "react"
import React, { useCallback, useEffect } from "react"
import {
useActionFlowTokenAmount,
useAppDispatch,
Expand All @@ -18,7 +18,7 @@ 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"
import { setStatus, setTxHash } from "#/store/action-flow"

const DELAY = ONE_SEC_IN_MILLISECONDS
const TOAST_ID = TOAST_IDS.DEPOSIT_TRANSACTION_ERROR
Expand Down Expand Up @@ -64,11 +64,17 @@ export default function DepositBTCModal() {

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

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

useEffect(() => {
if (transactionHash) {
dispatch(setTxHash(transactionHash))
}
}, [dispatch, transactionHash])

const handledDepositBTC = useCallback(async () => {
if (!tokenAmount?.amount || !btcAddress || !depositReceipt || !ethAccount)
return
Expand Down
8 changes: 6 additions & 2 deletions dapp/src/components/TransactionModal/ModalContentWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react"
import {
useActionFlowStatus,
useActionFlowTokenAmount,
useActionFlowTxHash,
useActionFlowType,
useRequestBitcoinAccount,
useRequestEthereumAccount,
Expand All @@ -27,6 +28,7 @@ export default function ModalContentWrapper({
const status = useActionFlowStatus()
const type = useActionFlowType()
const tokenAmount = useActionFlowTokenAmount()
const txHash = useActionFlowTxHash()

if (!btcAccount || !isSupportedBTCAddressType(btcAccount.address))
return (
Expand All @@ -50,8 +52,10 @@ export default function ModalContentWrapper({

if (status === PROCESS_STATUSES.LOADING) return <LoadingModal />

if (status === PROCESS_STATUSES.SUCCEEDED)
return <SuccessModal type={type} tokenAmount={tokenAmount} />
if (status === PROCESS_STATUSES.SUCCEEDED && txHash)
return (
<SuccessModal type={type} tokenAmount={tokenAmount} txHash={txHash} />
)

if (status === PROCESS_STATUSES.FAILED) return <ErrorModal type={type} />

Expand Down
42 changes: 35 additions & 7 deletions dapp/src/components/TransactionModal/SuccessModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react"
import {
Button,
HStack,
Icon,
ModalBody,
ModalFooter,
ModalHeader,
Expand All @@ -11,6 +12,9 @@ import { LoadingSpinnerSuccessIcon } from "#/assets/icons"
import { useModal } from "#/hooks"
import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion"
import { ACTION_FLOW_TYPES, ActionFlowType, TokenAmount } from "#/types"
import { useNavigate } from "react-router-dom"
import { routerPath } from "#/router/path"
import { IconArrowUpRight } from "@tabler/icons-react"
import { TextMd } from "../shared/Typography"
import Spinner from "../shared/Spinner"
import BlockExplorerLink from "../shared/BlockExplorerLink"
Expand All @@ -19,13 +23,13 @@ const CONTENT: Record<
ActionFlowType,
{
header: string
renderBody: (tokenAmount: TokenAmount) => React.ReactNode
renderBody: (tokenAmount: TokenAmount, txHash: string) => React.ReactNode
footer: string
}
> = {
[ACTION_FLOW_TYPES.STAKE]: {
header: "Deposit received",
renderBody: (tokenAmount) => (
renderBody: (tokenAmount, txHash) => (
<>
<VStack spacing={0}>
<CurrencyBalanceWithConversion
Expand All @@ -42,8 +46,18 @@ const CONTENT: Record<
}}
/>
</VStack>
{/* TODO: Use correct tx hash and update styles */}
<BlockExplorerLink id="" type="transaction" chain="bitcoin" mt={2} />
{/* TODO: Update styles */}
<BlockExplorerLink
id={txHash}
type="transaction"
chain="bitcoin"
mt={2}
>
<HStack gap={1}>
<TextMd fontWeight="semibold">View on Mempool</TextMd>
<Icon as={IconArrowUpRight} color="brand.400" boxSize={5} />
</HStack>
</BlockExplorerLink>
</>
),
footer: "The staking will continue in the background",
Expand All @@ -63,10 +77,16 @@ const CONTENT: Record<
type SuccessModalProps = {
type: ActionFlowType
tokenAmount: TokenAmount
txHash: string
}

export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) {
export default function SuccessModal({
type,
tokenAmount,
txHash,
}: SuccessModalProps) {
const { closeModal } = useModal()
const navigate = useNavigate()

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

Expand All @@ -76,11 +96,19 @@ export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) {
<ModalBody gap={10}>
<VStack gap={4}>
<LoadingSpinnerSuccessIcon boxSize={20} />
{renderBody(tokenAmount)}
{renderBody(tokenAmount, txHash)}
</VStack>
</ModalBody>
<ModalFooter pt={0}>
<Button size="lg" width="100%" variant="outline" onClick={closeModal}>
<Button
size="lg"
width="100%"
variant="outline"
onClick={() => {
closeModal()
navigate(routerPath.dashboard)
}}
>
Go to dashboard
</Button>
<HStack spacing={2}>
Expand Down
4 changes: 2 additions & 2 deletions dapp/src/constants/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { EthereumNetwork, BitcoinNetwork } from "@acre-btc/sdk"

const BLOCK_EXPLORER_TESTNET = {
ethereum: { title: "Etherscan", url: "https://sepolia.etherscan.io" },
bitcoin: { title: "Mempool Space", url: "https://mempool.space/testnet" },
bitcoin: { title: "Mempool", url: "https://mempool.space/testnet" },
}

const BLOCK_EXPLORER_MAINNET = {
ethereum: { title: "Etherscan", url: "https://etherscan.io" },
bitcoin: { title: "Mempool Space", url: "https://mempool.space" },
bitcoin: { title: "Mempool", url: "https://mempool.space" },
}

export const BLOCK_EXPLORER: Record<Chain, { title: string; url: string }> =
Expand Down
1 change: 1 addition & 0 deletions dapp/src/hooks/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from "./useActionFlowType"
export * from "./useActionFlowStatus"
export * from "./useActionFlowActiveStep"
export * from "./useActionFlowTokenAmount"
export * from "./useActionFlowTxHash"
export * from "./useCompletedActivities"
export * from "./useLatestActivities"
6 changes: 6 additions & 0 deletions dapp/src/hooks/store/useActionFlowTxHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { selectActionFlowTxHash } from "#/store/action-flow"
import { useAppSelector } from "./useAppSelector"

export function useActionFlowTxHash() {
return useAppSelector(selectActionFlowTxHash)
}
3 changes: 3 additions & 0 deletions dapp/src/store/action-flow/actionFlowSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export const selectActionFlowStatus = (state: RootState): ProcessStatus =>
export const selectActionFlowTokenAmount = (
state: RootState,
): TokenAmount | undefined => state.actionFlow.tokenAmount

export const selectActionFlowTxHash = (state: RootState): string | undefined =>
state.actionFlow.txHash
16 changes: 14 additions & 2 deletions dapp/src/store/action-flow/actionFlowSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ type ActionFlowState = {
activeStep: number
status: ProcessStatus
tokenAmount?: TokenAmount
txHash?: string
}

const initialState: ActionFlowState = {
type: ACTION_FLOW_TYPES.STAKE,
activeStep: 1,
status: PROCESS_STATUSES.IDLE,
tokenAmount: undefined,
txHash: undefined,
}

export const actionFlowSlice = createSlice({
Expand All @@ -37,6 +39,9 @@ export const actionFlowSlice = createSlice({
setTokenAmount(state, action: PayloadAction<TokenAmount | undefined>) {
state.tokenAmount = action.payload
},
setTxHash(state, action: PayloadAction<string | undefined>) {
state.txHash = action.payload
},
goNextStep(state) {
state.activeStep += 1
},
Expand All @@ -45,9 +50,16 @@ export const actionFlowSlice = createSlice({
state.activeStep = initialState.activeStep
state.status = initialState.status
state.tokenAmount = initialState.tokenAmount
state.txHash = initialState.txHash
},
},
})

export const { setType, setStatus, setTokenAmount, goNextStep, resetState } =
actionFlowSlice.actions
export const {
setType,
setStatus,
setTokenAmount,
setTxHash,
goNextStep,
resetState,
} = actionFlowSlice.actions
9 changes: 6 additions & 3 deletions dapp/src/store/wallet/walletSelector.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { createSelector } from "@reduxjs/toolkit"
import { isActivityCompleted } from "#/utils"
import { isActivityCompleted, sortActivitiesByTimestamp } from "#/utils"
import { RootState } from ".."

export const selectLatestActivities = createSelector(
(state: RootState) => state.wallet.latestActivities,
(latestActivities) => Object.values(latestActivities),
(latestActivities) =>
sortActivitiesByTimestamp(Object.values(latestActivities)),
)

export const selectCompletedActivities = createSelector(
(state: RootState) => state.wallet.activities,
(activities) =>
activities.filter((activity) => isActivityCompleted(activity)),
sortActivitiesByTimestamp(
activities.filter((activity) => isActivityCompleted(activity)),
),
)
5 changes: 5 additions & 0 deletions dapp/src/utils/activities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ import { Activity } from "#/types"

export const isActivityCompleted = (activity: Activity): boolean =>
activity.status === "completed"

export const sortActivitiesByTimestamp = (activities: Activity[]): Activity[] =>
activities.sort(
(activity1, activity2) => activity1.timestamp - activity2.timestamp,
)

0 comments on commit ae598d1

Please sign in to comment.