MP-2802: created get started overlay (#351)

This commit is contained in:
Linkie Link 2023-08-08 21:50:07 +02:00 committed by GitHub
parent f4fc2dcfcc
commit d494bce26a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 289 additions and 88 deletions

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect } from 'react' import { useCallback, useEffect } from 'react'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import AccountFundFirst from 'components/Account/AccountFund' import AccountFund from 'components/Account/AccountFund'
import FullOverlayContent from 'components/FullOverlayContent' import FullOverlayContent from 'components/FullOverlayContent'
import WalletSelect from 'components/Wallet/WalletSelect' import WalletSelect from 'components/Wallet/WalletSelect'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
@ -17,7 +17,7 @@ export default function AccountCreateFirst() {
const [isCreating, setIsCreating] = useToggle(false) const [isCreating, setIsCreating] = useToggle(false)
useEffect(() => { useEffect(() => {
if (!address) useStore.setState({ focusComponent: <WalletSelect /> }) if (!address) useStore.setState({ focusComponent: { component: <WalletSelect /> } })
}, [address]) }, [address])
const handleClick = useCallback(async () => { const handleClick = useCallback(async () => {
@ -26,7 +26,14 @@ export default function AccountCreateFirst() {
setIsCreating(false) setIsCreating(false)
if (accountId) { if (accountId) {
navigate(getRoute(getPage(pathname), address, accountId)) navigate(getRoute(getPage(pathname), address, accountId))
useStore.setState({ focusComponent: <AccountFundFirst /> }) useStore.setState({
focusComponent: {
component: <AccountFund />,
onClose: () => {
useStore.setState({ getStartedModal: true })
},
},
})
} }
}, [createAccount, navigate, pathname, address, setIsCreating]) }, [createAccount, navigate, pathname, address, setIsCreating])

View File

@ -96,7 +96,7 @@ export default function AccountFund() {
useEffect(() => { useEffect(() => {
if (BN(baseBalance).isLessThan(hardcodedFee.amount[0].amount)) { if (BN(baseBalance).isLessThan(hardcodedFee.amount[0].amount)) {
useStore.setState({ focusComponent: <WalletBridges /> }) useStore.setState({ focusComponent: { component: <WalletBridges /> } })
} }
}, [baseBalance]) }, [baseBalance])

View File

@ -2,7 +2,7 @@ import classNames from 'classnames'
import { useCallback, useEffect } from 'react' import { useCallback, useEffect } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom' import { useLocation, useNavigate, useParams } from 'react-router-dom'
import AccountFundFirst from 'components/Account/AccountFund' import AccountFund from 'components/Account/AccountFund'
import AccountStats from 'components/Account/AccountStats' import AccountStats from 'components/Account/AccountStats'
import Button from 'components/Button' import Button from 'components/Button'
import Card from 'components/Card' import Card from 'components/Card'
@ -89,7 +89,14 @@ export default function AccountList(props: Props) {
leftIcon={<ArrowUpLine />} leftIcon={<ArrowUpLine />}
onClick={() => { onClick={() => {
if (positionBalance.isLessThanOrEqualTo(0)) { if (positionBalance.isLessThanOrEqualTo(0)) {
useStore.setState({ focusComponent: <AccountFundFirst /> }) useStore.setState({
focusComponent: {
component: <AccountFund />,
onClose: () => {
useStore.setState({ getStartedModal: true })
},
},
})
return return
} }
useStore.setState({ fundAndWithdrawModal: 'fund' }) useStore.setState({ fundAndWithdrawModal: 'fund' })

View File

@ -3,7 +3,7 @@ import { useCallback, useEffect } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom' import { useLocation, useNavigate, useParams } from 'react-router-dom'
import AccountCreateFirst from 'components/Account/AccountCreateFirst' import AccountCreateFirst from 'components/Account/AccountCreateFirst'
import AccountFundFirst from 'components/Account/AccountFund' import AccountFund from 'components/Account/AccountFund'
import AccountList from 'components/Account/AccountList' import AccountList from 'components/Account/AccountList'
import Button from 'components/Button' import Button from 'components/Button'
import { CircularProgress } from 'components/CircularProgress' import { CircularProgress } from 'components/CircularProgress'
@ -57,18 +57,25 @@ export default function AccountMenuContent(props: Props) {
if (accountId) { if (accountId) {
navigate(getRoute(getPage(pathname), address, accountId)) navigate(getRoute(getPage(pathname), address, accountId))
useStore.setState({ focusComponent: <AccountFundFirst /> }) useStore.setState({
focusComponent: {
component: <AccountFund />,
onClose: () => {
useStore.setState({ getStartedModal: true })
},
},
})
} }
}, [createAccount, navigate, pathname, address, setShowMenu, setIsCreating]) }, [createAccount, navigate, pathname, address, setShowMenu, setIsCreating])
const handleCreateAccountClick = useCallback(() => { const handleCreateAccountClick = useCallback(() => {
setShowMenu(!showMenu) setShowMenu(!showMenu)
if (!checkHasFunds() && !hasCreditAccounts) { if (!checkHasFunds() && !hasCreditAccounts) {
useStore.setState({ focusComponent: <WalletBridges /> }) useStore.setState({ focusComponent: { component: <WalletBridges /> } })
return return
} }
if (!hasCreditAccounts) { if (!hasCreditAccounts) {
useStore.setState({ focusComponent: <AccountCreateFirst /> }) useStore.setState({ focusComponent: { component: <AccountCreateFirst /> } })
return return
} }
}, [checkHasFunds, hasCreditAccounts, setShowMenu, showMenu]) }, [checkHasFunds, hasCreditAccounts, setShowMenu, showMenu])
@ -127,7 +134,7 @@ export default function AccountMenuContent(props: Props) {
)} )}
> >
{isAccountSelected && isLoadingAccount && ( {isAccountSelected && isLoadingAccount && (
<div className='flex h-full w-full items-center justify-center p-4'> <div className='flex items-center justify-center w-full h-full p-4'>
<CircularProgress size={40} /> <CircularProgress size={40} />
</div> </div>
)} )}

View File

@ -16,7 +16,7 @@ export default function ActionButton(props: ButtonProps) {
const { accountId } = useParams() const { accountId } = useParams()
const handleCreateAccountClick = useCallback(() => { const handleCreateAccountClick = useCallback(() => {
useStore.setState({ focusComponent: <AccountCreateFirst /> }) useStore.setState({ focusComponent: { component: <AccountCreateFirst /> } })
}, []) }, [])
if (!address) return <WalletConnectButton {...defaultProps} /> if (!address) return <WalletConnectButton {...defaultProps} />

View File

@ -1,34 +1,17 @@
import { ExternalLink } from 'components/Icons' import { ExternalLink } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import { TextLink } from 'components/TextLink'
import { DocURL } from 'types/enums/docURL'
interface Props { interface Props {
type: DocLinkType type: DocLinkType
} }
function getData(type: string) { function getData(type: string) {
if (type === 'account') if (type === 'account') return ['Why mint your account?', 'Learn more', DocURL.ROVER_INTRO_URL]
return [ if (type === 'fund') return ['Why fund your account?', 'Learn more', DocURL.MANAGE_ACCOUNT_URL]
'Why mint your account?', if (type === 'wallet') return ['New with wallets?', 'Learn more', DocURL.WALLET_INTRO_URL]
'Learn more', return ['By continuing you accept our', 'terms of service', DocURL.TERMS_OF_SERVICE_URL]
'https://docs.marsprotocol.io/docs/learn/workstation/rover/rover-intro',
]
if (type === 'fund')
return [
'Why fund your account?',
'Learn more',
'https://docs.marsprotocol.io/docs/learn/workstation/rover/managing-credit-accounts',
]
if (type === 'wallet')
return [
'New with wallets?',
'Learn more',
'https://docs.marsprotocol.io/docs/learn/workstation/basics/basics-intro',
]
return [
'By continuing you accept our',
'terms of service',
'https://docs.marsprotocol.io/docs/overview/legal/terms-of-service',
]
} }
export default function DocsLink(props: Props) { export default function DocsLink(props: Props) {
@ -37,10 +20,15 @@ export default function DocsLink(props: Props) {
return ( return (
<Text size='sm' className='w-full pt-3 text-center text-white/60'> <Text size='sm' className='w-full pt-3 text-center text-white/60'>
{`${intro} `} {`${intro} `}
<a href={url} target='_blank' className='leading-4 text-white hover:underline'> <TextLink
href={url}
target='_blank'
className='leading-4 text-white hover:underline'
title={linkText}
>
{linkText} {linkText}
<ExternalLink className='-mt-1 ml-1 inline w-3.5' /> <ExternalLink className='-mt-1 ml-1 inline w-3.5' />
</a> </TextLink>
</Text> </Text>
) )
} }

View File

@ -20,6 +20,7 @@ export default function DesktopHeader() {
const focusComponent = useStore((s) => s.focusComponent) const focusComponent = useStore((s) => s.focusComponent)
function handleCloseFocusMode() { function handleCloseFocusMode() {
if (focusComponent.onClose) focusComponent.onClose()
useStore.setState({ focusComponent: null }) useStore.setState({ focusComponent: null })
} }
@ -39,7 +40,7 @@ export default function DesktopHeader() {
> >
<DesktopNavigation /> <DesktopNavigation />
{focusComponent ? ( {focusComponent ? (
<div className='flex w-full justify-between'> <div className='flex justify-between w-full'>
<div className='flex h-5 w-13' /> <div className='flex h-5 w-13' />
{address && <Wallet />} {address && <Wallet />}
<EscButton onClick={handleCloseFocusMode} /> <EscButton onClick={handleCloseFocusMode} />

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M22 12C22 17.5228 17.5228 22 12 22M22 12C22 6.47715 17.5228 2 12 2M22 12H20M12 22C6.47715 22 2 17.5228 2 12M12 22V20M2 12C2 6.47715 6.47715 2 12 2M2 12H4M12 2V4M19.0711 19.0711L17.6569 17.6569M6.34315 6.34315L4.92893 4.92893M17.6569 6.34315L19.0711 4.92893M4.92893 19.0711L6.34315 17.6569M8 12L10.5 10.5L12 8L13.5 10.5L16 12L13.5 13.5L12 16L10.5 13.5L8 12Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3.33398 12.4974C3.33398 12.4974 4.16732 11.6641 6.66732 11.6641C9.16732 11.6641 10.834 13.3307 13.334 13.3307C15.834 13.3307 16.6673 12.4974 16.6673 12.4974V3.33073C16.6673 3.33073 15.834 4.16406 13.334 4.16406C10.834 4.16406 9.16732 2.4974 6.66732 2.4974C4.16732 2.4974 3.33398 3.33073 3.33398 3.33073M3.33399 18.3307L3.33398 1.66406"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M10.2739 5.95728C9.79691 6.29735 9.21316 6.4974 8.58268 6.4974C6.97185 6.4974 5.66602 5.19156 5.66602 3.58073C5.66602 1.9699 6.97185 0.664062 8.58268 0.664062C9.62684 0.664062 10.5428 1.21274 11.0581 2.03751M3.99935 15.7367H6.17459C6.45821 15.7367 6.74009 15.7704 7.01503 15.8379L9.31343 16.3965C9.81214 16.518 10.3317 16.5298 10.8356 16.4319L13.3768 15.9375C14.0481 15.8067 14.6657 15.4853 15.1496 15.0145L16.9476 13.2655C17.461 12.7669 17.461 11.9578 16.9476 11.4583C16.4853 11.0086 15.7533 10.958 15.2303 11.3393L13.1348 12.8681C12.8348 13.0875 12.4696 13.2056 12.0941 13.2056H10.0706L11.3586 13.2055C12.0845 13.2055 12.6726 12.6335 12.6726 11.9273V11.6717C12.6726 11.0853 12.2623 10.574 11.6777 10.4323L9.68984 9.94885C9.36633 9.87038 9.03502 9.83073 8.70197 9.83073C7.89796 9.83073 6.44259 10.4964 6.44259 10.4964L3.99935 11.5181M15.666 4.41406C15.666 6.02489 14.3602 7.33073 12.7493 7.33073C11.1385 7.33073 9.83268 6.02489 9.83268 4.41406C9.83268 2.80323 11.1385 1.4974 12.7493 1.4974C14.3602 1.4974 15.666 2.80323 15.666 4.41406ZM0.666016 11.1641L0.666016 15.9974C0.666016 16.4641 0.666016 16.6975 0.756843 16.8757C0.836738 17.0325 0.964222 17.16 1.12102 17.2399C1.29928 17.3307 1.53264 17.3307 1.99935 17.3307H2.66602C3.13273 17.3307 3.36608 17.3307 3.54434 17.2399C3.70114 17.16 3.82863 17.0325 3.90852 16.8757C3.99935 16.6975 3.99935 16.4641 3.99935 15.9974V11.1641C3.99935 10.6974 3.99935 10.464 3.90852 10.2857C3.82863 10.1289 3.70114 10.0015 3.54434 9.92156C3.36608 9.83073 3.13273 9.83073 2.66602 9.83073L1.99935 9.83073C1.53264 9.83073 1.29928 9.83073 1.12102 9.92156C0.964222 10.0015 0.836738 10.1289 0.756844 10.2857C0.666016 10.464 0.666016 10.6974 0.666016 11.1641Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M6.66732 18.3307V16.6641M7.91732 12.4974V5.83073M13.334 18.3307V16.6641M12.084 12.4974V5.83073M7.33398 16.6641H12.6673C14.0674 16.6641 14.7675 16.6641 15.3023 16.3916C15.7727 16.1519 16.1552 15.7694 16.3948 15.299C16.6673 14.7643 16.6673 14.0642 16.6673 12.6641V5.66406C16.6673 4.26393 16.6673 3.56387 16.3948 3.02909C16.1552 2.55868 15.7727 2.17623 15.3023 1.93655C14.7675 1.66406 14.0674 1.66406 12.6673 1.66406H7.33398C5.93385 1.66406 5.23379 1.66406 4.69901 1.93655C4.2286 2.17623 3.84615 2.55868 3.60647 3.02909C3.33398 3.56387 3.33398 4.26393 3.33398 5.66406V12.6641C3.33398 14.0642 3.33398 14.7643 3.60647 15.299C3.84615 15.7694 4.2286 16.1519 4.69901 16.3916C5.23379 16.6641 5.93385 16.6641 7.33398 16.6641Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 898 B

View File

@ -13,6 +13,7 @@ export { default as ChevronDown } from 'components/Icons/ChevronDown.svg'
export { default as ChevronLeft } from 'components/Icons/ChevronLeft.svg' export { default as ChevronLeft } from 'components/Icons/ChevronLeft.svg'
export { default as ChevronRight } from 'components/Icons/ChevronRight.svg' export { default as ChevronRight } from 'components/Icons/ChevronRight.svg'
export { default as ChevronUp } from 'components/Icons/ChevronUp.svg' export { default as ChevronUp } from 'components/Icons/ChevronUp.svg'
export { default as Compass } from 'components/Icons/Compass.svg'
export { default as Copy } from 'components/Icons/Copy.svg' export { default as Copy } from 'components/Icons/Copy.svg'
export { default as Cross } from 'components/Icons/Cross.svg' export { default as Cross } from 'components/Icons/Cross.svg'
export { default as CrossCircled } from 'components/Icons/CrossCircled.svg' export { default as CrossCircled } from 'components/Icons/CrossCircled.svg'
@ -20,11 +21,15 @@ export { default as Enter } from 'components/Icons/Enter.svg'
export { default as ExclamationMarkCircled } from 'components/Icons/ExclamationMarkCircled.svg' export { default as ExclamationMarkCircled } from 'components/Icons/ExclamationMarkCircled.svg'
export { default as ExclamationMarkTriangle } from 'components/Icons/ExclamationMarkTriangle.svg' export { default as ExclamationMarkTriangle } from 'components/Icons/ExclamationMarkTriangle.svg'
export { default as ExternalLink } from 'components/Icons/ExternalLink.svg' export { default as ExternalLink } from 'components/Icons/ExternalLink.svg'
export { default as Flag } from 'components/Icons/Flag.svg'
export { default as Gear } from 'components/Icons/Gear.svg' export { default as Gear } from 'components/Icons/Gear.svg'
export { default as HandCoins } from 'components/Icons/HandCoins.svg'
export { default as Heart } from 'components/Icons/Heart.svg' export { default as Heart } from 'components/Icons/Heart.svg'
export { default as InfoCircle } from 'components/Icons/InfoCircle.svg'
export { default as LockLocked } from 'components/Icons/LockLocked.svg' export { default as LockLocked } from 'components/Icons/LockLocked.svg'
export { default as LockUnlocked } from 'components/Icons/LockUnlocked.svg' export { default as LockUnlocked } from 'components/Icons/LockUnlocked.svg'
export { default as Logo } from 'components/Icons/Logo.svg' export { default as Logo } from 'components/Icons/Logo.svg'
export { default as Luggage } from 'components/Icons/Luggage.svg'
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg' export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'
export { default as Osmo } from 'components/Icons/Osmo.svg' export { default as Osmo } from 'components/Icons/Osmo.svg'
export { default as OverlayMark } from 'components/Icons/OverlayMark.svg' export { default as OverlayMark } from 'components/Icons/OverlayMark.svg'
@ -44,5 +49,4 @@ export { default as SwapIcon } from 'components/Icons/SwapIcon.svg'
export { default as TrashBin } from 'components/Icons/TrashBin.svg' export { default as TrashBin } from 'components/Icons/TrashBin.svg'
export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg' export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg'
export { default as Wallet } from 'components/Icons/Wallet.svg' export { default as Wallet } from 'components/Icons/Wallet.svg'
export { default as InfoCircle } from 'components/Icons/InfoCircle.svg'
// @endindex // @endindex

View File

@ -95,7 +95,7 @@ export default function FundAccount(props: Props) {
useEffect(() => { useEffect(() => {
if (BN(baseBalance).isLessThan(hardcodedFee.amount[0].amount)) { if (BN(baseBalance).isLessThan(hardcodedFee.amount[0].amount)) {
useStore.setState({ focusComponent: <WalletBridges /> }) useStore.setState({ focusComponent: { component: <WalletBridges /> } })
} }
}, [baseBalance]) }, [baseBalance])

View File

@ -0,0 +1,93 @@
import { useCallback } from 'react'
import { ChevronRight, Compass, Flag, HandCoins, Luggage } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import useStore from 'store'
import { DocURL } from 'types/enums/docURL'
interface TutorialItemProps {
title: string
description: string
link: string
icon: React.ReactNode
}
function TutorialItem(props: TutorialItemProps) {
return (
<a
href={props.link}
title={props.title}
target='_blank'
className='flex items-center px-2 py-3 rounded-base hover:bg-white/5 group/item'
>
<div className='flex items-center justify-center w-8 h-8 rounded-base bg-white/5'>
{props.icon}
</div>
<div className='flex flex-col ml-3'>
<Text size='sm' className='pb-1'>
{props.title}
</Text>
<Text size='sm' className='text-white/50'>
{props.description}
</Text>
</div>
<div className='flex items-center justify-end flex-grow h-full pr-4'>
<ChevronRight className='w-3 h-3 opacity-0 group-hover/item:opacity-100' />
</div>
</a>
)
}
export default function GetStartedModal() {
const modal = useStore((s) => s.getStartedModal)
const onClose = useCallback(() => {
useStore.setState({ getStartedModal: false })
}, [])
if (!modal) return null
return (
<Modal
onClose={onClose}
header={<Text>Get Started</Text>}
className='relative'
headerClassName='gradient-header p-4 border-b-white/5 border-b'
contentClassName='flex flex-col p-3 pb-8'
>
<Text size='sm' className='p-2 pt-0 text-white/50'>
Tutorials
</Text>
<TutorialItem
title='Beginner Trading'
description='Learn how to conduct basic swapping, setting slippage percentages and more.'
link={DocURL.TRADING_INTRO_URL}
icon={<Compass className='w-5 opacity-50 group-hover/item:opacity-100' />}
/>
<TutorialItem
title='Advanced Trading'
description='Learn how to margin trade, stop-loss trade and operate our advanced trading module.'
link={DocURL.ADVANCED_TRADING_URL}
icon={<Luggage className='w-5 opacity-50 group-hover/item:opacity-100' />}
/>
<TutorialItem
title='Concentrated Liquidity'
description='Understand how to maximise your profits using concentrated liquidity'
link={DocURL.CONCENTRATED_LIQUIDITY_URL}
icon={<Flag className='w-5 opacity-50 group-hover/item:opacity-100' />}
/>
<TutorialItem
title='Borrowing & Lending'
description='Borrow and lend money against your assets on the blockchain'
link={DocURL.BORROW_LENDING_URL}
icon={<HandCoins className='w-5 opacity-50 group-hover/item:opacity-100' />}
/>
<Text
size='sm'
className='absolute bottom-0 left-0 w-full px-4 py-1 rounded-b-base bg-black/80 text-white/30'
>
You can access this modal via the settings if you decide to close it.
</Text>
</Modal>
)
}

View File

@ -4,6 +4,7 @@ import {
AlertDialogController, AlertDialogController,
BorrowModal, BorrowModal,
FundAndWithdrawModal, FundAndWithdrawModal,
GetStartedModal,
LendAndReclaimModalController, LendAndReclaimModalController,
SettingsModal, SettingsModal,
UnlockModal, UnlockModal,
@ -19,6 +20,7 @@ export default function ModalsContainer() {
<AddVaultBorrowAssetsModal /> <AddVaultBorrowAssetsModal />
<BorrowModal /> <BorrowModal />
<FundAndWithdrawModal /> <FundAndWithdrawModal />
<GetStartedModal />
<LendAndReclaimModalController /> <LendAndReclaimModalController />
<SettingsModal /> <SettingsModal />
<UnlockModal /> <UnlockModal />

View File

@ -5,7 +5,7 @@ import Text from 'components/Text'
interface Props { interface Props {
label: string label: string
decsription: string description: string
className?: string className?: string
children: ReactNode | ReactNode[] children: ReactNode | ReactNode[]
} }
@ -23,7 +23,7 @@ export default function SettingsOptions(props: Props) {
{props.label} {props.label}
</Text> </Text>
<Text size='xs' className='text-white/50'> <Text size='xs' className='text-white/50'>
{props.decsription} {props.description}
</Text> </Text>
</div> </div>
<div className='flex w-60 flex-wrap justify-end'>{props.children}</div> <div className='flex w-60 flex-wrap justify-end'>{props.children}</div>

View File

@ -8,7 +8,7 @@ interface Props {
name: string name: string
value: boolean value: boolean
label: string label: string
decsription: string description: string | React.ReactNode
className?: string className?: string
withStatus?: boolean withStatus?: boolean
} }
@ -21,18 +21,22 @@ export default function SettingsSwitch(props: Props) {
props.className, props.className,
)} )}
> >
<div className='flex w-100 flex-wrap'> <div className='flex flex-wrap w-100'>
<Text size='lg' className='mb-2 w-full'> <Text size='lg' className='w-full mb-2'>
{props.label} {props.label}
</Text> </Text>
<Text size='xs' className='text-white/50'> {typeof props.description === 'string' ? (
{props.decsription} <Text size='xs' className='text-white/50'>
</Text> {props.description}
</Text>
) : (
<>{props.description}</>
)}
</div> </div>
<div className='flex w-60 flex-wrap justify-end'> <div className='flex flex-wrap justify-end w-60'>
<Switch name={props.name} checked={props.value} onChange={props.onChange} /> <Switch name={props.name} checked={props.value} onChange={props.onChange} />
{props.withStatus && ( {props.withStatus && (
<Text size='sm' className='mt-2 w-full text-end'> <Text size='sm' className='w-full mt-2 text-end'>
{props.value ? 'ON' : 'OFF'} {props.value ? 'ON' : 'OFF'}
</Text> </Text>
)} )}

View File

@ -10,6 +10,7 @@ import SettingsSwitch from 'components/Modals/Settings/SettingsSwitch'
import NumberInput from 'components/NumberInput' import NumberInput from 'components/NumberInput'
import Select from 'components/Select' import Select from 'components/Select'
import Text from 'components/Text' import Text from 'components/Text'
import { TextLink } from 'components/TextLink'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { import {
DISPLAY_CURRENCY_KEY, DISPLAY_CURRENCY_KEY,
@ -17,6 +18,7 @@ import {
PREFERRED_ASSET_KEY, PREFERRED_ASSET_KEY,
REDUCE_MOTION_KEY, REDUCE_MOTION_KEY,
SLIPPAGE_KEY, SLIPPAGE_KEY,
TUTORIAL_KEY,
} from 'constants/localStore' } from 'constants/localStore'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useAlertDialog from 'hooks/useAlertDialog' import useAlertDialog from 'hooks/useAlertDialog'
@ -47,6 +49,7 @@ export default function SettingsModal() {
REDUCE_MOTION_KEY, REDUCE_MOTION_KEY,
DEFAULT_SETTINGS.reduceMotion, DEFAULT_SETTINGS.reduceMotion,
) )
const [tutorial, setTutorial] = useLocalStorage<boolean>(TUTORIAL_KEY, DEFAULT_SETTINGS.tutorial)
const [lendAssets, setLendAssets] = useLocalStorage<boolean>( const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
LEND_ASSETS_KEY, LEND_ASSETS_KEY,
DEFAULT_SETTINGS.lendAssets, DEFAULT_SETTINGS.lendAssets,
@ -59,7 +62,7 @@ export default function SettingsModal() {
label: ( label: (
<div className='flex w-full gap-2' key={index}> <div className='flex w-full gap-2' key={index}>
{asset.denom === 'usd' ? ( {asset.denom === 'usd' ? (
<Text size='sm' className='h-4 w-4 text-center leading-4'> <Text size='sm' className='w-4 h-4 leading-4 text-center'>
{asset.symbol} {asset.symbol}
</Text> </Text>
) : ( ) : (
@ -105,6 +108,13 @@ export default function SettingsModal() {
[setLendAssets], [setLendAssets],
) )
const handleTutorial = useCallback(
(value: boolean) => {
setTutorial(value)
},
[setTutorial],
)
const handlePreferredAsset = useCallback( const handlePreferredAsset = useCallback(
(value: string) => { (value: string) => {
setPreferredAsset(value) setPreferredAsset(value)
@ -177,7 +187,7 @@ export default function SettingsModal() {
const showResetModal = useCallback(() => { const showResetModal = useCallback(() => {
showResetDialog({ showResetDialog({
icon: ( icon: (
<div className='flex h-full w-full p-3'> <div className='flex w-full h-full p-3'>
<ArrowCircle /> <ArrowCircle />
</div> </div>
), ),
@ -219,7 +229,7 @@ export default function SettingsModal() {
name='lendAssets' name='lendAssets'
value={lendAssets} value={lendAssets}
label='Lend assets in credit account' label='Lend assets in credit account'
decsription='By turning this on you will automatically lend out all the assets you deposit into your credit account to earn yield.' description='By turning this on you will automatically lend out all the assets you deposit into your credit account to earn yield.'
withStatus withStatus
/> />
<SettingsSwitch <SettingsSwitch
@ -227,13 +237,36 @@ export default function SettingsModal() {
name='reduceMotion' name='reduceMotion'
value={reduceMotion} value={reduceMotion}
label='Reduce Motion' label='Reduce Motion'
decsription='Turns off all animations inside the dApp. Turning animations off can increase the description='Turns off all animations inside the dApp. Turning animations off can increase the
overall performance on lower-end hardware.' overall performance on lower-end hardware.'
withStatus withStatus
/> />
<SettingsSwitch
onChange={handleTutorial}
name='tutoruial'
value={tutorial}
label='Tutorial'
description={
<Text size='xs' className='text-white/50'>
Show tutorial elements in the UI. Like the{' '}
<TextLink
title='Get Started Modal'
color='secondary'
textSize='extraSmall'
className='leading-4 text-white hover:underline'
onClick={() => {
useStore.setState({ settingsModal: false, getStartedModal: true })
}}
>
Get Started Modal.
</TextLink>
</Text>
}
withStatus
/>
<SettingsOptions <SettingsOptions
label='Preferred asset' label='Preferred asset'
decsription='By selecting a different asset you always have the trading pair or asset selector description='By selecting a different asset you always have the trading pair or asset selector
pre-filled with this asset.' pre-filled with this asset.'
className='pb-6' className='pb-6'
> >
@ -242,7 +275,7 @@ export default function SettingsModal() {
options={preferredAssetsOptions} options={preferredAssetsOptions}
defaultValue={preferredAsset} defaultValue={preferredAsset}
onChange={handlePreferredAsset} onChange={handlePreferredAsset}
className='relative w-60 rounded-base border border-white/10' className='relative border w-60 rounded-base border-white/10'
containerClassName='justify-end mb-4' containerClassName='justify-end mb-4'
/> />
<Select <Select
@ -250,13 +283,13 @@ export default function SettingsModal() {
options={displayCurrenciesOptions} options={displayCurrenciesOptions}
defaultValue={displayCurrency} defaultValue={displayCurrency}
onChange={handleDisplayCurrency} onChange={handleDisplayCurrency}
className='relative w-60 rounded-base border border-white/10' className='relative border w-60 rounded-base border-white/10'
containerClassName='justify-end' containerClassName='justify-end'
/> />
</SettingsOptions> </SettingsOptions>
<SettingsOptions <SettingsOptions
label='Slippage tolerance' label='Slippage tolerance'
decsription='Some vaults require token swaps. The transaction will fail if the price of the swap asset changes unfavourably by more than this percentage' description='Some vaults require token swaps. The transaction will fail if the price of the swap asset changes unfavourably by more than this percentage'
className='pb-21' className='pb-21'
> >
{slippages.map((value) => ( {slippages.map((value) => (
@ -298,7 +331,7 @@ export default function SettingsModal() {
% %
</Button> </Button>
</SettingsOptions> </SettingsOptions>
<div className='flex w-full justify-between'> <div className='flex justify-between w-full'>
<Button <Button
color='quaternary' color='quaternary'
variant='transparent' variant='transparent'

View File

@ -3,6 +3,7 @@ export { default as AddVaultBorrowAssetsModal } from 'components/Modals/AddVault
export { default as AlertDialogController } from 'components/Modals/AlertDialog' export { default as AlertDialogController } from 'components/Modals/AlertDialog'
export { default as BorrowModal } from 'components/Modals/BorrowModal' export { default as BorrowModal } from 'components/Modals/BorrowModal'
export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw' export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw'
export { default as GetStartedModal } from 'components/Modals/GetStartedModal'
export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim' export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim'
export { default as SettingsModal } from 'components/Modals/Settings' export { default as SettingsModal } from 'components/Modals/Settings'
export { default as UnlockModal } from 'components/Modals/Unlock' export { default as UnlockModal } from 'components/Modals/Unlock'

View File

@ -15,9 +15,9 @@ interface BenefitsProps {
function Benefits({ benefits }: BenefitsProps) { function Benefits({ benefits }: BenefitsProps) {
return ( return (
<ul className='w-full list-none px-0'> <ul className='w-full px-0 list-none'>
{benefits.map((benefit, index) => ( {benefits.map((benefit, index) => (
<li className='relative mb-6 flex h-6 w-full items-center px-0 pl-8' key={index}> <li className='relative flex items-center w-full h-6 px-0 pl-8 mb-6' key={index}>
<div <div
className={classNames( className={classNames(
'absolute left-0 top-0 isolate h-6 w-6 rounded-full bg-white/10', 'absolute left-0 top-0 isolate h-6 w-6 rounded-full bg-white/10',
@ -40,7 +40,7 @@ export default function TermsOfService() {
const handleAgreeTermsOfService = useCallback(() => { const handleAgreeTermsOfService = useCallback(() => {
setHasAgreedToTerms(true) setHasAgreedToTerms(true)
useStore.setState({ focusComponent: <WalletSelect /> }) useStore.setState({ focusComponent: { component: <WalletSelect /> } })
}, [setHasAgreedToTerms]) }, [setHasAgreedToTerms])
return ( return (

View File

@ -6,9 +6,10 @@ interface Props extends React.HTMLProps<HTMLAnchorElement> {
className?: string className?: string
color?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' color?: 'primary' | 'secondary' | 'tertiary' | 'quaternary'
externalLink?: boolean externalLink?: boolean
textSize?: 'small' | 'medium' | 'large' textSize?: 'extraSmall' | 'small' | 'medium' | 'large'
text?: string | ReactNode text?: string | ReactNode
uppercase?: boolean uppercase?: boolean
onClick?: () => void
} }
const colorClasses = { const colorClasses = {
@ -19,6 +20,7 @@ const colorClasses = {
quaternary: 'hover:text-white active:text-white', quaternary: 'hover:text-white active:text-white',
} }
const textSizeClasses = { const textSizeClasses = {
extraSmall: 'text-xs',
small: 'text-sm', small: 'text-sm',
medium: 'text-base', medium: 'text-base',
large: 'text-lg', large: 'text-lg',
@ -56,7 +58,7 @@ export const TextLink = React.forwardRef(function TextLink(
? (e) => { ? (e) => {
e.preventDefault() e.preventDefault()
if (disabled) return if (disabled) return
onClick onClick()
} }
: undefined : undefined
} }

View File

@ -33,8 +33,8 @@ function Bridge({ name, url, image }: Bridge) {
}} }}
> >
<Image className='rounded-full' width={20} height={20} src={image} alt={name} /> <Image className='rounded-full' width={20} height={20} src={image} alt={name} />
<Text className='ml-2 flex-1 text-left'>{name}</Text> <Text className='flex-1 ml-2 text-left'>{name}</Text>
<ChevronRight className='h-4 w-4' /> <ChevronRight className='w-4 h-4' />
</Button> </Button>
) )
} }
@ -55,12 +55,12 @@ export default function WalletBridges() {
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
if (!currentWallet) return if (!currentWallet) return
disconnectWallet(currentWallet) disconnectWallet(currentWallet)
useStore.setState({ focusComponent: <WalletSelect /> }) useStore.setState({ focusComponent: { component: <WalletSelect /> } })
}, [currentWallet, disconnectWallet]) }, [currentWallet, disconnectWallet])
useEffect(() => { useEffect(() => {
if (hasFunds) { if (hasFunds) {
useStore.setState({ focusComponent: <WalletFetchBalancesAndAccounts /> }) useStore.setState({ focusComponent: { component: <WalletFetchBalancesAndAccounts /> } })
return return
} }
@ -82,13 +82,13 @@ export default function WalletBridges() {
}} }}
docs='wallet' docs='wallet'
> >
<div className='flex w-full flex-wrap gap-3'> <div className='flex flex-wrap w-full gap-3'>
{BRIDGES.map((bridge) => ( {BRIDGES.map((bridge) => (
<Bridge key={bridge.name} {...bridge} /> <Bridge key={bridge.name} {...bridge} />
))} ))}
</div> </div>
{IS_TESTNET && ( {IS_TESTNET && (
<div className='flex w-full flex-wrap gap-3'> <div className='flex flex-wrap w-full gap-3'>
<Text size='lg' className='mt-4 text-white'> <Text size='lg' className='mt-4 text-white'>
Need Testnet Funds? Need Testnet Funds?
</Text> </Text>

View File

@ -21,8 +21,8 @@ export default function WalletConnectButton(props: Props) {
const [hasAgreedToTerms] = useLocalStorage(TERMS_OF_SERVICE_KEY, false) const [hasAgreedToTerms] = useLocalStorage(TERMS_OF_SERVICE_KEY, false)
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
const focusedComponent = hasAgreedToTerms ? <WalletSelect /> : <TermsOfService /> const component = hasAgreedToTerms ? <WalletSelect /> : <TermsOfService />
useStore.setState({ focusComponent: focusedComponent }) useStore.setState({ focusComponent: { component } })
}, [hasAgreedToTerms]) }, [hasAgreedToTerms])
return ( return (

View File

@ -58,7 +58,7 @@ export default function WalletConnecting(props: Props) {
useStore.setState({ useStore.setState({
client: walletClient, client: walletClient,
address: response.account.address, address: response.account.address,
focusComponent: <WalletFetchBalancesAndAccounts />, focusComponent: { component: <WalletFetchBalancesAndAccounts /> },
}) })
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
@ -67,14 +67,16 @@ export default function WalletConnecting(props: Props) {
client: undefined, client: undefined,
address: undefined, address: undefined,
accounts: null, accounts: null,
focusComponent: ( focusComponent: {
<WalletSelect component: (
error={{ <WalletSelect
title: 'Failed to connect to wallet', error={{
message: mapErrorMessages(extensionProviderId, error.message), title: 'Failed to connect to wallet',
}} message: mapErrorMessages(extensionProviderId, error.message),
/> }}
), />
),
},
}) })
} }
} }

View File

@ -47,8 +47,8 @@ function WalletOption(props: WalletOptionProps) {
src={props.imageSrc} src={props.imageSrc}
alt={props.name} alt={props.name}
/> />
<Text className='ml-2 flex-1 text-left'>{props.name}</Text> <Text className='flex-1 ml-2 text-left'>{props.name}</Text>
<ChevronRight className='h-4 w-4' /> <ChevronRight className='w-4 h-4' />
</Button> </Button>
) )
} }
@ -61,7 +61,7 @@ export default function WalletSelect(props: Props) {
const handleConnectClick = (extensionProviderId: string) => { const handleConnectClick = (extensionProviderId: string) => {
useStore.setState({ useStore.setState({
focusComponent: <WalletConnecting providerId={extensionProviderId} />, focusComponent: { component: <WalletConnecting providerId={extensionProviderId} /> },
}) })
} }
@ -117,7 +117,7 @@ export default function WalletSelect(props: Props) {
text: 'Back', text: 'Back',
}} }}
> >
<div className='mb-4 rounded-sm bg-white p-2'> <div className='p-2 mb-4 bg-white rounded-sm'>
<QRCode value={qrCodeUrl} /> <QRCode value={qrCodeUrl} />
</div> </div>
</FullOverlayContent> </FullOverlayContent>
@ -129,7 +129,7 @@ export default function WalletSelect(props: Props) {
copy={`Deposit assets from your ${currentChain.name} address to your Mars credit account.`} copy={`Deposit assets from your ${currentChain.name} address to your Mars credit account.`}
docs='wallet' docs='wallet'
> >
<div className='flex w-full flex-wrap gap-3'> <div className='flex flex-wrap w-full gap-3'>
{!isMobile() && ( {!isMobile() && (
<> <>
{sortedExtensionProviders.map((provider) => { {sortedExtensionProviders.map((provider) => {

View File

@ -27,7 +27,7 @@ export default function Wallet() {
useStore.setState({ address: currentWallet.account.address }) useStore.setState({ address: currentWallet.account.address })
return return
} }
useStore.setState({ focusComponent: <WalletConnecting autoConnect /> }) useStore.setState({ focusComponent: { component: <WalletConnecting autoConnect /> } })
}, [currentWallet, client, address]) }, [currentWallet, client, address])
// Redirect when switching wallets or on first connection // Redirect when switching wallets or on first connection

View File

@ -7,4 +7,5 @@ export const DEFAULT_SETTINGS: Settings = {
preferredAsset: ASSETS[0].denom, preferredAsset: ASSETS[0].denom,
displayCurrency: ORACLE_DENOM, displayCurrency: ORACLE_DENOM,
slippage: 0.02, slippage: 0.02,
tutorial: true,
} }

View File

@ -6,3 +6,4 @@ export const LEND_ASSETS_KEY = 'lendAssets'
export const AUTO_LEND_ENABLED_ACCOUNT_IDS_KEY = 'autoLendEnabledAccountIds' export const AUTO_LEND_ENABLED_ACCOUNT_IDS_KEY = 'autoLendEnabledAccountIds'
export const SLIPPAGE_KEY = 'slippage' export const SLIPPAGE_KEY = 'slippage'
export const TERMS_OF_SERVICE_KEY = 'termsOfService' export const TERMS_OF_SERVICE_KEY = 'termsOfService'
export const TUTORIAL_KEY = 'tutorial'

View File

@ -33,7 +33,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<div className={classNames('mx-auto h-full w-full', !isFullWidth && 'max-w-content')}> <div className={classNames('mx-auto h-full w-full', !isFullWidth && 'max-w-content')}>
{focusComponent ? ( {focusComponent ? (
<div className='relative flex h-full w-full items-center justify-center'> <div className='relative flex h-full w-full items-center justify-center'>
{focusComponent} {focusComponent.component}
</div> </div>
) : ( ) : (
children children

View File

@ -9,6 +9,7 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
createAccountModal: false, createAccountModal: false,
fundAccountModal: false, fundAccountModal: false,
fundAndWithdrawModal: null, fundAndWithdrawModal: null,
getStartedModal: false,
lendAndReclaimModal: null, lendAndReclaimModal: null,
resetStettingsModal: false, resetStettingsModal: false,
settingsModal: false, settingsModal: false,

View File

@ -1,4 +1,12 @@
export enum DocURL { export enum DocURL {
ADVANCED_TRADING_URL = 'https://docs.marsprotocol.io',
BORROW_LENDING_URL = 'https://docs.marsprotocol.io',
COOKIE_POLICY_URL = 'https://docs.marsprotocol.io/mars-protocol/terms-of-service/mars-cookie-policy', COOKIE_POLICY_URL = 'https://docs.marsprotocol.io/mars-protocol/terms-of-service/mars-cookie-policy',
CONCENTRATED_LIQUIDITY_URL = 'https://docs.marsprotocol.io',
MANAGE_ACCOUNT_URL = 'https://docs.marsprotocol.io/docs/learn/workstation/rover/managing-credit-accounts',
ROVER_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/workstation/rover/rover-intro',
PRIVACY_POLICY_URL = 'https://docs.marsprotocol.io/mars-protocol/terms-of-service/mars-privacy-policy', PRIVACY_POLICY_URL = 'https://docs.marsprotocol.io/mars-protocol/terms-of-service/mars-privacy-policy',
TERMS_OF_SERVICE_URL = 'https://docs.marsprotocol.io/docs/overview/legal/terms-of-service',
TRADING_INTRO_URL = 'https://docs.marsprotocol.io',
WALLET_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/workstation/basics/basics-intro',
} }

View File

@ -7,3 +7,8 @@ interface CommonSlice {
selectedAccount: string | null selectedAccount: string | null
focusComponent: ReactNode focusComponent: ReactNode
} }
interface FocusComponent {
component: ReactNode
onClose?: () => void
}

View File

@ -6,6 +6,7 @@ interface ModalSlice {
createAccountModal: boolean createAccountModal: boolean
fundAccountModal: boolean fundAccountModal: boolean
fundAndWithdrawModal: 'fund' | 'withdraw' | null fundAndWithdrawModal: 'fund' | 'withdraw' | null
getStartedModal: boolean
lendAndReclaimModal: LendAndReclaimModalConfig | null lendAndReclaimModal: LendAndReclaimModalConfig | null
settingsModal: boolean settingsModal: boolean
unlockModal: UnlockModal | null unlockModal: UnlockModal | null

View File

@ -4,4 +4,5 @@ interface Settings {
preferredAsset: string preferredAsset: string
lendAssets: boolean lendAssets: boolean
slippage: number slippage: number
tutorial: boolean
} }