Mp 3413 pending tx toasts (#478)
* MP-3413: added pending transaction toast * fix: removed txLoader/broadcastInitialized * fix: replace the toast instead of closing the previous * fix: fixed the build * MP-3413: added transition on update and success checkmark * fix: changed Search for ChevronDown
This commit is contained in:
parent
b04f244d3e
commit
74127213aa
@ -36,7 +36,7 @@ export default function AccountFundContent(props: Props) {
|
|||||||
const deposit = useStore((s) => s.deposit)
|
const deposit = useStore((s) => s.deposit)
|
||||||
const accounts = useStore((s) => s.accounts)
|
const accounts = useStore((s) => s.accounts)
|
||||||
const walletAssetModal = useStore((s) => s.walletAssetsModal)
|
const walletAssetModal = useStore((s) => s.walletAssetsModal)
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
const [isConfirming, setIsConfirming] = useState(false)
|
||||||
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
|
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
|
||||||
LEND_ASSETS_KEY,
|
LEND_ASSETS_KEY,
|
||||||
DEFAULT_SETTINGS.lendAssets,
|
DEFAULT_SETTINGS.lendAssets,
|
||||||
@ -76,18 +76,27 @@ export default function AccountFundContent(props: Props) {
|
|||||||
|
|
||||||
const handleClick = useCallback(async () => {
|
const handleClick = useCallback(async () => {
|
||||||
if (!props.accountId) return
|
if (!props.accountId) return
|
||||||
const result = await deposit({
|
|
||||||
|
const depositObject = {
|
||||||
accountId: props.accountId,
|
accountId: props.accountId,
|
||||||
coins: fundingAssets,
|
coins: fundingAssets,
|
||||||
lend: isLending,
|
lend: isLending,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (props.isFullPage) {
|
||||||
|
setIsConfirming(true)
|
||||||
|
const result = await deposit(depositObject)
|
||||||
|
setIsConfirming(false)
|
||||||
if (result)
|
if (result)
|
||||||
useStore.setState({
|
useStore.setState({
|
||||||
fundAndWithdrawModal: null,
|
|
||||||
walletAssetsModal: null,
|
walletAssetsModal: null,
|
||||||
focusComponent: null,
|
focusComponent: null,
|
||||||
})
|
})
|
||||||
}, [props.accountId, deposit, fundingAssets, isLending])
|
} else {
|
||||||
|
deposit(depositObject)
|
||||||
|
useStore.setState({ fundAndWithdrawModal: null, walletAssetsModal: null })
|
||||||
|
}
|
||||||
|
}, [props.accountId, deposit, fundingAssets, isLending, props.isFullPage])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
|
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
|
||||||
@ -171,7 +180,7 @@ export default function AccountFundContent(props: Props) {
|
|||||||
max={balance}
|
max={balance}
|
||||||
balances={balances}
|
balances={balances}
|
||||||
maxText='Max'
|
maxText='Max'
|
||||||
disabled={showTxLoader}
|
disabled={isConfirming}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -184,7 +193,7 @@ export default function AccountFundContent(props: Props) {
|
|||||||
rightIcon={<Plus />}
|
rightIcon={<Plus />}
|
||||||
iconClassName='w-3'
|
iconClassName='w-3'
|
||||||
onClick={handleSelectAssetsClick}
|
onClick={handleSelectAssetsClick}
|
||||||
disabled={showTxLoader}
|
disabled={isConfirming}
|
||||||
/>
|
/>
|
||||||
<DepositCapMessage
|
<DepositCapMessage
|
||||||
action='fund'
|
action='fund'
|
||||||
@ -203,7 +212,7 @@ export default function AccountFundContent(props: Props) {
|
|||||||
className='w-full mt-4'
|
className='w-full mt-4'
|
||||||
text='Fund account'
|
text='Fund account'
|
||||||
disabled={!hasFundingAssets || depositCapReachedCoins.length > 0}
|
disabled={!hasFundingAssets || depositCapReachedCoins.length > 0}
|
||||||
showProgressIndicator={showTxLoader}
|
showProgressIndicator={isConfirming}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
color={props.isFullPage ? 'tertiary' : undefined}
|
color={props.isFullPage ? 'tertiary' : undefined}
|
||||||
size={props.isFullPage ? 'lg' : undefined}
|
size={props.isFullPage ? 'lg' : undefined}
|
||||||
|
@ -57,7 +57,7 @@ export default function AccountMenuContent(props: Props) {
|
|||||||
}, [transactionFeeCoinBalance])
|
}, [transactionFeeCoinBalance])
|
||||||
|
|
||||||
const performCreateAccount = useCallback(async () => {
|
const performCreateAccount = useCallback(async () => {
|
||||||
setShowMenu(true)
|
setShowMenu(false)
|
||||||
setIsCreating(true)
|
setIsCreating(true)
|
||||||
const accountId = await createAccount()
|
const accountId = await createAccount()
|
||||||
setIsCreating(false)
|
setIsCreating(false)
|
||||||
|
55
src/components/CheckMark.tsx
Normal file
55
src/components/CheckMark.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import { CheckCircled } from 'components/Icons'
|
||||||
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
|
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||||
|
import useLocalStorage from 'hooks/useLocalStorage'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
color?: string
|
||||||
|
size?: number
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CheckMark = ({ color = '#FFFFFF', size = 20, className }: Props) => {
|
||||||
|
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||||
|
const classes = classNames('inline-block relative', className)
|
||||||
|
|
||||||
|
if (reduceMotion)
|
||||||
|
return (
|
||||||
|
<CheckCircled
|
||||||
|
className={classes}
|
||||||
|
style={{ width: `${size}px`, height: `${size}px`, color: `${color}` }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes} style={{ width: `${size}px`, height: `${size}px` }}>
|
||||||
|
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130.2 130.2'>
|
||||||
|
<circle
|
||||||
|
className='animate-circle'
|
||||||
|
fill='none'
|
||||||
|
strokeDasharray='1000'
|
||||||
|
strokeDashoffset='0'
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth='6'
|
||||||
|
strokeMiterlimit='10'
|
||||||
|
cx='65.1'
|
||||||
|
cy='65.1'
|
||||||
|
r='62.1'
|
||||||
|
/>
|
||||||
|
<polyline
|
||||||
|
className='animate-check'
|
||||||
|
fill='none'
|
||||||
|
strokeDasharray='1000'
|
||||||
|
strokeDashoffset='-100'
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth='6'
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeMiterlimit='10'
|
||||||
|
points='100.2,40.2 51.5,88.8 29.8,67.5 '
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import Text from 'components/Text'
|
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||||
import useLocalStorage from 'hooks/useLocalStorage'
|
import useLocalStorage from 'hooks/useLocalStorage'
|
||||||
@ -25,9 +24,12 @@ export const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Pr
|
|||||||
className={classNames('flex items-center', loaderClasses)}
|
className={classNames('flex items-center', loaderClasses)}
|
||||||
style={{ width: `${size}px`, height: `${size}px` }}
|
style={{ width: `${size}px`, height: `${size}px` }}
|
||||||
>
|
>
|
||||||
<Text className='text-center' uppercase size='lg'>
|
<p
|
||||||
|
className='w-full text-center'
|
||||||
|
style={{ fontSize: `${size}px`, lineHeight: `${size}px`, color: `${color}` }}
|
||||||
|
>
|
||||||
...
|
...
|
||||||
</Text>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { ReactNode, useEffect, useRef } from 'react'
|
|||||||
|
|
||||||
import EscButton from 'components/Button/EscButton'
|
import EscButton from 'components/Button/EscButton'
|
||||||
import Card from 'components/Card'
|
import Card from 'components/Card'
|
||||||
import TransactionLoader from 'components/TransactionLoader'
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -23,11 +22,9 @@ interface Props {
|
|||||||
export default function Modal(props: Props) {
|
export default function Modal(props: Props) {
|
||||||
const ref: React.RefObject<HTMLDialogElement> = useRef(null)
|
const ref: React.RefObject<HTMLDialogElement> = useRef(null)
|
||||||
const modalClassName = props.modalClassName ?? 'max-w-modal'
|
const modalClassName = props.modalClassName ?? 'max-w-modal'
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
ref.current?.close()
|
ref.current?.close()
|
||||||
useStore.setState({ showTxLoader: false })
|
|
||||||
props.onClose()
|
props.onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +39,6 @@ export default function Modal(props: Props) {
|
|||||||
return () => {
|
return () => {
|
||||||
dialog?.removeAttribute('open')
|
dialog?.removeAttribute('open')
|
||||||
dialog?.close()
|
dialog?.close()
|
||||||
useStore.setState({ showTxLoader: false })
|
|
||||||
document.body.classList.remove('h-screen', 'overflow-hidden')
|
document.body.classList.remove('h-screen', 'overflow-hidden')
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
@ -76,7 +72,6 @@ export default function Modal(props: Props) {
|
|||||||
'flex-1 overflow-y-scroll scrollbar-hide relative',
|
'flex-1 overflow-y-scroll scrollbar-hide relative',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{showTxLoader && !props.hideTxLoader && <TransactionLoader />}
|
|
||||||
{props.children ? props.children : props.content}
|
{props.children ? props.children : props.content}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -35,13 +35,11 @@ function AccountDeleteModal(props: Props) {
|
|||||||
useStore.setState({ accountDeleteModal: null })
|
useStore.setState({ accountDeleteModal: null })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const deleteAccountHandler = useCallback(async () => {
|
const deleteAccountHandler = useCallback(() => {
|
||||||
const options = { accountId: modal.id, lends: modal.lends }
|
const options = { accountId: modal.id, lends: modal.lends }
|
||||||
const isSuccess = await deleteAccount(options)
|
deleteAccount(options)
|
||||||
if (isSuccess) {
|
|
||||||
navigate(getRoute(getPage(pathname), address))
|
navigate(getRoute(getPage(pathname), address))
|
||||||
closeDeleteAccountModal()
|
closeDeleteAccountModal()
|
||||||
}
|
|
||||||
}, [modal, deleteAccount, navigate, pathname, address, closeDeleteAccountModal])
|
}, [modal, deleteAccount, navigate, pathname, address, closeDeleteAccountModal])
|
||||||
|
|
||||||
const depositsAndLends = useMemo(
|
const depositsAndLends = useMemo(
|
||||||
@ -92,7 +90,6 @@ function AccountDeleteModal(props: Props) {
|
|||||||
positiveButton={{
|
positiveButton={{
|
||||||
text: 'Delete Account',
|
text: 'Delete Account',
|
||||||
icon: <ArrowRight />,
|
icon: <ArrowRight />,
|
||||||
isAsync: true,
|
|
||||||
onClick: deleteAccountHandler,
|
onClick: deleteAccountHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -120,7 +117,6 @@ function AccountDeleteModal(props: Props) {
|
|||||||
positiveButton={{
|
positiveButton={{
|
||||||
text: 'Delete Account',
|
text: 'Delete Account',
|
||||||
icon: <ArrowRight />,
|
icon: <ArrowRight />,
|
||||||
isAsync: true,
|
|
||||||
onClick: deleteAccountHandler,
|
onClick: deleteAccountHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -23,20 +23,11 @@ interface Props {
|
|||||||
|
|
||||||
function AlertDialog(props: Props) {
|
function AlertDialog(props: Props) {
|
||||||
const { title, icon, description, negativeButton, positiveButton } = props.config
|
const { title, icon, description, negativeButton, positiveButton } = props.config
|
||||||
const [isConfirming, setIsConfirming] = useState(false)
|
|
||||||
const handleButtonClick = (button?: AlertDialogButton) => {
|
const handleButtonClick = (button?: AlertDialogButton) => {
|
||||||
button?.onClick && button.onClick()
|
button?.onClick && button.onClick()
|
||||||
props.close()
|
props.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAsyncButtonClick(button?: AlertDialogButton) {
|
|
||||||
if (!button?.onClick) return
|
|
||||||
setIsConfirming(true)
|
|
||||||
await button.onClick()
|
|
||||||
setIsConfirming(false)
|
|
||||||
props.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
onClose={props.close}
|
onClose={props.close}
|
||||||
@ -66,12 +57,7 @@ function AlertDialog(props: Props) {
|
|||||||
color='primary'
|
color='primary'
|
||||||
className='px-6'
|
className='px-6'
|
||||||
rightIcon={positiveButton.icon ?? <YesIcon />}
|
rightIcon={positiveButton.icon ?? <YesIcon />}
|
||||||
showProgressIndicator={isConfirming}
|
onClick={() => handleButtonClick(positiveButton)}
|
||||||
onClick={() =>
|
|
||||||
positiveButton.isAsync
|
|
||||||
? handleAsyncButtonClick(positiveButton)
|
|
||||||
: handleButtonClick(positiveButton)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
@ -79,7 +65,6 @@ function AlertDialog(props: Props) {
|
|||||||
color='secondary'
|
color='secondary'
|
||||||
className='px-6'
|
className='px-6'
|
||||||
rightIcon={negativeButton?.icon ?? <NoIcon />}
|
rightIcon={negativeButton?.icon ?? <NoIcon />}
|
||||||
disabled={isConfirming}
|
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
onClick={() => handleButtonClick(negativeButton)}
|
onClick={() => handleButtonClick(negativeButton)}
|
||||||
/>
|
/>
|
||||||
|
@ -19,7 +19,6 @@ interface Props {
|
|||||||
coinBalances: Coin[]
|
coinBalances: Coin[]
|
||||||
actionButtonText: string
|
actionButtonText: string
|
||||||
contentHeader?: JSX.Element
|
contentHeader?: JSX.Element
|
||||||
showProgressIndicator: boolean
|
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onChange: (value: BigNumber) => void
|
onChange: (value: BigNumber) => void
|
||||||
onAction: (value: BigNumber, isMax: boolean) => void
|
onAction: (value: BigNumber, isMax: boolean) => void
|
||||||
@ -32,7 +31,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
|
|||||||
coinBalances,
|
coinBalances,
|
||||||
actionButtonText,
|
actionButtonText,
|
||||||
contentHeader = null,
|
contentHeader = null,
|
||||||
showProgressIndicator,
|
|
||||||
onClose,
|
onClose,
|
||||||
onChange,
|
onChange,
|
||||||
onAction,
|
onAction,
|
||||||
@ -77,12 +75,10 @@ export default function AssetAmountSelectActionModal(props: Props) {
|
|||||||
max={maxAmount}
|
max={maxAmount}
|
||||||
hasSelect
|
hasSelect
|
||||||
maxText='Max'
|
maxText='Max'
|
||||||
disabled={showProgressIndicator}
|
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Button
|
<Button
|
||||||
onClick={handleActionClick}
|
onClick={handleActionClick}
|
||||||
showProgressIndicator={showProgressIndicator}
|
|
||||||
disabled={!amount.toNumber()}
|
disabled={!amount.toNumber()}
|
||||||
className='w-full'
|
className='w-full'
|
||||||
text={actionButtonText}
|
text={actionButtonText}
|
||||||
|
@ -56,7 +56,6 @@ export default function BorrowModalController() {
|
|||||||
function BorrowModal(props: Props) {
|
function BorrowModal(props: Props) {
|
||||||
const { modal, account } = props
|
const { modal, account } = props
|
||||||
const [amount, setAmount] = useState(BN_ZERO)
|
const [amount, setAmount] = useState(BN_ZERO)
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const [borrowToWallet, setBorrowToWallet] = useToggle()
|
const [borrowToWallet, setBorrowToWallet] = useToggle()
|
||||||
const borrow = useStore((s) => s.borrow)
|
const borrow = useStore((s) => s.borrow)
|
||||||
const repay = useStore((s) => s.repay)
|
const repay = useStore((s) => s.repay)
|
||||||
@ -79,33 +78,30 @@ function BorrowModal(props: Props) {
|
|||||||
setAmount(BN_ZERO)
|
setAmount(BN_ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfirmClick() {
|
function onConfirmClick() {
|
||||||
if (!asset) return
|
if (!asset) return
|
||||||
let result
|
|
||||||
const { lend } = getDepositAndLendCoinsToSpend(
|
const { lend } = getDepositAndLendCoinsToSpend(
|
||||||
BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||||
account,
|
account,
|
||||||
)
|
)
|
||||||
if (isRepay) {
|
if (isRepay) {
|
||||||
result = await repay({
|
repay({
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||||
accountBalance: amount.isEqualTo(totalDebtRepayAmount),
|
accountBalance: amount.isEqualTo(totalDebtRepayAmount),
|
||||||
lend,
|
lend,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
result = await borrow({
|
borrow({
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||||
borrowToWallet,
|
borrowToWallet,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
|
||||||
resetState()
|
resetState()
|
||||||
useStore.setState({ borrowModal: null })
|
useStore.setState({ borrowModal: null })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
resetState()
|
resetState()
|
||||||
@ -229,7 +225,6 @@ function BorrowModal(props: Props) {
|
|||||||
max={max}
|
max={max}
|
||||||
className='w-full'
|
className='w-full'
|
||||||
maxText='Max'
|
maxText='Max'
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
{!isRepay && (
|
{!isRepay && (
|
||||||
<>
|
<>
|
||||||
@ -245,7 +240,6 @@ function BorrowModal(props: Props) {
|
|||||||
name='borrow-to-wallet'
|
name='borrow-to-wallet'
|
||||||
checked={borrowToWallet}
|
checked={borrowToWallet}
|
||||||
onChange={setBorrowToWallet}
|
onChange={setBorrowToWallet}
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -254,7 +248,6 @@ function BorrowModal(props: Props) {
|
|||||||
<Button
|
<Button
|
||||||
onClick={onConfirmClick}
|
onClick={onConfirmClick}
|
||||||
className='w-full'
|
className='w-full'
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
disabled={amount.isZero()}
|
disabled={amount.isZero()}
|
||||||
text={isRepay ? 'Repay' : 'Borrow'}
|
text={isRepay ? 'Repay' : 'Borrow'}
|
||||||
rightIcon={<ArrowRight />}
|
rightIcon={<ArrowRight />}
|
||||||
|
@ -28,7 +28,6 @@ export default function WithdrawFromAccount(props: Props) {
|
|||||||
ASSETS.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? ASSETS[0]
|
ASSETS.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? ASSETS[0]
|
||||||
const withdraw = useStore((s) => s.withdraw)
|
const withdraw = useStore((s) => s.withdraw)
|
||||||
const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle()
|
const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle()
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
|
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
|
||||||
const [amount, setAmount] = useState(BN_ZERO)
|
const [amount, setAmount] = useState(BN_ZERO)
|
||||||
const { simulateWithdraw } = useUpdatedAccount(account)
|
const { simulateWithdraw } = useUpdatedAccount(account)
|
||||||
@ -56,13 +55,7 @@ export default function WithdrawFromAccount(props: Props) {
|
|||||||
setAmount(val)
|
setAmount(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetState() {
|
function onConfirm() {
|
||||||
setCurrentAsset(defaultAsset)
|
|
||||||
setAmount(BN_ZERO)
|
|
||||||
onChangeAmount(BN_ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onConfirm() {
|
|
||||||
const coins = [
|
const coins = [
|
||||||
{
|
{
|
||||||
coin: BNCoin.fromDenomAndBigNumber(currentAsset.denom, amount),
|
coin: BNCoin.fromDenomAndBigNumber(currentAsset.denom, amount),
|
||||||
@ -80,18 +73,14 @@ export default function WithdrawFromAccount(props: Props) {
|
|||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const result = await withdraw({
|
withdraw({
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
coins,
|
coins,
|
||||||
borrow,
|
borrow,
|
||||||
reclaims,
|
reclaims,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result) {
|
|
||||||
resetState()
|
|
||||||
useStore.setState({ fundAndWithdrawModal: null })
|
useStore.setState({ fundAndWithdrawModal: null })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const coin = BNCoin.fromDenomAndBigNumber(currentAsset.denom, withdrawAmount.plus(debtAmount))
|
const coin = BNCoin.fromDenomAndBigNumber(currentAsset.denom, withdrawAmount.plus(debtAmount))
|
||||||
@ -123,7 +112,6 @@ export default function WithdrawFromAccount(props: Props) {
|
|||||||
accountId={account.id}
|
accountId={account.id}
|
||||||
hasSelect
|
hasSelect
|
||||||
maxText='Max'
|
maxText='Max'
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
<Divider className='my-6' />
|
<Divider className='my-6' />
|
||||||
<div className='flex flex-wrap w-full'>
|
<div className='flex flex-wrap w-full'>
|
||||||
@ -138,18 +126,11 @@ export default function WithdrawFromAccount(props: Props) {
|
|||||||
name='borrow-to-wallet'
|
name='borrow-to-wallet'
|
||||||
checked={withdrawWithBorrowing}
|
checked={withdrawWithBorrowing}
|
||||||
onChange={setWithdrawWithBorrowing}
|
onChange={setWithdrawWithBorrowing}
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button onClick={onConfirm} className='w-full' text={'Withdraw'} rightIcon={<ArrowRight />} />
|
||||||
onClick={onConfirm}
|
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
className='w-full'
|
|
||||||
text={'Withdraw'}
|
|
||||||
rightIcon={<ArrowRight />}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
|||||||
const lend = useStore((s) => s.lend)
|
const lend = useStore((s) => s.lend)
|
||||||
const reclaim = useStore((s) => s.reclaim)
|
const reclaim = useStore((s) => s.reclaim)
|
||||||
const { close } = useLendAndReclaimModal()
|
const { close } = useLendAndReclaimModal()
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const { simulateLending } = useUpdatedAccount(currentAccount)
|
const { simulateLending } = useUpdatedAccount(currentAccount)
|
||||||
|
|
||||||
const { data, action } = config
|
const { data, action } = config
|
||||||
@ -45,14 +44,18 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const handleAction = useCallback(
|
const handleAction = useCallback(
|
||||||
async (value: BigNumber, isMax: boolean) => {
|
(value: BigNumber, isMax: boolean) => {
|
||||||
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
|
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
|
||||||
const options = {
|
const options = {
|
||||||
accountId: currentAccount.id,
|
accountId: currentAccount.id,
|
||||||
coin,
|
coin,
|
||||||
isMax,
|
isMax,
|
||||||
}
|
}
|
||||||
await (isLendAction ? lend : reclaim)(options)
|
if (isLendAction) {
|
||||||
|
lend(options)
|
||||||
|
} else {
|
||||||
|
reclaim(options)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
},
|
},
|
||||||
[asset.denom, close, currentAccount.id, isLendAction, lend, reclaim],
|
[asset.denom, close, currentAccount.id, isLendAction, lend, reclaim],
|
||||||
@ -63,7 +66,6 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
|||||||
contentHeader={<DetailsHeader data={data} />}
|
contentHeader={<DetailsHeader data={data} />}
|
||||||
coinBalances={coinBalances}
|
coinBalances={coinBalances}
|
||||||
actionButtonText={actionText}
|
actionButtonText={actionText}
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
title={`${actionText} ${asset.symbol}`}
|
title={`${actionText} ${asset.symbol}`}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
onAction={handleAction}
|
onAction={handleAction}
|
||||||
|
@ -12,12 +12,11 @@ interface Props {
|
|||||||
|
|
||||||
export default function UnlockModalContent(props: Props) {
|
export default function UnlockModalContent(props: Props) {
|
||||||
const unlock = useStore((s) => s.unlock)
|
const unlock = useStore((s) => s.unlock)
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const { accountId } = useParams()
|
const { accountId } = useParams()
|
||||||
|
|
||||||
async function onConfirm() {
|
function onConfirm() {
|
||||||
if (!accountId) return
|
if (!accountId) return
|
||||||
await unlock({
|
unlock({
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
vault: props.depositedVault,
|
vault: props.depositedVault,
|
||||||
amount: props.depositedVault.amounts.locked.toString(),
|
amount: props.depositedVault.amounts.locked.toString(),
|
||||||
@ -38,7 +37,6 @@ export default function UnlockModalContent(props: Props) {
|
|||||||
className='px-6'
|
className='px-6'
|
||||||
rightIcon={<YesIcon />}
|
rightIcon={<YesIcon />}
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text='No'
|
text='No'
|
||||||
@ -47,7 +45,6 @@ export default function UnlockModalContent(props: Props) {
|
|||||||
rightIcon={<NoIcon />}
|
rightIcon={<NoIcon />}
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
onClick={props.onClose}
|
onClick={props.onClose}
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -40,7 +40,6 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
const { data: prices } = usePrices()
|
const { data: prices } = usePrices()
|
||||||
const vaultModal = useStore((s) => s.vaultModal)
|
const vaultModal = useStore((s) => s.vaultModal)
|
||||||
const depositIntoVault = useStore((s) => s.depositIntoVault)
|
const depositIntoVault = useStore((s) => s.depositIntoVault)
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const updatedAccount = useStore((s) => s.updatedAccount)
|
const updatedAccount = useStore((s) => s.updatedAccount)
|
||||||
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
||||||
const [percentage, setPercentage] = useState<number>(0)
|
const [percentage, setPercentage] = useState<number>(0)
|
||||||
@ -143,19 +142,17 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
setPercentage(calculateSliderPercentage(maxBorrowAmounts, props.borrowings))
|
setPercentage(calculateSliderPercentage(maxBorrowAmounts, props.borrowings))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfirm() {
|
function onConfirm() {
|
||||||
if (!updatedAccount || !vaultModal) return
|
if (!updatedAccount || !vaultModal) return
|
||||||
const isSuccess = await depositIntoVault({
|
depositIntoVault({
|
||||||
accountId: updatedAccount.id,
|
accountId: updatedAccount.id,
|
||||||
actions: props.depositActions,
|
actions: props.depositActions,
|
||||||
deposits: props.deposits,
|
deposits: props.deposits,
|
||||||
borrowings: props.borrowings,
|
borrowings: props.borrowings,
|
||||||
isCreate: vaultModal.isCreate,
|
isCreate: vaultModal.isCreate,
|
||||||
})
|
})
|
||||||
if (isSuccess) {
|
|
||||||
useStore.setState({ vaultModal: null })
|
useStore.setState({ vaultModal: null })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col flex-1 gap-4 p-4'>
|
<div className='flex flex-col flex-1 gap-4 p-4'>
|
||||||
@ -173,13 +170,10 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
maxText='Max Borrow'
|
maxText='Max Borrow'
|
||||||
onChange={(amount) => updateAssets(coin.denom, amount)}
|
onChange={(amount) => updateAssets(coin.denom, amount)}
|
||||||
onDelete={() => onDelete(coin.denom)}
|
onDelete={() => onDelete(coin.denom)}
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{props.borrowings.length === 1 && (
|
{props.borrowings.length === 1 && <Slider onChange={onChangeSlider} value={percentage} />}
|
||||||
<Slider onChange={onChangeSlider} value={percentage} disabled={showTxLoader} />
|
|
||||||
)}
|
|
||||||
{props.borrowings.length === 0 && (
|
{props.borrowings.length === 0 && (
|
||||||
<div className='flex items-center gap-4 py-2'>
|
<div className='flex items-center gap-4 py-2'>
|
||||||
<div className='w-4'>
|
<div className='w-4'>
|
||||||
@ -192,12 +186,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button text='Select borrow assets +' color='tertiary' onClick={addAsset} />
|
||||||
text='Select borrow assets +'
|
|
||||||
color='tertiary'
|
|
||||||
onClick={addAsset}
|
|
||||||
disabled={showTxLoader}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DepositCapMessage
|
<DepositCapMessage
|
||||||
action='deposit'
|
action='deposit'
|
||||||
@ -233,7 +222,6 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
color='primary'
|
color='primary'
|
||||||
text='Deposit'
|
text='Deposit'
|
||||||
rightIcon={<ArrowRight />}
|
rightIcon={<ArrowRight />}
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
disabled={!props.depositActions.length || props.depositCapReachedCoins.length > 0}
|
disabled={!props.depositActions.length || props.depositCapReachedCoins.length > 0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,7 +18,6 @@ import { demagnify } from 'utils/formatters'
|
|||||||
export default function WithdrawFromVaultsModal() {
|
export default function WithdrawFromVaultsModal() {
|
||||||
const modal = useStore((s) => s.withdrawFromVaultsModal)
|
const modal = useStore((s) => s.withdrawFromVaultsModal)
|
||||||
const { accountId } = useParams()
|
const { accountId } = useParams()
|
||||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
|
||||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||||
@ -27,9 +26,9 @@ export default function WithdrawFromVaultsModal() {
|
|||||||
useStore.setState({ withdrawFromVaultsModal: null })
|
useStore.setState({ withdrawFromVaultsModal: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function withdrawHandler() {
|
function withdrawHandler() {
|
||||||
if (!accountId || !modal) return
|
if (!accountId || !modal) return
|
||||||
await withdrawFromVaults({
|
withdrawFromVaults({
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
vaults: modal,
|
vaults: modal,
|
||||||
slippage,
|
slippage,
|
||||||
@ -87,12 +86,7 @@ export default function WithdrawFromVaultsModal() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<Button
|
<Button onClick={withdrawHandler} className='w-full mt-4' text='Withdraw from all' />
|
||||||
showProgressIndicator={showTxLoader}
|
|
||||||
onClick={withdrawHandler}
|
|
||||||
className='w-full mt-4'
|
|
||||||
text='Withdraw from all'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
|
@ -6,8 +6,11 @@ import { ChevronDown, ChevronRight } from 'components/Icons'
|
|||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import { ASSETS } from 'constants/assets'
|
import { ASSETS } from 'constants/assets'
|
||||||
import { BN_ZERO } from 'constants/math'
|
import { BN_ZERO } from 'constants/math'
|
||||||
|
import useMarketAssets from 'hooks/useMarketAssets'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { byDenom } from 'utils/array'
|
||||||
import { formatValue } from 'utils/formatters'
|
import { formatValue } from 'utils/formatters'
|
||||||
|
import { convertAprToApy } from 'utils/parsers'
|
||||||
|
|
||||||
interface Props extends SelectOption {
|
interface Props extends SelectOption {
|
||||||
isSelected?: boolean
|
isSelected?: boolean
|
||||||
@ -19,6 +22,7 @@ interface Props extends SelectOption {
|
|||||||
|
|
||||||
export default function Option(props: Props) {
|
export default function Option(props: Props) {
|
||||||
const isCoin = !!props.denom
|
const isCoin = !!props.denom
|
||||||
|
const { data: marketAssets } = useMarketAssets()
|
||||||
|
|
||||||
function handleOnClick(value: string | undefined) {
|
function handleOnClick(value: string | undefined) {
|
||||||
if (!props.onClick || !value) return
|
if (!props.onClick || !value) return
|
||||||
@ -28,6 +32,7 @@ export default function Option(props: Props) {
|
|||||||
if (isCoin) {
|
if (isCoin) {
|
||||||
const asset = ASSETS.find((asset) => asset.denom === props.denom) ?? ASSETS[0]
|
const asset = ASSETS.find((asset) => asset.denom === props.denom) ?? ASSETS[0]
|
||||||
const balance = props.amount ?? BN_ZERO
|
const balance = props.amount ?? BN_ZERO
|
||||||
|
const marketAsset = marketAssets.find(byDenom(asset.denom))
|
||||||
|
|
||||||
if (props.isDisplay) {
|
if (props.isDisplay) {
|
||||||
return (
|
return (
|
||||||
@ -74,7 +79,12 @@ export default function Option(props: Props) {
|
|||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size='sm' className='col-span-2 text-white/50'>
|
<Text size='sm' className='col-span-2 text-white/50'>
|
||||||
{formatValue(5, { maxDecimals: 2, minDecimals: 0, prefix: 'APY ', suffix: '%' })}
|
{formatValue(convertAprToApy((marketAsset?.borrowRate ?? 0) * 100, 365), {
|
||||||
|
maxDecimals: 2,
|
||||||
|
minDecimals: 0,
|
||||||
|
prefix: 'APY ',
|
||||||
|
suffix: '%',
|
||||||
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
<DisplayCurrency
|
<DisplayCurrency
|
||||||
className='col-span-2 text-sm text-right text-white/50'
|
className='col-span-2 text-sm text-right text-white/50'
|
||||||
|
@ -104,7 +104,12 @@ export default function Select(props: Props) {
|
|||||||
{props.title}
|
{props.title}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{props.options.map((option: SelectOption, index: number) => (
|
<div className='w-full overflow-y-scroll h-70 scrollbar-hide'>
|
||||||
|
{props.options
|
||||||
|
.sort((a, b) =>
|
||||||
|
(a.amount?.toNumber() ?? 0) > (b.amount?.toNumber() ?? 0) ? -1 : 1,
|
||||||
|
)
|
||||||
|
.map((option: SelectOption, index: number) => (
|
||||||
<Option
|
<Option
|
||||||
key={index}
|
key={index}
|
||||||
{...option}
|
{...option}
|
||||||
@ -117,6 +122,7 @@ export default function Select(props: Props) {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
import classNames from 'classnames'
|
|
||||||
import { ReactNode } from 'react'
|
|
||||||
import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
|
|
||||||
import { mutate } from 'swr'
|
|
||||||
|
|
||||||
import { CheckCircled, Cross, CrossCircled, ExternalLink } from 'components/Icons'
|
|
||||||
import Text from 'components/Text'
|
|
||||||
import { TextLink } from 'components/TextLink'
|
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
|
||||||
import { EXPLORER_NAME, EXPLORER_TX_URL } from 'constants/explorer'
|
|
||||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
|
||||||
import useLocalStorage from 'hooks/useLocalStorage'
|
|
||||||
import useTransactionStore from 'hooks/useTransactionStore'
|
|
||||||
import useStore from 'store'
|
|
||||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
|
||||||
import { BN } from 'utils/helpers'
|
|
||||||
|
|
||||||
export function generateToastContent(content: ToastSuccess['content']): ReactNode {
|
|
||||||
return content.map((item, index) => (
|
|
||||||
<div className='flex flex-wrap w-full' key={index}>
|
|
||||||
{item.coins.length > 0 && (
|
|
||||||
<>
|
|
||||||
<Text size='sm' className='w-full mb-1 text-white'>
|
|
||||||
{item.text}
|
|
||||||
</Text>
|
|
||||||
<ul className='flex flex-wrap w-full gap-1 p-1 pl-4 list-disc'>
|
|
||||||
{item.coins.map((coin) =>
|
|
||||||
BN(coin.amount).isZero() ? null : (
|
|
||||||
<li className='w-full p-0 text-sm text-white' key={coin.denom}>
|
|
||||||
{formatAmountWithSymbol(coin)}
|
|
||||||
</li>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Toaster() {
|
|
||||||
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
|
||||||
const toast = useStore((s) => s.toast)
|
|
||||||
const isError = toast?.isError
|
|
||||||
const { addTransaction } = useTransactionStore()
|
|
||||||
|
|
||||||
if (toast) {
|
|
||||||
if (!isError) addTransaction(toast)
|
|
||||||
const Msg = () => (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'relative isolate m-0 flex w-full flex-wrap rounded-sm p-6 shadow-overlay backdrop-blur-lg',
|
|
||||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
|
|
||||||
isError ? 'bg-error-bg/20' : 'bg-success-bg/20',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className='flex w-full gap-2 mb-4'>
|
|
||||||
<div className={classNames('rounded-sm p-1.5', isError ? 'bg-error' : 'bg-success')}>
|
|
||||||
<span className='block w-4 h-4 text-white'>
|
|
||||||
{isError ? <CrossCircled /> : <CheckCircled />}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Text
|
|
||||||
className={classNames(
|
|
||||||
'flex items-center font-bold',
|
|
||||||
isError ? 'text-error' : 'text-success',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{isError ? (toast.title ? toast.title : 'Error') : 'Success'}
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
{!isError && toast.accountId && (
|
|
||||||
<Text className='mb-1 font-bold text-white'>{`Credit Account ${toast.accountId}`}</Text>
|
|
||||||
)}
|
|
||||||
{toast.message && (
|
|
||||||
<Text size='sm' className='w-full mb-1 text-white'>
|
|
||||||
{toast.message}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
{!isError && toast.content?.length > 0 && generateToastContent(toast.content)}
|
|
||||||
{toast.hash && (
|
|
||||||
<div className='w-full'>
|
|
||||||
<TextLink
|
|
||||||
href={`${EXPLORER_TX_URL}${toast.hash}`}
|
|
||||||
target='_blank'
|
|
||||||
className={classNames(
|
|
||||||
'leading-4 underline mt-4 hover:no-underline hover:text-white',
|
|
||||||
isError ? 'text-error' : 'text-success',
|
|
||||||
)}
|
|
||||||
title={`View on ${EXPLORER_NAME}`}
|
|
||||||
>
|
|
||||||
{`View on ${EXPLORER_NAME}`}
|
|
||||||
<ExternalLink className='-mt-0.5 ml-2 inline w-3.5' />
|
|
||||||
</TextLink>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className='absolute right-6 top-8 '>
|
|
||||||
<Cross className={classNames('h-2 w-2', isError ? 'text-error' : 'text-success')} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
createToast(Msg, {
|
|
||||||
icon: false,
|
|
||||||
draggable: false,
|
|
||||||
closeOnClick: true,
|
|
||||||
progressClassName: classNames('h-[1px] bg-none', isError ? 'bg-error' : 'bg-success'),
|
|
||||||
})
|
|
||||||
|
|
||||||
useStore.setState({ toast: null })
|
|
||||||
mutate(() => true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToastContainer
|
|
||||||
autoClose={5000}
|
|
||||||
closeButton={false}
|
|
||||||
position='top-right'
|
|
||||||
newestOnTop
|
|
||||||
closeOnClick
|
|
||||||
transition={reduceMotion ? undefined : Slide}
|
|
||||||
className='p-0'
|
|
||||||
toastClassName='top-[73px] z-20 m-0 mb-4 flex w-full bg-transparent p-0'
|
|
||||||
bodyClassName='p-0 m-0 w-full flex -z-1'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
215
src/components/Toaster/index.tsx
Normal file
215
src/components/Toaster/index.tsx
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
import { Slide, ToastContainer, toast as toastify } from 'react-toastify'
|
||||||
|
import { mutate } from 'swr'
|
||||||
|
|
||||||
|
import { CheckMark } from 'components/CheckMark'
|
||||||
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
|
import { ChevronDown, Cross, CrossCircled, ExternalLink } from 'components/Icons'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import { TextLink } from 'components/TextLink'
|
||||||
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
|
import { EXPLORER_NAME, EXPLORER_TX_URL } from 'constants/explorer'
|
||||||
|
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||||
|
import useLocalStorage from 'hooks/useLocalStorage'
|
||||||
|
import useTransactionStore from 'hooks/useTransactionStore'
|
||||||
|
import useStore from 'store'
|
||||||
|
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
const toastBodyClasses = classNames(
|
||||||
|
'flex flex-wrap w-full group/transaction',
|
||||||
|
'rounded-sm p-4 shadow-overlay backdrop-blur-lg',
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
|
||||||
|
)
|
||||||
|
|
||||||
|
function isPromise(object?: any): object is ToastPending {
|
||||||
|
if (!object) return false
|
||||||
|
return 'promise' in object
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateToastContent(content: ToastSuccess['content']): ReactNode {
|
||||||
|
return content.map((item, index) => (
|
||||||
|
<div className='flex flex-wrap w-full' key={index}>
|
||||||
|
{item.coins.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Text size='sm' className='w-full mb-1 text-white'>
|
||||||
|
{item.text}
|
||||||
|
</Text>
|
||||||
|
<ul className='flex flex-wrap w-full gap-1 p-1 pl-4 list-disc'>
|
||||||
|
{item.coins.map((coin) =>
|
||||||
|
BN(coin.amount).isZero() ? null : (
|
||||||
|
<li className='w-full p-0 text-sm text-white' key={coin.denom}>
|
||||||
|
{formatAmountWithSymbol(coin)}
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Toaster() {
|
||||||
|
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||||
|
const toast = useStore((s) => s.toast)
|
||||||
|
const { addTransaction } = useTransactionStore()
|
||||||
|
|
||||||
|
const handlePending = (toast: ToastPending) => {
|
||||||
|
const Content = () => (
|
||||||
|
<div className='relative flex flex-wrap w-full m-0 isolate'>
|
||||||
|
<div className='flex items-center w-full gap-2 mb-2'>
|
||||||
|
<div className='rounded-sm p-1.5 pt-1 bg-info w-7 h-7 flex items-center'>
|
||||||
|
<CircularProgress size={16} />
|
||||||
|
</div>
|
||||||
|
<Text className='flex items-center font-bold text-info'>Pending Transaction</Text>
|
||||||
|
</div>
|
||||||
|
<Text size='sm' className='w-full text-white'>
|
||||||
|
Approve the transaction
|
||||||
|
<br />
|
||||||
|
and wait for its confirmation.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
toastify(Content, {
|
||||||
|
toastId: toast.id,
|
||||||
|
className: classNames(toastBodyClasses, 'toast-pending'),
|
||||||
|
icon: false,
|
||||||
|
draggable: false,
|
||||||
|
closeOnClick: false,
|
||||||
|
hideProgressBar: true,
|
||||||
|
autoClose: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResponse = (toast: ToastResponse, details?: boolean) => {
|
||||||
|
const isError = toast?.isError
|
||||||
|
if (!isError) addTransaction(toast)
|
||||||
|
const generalMessage = isError ? 'Transaction failed!' : 'Transaction completed successfully!'
|
||||||
|
const showDetailElement = !!(!details && toast.hash)
|
||||||
|
const Msg = () => (
|
||||||
|
<div className='relative flex flex-wrap w-full m-0 isolate'>
|
||||||
|
<div className='flex w-full gap-2 mb-2'>
|
||||||
|
{isError ? (
|
||||||
|
<div className='rounded-sm p-1.5 bg-error'>
|
||||||
|
<span className='block w-4 h-4 text-white'>
|
||||||
|
<CrossCircled />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='rounded-sm p-1.5 pt-1 bg-success w-7 h-7 flex items-center'>
|
||||||
|
<CheckMark size={16} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center font-bold',
|
||||||
|
isError ? 'text-error' : 'text-success',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isError ? (toast.title ? toast.title : 'Error') : 'Success'}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<Text size='sm' className='w-full mb-2 text-white'>
|
||||||
|
{showDetailElement ? generalMessage : toast.message}
|
||||||
|
</Text>
|
||||||
|
{showDetailElement && (
|
||||||
|
<Text
|
||||||
|
size='sm'
|
||||||
|
className='flex items-center w-auto pb-0.5 text-white border-b border-white/40 border-dashed group-hover/transaction:opacity-0'
|
||||||
|
>
|
||||||
|
<ChevronDown className='w-3 mr-1' />
|
||||||
|
Transaction Details
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'w-full flex-wrap',
|
||||||
|
showDetailElement && 'hidden group-hover/transaction:flex',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{!isError && toast.accountId && (
|
||||||
|
<Text className='mb-1 font-bold text-white'>{`Credit Account ${toast.accountId}`}</Text>
|
||||||
|
)}
|
||||||
|
{showDetailElement && toast.message && (
|
||||||
|
<Text size='sm' className='w-full mb-1 text-white'>
|
||||||
|
{toast.message}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{!isError && toast.content?.length > 0 && generateToastContent(toast.content)}
|
||||||
|
{toast.hash && (
|
||||||
|
<div className='w-full'>
|
||||||
|
<TextLink
|
||||||
|
href={`${EXPLORER_TX_URL}${toast.hash}`}
|
||||||
|
target='_blank'
|
||||||
|
className={classNames(
|
||||||
|
'leading-4 underline mt-4 hover:no-underline hover:text-white',
|
||||||
|
isError ? 'text-error' : 'text-success',
|
||||||
|
)}
|
||||||
|
title={`View on ${EXPLORER_NAME}`}
|
||||||
|
>
|
||||||
|
{`View on ${EXPLORER_NAME}`}
|
||||||
|
<ExternalLink className='-mt-0.5 ml-2 inline w-3.5' />
|
||||||
|
</TextLink>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='absolute top-0 right-0'>
|
||||||
|
<Cross className={classNames('h-2 w-2', isError ? 'text-error' : 'text-success')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const toastElement = document.getElementById(String(toast.id))
|
||||||
|
|
||||||
|
if (toastElement) {
|
||||||
|
toastify.update(toast.id, {
|
||||||
|
render: Msg,
|
||||||
|
className: toastBodyClasses,
|
||||||
|
type: isError ? 'error' : 'success',
|
||||||
|
icon: false,
|
||||||
|
draggable: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
autoClose: 5000,
|
||||||
|
progressClassName: classNames('h-[1px] bg-none', isError ? 'bg-error' : 'bg-success'),
|
||||||
|
hideProgressBar: false,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toastify(Msg, {
|
||||||
|
toastId: toast.id,
|
||||||
|
className: toastBodyClasses,
|
||||||
|
type: isError ? 'error' : 'success',
|
||||||
|
icon: false,
|
||||||
|
draggable: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
autoClose: 5000,
|
||||||
|
progressClassName: classNames('h-[1px] bg-none', isError ? 'bg-error' : 'bg-success'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useStore.setState({ toast: null })
|
||||||
|
mutate(() => true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toast) {
|
||||||
|
if (isPromise(toast)) {
|
||||||
|
handlePending(toast)
|
||||||
|
} else {
|
||||||
|
handleResponse(toast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastContainer
|
||||||
|
closeButton={false}
|
||||||
|
position='top-right'
|
||||||
|
newestOnTop
|
||||||
|
closeOnClick={false}
|
||||||
|
transition={reduceMotion ? undefined : Slide}
|
||||||
|
bodyClassName='p-0 m-0 -z-1'
|
||||||
|
className='mt-[73px]'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -1,307 +0,0 @@
|
|||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
import Text from 'components/Text'
|
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
|
||||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
|
||||||
import useLocalStorage from 'hooks/useLocalStorage'
|
|
||||||
|
|
||||||
export default function TransactionLoader() {
|
|
||||||
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='absolute z-50 flex flex-wrap items-center content-center justify-center w-full h-full text-white bg-black/80'>
|
|
||||||
<div className='w-[120px] h-[120px]'>
|
|
||||||
<svg version='1.1' x='0px' y='0px' viewBox='0 0 120 120'>
|
|
||||||
<path
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeWidth='1'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
d='M104.5,27.7L92.3,15.5L77,7.7L60,5
|
|
||||||
L43,7.7l-15.3,7.8L15.5,27.7L7.7,43L5,60l2.7,17l7.8,15.3l12.2,12.2l15.3,7.8l17,2.7l17-2.7l15.3-7.8l12.2-12.2l7.8-15.3l2.7-17
|
|
||||||
l-2.7-17L104.5,27.7z'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[700ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='60'
|
|
||||||
y1='60'
|
|
||||||
x2='27.7'
|
|
||||||
y2='15.5'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[900ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='60'
|
|
||||||
y1='60'
|
|
||||||
x2='92.3'
|
|
||||||
y2='15.5'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[1100ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='112.3'
|
|
||||||
y1='77'
|
|
||||||
x2='60'
|
|
||||||
y2='60'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[1300ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='60'
|
|
||||||
y1='115'
|
|
||||||
x2='60'
|
|
||||||
y2='60'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[1500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='7.7'
|
|
||||||
y1='77'
|
|
||||||
x2='60'
|
|
||||||
y2='60'
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[1800ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
points='47.5,42.9 72.3,43 80.1,66.5 60,81.1
|
|
||||||
39.8,66.5 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2100ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
points='60,22.1 47.5,42.9 23.9,48.3 39.8,66.5
|
|
||||||
37.7,90.7 60,81.1 82.3,90.7 80.1,66.5 96.1,48.3 72.3,43 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2400ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
points='34.4,24.8 60,22.1 85.5,24.8 96.1,48.3
|
|
||||||
101.3,73.4 82.3,90.7 60,103.5 37.7,90.7 18.6,73.4 23.9,48.3 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeLinejoin='round'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
points='34.4,24.8 43,7.7
|
|
||||||
60,22.1 77,7.7 85.5,24.8 104.5,27.7 96.1,48.3 115,60 101.3,73.4 104.5,92.3 82.3,90.7 77,112.3 60,103.5 43,112.3 37.7,90.7
|
|
||||||
15.5,92.3 18.6,73.4 5,60 23.9,48.3 15.5,27.7 '
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='60'
|
|
||||||
y1='5'
|
|
||||||
x2='60'
|
|
||||||
y2='22.1'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='112.3'
|
|
||||||
y1='43'
|
|
||||||
x2='96.1'
|
|
||||||
y2='48.3'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='92.3'
|
|
||||||
y1='104.5'
|
|
||||||
x2='82.3'
|
|
||||||
y2='90.7'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='27.7'
|
|
||||||
y1='104.5'
|
|
||||||
x2='37.7'
|
|
||||||
y2='90.7'
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'opacity-0 animate-loaderFade',
|
|
||||||
'animate-delay-[2500ms]',
|
|
||||||
)}
|
|
||||||
fill='none'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeLinejoin='round'
|
|
||||||
strokeMiterlimit='10'
|
|
||||||
x1='7.7'
|
|
||||||
y1='43'
|
|
||||||
x2='23.9'
|
|
||||||
y2='48.3'
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[5500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='60,60 72.3,43 80.1,66.5 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[11000ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='60,22.1 47.5,42.9 34.4,24.8 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[16500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='39.8,66.5 37.7,90.7 60,81.1 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[22000ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='18.6,73.4 23.9,48.3 5,60 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[27500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='82.3,90.7 101.3,73.4 104.5,92.3 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[33000ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='96.1,48.3 104.5,27.7 112.3,43 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[38500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='85.5,24.8 77,7.7 91.9,15.3 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[44000ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='60,103.5 82.3,90.7 60,81.1 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[49500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='15.8,92.3 37.7,90.7 18.6,73.4 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[55000ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='15.8,27.4 34.4,24.8 23.9,48.3 '
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
className={classNames(
|
|
||||||
!reduceMotion && 'animate-loaderGlow',
|
|
||||||
'opacity-0 animate-delay-[60500ms]',
|
|
||||||
)}
|
|
||||||
fill='currentColor'
|
|
||||||
points='43,7.7 60,5 60,22.1 '
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<Text
|
|
||||||
className={classNames(
|
|
||||||
'p-4 text-center text-white/70 w-full',
|
|
||||||
!reduceMotion && 'animate-fadein delay-4000',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Broadcasting transaction...
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ import { useShuttle } from '@delphi-labs/shuttle-react'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import QRCode from 'react-qr-code'
|
import QRCode from 'react-qr-code'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import FullOverlayContent from 'components/FullOverlayContent'
|
import FullOverlayContent from 'components/FullOverlayContent'
|
||||||
@ -104,6 +105,7 @@ export default function WalletSelect(props: Props) {
|
|||||||
if (error?.message && error?.title) {
|
if (error?.message && error?.title) {
|
||||||
useStore.setState({
|
useStore.setState({
|
||||||
toast: {
|
toast: {
|
||||||
|
id: moment.now(),
|
||||||
isError: true,
|
isError: true,
|
||||||
title: error.title,
|
title: error.title,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
@ -8,12 +8,12 @@ import Footer from 'components/Footer'
|
|||||||
import DesktopHeader from 'components/Header/DesktopHeader'
|
import DesktopHeader from 'components/Header/DesktopHeader'
|
||||||
import ModalsContainer from 'components/Modals/ModalsContainer'
|
import ModalsContainer from 'components/Modals/ModalsContainer'
|
||||||
import PageMetadata from 'components/PageMetadata'
|
import PageMetadata from 'components/PageMetadata'
|
||||||
import Toaster from 'components/Toaster'
|
|
||||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
import useLocalStorage from 'hooks/useLocalStorage'
|
import useLocalStorage from 'hooks/useLocalStorage'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import Toaster from 'components/Toaster'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
focusComponent: FocusComponent | null
|
focusComponent: FocusComponent | null
|
||||||
|
@ -41,11 +41,13 @@ export default function createBroadcastSlice(
|
|||||||
get: GetState<Store>,
|
get: GetState<Store>,
|
||||||
): BroadcastSlice {
|
): BroadcastSlice {
|
||||||
const handleResponseMessages = (props: HandleResponseProps) => {
|
const handleResponseMessages = (props: HandleResponseProps) => {
|
||||||
const { accountId, response, action, lend, changes, target, message } = props
|
const { id, accountId, response, action, lend, changes, target, message } = props
|
||||||
|
if (!response) return
|
||||||
|
|
||||||
if (response.error || response.result?.response.code !== 0) {
|
if (response.error || response.result?.response.code !== 0) {
|
||||||
set({
|
set({
|
||||||
toast: {
|
toast: {
|
||||||
|
id,
|
||||||
message: generateErrorMessage(response),
|
message: generateErrorMessage(response),
|
||||||
isError: true,
|
isError: true,
|
||||||
hash: response.result?.hash,
|
hash: response.result?.hash,
|
||||||
@ -55,6 +57,7 @@ export default function createBroadcastSlice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toast: ToastResponse = {
|
const toast: ToastResponse = {
|
||||||
|
id,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
isError: false,
|
isError: false,
|
||||||
hash: response?.result?.hash,
|
hash: response?.result?.hash,
|
||||||
@ -152,7 +155,6 @@ export default function createBroadcastSlice(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
toast: null,
|
toast: null,
|
||||||
showTxLoader: false,
|
|
||||||
borrow: async (options: { accountId: string; coin: BNCoin; borrowToWallet: boolean }) => {
|
borrow: async (options: { accountId: string; coin: BNCoin; borrowToWallet: boolean }) => {
|
||||||
const borrowAction: Action = { borrow: options.coin.toCoin() }
|
const borrowAction: Action = { borrow: options.coin.toCoin() }
|
||||||
const withdrawAction: Action = { withdraw: options.coin.toActionCoin() }
|
const withdrawAction: Action = { withdraw: options.coin.toActionCoin() }
|
||||||
@ -174,48 +176,45 @@ export default function createBroadcastSlice(
|
|||||||
lend: { denom: options.coin.denom, amount: 'account_balance' },
|
lend: { denom: options.coin.denom, amount: 'account_balance' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'borrow',
|
action: 'borrow',
|
||||||
lend: checkAutoLendEnabled(options.accountId),
|
lend: checkAutoLendEnabled(options.accountId),
|
||||||
target: options.borrowToWallet ? 'wallet' : 'account',
|
target: options.borrowToWallet ? 'wallet' : 'account',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { debts: [options.coin] },
|
changes: { debts: [options.coin] },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
createAccount: async () => {
|
createAccount: async () => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
create_credit_account: 'default',
|
create_credit_account: 'default',
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
set({ createAccountModal: false })
|
get().setToast({
|
||||||
const id = response.result
|
|
||||||
? getSingleValueFromBroadcastResult(response.result, 'wasm', 'token_id')
|
|
||||||
: null
|
|
||||||
|
|
||||||
handleResponseMessages({
|
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
accountId: id ?? undefined,
|
message: `Created the Credit Account`,
|
||||||
message: id ? `Created the Credit Account` : undefined,
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (id)
|
return response.then((response) =>
|
||||||
set({
|
response.result
|
||||||
fundAccountModal: true,
|
? getSingleValueFromBroadcastResult(response.result, 'wasm', 'token_id')
|
||||||
})
|
: null,
|
||||||
|
)
|
||||||
return id
|
|
||||||
},
|
},
|
||||||
deleteAccount: async (options: { accountId: string; lends: BNCoin[] }) => {
|
deleteAccount: async (options: { accountId: string; lends: BNCoin[] }) => {
|
||||||
const reclaimMsg = options.lends.map((coin) => {
|
const reclaimMsg = options.lends.map((coin) => {
|
||||||
@ -237,21 +236,23 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [
|
messages: [
|
||||||
generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, refundMessage, []),
|
generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, refundMessage, []),
|
||||||
generateExecutionMessage(get().address, ENV.ADDRESS_ACCOUNT_NFT, burnMessage, []),
|
generateExecutionMessage(get().address, ENV.ADDRESS_ACCOUNT_NFT, burnMessage, []),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
message: `Deleted the Credit Account`,
|
message: `Deleted the Credit Account`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
claimRewards: (options: { accountId: string }) => {
|
claimRewards: (options: { accountId: string }) => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
@ -271,18 +272,20 @@ export default function createBroadcastSlice(
|
|||||||
const estimateFee = () => getEstimatedFee(messages)
|
const estimateFee = () => getEstimatedFee(messages)
|
||||||
|
|
||||||
const execute = async () => {
|
const execute = async () => {
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages,
|
messages,
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
action: 'create',
|
options: {
|
||||||
|
action: 'claim',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
message: `Claimed rewards`,
|
message: `Claimed rewards`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { estimateFee, execute }
|
return { estimateFee, execute }
|
||||||
@ -307,19 +310,21 @@ export default function createBroadcastSlice(
|
|||||||
|
|
||||||
const funds = options.coins.map((coin) => coin.toCoin())
|
const funds = options.coins.map((coin) => coin.toCoin())
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, funds)],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, funds)],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'deposit',
|
action: 'deposit',
|
||||||
lend: options.lend,
|
lend: options.lend,
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { deposits: options.coins },
|
changes: { deposits: options.coins },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
unlock: async (options: { accountId: string; vault: DepositedVault; amount: string }) => {
|
unlock: async (options: { accountId: string; vault: DepositedVault; amount: string }) => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
@ -336,17 +341,20 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'unlock',
|
action: 'unlock',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
message: `Requested unlock for ${options.vault.name}`,
|
message: `Requested unlock for ${options.vault.name}`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return !!response.result
|
|
||||||
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
|
|
||||||
withdrawFromVaults: async (options: {
|
withdrawFromVaults: async (options: {
|
||||||
@ -391,18 +399,22 @@ export default function createBroadcastSlice(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
const vaultsString = options.vaults.length === 1 ? 'vault' : 'vaults'
|
const vaultsString = options.vaults.length === 1 ? 'vault' : 'vaults'
|
||||||
handleResponseMessages({
|
|
||||||
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'withdraw',
|
action: 'withdraw',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
message: `Withdrew ${options.vaults.length} unlocked ${vaultsString} to the account`,
|
message: `Withdrew ${options.vaults.length} unlocked ${vaultsString} to the account`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return !!response.result
|
|
||||||
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
depositIntoVault: async (options: {
|
depositIntoVault: async (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
@ -418,20 +430,22 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
const depositedCoins = getVaultDepositCoinsFromActions(options.actions)
|
const depositedCoins = getVaultDepositCoinsFromActions(options.actions)
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: options.isCreate ? 'vaultCreate' : 'vault',
|
action: options.isCreate ? 'vaultCreate' : 'vault',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { deposits: depositedCoins },
|
changes: { deposits: depositedCoins },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
withdraw: async (options: {
|
withdraw: async (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
@ -456,19 +470,21 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'withdraw',
|
action: 'withdraw',
|
||||||
target: 'wallet',
|
target: 'wallet',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { deposits: options.coins.map((coin) => coin.coin) },
|
changes: { deposits: options.coins.map((coin) => coin.coin) },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
repay: async (options: {
|
repay: async (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
@ -494,18 +510,20 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'repay',
|
action: 'repay',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { deposits: [options.coin] },
|
changes: { deposits: [options.coin] },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
lend: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
lend: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
@ -519,18 +537,20 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'lend',
|
action: 'lend',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { lends: [options.coin] },
|
changes: { lends: [options.coin] },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
reclaim: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
reclaim: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
@ -544,19 +564,21 @@ export default function createBroadcastSlice(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||||
})
|
})
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'withdraw',
|
action: 'withdraw',
|
||||||
target: 'account',
|
target: 'account',
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
changes: { deposits: [options.coin] },
|
changes: { deposits: [options.coin] },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!response.result
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
swap: (options: {
|
swap: (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
@ -600,31 +622,60 @@ export default function createBroadcastSlice(
|
|||||||
const estimateFee = () => getEstimatedFee(messages)
|
const estimateFee = () => getEstimatedFee(messages)
|
||||||
|
|
||||||
const execute = async () => {
|
const execute = async () => {
|
||||||
const response = await get().executeMsg({
|
const response = get().executeMsg({
|
||||||
messages,
|
messages,
|
||||||
})
|
})
|
||||||
|
|
||||||
const coinOut = getTokenOutFromSwapResponse(response, options.denomOut)
|
const swapOptions = { denomOut: options.denomOut, coinIn: options.coinIn }
|
||||||
const successMessage = `Swapped ${formatAmountWithSymbol(
|
|
||||||
options.coinIn.toCoin(),
|
|
||||||
)} for ${formatAmountWithSymbol(coinOut)}`
|
|
||||||
|
|
||||||
handleResponseMessages({
|
get().setToast({
|
||||||
response,
|
response,
|
||||||
|
options: {
|
||||||
action: 'swap',
|
action: 'swap',
|
||||||
message: successMessage,
|
|
||||||
accountId: options.accountId,
|
accountId: options.accountId,
|
||||||
|
},
|
||||||
|
swapOptions,
|
||||||
})
|
})
|
||||||
return !!response.result
|
|
||||||
|
return response.then((response) => !!response.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { estimateFee, execute }
|
return { estimateFee, execute }
|
||||||
},
|
},
|
||||||
|
setToast: (toast: ToastObject) => {
|
||||||
|
const id = moment().unix()
|
||||||
|
set({
|
||||||
|
toast: {
|
||||||
|
id,
|
||||||
|
promise: toast.response,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
toast.response.then((response) => {
|
||||||
|
if (toast.options.action === 'create') {
|
||||||
|
toast.options.accountId =
|
||||||
|
getSingleValueFromBroadcastResult(response.result, 'wasm', 'token_id') ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toast.options.action === 'swap' && toast.swapOptions) {
|
||||||
|
const coinOut = getTokenOutFromSwapResponse(response, toast.swapOptions.denomOut)
|
||||||
|
const successMessage = `Swapped ${formatAmountWithSymbol(
|
||||||
|
toast.swapOptions.coinIn.toCoin(),
|
||||||
|
)} for ${formatAmountWithSymbol(coinOut)}`
|
||||||
|
toast.options.message = successMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResponseMessages({
|
||||||
|
id,
|
||||||
|
response,
|
||||||
|
...toast.options,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
executeMsg: async (options: { messages: MsgExecuteContract[] }): Promise<BroadcastResult> => {
|
executeMsg: async (options: { messages: MsgExecuteContract[] }): Promise<BroadcastResult> => {
|
||||||
try {
|
try {
|
||||||
const client = get().client
|
const client = get().client
|
||||||
if (!client) return { error: 'no client detected' }
|
if (!client) return { error: 'no client detected' }
|
||||||
set({ showTxLoader: true })
|
|
||||||
const fee = await getEstimatedFee(options.messages)
|
const fee = await getEstimatedFee(options.messages)
|
||||||
const broadcastOptions = {
|
const broadcastOptions = {
|
||||||
messages: options.messages,
|
messages: options.messages,
|
||||||
@ -634,9 +685,7 @@ export default function createBroadcastSlice(
|
|||||||
wallet: client.connectedWallet,
|
wallet: client.connectedWallet,
|
||||||
mobile: isMobile,
|
mobile: isMobile,
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await client.broadcast(broadcastOptions)
|
const result = await client.broadcast(broadcastOptions)
|
||||||
set({ showTxLoader: false })
|
|
||||||
if (result.hash) {
|
if (result.hash) {
|
||||||
return { result }
|
return { result }
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
|
|||||||
accountDeleteModal: null,
|
accountDeleteModal: null,
|
||||||
alertDialog: null,
|
alertDialog: null,
|
||||||
borrowModal: null,
|
borrowModal: null,
|
||||||
createAccountModal: false,
|
|
||||||
fundAccountModal: false,
|
|
||||||
fundAndWithdrawModal: null,
|
fundAndWithdrawModal: null,
|
||||||
getStartedModal: false,
|
getStartedModal: false,
|
||||||
lendAndReclaimModal: null,
|
lendAndReclaimModal: null,
|
||||||
|
@ -42,3 +42,18 @@
|
|||||||
appearance: none;
|
appearance: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Toastify__toast {
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 1500ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
background-color: #fedb7c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast--success {
|
||||||
|
background: #6ce9a633;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast--error {
|
||||||
|
background-color: #fda29b33 !important;
|
||||||
|
}
|
||||||
|
26
src/types/interfaces/store/broadcast.d.ts
vendored
26
src/types/interfaces/store/broadcast.d.ts
vendored
@ -11,7 +11,26 @@ interface ExecutableTx {
|
|||||||
estimateFee: () => Promise<StdFee>
|
estimateFee: () => Promise<StdFee>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ToastObjectOptions extends HandleResponseProps {
|
||||||
|
id?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToastObject {
|
||||||
|
response: Promise<BroadcastResult>
|
||||||
|
options: ToastObjectOptions
|
||||||
|
swapOptions?: {
|
||||||
|
coinIn: BNCoin
|
||||||
|
denomOut: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToastPending {
|
||||||
|
id: number
|
||||||
|
promise: Promise<BroadcastResult>
|
||||||
|
}
|
||||||
|
|
||||||
type ToastResponse = {
|
type ToastResponse = {
|
||||||
|
id: number
|
||||||
hash?: string
|
hash?: string
|
||||||
title?: string
|
title?: string
|
||||||
} & (ToastSuccess | ToastError)
|
} & (ToastSuccess | ToastError)
|
||||||
@ -36,7 +55,8 @@ interface ToastStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface HandleResponseProps {
|
interface HandleResponseProps {
|
||||||
response: BroadcastResult
|
id: number
|
||||||
|
response?: BroadcastResult
|
||||||
action:
|
action:
|
||||||
| 'deposit'
|
| 'deposit'
|
||||||
| 'withdraw'
|
| 'withdraw'
|
||||||
@ -83,6 +103,7 @@ interface BroadcastSlice {
|
|||||||
accountBalance?: boolean
|
accountBalance?: boolean
|
||||||
lend?: BNCoin
|
lend?: BNCoin
|
||||||
}) => Promise<boolean>
|
}) => Promise<boolean>
|
||||||
|
setToast: (toast: ToastObject) => void
|
||||||
swap: (options: {
|
swap: (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
coinIn: BNCoin
|
coinIn: BNCoin
|
||||||
@ -92,7 +113,7 @@ interface BroadcastSlice {
|
|||||||
slippage: number
|
slippage: number
|
||||||
isMax?: boolean
|
isMax?: boolean
|
||||||
}) => ExecutableTx
|
}) => ExecutableTx
|
||||||
toast: ToastResponse | null
|
toast: ToastResponse | ToastPending | null
|
||||||
unlock: (options: {
|
unlock: (options: {
|
||||||
accountId: string
|
accountId: string
|
||||||
vault: DepositedVault
|
vault: DepositedVault
|
||||||
@ -109,5 +130,4 @@ interface BroadcastSlice {
|
|||||||
borrow: BNCoin[]
|
borrow: BNCoin[]
|
||||||
reclaims: ActionCoin[]
|
reclaims: ActionCoin[]
|
||||||
}) => Promise<boolean>
|
}) => Promise<boolean>
|
||||||
showTxLoader: boolean
|
|
||||||
}
|
}
|
||||||
|
2
src/types/interfaces/store/common.d.ts
vendored
2
src/types/interfaces/store/common.d.ts
vendored
@ -11,6 +11,6 @@ interface CommonSlice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FocusComponent {
|
interface FocusComponent {
|
||||||
component: ReactNode
|
component: import('react').JSX.Element | null
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
}
|
}
|
||||||
|
2
src/types/interfaces/store/modals.d.ts
vendored
2
src/types/interfaces/store/modals.d.ts
vendored
@ -3,8 +3,6 @@ interface ModalSlice {
|
|||||||
addVaultBorrowingsModal: AddVaultBorrowingsModal | null
|
addVaultBorrowingsModal: AddVaultBorrowingsModal | null
|
||||||
alertDialog: AlertDialogConfig | null
|
alertDialog: AlertDialogConfig | null
|
||||||
borrowModal: BorrowModal | null
|
borrowModal: BorrowModal | null
|
||||||
createAccountModal: boolean
|
|
||||||
fundAccountModal: boolean
|
|
||||||
fundAndWithdrawModal: 'fund' | 'withdraw' | null
|
fundAndWithdrawModal: 'fund' | 'withdraw' | null
|
||||||
getStartedModal: boolean
|
getStartedModal: boolean
|
||||||
lendAndReclaimModal: LendAndReclaimModalConfig | null
|
lendAndReclaimModal: LendAndReclaimModalConfig | null
|
||||||
|
@ -39,6 +39,8 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
animation: {
|
animation: {
|
||||||
|
check: 'check 1.5s ease-in-out forwards',
|
||||||
|
circle: 'circle 1.5s ease-in-out forwards',
|
||||||
fadein: 'fadein 1s ease-in-out forwards',
|
fadein: 'fadein 1s ease-in-out forwards',
|
||||||
glow: 'glow 1000ms ease-in-out forwards',
|
glow: 'glow 1000ms ease-in-out forwards',
|
||||||
progress: 'spin 1200ms cubic-bezier(0.5, 0, 0.5, 1) infinite',
|
progress: 'spin 1200ms cubic-bezier(0.5, 0, 0.5, 1) infinite',
|
||||||
@ -139,11 +141,28 @@ module.exports = {
|
|||||||
4.5: '18px',
|
4.5: '18px',
|
||||||
15: '60px',
|
15: '60px',
|
||||||
55: '220px',
|
55: '220px',
|
||||||
|
70: '280px',
|
||||||
},
|
},
|
||||||
hueRotate: {
|
hueRotate: {
|
||||||
'-82': '-82deg',
|
'-82': '-82deg',
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
|
check: {
|
||||||
|
'0%': {
|
||||||
|
strokeDashoffset: -100,
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
strokeDashoffset: 900,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
'0%': {
|
||||||
|
strokeDashoffset: 1000,
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
strokeDashoffset: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
fadein: {
|
fadein: {
|
||||||
'0%': { opacity: 0 },
|
'0%': { opacity: 0 },
|
||||||
'100%': { opacity: 1 },
|
'100%': { opacity: 1 },
|
||||||
@ -178,6 +197,7 @@ module.exports = {
|
|||||||
8: '32px',
|
8: '32px',
|
||||||
10: '40px',
|
10: '40px',
|
||||||
14: '56px',
|
14: '56px',
|
||||||
|
30.5: '122px',
|
||||||
},
|
},
|
||||||
maxWidth: {
|
maxWidth: {
|
||||||
content: '1024px',
|
content: '1024px',
|
||||||
|
Loading…
Reference in New Issue
Block a user