Merge pull request #12 from blockworks-foundation/onboarding-tour
add onboarding ui tour
This commit is contained in:
commit
b3f46655a7
|
@ -8,7 +8,12 @@ import BottomBar from './mobile/BottomBar'
|
|||
import BounceLoader from './shared/BounceLoader'
|
||||
import TopBar from './TopBar'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { SIDEBAR_COLLAPSE_KEY } from '../utils/constants'
|
||||
import {
|
||||
IS_ONBOARDED_KEY,
|
||||
ONBOARDING_TOUR_KEY,
|
||||
SIDEBAR_COLLAPSE_KEY,
|
||||
} from '../utils/constants'
|
||||
import OnboardingTour from './OnboardingTour'
|
||||
|
||||
const Layout = ({ children }: { children: ReactNode }) => {
|
||||
const connected = mangoStore((s) => s.connected)
|
||||
|
@ -17,6 +22,8 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
SIDEBAR_COLLAPSE_KEY,
|
||||
false
|
||||
)
|
||||
const [showOnboardingTour] = useLocalStorageState(ONBOARDING_TOUR_KEY, false)
|
||||
const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||
const { width } = useViewport()
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -77,6 +84,9 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showOnboardingTour && isOnboarded && connected ? (
|
||||
<OnboardingTour />
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,14 +13,16 @@ import { useLocalStorageStringState } from '../hooks/useLocalStorageState'
|
|||
import { LAST_ACCOUNT_KEY } from '../utils/constants'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { retryFn } from '../utils'
|
||||
import Loading from './shared/Loading'
|
||||
|
||||
const MangoAccountsList = ({
|
||||
mangoAccount,
|
||||
}: {
|
||||
mangoAccount: MangoAccount
|
||||
mangoAccount: MangoAccount | undefined
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoAccounts = mangoStore((s) => s.mangoAccounts.accounts)
|
||||
const loading = mangoStore((s) => s.mangoAccount.initialLoad)
|
||||
const [showNewAccountModal, setShowNewAccountModal] = useState(false)
|
||||
const [, setLastAccountViewed] = useLocalStorageStringState(LAST_ACCOUNT_KEY)
|
||||
|
||||
|
@ -45,7 +47,7 @@ const MangoAccountsList = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="step-one">
|
||||
<Popover>
|
||||
{({ open }) => (
|
||||
<>
|
||||
|
@ -53,7 +55,7 @@ const MangoAccountsList = ({
|
|||
<div className="mr-2">
|
||||
<p className="text-right text-xs">{t('accounts')}</p>
|
||||
<p className="text-left text-sm font-bold text-th-fgd-1">
|
||||
{mangoAccount.name}
|
||||
{mangoAccount ? mangoAccount.name : 'No Accounts'}
|
||||
</p>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
|
@ -75,7 +77,9 @@ const MangoAccountsList = ({
|
|||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Panel className="absolute top-[13.5px] -right-5 z-10 mr-4 w-56 rounded-md rounded-t-none border border-th-bkg-3 bg-th-bkg-1 p-4">
|
||||
{mangoAccounts.length ? (
|
||||
{loading ? (
|
||||
<Loading />
|
||||
) : mangoAccounts.length ? (
|
||||
mangoAccounts.map((acc) => (
|
||||
<div key={acc.publicKey.toString()}>
|
||||
<button
|
||||
|
@ -84,14 +88,16 @@ const MangoAccountsList = ({
|
|||
>
|
||||
{acc.name}
|
||||
{acc.publicKey.toString() ===
|
||||
mangoAccount.publicKey.toString() ? (
|
||||
mangoAccount!.publicKey.toString() ? (
|
||||
<CheckCircleIcon className="h-5 w-5 text-th-green" />
|
||||
) : null}
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p>Loading...</p>
|
||||
<p className="mb-4 text-center text-sm">
|
||||
Create your first account 😎
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
<LinkButton
|
||||
|
@ -114,7 +120,7 @@ const MangoAccountsList = ({
|
|||
onClose={() => setShowNewAccountModal(false)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
CardinalOrientation,
|
||||
MaskOptions,
|
||||
Walktour,
|
||||
WalktourLogic,
|
||||
} from 'walktour'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { ONBOARDING_TOUR_KEY } from '../utils/constants'
|
||||
|
||||
const OnboardingTour = () => {
|
||||
const [, setShowOnboardingTour] = useLocalStorageState(ONBOARDING_TOUR_KEY)
|
||||
const router = useRouter()
|
||||
|
||||
const renderTooltip = (tourLogic: WalktourLogic | undefined) => {
|
||||
const { title, description } = tourLogic!.stepContent
|
||||
const { next, prev, close, allSteps, stepIndex } = tourLogic!
|
||||
|
||||
const handleClose = () => {
|
||||
setShowOnboardingTour(false)
|
||||
close()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative w-72 rounded-lg bg-gradient-to-b from-gradient-start via-gradient-mid to-gradient-end p-4">
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className={`absolute right-4 top-4 z-50 text-th-bkg-3 focus:outline-none md:right-2 md:top-2 md:hover:text-th-primary`}
|
||||
>
|
||||
<XMarkIcon className={`h-5 w-5`} />
|
||||
</button>
|
||||
<h3 className="text-th-bkg-1">{title}</h3>
|
||||
<p className="text-sm text-th-bkg-1">{description}</p>
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
{stepIndex !== 0 ? (
|
||||
<button
|
||||
className="default-transition h-8 rounded-md border border-th-bkg-1 px-3 font-bold text-th-bkg-1 focus:outline-none md:hover:border-th-bkg-3 md:hover:text-th-bkg-3"
|
||||
onClick={() => prev()}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
) : (
|
||||
<div className="h-8 w-[58.25px]" />
|
||||
)}
|
||||
<div className="flex space-x-2">
|
||||
{allSteps.map((s, i) => (
|
||||
<div
|
||||
className={`h-1 w-1 rounded-full ${
|
||||
i === stepIndex ? 'bg-th-primary' : 'bg-[rgba(0,0,0,0.2)]'
|
||||
}`}
|
||||
key={s.title}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{stepIndex !== allSteps.length - 1 ? (
|
||||
<button
|
||||
className="default-transition h-8 rounded-md bg-th-bkg-1 px-3 font-bold text-th-fgd-1 focus:outline-none md:hover:bg-th-bkg-3"
|
||||
onClick={() => next()}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="default-transition h-8 rounded-md bg-th-bkg-1 px-3 font-bold text-th-fgd-1 focus:outline-none md:hover:bg-th-bkg-3"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
selector: '#step-one',
|
||||
title: 'Your Accounts',
|
||||
description:
|
||||
'Switch between accounts and create new ones. Use multiple accounts to trade isolated margin and protect your capital from liquidation.',
|
||||
orientationPreferences: [CardinalOrientation.SOUTHEAST],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-two',
|
||||
title: 'Account Value',
|
||||
description:
|
||||
'The value of your assets (deposits) minus the value of your liabilities (borrows).',
|
||||
orientationPreferences: [CardinalOrientation.EASTNORTH],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-three',
|
||||
title: 'Health',
|
||||
description:
|
||||
'If your account health reaches 0% your account will be liquidated. You can increase the health of your account by making a deposit.',
|
||||
orientationPreferences: [CardinalOrientation.SOUTHWEST],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-four',
|
||||
title: 'Free Collateral',
|
||||
description:
|
||||
"The amount of capital you have to trade or borrow against. When your free collateral reaches $0 you won't be able to make withdrawals.",
|
||||
orientationPreferences: [CardinalOrientation.SOUTHWEST],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-five',
|
||||
title: 'Total Interest Value',
|
||||
description:
|
||||
'The value of interest earned (deposits) minus interest paid (borrows).',
|
||||
orientationPreferences: [CardinalOrientation.SOUTHWEST],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-six',
|
||||
title: 'Health Check',
|
||||
description:
|
||||
'Check the health of your account from any screen in the app. A green heart represents good health, orange okay and red poor.',
|
||||
orientationPreferences: [CardinalOrientation.EASTSOUTH],
|
||||
movingTarget: true,
|
||||
customNextFunc: (tourLogic: WalktourLogic) => {
|
||||
router.push('/trade')
|
||||
setTimeout(() => tourLogic.next(), 1000)
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: '#step-seven',
|
||||
title: 'Trade',
|
||||
description:
|
||||
"You choose the quote token of your trades. This means you can easily trade tokens on their relative strength vs. another token. Let's say your thesis is BTC will see diminishing returns relative to SOL. You can sell BTC and buy SOL. Now you are long SOL/BTC",
|
||||
orientationPreferences: [CardinalOrientation.CENTER],
|
||||
customPrevFunc: (tourLogic: WalktourLogic) => {
|
||||
router.push('/')
|
||||
tourLogic.prev()
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: '#step-eight',
|
||||
title: 'Trade Settings',
|
||||
description:
|
||||
'Edit your slippage settings and toggle margin on and off. When margin is off your trades will be limited by your balance for each token.',
|
||||
orientationPreferences: [CardinalOrientation.WESTNORTH],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-nine',
|
||||
title: 'Token to Sell',
|
||||
description:
|
||||
'Select the token you want to sell. If your sell size is above your token balance a loan will be opened to cover the shortfall.',
|
||||
orientationPreferences: [CardinalOrientation.WESTNORTH],
|
||||
movingTarget: true,
|
||||
},
|
||||
{
|
||||
selector: '#step-ten',
|
||||
title: 'Health Impact',
|
||||
description:
|
||||
'Projects the health of your account before you make a trade.',
|
||||
orientationPreferences: [CardinalOrientation.WESTNORTH],
|
||||
movingTarget: true,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Walktour
|
||||
customTooltipRenderer={renderTooltip}
|
||||
steps={steps}
|
||||
updateInterval={200}
|
||||
disableCloseOnClick
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default OnboardingTour
|
|
@ -9,6 +9,7 @@ import {
|
|||
CurrencyDollarIcon,
|
||||
ChartBarIcon,
|
||||
Cog8ToothIcon,
|
||||
InformationCircleIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { useRouter } from 'next/router'
|
||||
|
@ -17,12 +18,26 @@ import { Fragment, ReactNode, useEffect, useState } from 'react'
|
|||
import { Disclosure, Popover, Transition } from '@headlessui/react'
|
||||
import MangoAccountSummary from './account/MangoAccountSummary'
|
||||
import Tooltip from './shared/Tooltip'
|
||||
import { HealthType } from '@blockworks-foundation/mango-v4'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { ONBOARDING_TOUR_KEY } from '../utils/constants'
|
||||
|
||||
const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
||||
const [, setShowOnboardingTour] = useLocalStorageState(ONBOARDING_TOUR_KEY)
|
||||
const { t } = useTranslation('common')
|
||||
const { connected } = useWallet()
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const router = useRouter()
|
||||
const { pathname } = router
|
||||
|
||||
const handleTakeTour = () => {
|
||||
if (pathname !== '/') {
|
||||
router.push('/')
|
||||
}
|
||||
setShowOnboardingTour(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col justify-between transition-all duration-500 ${
|
||||
|
@ -126,10 +141,51 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
isExternal
|
||||
showTooltip={false}
|
||||
/>
|
||||
{connected ? (
|
||||
<button
|
||||
className="default-transition mt-1 flex items-center px-4 text-th-fgd-2 md:hover:text-th-primary"
|
||||
onClick={handleTakeTour}
|
||||
>
|
||||
<InformationCircleIcon className="mr-3 h-5 w-5" />
|
||||
<span className="text-base">Take UI Tour</span>
|
||||
</button>
|
||||
) : null}
|
||||
</ExpandableMenuItem>
|
||||
</div>
|
||||
</div>
|
||||
<MangoAccountSummary collapsed={collapsed} />
|
||||
<div className="border-t border-th-bkg-3">
|
||||
<ExpandableMenuItem
|
||||
collapsed={collapsed}
|
||||
icon={
|
||||
<HealthHeart
|
||||
health={
|
||||
mangoAccount
|
||||
? mangoAccount.getHealthRatioUi(HealthType.maint)
|
||||
: undefined
|
||||
}
|
||||
size={32}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<div className="text-left">
|
||||
<p className="mb-0.5 whitespace-nowrap text-xs">Health Check</p>
|
||||
<p className="whitespace-nowrap text-sm font-bold text-th-fgd-1">
|
||||
{mangoAccount
|
||||
? mangoAccount.name
|
||||
: connected
|
||||
? 'No Account'
|
||||
: 'Connect'}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
alignBottom
|
||||
hideIconBg
|
||||
>
|
||||
<div className="px-4 pb-4 pt-2">
|
||||
<MangoAccountSummary />
|
||||
</div>
|
||||
</ExpandableMenuItem>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -50,9 +50,7 @@ const TopBar = () => {
|
|||
</span>
|
||||
{connected ? (
|
||||
<div className="flex items-center space-x-4">
|
||||
{mangoAccount ? (
|
||||
<MangoAccountsList mangoAccount={mangoAccount} />
|
||||
) : null}
|
||||
<MangoAccountsList mangoAccount={mangoAccount} />
|
||||
<ConnectedMenu />
|
||||
</div>
|
||||
) : isOnboarded ? (
|
||||
|
|
|
@ -130,14 +130,14 @@ const AccountPage = () => {
|
|||
}, [totalInterestData])
|
||||
|
||||
const maintHealth = useMemo(() => {
|
||||
return mangoAccount ? mangoAccount.getHealthRatioUi(HealthType.maint) : 100
|
||||
return mangoAccount ? mangoAccount.getHealthRatioUi(HealthType.maint) : 0
|
||||
}, [mangoAccount])
|
||||
|
||||
return !chartToShow ? (
|
||||
<>
|
||||
<div className="mb-8 flex flex-col md:mb-10 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div className="mb-4 flex items-center space-x-6 lg:mb-0">
|
||||
<div>
|
||||
<div id="step-two">
|
||||
<p className="mb-1.5">{t('account-value')}</p>
|
||||
<div className="mb-1 flex items-center text-5xl font-bold text-th-fgd-1">
|
||||
$
|
||||
|
@ -221,21 +221,25 @@ const AccountPage = () => {
|
|||
</div>
|
||||
<div className="grid grid-cols-3 gap-x-6 border-b border-th-bkg-3 md:border-b-0">
|
||||
<div className="col-span-3 border-t border-th-bkg-3 py-4 md:col-span-1 md:border-l md:border-t-0 md:pl-6 lg:col-span-1">
|
||||
<p className="text-th-fgd-3">{t('health')}</p>
|
||||
<p className="text-2xl font-bold text-th-fgd-1">{maintHealth}%</p>
|
||||
<div id="step-three">
|
||||
<p className="text-th-fgd-3">{t('health')}</p>
|
||||
<p className="text-2xl font-bold text-th-fgd-1">{maintHealth}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-3 border-t border-th-bkg-3 py-4 md:col-span-1 md:border-l md:border-t-0 md:pl-6 lg:col-span-1">
|
||||
<p className="text-th-fgd-3">{t('free-collateral')}</p>
|
||||
<p className="text-2xl font-bold text-th-fgd-1">
|
||||
{mangoAccount
|
||||
? formatFixedDecimals(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue()!.toNumber()
|
||||
),
|
||||
true
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
<div id="step-four">
|
||||
<p className="text-th-fgd-3">{t('free-collateral')}</p>
|
||||
<p className="text-2xl font-bold text-th-fgd-1">
|
||||
{mangoAccount
|
||||
? formatFixedDecimals(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue()!.toNumber()
|
||||
),
|
||||
true
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="col-span-4 flex items-center justify-between border-t border-th-bkg-3 py-4 md:col-span-2 md:border-l md:border-t-0 md:pl-6 lg:col-span-1">
|
||||
<div>
|
||||
|
@ -254,7 +258,7 @@ const AccountPage = () => {
|
|||
) : null}
|
||||
</div> */}
|
||||
<div className="col-span-3 flex items-center justify-between border-t border-th-bkg-3 py-4 md:col-span-1 md:border-l md:border-t-0 md:pl-6 lg:col-span-1">
|
||||
<div>
|
||||
<div id="step-five">
|
||||
<p className="text-th-fgd-3">{t('total-interest-value')}</p>
|
||||
<p className="text-2xl font-bold text-th-fgd-1">
|
||||
{formatFixedDecimals(interestTotalValue, true)}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
const HealthHeart = ({ health, size }: { health: number; size: number }) => {
|
||||
const HealthHeart = ({
|
||||
health,
|
||||
size,
|
||||
}: {
|
||||
health: number | undefined
|
||||
size: number
|
||||
}) => {
|
||||
const styles = {
|
||||
height: `${size}px`,
|
||||
width: `${size}px`,
|
||||
|
@ -6,13 +12,16 @@ const HealthHeart = ({ health, size }: { health: number; size: number }) => {
|
|||
|
||||
return (
|
||||
<svg
|
||||
id="step-six"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={
|
||||
health > 15 && health < 50
|
||||
? 'text-th-orange'
|
||||
: health >= 50
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
health
|
||||
? health > 15 && health < 50
|
||||
? 'text-th-orange'
|
||||
: health >= 50
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
: 'text-th-fgd-4'
|
||||
}
|
||||
style={styles}
|
||||
viewBox="0 0 20 20"
|
||||
|
@ -30,7 +39,13 @@ const HealthHeart = ({ health, size }: { health: number; size: number }) => {
|
|||
keyTimes="0;0.5;1"
|
||||
values="1;1.1;1"
|
||||
dur={
|
||||
health > 15 && health < 50 ? '1s' : health >= 50 ? '2s' : '0.33s'
|
||||
health
|
||||
? health > 15 && health < 50
|
||||
? '1s'
|
||||
: health >= 50
|
||||
? '2s'
|
||||
: '0.33s'
|
||||
: '0s'
|
||||
}
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
|
@ -38,7 +53,13 @@ const HealthHeart = ({ health, size }: { health: number; size: number }) => {
|
|||
attributeName="opacity"
|
||||
values="0.8;1;0.8"
|
||||
dur={
|
||||
health > 15 && health < 50 ? '1s' : health >= 50 ? '2s' : '0.33s'
|
||||
health
|
||||
? health > 15 && health < 50
|
||||
? '1s'
|
||||
: health >= 50
|
||||
? '2s'
|
||||
: '0.33s'
|
||||
: '0s'
|
||||
}
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
|
|
|
@ -10,89 +10,64 @@ import DepositModal from '../modals/DepositModal'
|
|||
import WithdrawModal from '../modals/WithdrawModal'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { ArrowDownTrayIcon } from '@heroicons/react/20/solid'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import HealthHeart from './HealthHeart'
|
||||
import { ExpandableMenuItem } from '../SideNav'
|
||||
|
||||
const MangoAccountSummary = ({ collapsed }: { collapsed: boolean }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected } = useWallet()
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const [showDepositModal, setShowDepositModal] = useState(false)
|
||||
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
{mangoAccount ? (
|
||||
<div className="border-t border-th-bkg-3">
|
||||
<ExpandableMenuItem
|
||||
collapsed={collapsed}
|
||||
icon={
|
||||
<HealthHeart
|
||||
health={mangoAccount.getHealthRatioUi(HealthType.maint)!}
|
||||
size={32}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<div className="text-left">
|
||||
<p className="mb-0.5 whitespace-nowrap text-xs">Health Check</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
{mangoAccount.name}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
alignBottom
|
||||
hideIconBg
|
||||
>
|
||||
<div className="px-4 pb-4 pt-2">
|
||||
<div className="mb-4 space-y-2">
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">{t('health')}</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
{mangoAccount
|
||||
? mangoAccount.getHealthRatioUi(HealthType.maint)
|
||||
: 100}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">{t('account-value')}</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
$
|
||||
{mangoAccount
|
||||
? formatDecimal(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.getEquity()!.toNumber()
|
||||
),
|
||||
2
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">
|
||||
{t('free-collateral')}
|
||||
</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
$
|
||||
{mangoAccount
|
||||
? formatDecimal(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue()!.toNumber()
|
||||
),
|
||||
2
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
className="flex w-full items-center justify-center"
|
||||
onClick={() => setShowDepositModal(true)}
|
||||
>
|
||||
<ArrowDownTrayIcon className="mr-2 h-5 w-5" />
|
||||
{t('deposit')}
|
||||
</Button>
|
||||
{/* <Button
|
||||
<div className="mb-4 space-y-2">
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">{t('health')}</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
{mangoAccount ? mangoAccount.getHealthRatioUi(HealthType.maint) : 0}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">{t('account-value')}</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
$
|
||||
{mangoAccount
|
||||
? formatDecimal(
|
||||
toUiDecimalsForQuote(mangoAccount.getEquity()!.toNumber()),
|
||||
2
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-3">{t('free-collateral')}</p>
|
||||
<p className="text-sm font-bold text-th-fgd-1">
|
||||
$
|
||||
{mangoAccount
|
||||
? formatDecimal(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue()!.toNumber()
|
||||
),
|
||||
2
|
||||
)
|
||||
: (0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
className="flex w-full items-center justify-center"
|
||||
disabled={!mangoAccount || !connected}
|
||||
onClick={() => setShowDepositModal(true)}
|
||||
>
|
||||
<ArrowDownTrayIcon className="mr-2 h-5 w-5" />
|
||||
{t('deposit')}
|
||||
</Button>
|
||||
{/* <Button
|
||||
className="w-full"
|
||||
onClick={() => setShowWithdrawModal(true)}
|
||||
secondary
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
ArrowDownTrayIcon,
|
||||
CheckCircleIcon,
|
||||
FireIcon,
|
||||
InformationCircleIcon,
|
||||
PencilIcon,
|
||||
PlusCircleIcon,
|
||||
XMarkIcon,
|
||||
|
@ -28,7 +29,7 @@ import ActionTokenList from '../account/ActionTokenList'
|
|||
import { walletBalanceForToken } from './DepositModal'
|
||||
import { floorToDecimal } from '../../utils/numbers'
|
||||
import { handleWalletConnect } from '../wallet/ConnectWalletButton'
|
||||
import { IS_ONBOARDED_KEY } from '../../utils/constants'
|
||||
import { IS_ONBOARDED_KEY, ONBOARDING_TOUR_KEY } from '../../utils/constants'
|
||||
import ParticlesBackground from '../ParticlesBackground'
|
||||
import ButtonGroup from '../forms/ButtonGroup'
|
||||
import Decimal from 'decimal.js'
|
||||
|
@ -51,19 +52,21 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
const [submitDeposit, setSubmitDeposit] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const [, setIsOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||
const [, setShowTour] = useLocalStorageState(ONBOARDING_TOUR_KEY)
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
||||
const handleNextStep = () => {
|
||||
setShowSetupStep(showSetupStep + 1)
|
||||
}
|
||||
|
||||
const handleSaveProfile = () => {
|
||||
// save profile details to db then:
|
||||
setShowSetupStep(2)
|
||||
}
|
||||
// const handleSaveProfile = () => {
|
||||
// // save profile details to db then:
|
||||
// setShowSetupStep(2)
|
||||
// }
|
||||
|
||||
const handleEndOnboarding = () => {
|
||||
const handleShowTour = () => {
|
||||
onClose()
|
||||
setShowTour(true)
|
||||
}
|
||||
|
||||
const connectWallet = async () => {
|
||||
|
@ -112,8 +115,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
})
|
||||
console.error(e)
|
||||
}
|
||||
setIsOnboarded(true)
|
||||
}, [accountName, wallet, t, setIsOnboarded])
|
||||
}, [accountName, wallet, t])
|
||||
|
||||
const handleDeposit = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
|
@ -138,7 +140,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
})
|
||||
|
||||
await actions.reloadMangoAccount()
|
||||
onClose()
|
||||
setShowSetupStep(4)
|
||||
setSubmitDeposit(false)
|
||||
} catch (e: any) {
|
||||
notify({
|
||||
|
@ -150,7 +152,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
setSubmitDeposit(false)
|
||||
console.error(e)
|
||||
}
|
||||
}, [depositAmount, depositToken, onClose, setIsOnboarded])
|
||||
}, [depositAmount, depositToken, onClose])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount && showSetupStep === 2) {
|
||||
|
@ -207,11 +209,11 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
>
|
||||
<div className="min-h-screen px-4 text-center">
|
||||
<Dialog.Overlay
|
||||
className={`intro-bg pointer-events-none fixed inset-0 bg-th-bkg-1`}
|
||||
className={`intro-bg pointer-events-none fixed inset-0 bg-th-bkg-1 opacity-80`}
|
||||
/>
|
||||
<div className="absolute top-6 left-6 z-10" id="repulse">
|
||||
{/* <div className="absolute top-6 left-6 z-10" id="repulse">
|
||||
<img className="h-10 w-auto" src="/logos/logo-mark.svg" alt="next" />
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="absolute top-6 right-6 z-10" id="repulse">
|
||||
<IconButton hideBg onClick={() => onClose()}>
|
||||
<XMarkIcon className="h-6 w-6 text-th-fgd-2" />
|
||||
|
@ -220,7 +222,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
<div className="absolute bottom-0 left-0 z-10 flex h-1.5 w-full flex-grow bg-th-bkg-3">
|
||||
<div
|
||||
style={{
|
||||
width: `${(showSetupStep / 3) * 100}%`,
|
||||
width: `${(showSetupStep / 4) * 100}%`,
|
||||
}}
|
||||
className="flex rounded bg-th-primary transition-all duration-700 ease-out"
|
||||
/>
|
||||
|
@ -435,7 +437,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
</Button>
|
||||
<LinkButton
|
||||
className="flex w-full justify-center"
|
||||
onClick={onClose}
|
||||
onClick={() => setShowSetupStep(4)}
|
||||
>
|
||||
<span className="default-transition text-th-fgd-4 underline md:hover:text-th-fgd-3 md:hover:no-underline">
|
||||
Skip for now
|
||||
|
@ -562,7 +564,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
Deposit
|
||||
</div>
|
||||
</Button>
|
||||
<LinkButton onClick={handleEndOnboarding}>
|
||||
<LinkButton onClick={() => setShowSetupStep(4)}>
|
||||
<span className="default-transition text-th-fgd-4 underline md:hover:text-th-fgd-3 md:hover:no-underline">
|
||||
Skip for now
|
||||
</span>
|
||||
|
@ -571,6 +573,46 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
</div>
|
||||
)}
|
||||
</EnterRightExitLeft>
|
||||
<EnterRightExitLeft
|
||||
className="absolute top-0.5 left-0 z-20 w-full rounded-lg bg-th-bkg-1 p-6"
|
||||
show={showSetupStep === 4}
|
||||
style={{ height: 'calc(100% - 12px)' }}
|
||||
>
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div>
|
||||
<h2 className="mb-6 text-4xl">That's a wrap</h2>
|
||||
<p className="mb-2">
|
||||
We recommend taking a short tour to get familiar with our
|
||||
new interface.
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
Or, jump in the deep end and start trading.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Button
|
||||
className="flex-1"
|
||||
secondary
|
||||
onClick={onClose}
|
||||
size="large"
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
Get Started
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1"
|
||||
onClick={handleShowTour}
|
||||
size="large"
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
<InformationCircleIcon className="mr-2 h-5 w-5" />
|
||||
Show Tour
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</EnterRightExitLeft>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -239,15 +239,17 @@ const SwapForm = () => {
|
|||
</EnterBottomExitBottom>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h3>{t('trade')}</h3>
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
onClick={() => setShowSettings(true)}
|
||||
size="small"
|
||||
>
|
||||
<Cog8ToothIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
<div id="step-eight">
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
onClick={() => setShowSettings(true)}
|
||||
size="small"
|
||||
>
|
||||
<Cog8ToothIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div id="step-nine" className="mb-2 flex items-center justify-between">
|
||||
<p className="text-th-fgd-3">{t('sell')}</p>
|
||||
<MaxSwapAmount
|
||||
amountWithBorrow={amountWithBorrow}
|
||||
|
@ -378,42 +380,43 @@ const SwapForm = () => {
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
{!!mangoAccount ? (
|
||||
<div
|
||||
className={`bg-th-bkg-3 px-6 transition-all ${
|
||||
showHealthImpact ? 'max-h-40 py-4 ' : 'h-0'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm">{t('health-impact')}</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm text-th-fgd-1">{currentMaintHealth}%</p>
|
||||
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
<p
|
||||
className={`${
|
||||
maintProjectedHealth! < 50 && maintProjectedHealth! > 15
|
||||
? 'text-th-orange'
|
||||
: maintProjectedHealth! <= 15
|
||||
? 'text-th-red'
|
||||
: 'text-th-green'
|
||||
} text-sm`}
|
||||
{/* {!!mangoAccount ? ( */}
|
||||
<div
|
||||
id="step-ten"
|
||||
className={`bg-th-bkg-3 px-6 transition-all ${
|
||||
showHealthImpact ? 'max-h-40 py-4 ' : 'h-0'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm">{t('health-impact')}</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm text-th-fgd-1">{currentMaintHealth}%</p>
|
||||
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
<p
|
||||
className={`${
|
||||
maintProjectedHealth! < 50 && maintProjectedHealth! > 15
|
||||
? 'text-th-orange'
|
||||
: maintProjectedHealth! <= 15
|
||||
? 'text-th-red'
|
||||
: 'text-th-green'
|
||||
} text-sm`}
|
||||
>
|
||||
{maintProjectedHealth!}%{' '}
|
||||
<span
|
||||
className={`text-xs ${
|
||||
maintProjectedHealth! >= currentMaintHealth!
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
}`}
|
||||
>
|
||||
{maintProjectedHealth!}%{' '}
|
||||
<span
|
||||
className={`text-xs ${
|
||||
maintProjectedHealth! >= currentMaintHealth!
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
}`}
|
||||
>
|
||||
({maintProjectedHealth! >= currentMaintHealth! ? '+' : ''}
|
||||
{maintProjectedHealth! - currentMaintHealth!}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
({maintProjectedHealth! >= currentMaintHealth! ? '+' : ''}
|
||||
{maintProjectedHealth! - currentMaintHealth!}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{/* ) : null} */}
|
||||
</ContentBox>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"react-window": "^1.8.7",
|
||||
"recharts": "^2.1.12",
|
||||
"tsparticles": "^2.2.4",
|
||||
"walktour": "^5.1.1",
|
||||
"zustand": "^4.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -14,6 +14,8 @@ export const ALPHA_DEPOSIT_LIMIT = 20
|
|||
|
||||
export const SIDEBAR_COLLAPSE_KEY = 'sidebar-0.1'
|
||||
|
||||
export const ONBOARDING_TOUR_KEY = 'showOnboardingTour'
|
||||
|
||||
export const PROFILE_CATEGORIES = [
|
||||
'borrower',
|
||||
'day-trader',
|
||||
|
|
|
@ -5445,6 +5445,11 @@ wait-on@6.0.0:
|
|||
minimist "^1.2.5"
|
||||
rxjs "^7.1.0"
|
||||
|
||||
walktour@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/walktour/-/walktour-5.1.1.tgz#951b5bce2abed0ae4209dc74d4d79f252a85e813"
|
||||
integrity sha512-pRjbjjBGddgiWaWryE+H6DxOeAuUgIZJ5ck0hOVrhZ4QNgZDs1okuGMXDKx3Gi4hi/N9ESwATyyB5hb7kiK7wQ==
|
||||
|
||||
web-streams-polyfill@^3.0.3:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
|
||||
|
|
Loading…
Reference in New Issue