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 accounts = useStore((s) => s.accounts)
|
||||
const walletAssetModal = useStore((s) => s.walletAssetsModal)
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
|
||||
LEND_ASSETS_KEY,
|
||||
DEFAULT_SETTINGS.lendAssets,
|
||||
@ -76,18 +76,27 @@ export default function AccountFundContent(props: Props) {
|
||||
|
||||
const handleClick = useCallback(async () => {
|
||||
if (!props.accountId) return
|
||||
const result = await deposit({
|
||||
|
||||
const depositObject = {
|
||||
accountId: props.accountId,
|
||||
coins: fundingAssets,
|
||||
lend: isLending,
|
||||
})
|
||||
if (result)
|
||||
useStore.setState({
|
||||
fundAndWithdrawModal: null,
|
||||
walletAssetsModal: null,
|
||||
focusComponent: null,
|
||||
})
|
||||
}, [props.accountId, deposit, fundingAssets, isLending])
|
||||
}
|
||||
|
||||
if (props.isFullPage) {
|
||||
setIsConfirming(true)
|
||||
const result = await deposit(depositObject)
|
||||
setIsConfirming(false)
|
||||
if (result)
|
||||
useStore.setState({
|
||||
walletAssetsModal: null,
|
||||
focusComponent: null,
|
||||
})
|
||||
} else {
|
||||
deposit(depositObject)
|
||||
useStore.setState({ fundAndWithdrawModal: null, walletAssetsModal: null })
|
||||
}
|
||||
}, [props.accountId, deposit, fundingAssets, isLending, props.isFullPage])
|
||||
|
||||
useEffect(() => {
|
||||
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
|
||||
@ -171,7 +180,7 @@ export default function AccountFundContent(props: Props) {
|
||||
max={balance}
|
||||
balances={balances}
|
||||
maxText='Max'
|
||||
disabled={showTxLoader}
|
||||
disabled={isConfirming}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@ -184,7 +193,7 @@ export default function AccountFundContent(props: Props) {
|
||||
rightIcon={<Plus />}
|
||||
iconClassName='w-3'
|
||||
onClick={handleSelectAssetsClick}
|
||||
disabled={showTxLoader}
|
||||
disabled={isConfirming}
|
||||
/>
|
||||
<DepositCapMessage
|
||||
action='fund'
|
||||
@ -203,7 +212,7 @@ export default function AccountFundContent(props: Props) {
|
||||
className='w-full mt-4'
|
||||
text='Fund account'
|
||||
disabled={!hasFundingAssets || depositCapReachedCoins.length > 0}
|
||||
showProgressIndicator={showTxLoader}
|
||||
showProgressIndicator={isConfirming}
|
||||
onClick={handleClick}
|
||||
color={props.isFullPage ? 'tertiary' : undefined}
|
||||
size={props.isFullPage ? 'lg' : undefined}
|
||||
|
@ -57,7 +57,7 @@ export default function AccountMenuContent(props: Props) {
|
||||
}, [transactionFeeCoinBalance])
|
||||
|
||||
const performCreateAccount = useCallback(async () => {
|
||||
setShowMenu(true)
|
||||
setShowMenu(false)
|
||||
setIsCreating(true)
|
||||
const accountId = await createAccount()
|
||||
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 Text from 'components/Text'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
@ -25,9 +24,12 @@ export const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Pr
|
||||
className={classNames('flex items-center', loaderClasses)}
|
||||
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>
|
||||
)
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { ReactNode, useEffect, useRef } from 'react'
|
||||
|
||||
import EscButton from 'components/Button/EscButton'
|
||||
import Card from 'components/Card'
|
||||
import TransactionLoader from 'components/TransactionLoader'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
@ -23,11 +22,9 @@ interface Props {
|
||||
export default function Modal(props: Props) {
|
||||
const ref: React.RefObject<HTMLDialogElement> = useRef(null)
|
||||
const modalClassName = props.modalClassName ?? 'max-w-modal'
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
|
||||
function onClose() {
|
||||
ref.current?.close()
|
||||
useStore.setState({ showTxLoader: false })
|
||||
props.onClose()
|
||||
}
|
||||
|
||||
@ -42,7 +39,6 @@ export default function Modal(props: Props) {
|
||||
return () => {
|
||||
dialog?.removeAttribute('open')
|
||||
dialog?.close()
|
||||
useStore.setState({ showTxLoader: false })
|
||||
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',
|
||||
)}
|
||||
>
|
||||
{showTxLoader && !props.hideTxLoader && <TransactionLoader />}
|
||||
{props.children ? props.children : props.content}
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -35,13 +35,11 @@ function AccountDeleteModal(props: Props) {
|
||||
useStore.setState({ accountDeleteModal: null })
|
||||
}, [])
|
||||
|
||||
const deleteAccountHandler = useCallback(async () => {
|
||||
const deleteAccountHandler = useCallback(() => {
|
||||
const options = { accountId: modal.id, lends: modal.lends }
|
||||
const isSuccess = await deleteAccount(options)
|
||||
if (isSuccess) {
|
||||
navigate(getRoute(getPage(pathname), address))
|
||||
closeDeleteAccountModal()
|
||||
}
|
||||
deleteAccount(options)
|
||||
navigate(getRoute(getPage(pathname), address))
|
||||
closeDeleteAccountModal()
|
||||
}, [modal, deleteAccount, navigate, pathname, address, closeDeleteAccountModal])
|
||||
|
||||
const depositsAndLends = useMemo(
|
||||
@ -92,7 +90,6 @@ function AccountDeleteModal(props: Props) {
|
||||
positiveButton={{
|
||||
text: 'Delete Account',
|
||||
icon: <ArrowRight />,
|
||||
isAsync: true,
|
||||
onClick: deleteAccountHandler,
|
||||
}}
|
||||
/>
|
||||
@ -120,7 +117,6 @@ function AccountDeleteModal(props: Props) {
|
||||
positiveButton={{
|
||||
text: 'Delete Account',
|
||||
icon: <ArrowRight />,
|
||||
isAsync: true,
|
||||
onClick: deleteAccountHandler,
|
||||
}}
|
||||
/>
|
||||
|
@ -23,20 +23,11 @@ interface Props {
|
||||
|
||||
function AlertDialog(props: Props) {
|
||||
const { title, icon, description, negativeButton, positiveButton } = props.config
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const handleButtonClick = (button?: AlertDialogButton) => {
|
||||
button?.onClick && button.onClick()
|
||||
props.close()
|
||||
}
|
||||
|
||||
async function handleAsyncButtonClick(button?: AlertDialogButton) {
|
||||
if (!button?.onClick) return
|
||||
setIsConfirming(true)
|
||||
await button.onClick()
|
||||
setIsConfirming(false)
|
||||
props.close()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={props.close}
|
||||
@ -66,12 +57,7 @@ function AlertDialog(props: Props) {
|
||||
color='primary'
|
||||
className='px-6'
|
||||
rightIcon={positiveButton.icon ?? <YesIcon />}
|
||||
showProgressIndicator={isConfirming}
|
||||
onClick={() =>
|
||||
positiveButton.isAsync
|
||||
? handleAsyncButtonClick(positiveButton)
|
||||
: handleButtonClick(positiveButton)
|
||||
}
|
||||
onClick={() => handleButtonClick(positiveButton)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
@ -79,7 +65,6 @@ function AlertDialog(props: Props) {
|
||||
color='secondary'
|
||||
className='px-6'
|
||||
rightIcon={negativeButton?.icon ?? <NoIcon />}
|
||||
disabled={isConfirming}
|
||||
tabIndex={1}
|
||||
onClick={() => handleButtonClick(negativeButton)}
|
||||
/>
|
||||
|
@ -19,7 +19,6 @@ interface Props {
|
||||
coinBalances: Coin[]
|
||||
actionButtonText: string
|
||||
contentHeader?: JSX.Element
|
||||
showProgressIndicator: boolean
|
||||
onClose: () => void
|
||||
onChange: (value: BigNumber) => void
|
||||
onAction: (value: BigNumber, isMax: boolean) => void
|
||||
@ -32,7 +31,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
|
||||
coinBalances,
|
||||
actionButtonText,
|
||||
contentHeader = null,
|
||||
showProgressIndicator,
|
||||
onClose,
|
||||
onChange,
|
||||
onAction,
|
||||
@ -77,12 +75,10 @@ export default function AssetAmountSelectActionModal(props: Props) {
|
||||
max={maxAmount}
|
||||
hasSelect
|
||||
maxText='Max'
|
||||
disabled={showProgressIndicator}
|
||||
/>
|
||||
<Divider />
|
||||
<Button
|
||||
onClick={handleActionClick}
|
||||
showProgressIndicator={showProgressIndicator}
|
||||
disabled={!amount.toNumber()}
|
||||
className='w-full'
|
||||
text={actionButtonText}
|
||||
|
@ -56,7 +56,6 @@ export default function BorrowModalController() {
|
||||
function BorrowModal(props: Props) {
|
||||
const { modal, account } = props
|
||||
const [amount, setAmount] = useState(BN_ZERO)
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const [borrowToWallet, setBorrowToWallet] = useToggle()
|
||||
const borrow = useStore((s) => s.borrow)
|
||||
const repay = useStore((s) => s.repay)
|
||||
@ -79,32 +78,29 @@ function BorrowModal(props: Props) {
|
||||
setAmount(BN_ZERO)
|
||||
}
|
||||
|
||||
async function onConfirmClick() {
|
||||
function onConfirmClick() {
|
||||
if (!asset) return
|
||||
let result
|
||||
const { lend } = getDepositAndLendCoinsToSpend(
|
||||
BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||
account,
|
||||
)
|
||||
if (isRepay) {
|
||||
result = await repay({
|
||||
repay({
|
||||
accountId: account.id,
|
||||
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||
accountBalance: amount.isEqualTo(totalDebtRepayAmount),
|
||||
lend,
|
||||
})
|
||||
} else {
|
||||
result = await borrow({
|
||||
borrow({
|
||||
accountId: account.id,
|
||||
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
|
||||
borrowToWallet,
|
||||
})
|
||||
}
|
||||
|
||||
if (result) {
|
||||
resetState()
|
||||
useStore.setState({ borrowModal: null })
|
||||
}
|
||||
resetState()
|
||||
useStore.setState({ borrowModal: null })
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
@ -229,7 +225,6 @@ function BorrowModal(props: Props) {
|
||||
max={max}
|
||||
className='w-full'
|
||||
maxText='Max'
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
{!isRepay && (
|
||||
<>
|
||||
@ -245,7 +240,6 @@ function BorrowModal(props: Props) {
|
||||
name='borrow-to-wallet'
|
||||
checked={borrowToWallet}
|
||||
onChange={setBorrowToWallet}
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
@ -254,7 +248,6 @@ function BorrowModal(props: Props) {
|
||||
<Button
|
||||
onClick={onConfirmClick}
|
||||
className='w-full'
|
||||
showProgressIndicator={showTxLoader}
|
||||
disabled={amount.isZero()}
|
||||
text={isRepay ? 'Repay' : 'Borrow'}
|
||||
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]
|
||||
const withdraw = useStore((s) => s.withdraw)
|
||||
const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle()
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
|
||||
const [amount, setAmount] = useState(BN_ZERO)
|
||||
const { simulateWithdraw } = useUpdatedAccount(account)
|
||||
@ -56,13 +55,7 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
setAmount(val)
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
setCurrentAsset(defaultAsset)
|
||||
setAmount(BN_ZERO)
|
||||
onChangeAmount(BN_ZERO)
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
function onConfirm() {
|
||||
const coins = [
|
||||
{
|
||||
coin: BNCoin.fromDenomAndBigNumber(currentAsset.denom, amount),
|
||||
@ -80,17 +73,13 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
]
|
||||
: []
|
||||
|
||||
const result = await withdraw({
|
||||
withdraw({
|
||||
accountId: account.id,
|
||||
coins,
|
||||
borrow,
|
||||
reclaims,
|
||||
})
|
||||
|
||||
if (result) {
|
||||
resetState()
|
||||
useStore.setState({ fundAndWithdrawModal: null })
|
||||
}
|
||||
useStore.setState({ fundAndWithdrawModal: null })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -123,7 +112,6 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
accountId={account.id}
|
||||
hasSelect
|
||||
maxText='Max'
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
<Divider className='my-6' />
|
||||
<div className='flex flex-wrap w-full'>
|
||||
@ -138,18 +126,11 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
name='borrow-to-wallet'
|
||||
checked={withdrawWithBorrowing}
|
||||
onChange={setWithdrawWithBorrowing}
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onConfirm}
|
||||
showProgressIndicator={showTxLoader}
|
||||
className='w-full'
|
||||
text={'Withdraw'}
|
||||
rightIcon={<ArrowRight />}
|
||||
/>
|
||||
<Button onClick={onConfirm} className='w-full' text={'Withdraw'} rightIcon={<ArrowRight />} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
||||
const lend = useStore((s) => s.lend)
|
||||
const reclaim = useStore((s) => s.reclaim)
|
||||
const { close } = useLendAndReclaimModal()
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const { simulateLending } = useUpdatedAccount(currentAccount)
|
||||
|
||||
const { data, action } = config
|
||||
@ -45,14 +44,18 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
||||
)
|
||||
|
||||
const handleAction = useCallback(
|
||||
async (value: BigNumber, isMax: boolean) => {
|
||||
(value: BigNumber, isMax: boolean) => {
|
||||
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
|
||||
const options = {
|
||||
accountId: currentAccount.id,
|
||||
coin,
|
||||
isMax,
|
||||
}
|
||||
await (isLendAction ? lend : reclaim)(options)
|
||||
if (isLendAction) {
|
||||
lend(options)
|
||||
} else {
|
||||
reclaim(options)
|
||||
}
|
||||
close()
|
||||
},
|
||||
[asset.denom, close, currentAccount.id, isLendAction, lend, reclaim],
|
||||
@ -63,7 +66,6 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
||||
contentHeader={<DetailsHeader data={data} />}
|
||||
coinBalances={coinBalances}
|
||||
actionButtonText={actionText}
|
||||
showProgressIndicator={showTxLoader}
|
||||
title={`${actionText} ${asset.symbol}`}
|
||||
onClose={close}
|
||||
onAction={handleAction}
|
||||
|
@ -12,12 +12,11 @@ interface Props {
|
||||
|
||||
export default function UnlockModalContent(props: Props) {
|
||||
const unlock = useStore((s) => s.unlock)
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const { accountId } = useParams()
|
||||
|
||||
async function onConfirm() {
|
||||
function onConfirm() {
|
||||
if (!accountId) return
|
||||
await unlock({
|
||||
unlock({
|
||||
accountId: accountId,
|
||||
vault: props.depositedVault,
|
||||
amount: props.depositedVault.amounts.locked.toString(),
|
||||
@ -38,7 +37,6 @@ export default function UnlockModalContent(props: Props) {
|
||||
className='px-6'
|
||||
rightIcon={<YesIcon />}
|
||||
onClick={onConfirm}
|
||||
showProgressIndicator={showTxLoader}
|
||||
/>
|
||||
<Button
|
||||
text='No'
|
||||
@ -47,7 +45,6 @@ export default function UnlockModalContent(props: Props) {
|
||||
rightIcon={<NoIcon />}
|
||||
tabIndex={1}
|
||||
onClick={props.onClose}
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
@ -40,7 +40,6 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
const { data: prices } = usePrices()
|
||||
const vaultModal = useStore((s) => s.vaultModal)
|
||||
const depositIntoVault = useStore((s) => s.depositIntoVault)
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const updatedAccount = useStore((s) => s.updatedAccount)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
||||
const [percentage, setPercentage] = useState<number>(0)
|
||||
@ -143,18 +142,16 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
setPercentage(calculateSliderPercentage(maxBorrowAmounts, props.borrowings))
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
function onConfirm() {
|
||||
if (!updatedAccount || !vaultModal) return
|
||||
const isSuccess = await depositIntoVault({
|
||||
depositIntoVault({
|
||||
accountId: updatedAccount.id,
|
||||
actions: props.depositActions,
|
||||
deposits: props.deposits,
|
||||
borrowings: props.borrowings,
|
||||
isCreate: vaultModal.isCreate,
|
||||
})
|
||||
if (isSuccess) {
|
||||
useStore.setState({ vaultModal: null })
|
||||
}
|
||||
useStore.setState({ vaultModal: null })
|
||||
}
|
||||
|
||||
return (
|
||||
@ -173,13 +170,10 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
maxText='Max Borrow'
|
||||
onChange={(amount) => updateAssets(coin.denom, amount)}
|
||||
onDelete={() => onDelete(coin.denom)}
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{props.borrowings.length === 1 && (
|
||||
<Slider onChange={onChangeSlider} value={percentage} disabled={showTxLoader} />
|
||||
)}
|
||||
{props.borrowings.length === 1 && <Slider onChange={onChangeSlider} value={percentage} />}
|
||||
{props.borrowings.length === 0 && (
|
||||
<div className='flex items-center gap-4 py-2'>
|
||||
<div className='w-4'>
|
||||
@ -192,12 +186,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button
|
||||
text='Select borrow assets +'
|
||||
color='tertiary'
|
||||
onClick={addAsset}
|
||||
disabled={showTxLoader}
|
||||
/>
|
||||
<Button text='Select borrow assets +' color='tertiary' onClick={addAsset} />
|
||||
|
||||
<DepositCapMessage
|
||||
action='deposit'
|
||||
@ -233,7 +222,6 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
color='primary'
|
||||
text='Deposit'
|
||||
rightIcon={<ArrowRight />}
|
||||
showProgressIndicator={showTxLoader}
|
||||
disabled={!props.depositActions.length || props.depositCapReachedCoins.length > 0}
|
||||
/>
|
||||
</div>
|
||||
|
@ -18,7 +18,6 @@ import { demagnify } from 'utils/formatters'
|
||||
export default function WithdrawFromVaultsModal() {
|
||||
const modal = useStore((s) => s.withdrawFromVaultsModal)
|
||||
const { accountId } = useParams()
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
@ -27,9 +26,9 @@ export default function WithdrawFromVaultsModal() {
|
||||
useStore.setState({ withdrawFromVaultsModal: null })
|
||||
}
|
||||
|
||||
async function withdrawHandler() {
|
||||
function withdrawHandler() {
|
||||
if (!accountId || !modal) return
|
||||
await withdrawFromVaults({
|
||||
withdrawFromVaults({
|
||||
accountId: accountId,
|
||||
vaults: modal,
|
||||
slippage,
|
||||
@ -87,12 +86,7 @@ export default function WithdrawFromVaultsModal() {
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<Button
|
||||
showProgressIndicator={showTxLoader}
|
||||
onClick={withdrawHandler}
|
||||
className='w-full mt-4'
|
||||
text='Withdraw from all'
|
||||
/>
|
||||
<Button onClick={withdrawHandler} className='w-full mt-4' text='Withdraw from all' />
|
||||
</div>
|
||||
) : (
|
||||
<CircularProgress />
|
||||
|
@ -6,8 +6,11 @@ import { ChevronDown, ChevronRight } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { convertAprToApy } from 'utils/parsers'
|
||||
|
||||
interface Props extends SelectOption {
|
||||
isSelected?: boolean
|
||||
@ -19,6 +22,7 @@ interface Props extends SelectOption {
|
||||
|
||||
export default function Option(props: Props) {
|
||||
const isCoin = !!props.denom
|
||||
const { data: marketAssets } = useMarketAssets()
|
||||
|
||||
function handleOnClick(value: string | undefined) {
|
||||
if (!props.onClick || !value) return
|
||||
@ -28,6 +32,7 @@ export default function Option(props: Props) {
|
||||
if (isCoin) {
|
||||
const asset = ASSETS.find((asset) => asset.denom === props.denom) ?? ASSETS[0]
|
||||
const balance = props.amount ?? BN_ZERO
|
||||
const marketAsset = marketAssets.find(byDenom(asset.denom))
|
||||
|
||||
if (props.isDisplay) {
|
||||
return (
|
||||
@ -74,7 +79,12 @@ export default function Option(props: Props) {
|
||||
})}
|
||||
</Text>
|
||||
<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>
|
||||
<DisplayCurrency
|
||||
className='col-span-2 text-sm text-right text-white/50'
|
||||
|
@ -104,18 +104,24 @@ export default function Select(props: Props) {
|
||||
{props.title}
|
||||
</Text>
|
||||
)}
|
||||
{props.options.map((option: SelectOption, index: number) => (
|
||||
<Option
|
||||
key={index}
|
||||
{...option}
|
||||
isSelected={
|
||||
option?.value
|
||||
? option?.value === selected?.value
|
||||
: option?.denom === selected?.denom
|
||||
}
|
||||
onClick={handleChange}
|
||||
/>
|
||||
))}
|
||||
<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
|
||||
key={index}
|
||||
{...option}
|
||||
isSelected={
|
||||
option?.value
|
||||
? option?.value === selected?.value
|
||||
: option?.denom === selected?.denom
|
||||
}
|
||||
onClick={handleChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
</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 React, { useEffect, useState } from 'react'
|
||||
import QRCode from 'react-qr-code'
|
||||
import moment from 'moment'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import FullOverlayContent from 'components/FullOverlayContent'
|
||||
@ -104,6 +105,7 @@ export default function WalletSelect(props: Props) {
|
||||
if (error?.message && error?.title) {
|
||||
useStore.setState({
|
||||
toast: {
|
||||
id: moment.now(),
|
||||
isError: true,
|
||||
title: error.title,
|
||||
message: error.message,
|
||||
|
@ -8,12 +8,12 @@ import Footer from 'components/Footer'
|
||||
import DesktopHeader from 'components/Header/DesktopHeader'
|
||||
import ModalsContainer from 'components/Modals/ModalsContainer'
|
||||
import PageMetadata from 'components/PageMetadata'
|
||||
import Toaster from 'components/Toaster'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { REDUCE_MOTION_KEY } from 'constants/localStore'
|
||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import useStore from 'store'
|
||||
import Toaster from 'components/Toaster'
|
||||
|
||||
interface Props {
|
||||
focusComponent: FocusComponent | null
|
||||
|
@ -41,11 +41,13 @@ export default function createBroadcastSlice(
|
||||
get: GetState<Store>,
|
||||
): BroadcastSlice {
|
||||
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) {
|
||||
set({
|
||||
toast: {
|
||||
id,
|
||||
message: generateErrorMessage(response),
|
||||
isError: true,
|
||||
hash: response.result?.hash,
|
||||
@ -55,6 +57,7 @@ export default function createBroadcastSlice(
|
||||
}
|
||||
|
||||
const toast: ToastResponse = {
|
||||
id,
|
||||
accountId: accountId,
|
||||
isError: false,
|
||||
hash: response?.result?.hash,
|
||||
@ -152,7 +155,6 @@ export default function createBroadcastSlice(
|
||||
|
||||
return {
|
||||
toast: null,
|
||||
showTxLoader: false,
|
||||
borrow: async (options: { accountId: string; coin: BNCoin; borrowToWallet: boolean }) => {
|
||||
const borrowAction: Action = { borrow: options.coin.toCoin() }
|
||||
const withdrawAction: Action = { withdraw: options.coin.toActionCoin() }
|
||||
@ -174,48 +176,45 @@ export default function createBroadcastSlice(
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'borrow',
|
||||
lend: checkAutoLendEnabled(options.accountId),
|
||||
target: options.borrowToWallet ? 'wallet' : 'account',
|
||||
accountId: options.accountId,
|
||||
changes: { debts: [options.coin] },
|
||||
options: {
|
||||
action: 'borrow',
|
||||
lend: checkAutoLendEnabled(options.accountId),
|
||||
target: options.borrowToWallet ? 'wallet' : 'account',
|
||||
accountId: options.accountId,
|
||||
changes: { debts: [options.coin] },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
createAccount: async () => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
create_credit_account: 'default',
|
||||
}
|
||||
|
||||
const response = await get().executeMsg({
|
||||
const response = get().executeMsg({
|
||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||
})
|
||||
|
||||
set({ createAccountModal: false })
|
||||
const id = response.result
|
||||
? getSingleValueFromBroadcastResult(response.result, 'wasm', 'token_id')
|
||||
: null
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'create',
|
||||
accountId: id ?? undefined,
|
||||
message: id ? `Created the Credit Account` : undefined,
|
||||
options: {
|
||||
action: 'create',
|
||||
message: `Created the Credit Account`,
|
||||
},
|
||||
})
|
||||
|
||||
if (id)
|
||||
set({
|
||||
fundAccountModal: true,
|
||||
})
|
||||
|
||||
return id
|
||||
return response.then((response) =>
|
||||
response.result
|
||||
? getSingleValueFromBroadcastResult(response.result, 'wasm', 'token_id')
|
||||
: null,
|
||||
)
|
||||
},
|
||||
deleteAccount: async (options: { accountId: string; lends: BNCoin[] }) => {
|
||||
const reclaimMsg = options.lends.map((coin) => {
|
||||
@ -237,21 +236,23 @@ export default function createBroadcastSlice(
|
||||
},
|
||||
}
|
||||
|
||||
const response = await get().executeMsg({
|
||||
const response = get().executeMsg({
|
||||
messages: [
|
||||
generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, refundMessage, []),
|
||||
generateExecutionMessage(get().address, ENV.ADDRESS_ACCOUNT_NFT, burnMessage, []),
|
||||
],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'delete',
|
||||
accountId: options.accountId,
|
||||
message: `Deleted the Credit Account`,
|
||||
options: {
|
||||
action: 'delete',
|
||||
accountId: options.accountId,
|
||||
message: `Deleted the Credit Account`,
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
claimRewards: (options: { accountId: string }) => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
@ -271,18 +272,20 @@ export default function createBroadcastSlice(
|
||||
const estimateFee = () => getEstimatedFee(messages)
|
||||
|
||||
const execute = async () => {
|
||||
const response = await get().executeMsg({
|
||||
const response = get().executeMsg({
|
||||
messages,
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'create',
|
||||
accountId: options.accountId,
|
||||
message: `Claimed rewards`,
|
||||
options: {
|
||||
action: 'claim',
|
||||
accountId: options.accountId,
|
||||
message: `Claimed rewards`,
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
}
|
||||
|
||||
return { estimateFee, execute }
|
||||
@ -307,19 +310,21 @@ export default function createBroadcastSlice(
|
||||
|
||||
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)],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'deposit',
|
||||
lend: options.lend,
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: options.coins },
|
||||
options: {
|
||||
action: 'deposit',
|
||||
lend: options.lend,
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: options.coins },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
unlock: async (options: { accountId: string; vault: DepositedVault; amount: string }) => {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'unlock',
|
||||
accountId: options.accountId,
|
||||
message: `Requested unlock for ${options.vault.name}`,
|
||||
options: {
|
||||
action: 'unlock',
|
||||
accountId: options.accountId,
|
||||
message: `Requested unlock for ${options.vault.name}`,
|
||||
},
|
||||
})
|
||||
return !!response.result
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
|
||||
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, [])],
|
||||
})
|
||||
|
||||
const vaultsString = options.vaults.length === 1 ? 'vault' : 'vaults'
|
||||
handleResponseMessages({
|
||||
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'withdraw',
|
||||
accountId: options.accountId,
|
||||
message: `Withdrew ${options.vaults.length} unlocked ${vaultsString} to the account`,
|
||||
options: {
|
||||
action: 'withdraw',
|
||||
accountId: options.accountId,
|
||||
message: `Withdrew ${options.vaults.length} unlocked ${vaultsString} to the account`,
|
||||
},
|
||||
})
|
||||
return !!response.result
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
depositIntoVault: async (options: {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
const depositedCoins = getVaultDepositCoinsFromActions(options.actions)
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: options.isCreate ? 'vaultCreate' : 'vault',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: depositedCoins },
|
||||
options: {
|
||||
action: options.isCreate ? 'vaultCreate' : 'vault',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: depositedCoins },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
withdraw: async (options: {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'withdraw',
|
||||
target: 'wallet',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: options.coins.map((coin) => coin.coin) },
|
||||
options: {
|
||||
action: 'withdraw',
|
||||
target: 'wallet',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: options.coins.map((coin) => coin.coin) },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
repay: async (options: {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'repay',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: [options.coin] },
|
||||
options: {
|
||||
action: 'repay',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: [options.coin] },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
lend: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'lend',
|
||||
accountId: options.accountId,
|
||||
changes: { lends: [options.coin] },
|
||||
options: {
|
||||
action: 'lend',
|
||||
accountId: options.accountId,
|
||||
changes: { lends: [options.coin] },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
reclaim: async (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||
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, [])],
|
||||
})
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'withdraw',
|
||||
target: 'account',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: [options.coin] },
|
||||
options: {
|
||||
action: 'withdraw',
|
||||
target: 'account',
|
||||
accountId: options.accountId,
|
||||
changes: { deposits: [options.coin] },
|
||||
},
|
||||
})
|
||||
|
||||
return !!response.result
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
swap: (options: {
|
||||
accountId: string
|
||||
@ -600,31 +622,60 @@ export default function createBroadcastSlice(
|
||||
const estimateFee = () => getEstimatedFee(messages)
|
||||
|
||||
const execute = async () => {
|
||||
const response = await get().executeMsg({
|
||||
const response = get().executeMsg({
|
||||
messages,
|
||||
})
|
||||
|
||||
const coinOut = getTokenOutFromSwapResponse(response, options.denomOut)
|
||||
const successMessage = `Swapped ${formatAmountWithSymbol(
|
||||
options.coinIn.toCoin(),
|
||||
)} for ${formatAmountWithSymbol(coinOut)}`
|
||||
const swapOptions = { denomOut: options.denomOut, coinIn: options.coinIn }
|
||||
|
||||
handleResponseMessages({
|
||||
get().setToast({
|
||||
response,
|
||||
action: 'swap',
|
||||
message: successMessage,
|
||||
accountId: options.accountId,
|
||||
options: {
|
||||
action: 'swap',
|
||||
accountId: options.accountId,
|
||||
},
|
||||
swapOptions,
|
||||
})
|
||||
return !!response.result
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
}
|
||||
|
||||
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> => {
|
||||
try {
|
||||
const client = get().client
|
||||
if (!client) return { error: 'no client detected' }
|
||||
set({ showTxLoader: true })
|
||||
const fee = await getEstimatedFee(options.messages)
|
||||
const broadcastOptions = {
|
||||
messages: options.messages,
|
||||
@ -634,9 +685,7 @@ export default function createBroadcastSlice(
|
||||
wallet: client.connectedWallet,
|
||||
mobile: isMobile,
|
||||
}
|
||||
|
||||
const result = await client.broadcast(broadcastOptions)
|
||||
set({ showTxLoader: false })
|
||||
if (result.hash) {
|
||||
return { result }
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
|
||||
accountDeleteModal: null,
|
||||
alertDialog: null,
|
||||
borrowModal: null,
|
||||
createAccountModal: false,
|
||||
fundAccountModal: false,
|
||||
fundAndWithdrawModal: null,
|
||||
getStartedModal: false,
|
||||
lendAndReclaimModal: null,
|
||||
|
@ -42,3 +42,18 @@
|
||||
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>
|
||||
}
|
||||
|
||||
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 = {
|
||||
id: number
|
||||
hash?: string
|
||||
title?: string
|
||||
} & (ToastSuccess | ToastError)
|
||||
@ -36,7 +55,8 @@ interface ToastStore {
|
||||
}
|
||||
|
||||
interface HandleResponseProps {
|
||||
response: BroadcastResult
|
||||
id: number
|
||||
response?: BroadcastResult
|
||||
action:
|
||||
| 'deposit'
|
||||
| 'withdraw'
|
||||
@ -83,6 +103,7 @@ interface BroadcastSlice {
|
||||
accountBalance?: boolean
|
||||
lend?: BNCoin
|
||||
}) => Promise<boolean>
|
||||
setToast: (toast: ToastObject) => void
|
||||
swap: (options: {
|
||||
accountId: string
|
||||
coinIn: BNCoin
|
||||
@ -92,7 +113,7 @@ interface BroadcastSlice {
|
||||
slippage: number
|
||||
isMax?: boolean
|
||||
}) => ExecutableTx
|
||||
toast: ToastResponse | null
|
||||
toast: ToastResponse | ToastPending | null
|
||||
unlock: (options: {
|
||||
accountId: string
|
||||
vault: DepositedVault
|
||||
@ -109,5 +130,4 @@ interface BroadcastSlice {
|
||||
borrow: BNCoin[]
|
||||
reclaims: ActionCoin[]
|
||||
}) => 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 {
|
||||
component: ReactNode
|
||||
component: import('react').JSX.Element | null
|
||||
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
|
||||
alertDialog: AlertDialogConfig | null
|
||||
borrowModal: BorrowModal | null
|
||||
createAccountModal: boolean
|
||||
fundAccountModal: boolean
|
||||
fundAndWithdrawModal: 'fund' | 'withdraw' | null
|
||||
getStartedModal: boolean
|
||||
lendAndReclaimModal: LendAndReclaimModalConfig | null
|
||||
|
@ -39,6 +39,8 @@ module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
animation: {
|
||||
check: 'check 1.5s ease-in-out forwards',
|
||||
circle: 'circle 1.5s ease-in-out forwards',
|
||||
fadein: 'fadein 1s ease-in-out forwards',
|
||||
glow: 'glow 1000ms ease-in-out forwards',
|
||||
progress: 'spin 1200ms cubic-bezier(0.5, 0, 0.5, 1) infinite',
|
||||
@ -139,11 +141,28 @@ module.exports = {
|
||||
4.5: '18px',
|
||||
15: '60px',
|
||||
55: '220px',
|
||||
70: '280px',
|
||||
},
|
||||
hueRotate: {
|
||||
'-82': '-82deg',
|
||||
},
|
||||
keyframes: {
|
||||
check: {
|
||||
'0%': {
|
||||
strokeDashoffset: -100,
|
||||
},
|
||||
'100%': {
|
||||
strokeDashoffset: 900,
|
||||
},
|
||||
},
|
||||
circle: {
|
||||
'0%': {
|
||||
strokeDashoffset: 1000,
|
||||
},
|
||||
'100%': {
|
||||
strokeDashoffset: 0,
|
||||
},
|
||||
},
|
||||
fadein: {
|
||||
'0%': { opacity: 0 },
|
||||
'100%': { opacity: 1 },
|
||||
@ -178,6 +197,7 @@ module.exports = {
|
||||
8: '32px',
|
||||
10: '40px',
|
||||
14: '56px',
|
||||
30.5: '122px',
|
||||
},
|
||||
maxWidth: {
|
||||
content: '1024px',
|
||||
|
Loading…
Reference in New Issue
Block a user