Mp 3442 sage feedback pre mars v 2 trade page (#520)

* MP-3442: set autolend to true by default

* MP-3442: remember margin setting and toggle it on by default

* MP-3442: add balances to AssetSelector

* MP-3442: close assetOverlay on clickAway

* fix: fixed TradeModule z-index

* MP-3442: set priority of balance and cap

* fix: overlayState

* fix: adjusted to comments
This commit is contained in:
Linkie Link 2023-10-05 09:15:32 +02:00 committed by GitHub
parent 8a71f4664a
commit 17f13b6d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 109 additions and 35 deletions

View File

@ -11,7 +11,7 @@ NEXT_PUBLIC_RED_BANK=osmo1vxpdcw092n9rngvekve8g324c2yjx56496ck98ag4sdxr4p4zd4q0w
NEXT_PUBLIC_CREDIT_MANAGER=osmo1m83kw2vehyt9urxf79qa9rxk8chgs4464e5h8s37yhnw3pwauuqq7lux8r
NEXT_PUBLIC_INCENTIVES=osmo1r9w7k774vcxeuvq6ctq0z2j6wkkxpskucgjkqt0uu7u07l03s3js6ukge4
NEXT_PUBLIC_ZAPPER=osmo1q4kkvuy8wc9fs8sfm7zyeh4k25vssd0l68nrph8s7unvq5jdq67swrepj4
NEXT_PUBLIC_SWAPPER=osmo1xmhhdxgk9e83n4kmtlluzx38mya8q9r4hku5nys8cr7jg7sgpx5s8zkkg2
NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla
NEXT_PUBLIC_PARAMS=osmo1pzszwkyy0x9cu6p2uknwa3wccr79xwmqn9gj66fnjnayr28tzp6qh2n4qg
NEXT_PUBLIC_PYTH=osmo13ge29x4e2s63a8ytz2px8gurtyznmue4a69n5275692v3qn3ks8q7cwck7
NEXT_PUBLIC_API=http://localhost:3000/api

View File

@ -111,11 +111,12 @@ export default function Index(props: Props) {
)
const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS)
const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT)
return (
<FormattedNumber
className={className}
smallerThanThreshold={formattedAmount < MIN_AMOUNT}
amount={formattedAmount < MIN_AMOUNT ? MIN_AMOUNT : formattedAmount}
smallerThanThreshold={formattedAmount !== 0 && formattedAmount < MIN_AMOUNT}
amount={lowAmount}
options={{
maxDecimals: MAX_AMOUNT_DECIMALS,
minDecimals: 0,

View File

@ -1,16 +1,20 @@
import AssetImage from 'components/Asset/AssetImage'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { StarFilled, StarOutlined } from 'components/Icons'
import Text from 'components/Text'
import { FAVORITE_ASSETS_KEY } from 'constants/localStore'
import { BN_ONE } from 'constants/math'
import { BN_ONE, BN_ZERO, MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
import useLocalStorage from 'hooks/useLocalStorage'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { demagnify, formatAmountToPrecision } from 'utils/formatters'
interface Props {
asset: Asset
onSelectAsset: (asset: Asset) => void
depositCap?: DepositCap
balances: BNCoin[]
}
export default function AssetItem(props: Props) {
@ -19,6 +23,9 @@ export default function AssetItem(props: Props) {
FAVORITE_ASSETS_KEY,
[],
)
const amount = demagnify(props.balances.find(byDenom(asset.denom))?.amount ?? BN_ZERO, asset)
const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS)
const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT)
function handleToggleFavorite(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
event.stopPropagation()
@ -50,6 +57,30 @@ export default function AssetItem(props: Props) {
<Text size='xs'>{asset.symbol}</Text>
</div>
</div>
{props.balances.length > 0 && (
<div className='flex gap-1'>
<span className='text-xs text-left text-white/80'>Balance: </span>
{amount >= 1 ? (
<FormattedNumber
className='text-xs text-left text-white/80'
amount={amount}
options={{ abbreviated: true, maxDecimals: MAX_AMOUNT_DECIMALS }}
animate
/>
) : (
<FormattedNumber
className='text-xs text-left text-white/80'
smallerThanThreshold={formattedAmount !== 0 && formattedAmount < MIN_AMOUNT}
amount={lowAmount}
options={{
maxDecimals: MAX_AMOUNT_DECIMALS,
minDecimals: 0,
}}
animate
/>
)}
</div>
)}
{props.depositCap && (
<div className='flex gap-1'>
<span className='text-xs text-left text-white/60'>Cap Left: </span>

View File

@ -3,8 +3,12 @@ import classNames from 'classnames'
import { ChevronDown } from 'components/Icons'
import Text from 'components/Text'
import AssetItem from 'components/Trade/TradeModule/AssetSelector/AssetItem'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useMarketAssets from 'hooks/useMarketAssets'
import { useMemo } from 'react'
import { getMergedBalancesForAsset } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { getEnabledMarketAssets } from 'utils/assets'
interface Props {
type: 'buy' | 'sell'
@ -15,12 +19,17 @@ interface Props {
}
export default function AssetList(props: Props) {
const account = useCurrentAccount()
const { data: marketAssets } = useMarketAssets()
const balances = useMemo(() => {
if (!account) return []
return getMergedBalancesForAsset(account, getEnabledMarketAssets())
}, [account])
return (
<section>
<button
className='flex w-full items-center justify-between bg-black/20 p-4'
className='flex items-center justify-between w-full p-4 bg-black/20'
onClick={props.toggleOpen}
>
<Text>{props.type === 'buy' ? 'Buy asset' : 'Sell asset'}</Text>
@ -35,6 +44,7 @@ export default function AssetList(props: Props) {
<ul>
{props.assets.map((asset) => (
<AssetItem
balances={balances}
key={`${props.type}-${asset.symbol}`}
asset={asset}
onSelectAsset={props.onChangeAsset}

View File

@ -8,8 +8,6 @@ import Text from 'components/Text'
import AssetList from 'components/Trade/TradeModule/AssetSelector/AssetList'
import useFilteredAssets from 'hooks/useFilteredAssets'
export type OverlayState = 'buy' | 'sell' | 'closed'
interface Props {
state: OverlayState
buyAsset: Asset
@ -21,7 +19,6 @@ interface Props {
export default function AssetOverlay(props: Props) {
const { assets, searchString, onChangeSearch } = useFilteredAssets()
const handleClose = useCallback(() => props.onChangeState('closed'), [props])
const handleToggle = useCallback(
@ -51,8 +48,8 @@ export default function AssetOverlay(props: Props) {
}
return (
<Overlay className='w-full' show={props.state !== 'closed'} setShow={handleClose}>
<div className='flex justify-between overflow-hidden p-4'>
<Overlay className='inset-0 w-full' show={props.state !== 'closed'} setShow={handleClose}>
<div className='flex justify-between p-4 overflow-hidden'>
<Text>Select asset</Text>
<EscButton onClick={handleClose} enableKeyPress />
</div>

View File

@ -1,9 +1,10 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect } from 'react'
import { SwapIcon } from 'components/Icons'
import Text from 'components/Text'
import AssetButton from 'components/Trade/TradeModule/AssetSelector/AssetButton'
import AssetOverlay, { OverlayState } from 'components/Trade/TradeModule/AssetSelector/AssetOverlay'
import AssetOverlay from 'components/Trade/TradeModule/AssetSelector/AssetOverlay'
import useStore from 'store'
interface Props {
buyAsset: Asset
@ -14,7 +15,7 @@ interface Props {
export default function AssetSelector(props: Props) {
const { buyAsset, sellAsset, onChangeBuyAsset, onChangeSellAsset } = props
const [overlayState, setOverlayState] = useState<OverlayState>('closed')
const assetOverlayState = useStore((s) => s.assetOverlayState)
const handleSwapAssets = useCallback(() => {
onChangeBuyAsset(sellAsset)
@ -24,7 +25,7 @@ export default function AssetSelector(props: Props) {
const handleChangeBuyAsset = useCallback(
(asset: Asset) => {
onChangeBuyAsset(asset)
setOverlayState('sell')
useStore.setState({ assetOverlayState: 'sell' })
},
[onChangeBuyAsset],
)
@ -32,38 +33,41 @@ export default function AssetSelector(props: Props) {
const handleChangeSellAsset = useCallback(
(asset: Asset) => {
onChangeSellAsset(asset)
setOverlayState('closed')
useStore.setState({ assetOverlayState: 'closed' })
},
[onChangeSellAsset],
)
const handleChangeState = useCallback(
(state: OverlayState) => {
setOverlayState(state)
},
[setOverlayState],
)
const handleChangeState = useCallback((state: OverlayState) => {
useStore.setState({ assetOverlayState: state })
}, [])
useEffect(() => {
if (overlayState === 'closed') {
if (assetOverlayState === 'closed') {
onChangeBuyAsset(buyAsset)
onChangeSellAsset(sellAsset)
}
}, [onChangeBuyAsset, onChangeSellAsset, overlayState, buyAsset, sellAsset])
}, [onChangeBuyAsset, onChangeSellAsset, assetOverlayState, buyAsset, sellAsset])
return (
<div className='grid-rows-auto relative grid grid-cols-[1fr_min-content_1fr] gap-y-2 bg-white/5 p-3'>
<div className='grid-rows-auto grid grid-cols-[1fr_min-content_1fr] gap-y-2 bg-white/5 p-3'>
<Text size='sm'>Buy</Text>
<Text size='sm' className='col-start-3'>
Sell
</Text>
<AssetButton onClick={() => setOverlayState('buy')} asset={buyAsset} />
<AssetButton
onClick={() => useStore.setState({ assetOverlayState: 'buy' })}
asset={buyAsset}
/>
<button onClick={handleSwapAssets}>
<SwapIcon className='mx-2 w-4 place-self-center' />
<SwapIcon className='w-4 mx-2 place-self-center' />
</button>
<AssetButton onClick={() => setOverlayState('sell')} asset={sellAsset} />
<AssetButton
onClick={() => useStore.setState({ assetOverlayState: 'sell' })}
asset={sellAsset}
/>
<AssetOverlay
state={overlayState}
state={assetOverlayState}
onChangeState={handleChangeState}
buyAsset={buyAsset}
sellAsset={sellAsset}

View File

@ -35,14 +35,15 @@ interface Props {
export default function SwapForm(props: Props) {
const { buyAsset, sellAsset } = props
const useMargin = useStore((s) => s.useMargin)
const account = useCurrentAccount()
const swap = useStore((s) => s.swap)
const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
const { computeMaxSwapAmount } = useHealthComputer(account)
const { data: borrowAssets } = useMarketBorrowings()
const { data: marketAssets } = useMarketAssets()
const [isMarginChecked, setMarginChecked] = useToggle()
const isBorrowEnabled = !!marketAssets.find(byDenom(sellAsset.denom))?.borrowEnabled
const [isMarginChecked, setMarginChecked] = useToggle(isBorrowEnabled ? useMargin : false)
const [buyAssetAmount, setBuyAssetAmount] = useState(BN_ZERO)
const [sellAssetAmount, setSellAssetAmount] = useState(BN_ZERO)
const [maxBuyableAmountEstimation, setMaxBuyableAmountEstimation] = useState(BN_ZERO)
@ -173,17 +174,33 @@ export default function SwapForm(props: Props) {
[simulateTrade, isAutoLendEnabled],
)
const handleMarginToggleChange = useCallback(
(isMargin: boolean) => {
if (isBorrowEnabled) useStore.setState({ useMargin: isMargin })
setMarginChecked(isBorrowEnabled ? isMargin : false)
},
[isBorrowEnabled, setMarginChecked],
)
useEffect(() => {
setBuyAssetAmount(BN_ZERO)
setSellAssetAmount(BN_ZERO)
setMarginChecked(false)
setMarginChecked(isBorrowEnabled ? useMargin : false)
simulateTrade(
BNCoin.fromDenomAndBigNumber(buyAsset.denom, BN_ZERO),
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
isAutoLendEnabled ? 'lend' : 'deposit',
)
}, [buyAsset.denom, sellAsset.denom, simulateTrade, isAutoLendEnabled, setMarginChecked])
}, [
isBorrowEnabled,
useMargin,
buyAsset.denom,
sellAsset.denom,
isAutoLendEnabled,
simulateTrade,
setMarginChecked,
])
useEffect(() => {
const removeDepositAmount = sellAssetAmount.isGreaterThanOrEqualTo(sellSideMarginThreshold)
@ -229,7 +246,7 @@ export default function SwapForm(props: Props) {
<Divider />
<MarginToggle
checked={isMarginChecked}
onChange={setMarginChecked}
onChange={handleMarginToggleChange}
disabled={!borrowAsset?.isMarket}
borrowAssetSymbol={sellAsset.symbol}
/>

View File

@ -16,7 +16,7 @@ export default function TradeModule(props: Props) {
return (
<div
className={classNames(
'relative isolate max-w-full overflow-hidden rounded-base pb-4',
'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-50',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
'h-full',
)}

View File

@ -3,7 +3,7 @@ import { ORACLE_DENOM } from 'constants/oracle'
export const DEFAULT_SETTINGS: Settings = {
reduceMotion: false,
lendAssets: false,
lendAssets: true,
preferredAsset: ASSETS[0].denom,
displayCurrency: ORACLE_DENOM,
slippage: 0.02,

View File

@ -4,12 +4,14 @@ import MigrationBanner from 'components/MigrationBanner'
import AccountDetailsCard from 'components/Trade/AccountDetailsCard'
import TradeChart from 'components/Trade/TradeChart'
import TradeModule from 'components/Trade/TradeModule'
import useStore from 'store'
import { getEnabledMarketAssets } from 'utils/assets'
export default function TradePage() {
const enabledMarketAssets = getEnabledMarketAssets()
const [buyAsset, setBuyAsset] = useState(enabledMarketAssets[0])
const [sellAsset, setSellAsset] = useState(enabledMarketAssets[1])
const assetOverlayState = useStore((s) => s.assetOverlayState)
return (
<div className='flex flex-col w-full h-full gap-4'>
@ -25,6 +27,13 @@ export default function TradePage() {
<div />
<AccountDetailsCard />
</div>
{assetOverlayState !== 'closed' && (
<div
className='fixed top-0 left-0 z-40 block w-full h-full hover:cursor-pointer'
onClick={() => useStore.setState({ assetOverlayState: 'closed' })}
role='button'
/>
)}
</div>
)
}

View File

@ -11,5 +11,6 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
accountDetailsExpanded: false,
migrationBanner: true,
tutorial: true,
useMargin: true,
}
}

View File

@ -15,5 +15,6 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
vaultModal: null,
walletAssetsModal: null,
withdrawFromVaultsModal: null,
assetOverlayState: 'closed' as OverlayState,
}
}

View File

@ -0,0 +1 @@
type OverlayState = 'buy' | 'sell' | 'closed'

View File

@ -10,6 +10,7 @@ interface CommonSlice {
accountDetailsExpanded: boolean
migrationBanner: boolean
tutorial: boolean
useMargin: boolean
}
interface FocusComponent {

View File

@ -11,6 +11,7 @@ interface ModalSlice {
vaultModal: VaultModal | null
walletAssetsModal: WalletAssetModal | null
withdrawFromVaultsModal: DepositedVault[] | null
assetOverlayState: OverlayState
}
interface AlertDialogButton {