diff --git a/components/Layout.tsx b/components/Layout.tsx index 647b59c0..4dfd920e 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -13,10 +13,13 @@ import ConnectedMenu from './wallet/ConnectedMenu' import WalletIcon from './icons/WalletIcon' import BounceLoader from './shared/BounceLoader' import MangoAccountsList from './MangoAccountsList' +import { useWallet } from '@solana/wallet-adapter-react' +import CreateAccountModal from './modals/CreateAccountModal' export const IS_ONBOARDED_KEY = 'isOnboarded' const Layout = ({ children }: { children: ReactNode }) => { + const { connected } = useWallet() const actions = mangoStore((s) => s.actions) const mangoAccount = mangoStore((s) => s.mangoAccount.current) const loadingMangoAccount = mangoStore((s) => s.mangoAccount.loading) @@ -26,6 +29,7 @@ const Layout = ({ children }: { children: ReactNode }) => { const isMobile = width ? width < breakpoints.md : false const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY) const [showUserSetupModal, setShowUserSetupModal] = useState(false) + const [showFirstAccountModal, setShowFirstAccountModal] = useState(false) useEffect(() => { if (mangoAccount) { @@ -46,6 +50,12 @@ const Layout = ({ children }: { children: ReactNode }) => { } }, [width]) + useEffect(() => { + if (connected && isOnboarded && !loadingMangoAccount && !mangoAccount) { + setShowFirstAccountModal(true) + } + }, [connected, isOnboarded, loadingMangoAccount, mangoAccount]) + const handleCloseModal = useCallback(() => { setShowUserSetupModal(false) }, []) @@ -63,7 +73,7 @@ const Layout = ({ children }: { children: ReactNode }) => { return ( <> - {loadingMangoAccount && isOnboarded ? ( + {connected && loadingMangoAccount && isOnboarded ? (
@@ -146,6 +156,13 @@ const Layout = ({ children }: { children: ReactNode }) => { onClose={handleCloseModal} /> ) : null} + {showFirstAccountModal ? ( + setShowFirstAccountModal(false)} + isFirstAccount + /> + ) : null} ) } diff --git a/components/MangoAccountsList.tsx b/components/MangoAccountsList.tsx index 67d90caa..b04eb3ac 100644 --- a/components/MangoAccountsList.tsx +++ b/components/MangoAccountsList.tsx @@ -8,7 +8,7 @@ import { Popover, Transition } from '@headlessui/react' import { MangoAccount } from '@blockworks-foundation/mango-v4' import mangoStore from '../store/state' import { LinkButton } from './shared/Button' -import CreateNewAccountModal from './modals/CreateNewAccountModal' +import CreateAccountModal from './modals/CreateAccountModal' const handleSelectMangoAccount = async (acc: MangoAccount) => { const set = mangoStore.getState().set @@ -91,7 +91,7 @@ const MangoAccountsList = ({ )} {showNewAccountModal ? ( - setShowNewAccountModal(false)} /> diff --git a/components/modals/CloseAccountModal.tsx b/components/modals/CloseAccountModal.tsx index 9ae1eaf9..7a06b3de 100644 --- a/components/modals/CloseAccountModal.tsx +++ b/components/modals/CloseAccountModal.tsx @@ -10,8 +10,9 @@ import BounceLoader from '../shared/BounceLoader' const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => { const { t } = useTranslation('common') - const { disconnect } = useWallet() + const { wallet } = useWallet() const [loading, setLoading] = useState(false) + const set = mangoStore((s) => s.set) const handleCloseMangoAccount = async () => { const client = mangoStore.getState().client @@ -22,7 +23,6 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => { try { const tx = await client.closeMangoAccount(group, mangoAccount) if (tx) { - disconnect() setLoading(false) onClose() notify({ @@ -30,6 +30,11 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => { type: 'success', txid: tx, }) + set((state) => { + state.mangoAccount.loading = true + state.mangoAccount.current = undefined + }) + setTimeout(() => wallet?.adapter?.disconnect(), 1000) } } catch (e) { setLoading(false) diff --git a/components/modals/CreateNewAccountModal.tsx b/components/modals/CreateAccountModal.tsx similarity index 66% rename from components/modals/CreateNewAccountModal.tsx rename to components/modals/CreateAccountModal.tsx index 30180a0c..76e0e694 100644 --- a/components/modals/CreateNewAccountModal.tsx +++ b/components/modals/CreateAccountModal.tsx @@ -1,5 +1,4 @@ import { ChangeEvent, useState } from 'react' -import { MangoAccount } from '@blockworks-foundation/mango-v4' import { useTranslation } from 'next-i18next' import { ModalProps } from '../../types/modal' import Modal from '../shared/Modal' @@ -11,6 +10,8 @@ import Input from '../forms/Input' import Label from '../forms/Label' import { useWallet } from '@solana/wallet-adapter-react' import { Wallet } from '@project-serum/anchor' +import InlineNotification from '../shared/InlineNotification' +import { MangoAccount } from '@blockworks-foundation/mango-v4' const getNextAccountNumber = (accounts: MangoAccount[]): number => { if (accounts.length > 1) { @@ -25,13 +26,22 @@ const getNextAccountNumber = (accounts: MangoAccount[]): number => { return 0 } -const CreateNewAccountModal = ({ isOpen, onClose }: ModalProps) => { +interface CreateAccountModalProps { + isFirstAccount?: boolean +} + +type ModalCombinedProps = ModalProps & CreateAccountModalProps + +const CreateAccountModal = ({ + isFirstAccount, + isOpen, + onClose, +}: ModalCombinedProps) => { const { t } = useTranslation('common') const [loading, setLoading] = useState(false) const [name, setName] = useState('') const { wallet } = useWallet() - // This doesn't work yet... const handleNewAccount = async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group @@ -41,7 +51,15 @@ const CreateNewAccountModal = ({ isOpen, onClose }: ModalProps) => { setLoading(true) try { const newAccountNum = getNextAccountNumber(mangoAccounts) - const tx = await client.createMangoAccount(group, newAccountNum, name) + const tx = await client.createMangoAccount( + group, + newAccountNum, + name || `Account ${newAccountNum + 1}` + ) + actions.fetchMangoAccount( + wallet!.adapter as unknown as Wallet, + newAccountNum + ) actions.fetchMangoAccounts(wallet!.adapter as unknown as Wallet) if (tx) { setLoading(false) @@ -65,28 +83,40 @@ const CreateNewAccountModal = ({ isOpen, onClose }: ModalProps) => { return ( -
+
{loading ? ( ) : (
-

{t('new-account')}

-
- +
+ + +
)}
@@ -94,4 +124,4 @@ const CreateNewAccountModal = ({ isOpen, onClose }: ModalProps) => { ) } -export default CreateNewAccountModal +export default CreateAccountModal diff --git a/components/modals/UserSetupModal.tsx b/components/modals/UserSetupModal.tsx index 61e81679..09a216fd 100644 --- a/components/modals/UserSetupModal.tsx +++ b/components/modals/UserSetupModal.tsx @@ -20,12 +20,15 @@ import { EnterRightExitLeft, FadeInFadeOut } from '../shared/Transitions' import Image from 'next/image' import BounceLoader from '../shared/BounceLoader' import { notify } from '../../utils/notifications' +import { Wallet } from '@project-serum/anchor' const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { const { t } = useTranslation() - const { select, wallet, wallets } = useWallet() + const { connected, select, wallet, wallets } = useWallet() const mangoAccount = mangoStore((s) => s.mangoAccount.current) const mangoAccountLoading = mangoStore((s) => s.mangoAccount.loading) + const [accountName, setAccountName] = useState('') + const [loadingAccount, setLoadingAccount] = useState(false) const [profileName, setProfileName] = useState('') const [profileCategory, setProfileCategory] = useState('') const [showSetupStep, setShowSetupStep] = useState(0) @@ -56,11 +59,39 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { } } - useEffect(() => { - if (mangoAccount) { - setShowSetupStep(3) + const handleCreateAccount = async () => { + const client = mangoStore.getState().client + const group = mangoStore.getState().group + const actions = mangoStore.getState().actions + if (!group || !wallet) return + setLoadingAccount(true) + try { + const tx = await client.createMangoAccount( + group, + 0, + accountName || 'Account 1' + ) + actions.fetchMangoAccount(wallet!.adapter as unknown as Wallet) + // actions.fetchMangoAccounts(wallet!.adapter as unknown as Wallet) + if (tx) { + setLoadingAccount(false) + setShowSetupStep(4) + notify({ + title: t('new-account-success'), + type: 'success', + txid: tx, + }) + } + } catch (e: any) { + setLoadingAccount(false) + notify({ + title: t('new-account-failed'), + txid: e?.signature, + type: 'error', + }) + console.log(e) } - }, [mangoAccount]) + } const handleDeposit = async () => { const client = mangoStore.getState().client @@ -100,12 +131,25 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { } } + useEffect(() => { + if (mangoAccount && showSetupStep === 1) { + setIsOnboarded(true) + onClose() + } + }, [mangoAccount, showSetupStep]) + + useEffect(() => { + if (connected && !mangoAccountLoading) { + setShowSetupStep(2) + } + }, [connected, mangoAccountLoading]) + return (
@@ -163,7 +207,7 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { show={showSetupStep === 1} style={{ height: 'calc(100% - 12px)' }} > - {mangoAccountLoading ? ( + {connected && mangoAccountLoading ? (
@@ -172,10 +216,6 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => {

Connect Wallet

-

- If you don't have a Mango Account yet, we'll - create one for you. -

Choose Wallet

@@ -203,16 +243,9 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { ))}
-
- - -
+
)} @@ -269,15 +302,58 @@ const UserSetupModal = ({ isOpen, onClose }: ModalProps) => { show={showSetupStep === 3} style={{ height: 'calc(100% - 12px)' }} > - {submitDeposit ? ( + {loadingAccount ? (
- +
) : (
+
+

Create Account

+

You need a Mango Account to get started.

+
+
+
+
+
+ + +
+
+ )} + + + {submitDeposit ? ( +
+ +
+ ) : ( +
+

Fund Your Account

- + {!depositToken ? ( -
+
) : null} diff --git a/components/shared/DepositTokenList.tsx b/components/shared/DepositTokenList.tsx index 82c22110..647d06ff 100644 --- a/components/shared/DepositTokenList.tsx +++ b/components/shared/DepositTokenList.tsx @@ -16,7 +16,7 @@ const DepositTokenList = ({ onSelect }: { onSelect: (x: any) => void }) => {

{t('token')}

-

{t('rate')}

+

{t('deposit-rate')}

{t('wallet-balance')}

diff --git a/components/wallet/ConnectedMenu.tsx b/components/wallet/ConnectedMenu.tsx index e612677a..338c20dc 100644 --- a/components/wallet/ConnectedMenu.tsx +++ b/components/wallet/ConnectedMenu.tsx @@ -21,10 +21,11 @@ const ConnectedMenu = () => { const isMobile = width ? width < breakpoints.sm : false const handleDisconnect = useCallback(() => { - wallet?.adapter?.disconnect() set((state) => { + state.mangoAccount.loading = true state.mangoAccount.current = undefined }) + setTimeout(() => wallet?.adapter?.disconnect(), 500) notify({ type: 'info', title: t('wallet-disconnected'), diff --git a/components/wallet/WalletListener.tsx b/components/wallet/WalletListener.tsx index 0f4597ed..baa50c2c 100644 --- a/components/wallet/WalletListener.tsx +++ b/components/wallet/WalletListener.tsx @@ -5,14 +5,22 @@ import { Wallet } from '@project-serum/anchor' const WalletListener = () => { const { wallet, connected, disconnecting } = useWallet() + const mangoAccounts = mangoStore((s) => s.mangoAccounts) useEffect(() => { const actions = mangoStore.getState().actions const onConnect = async () => { if (!wallet) return - await actions.fetchMangoAccount(wallet.adapter as unknown as Wallet) - actions.fetchMangoAccounts(wallet.adapter as unknown as Wallet) + await actions.fetchMangoAccounts(wallet.adapter as unknown as Wallet) + if (mangoAccounts.length) { + actions.fetchMangoAccount( + wallet.adapter as unknown as Wallet, + mangoAccounts[0].accountNum + ) + } else { + actions.fetchMangoAccount(wallet.adapter as unknown as Wallet) + } actions.fetchProfilePicture(wallet.adapter as unknown as Wallet) actions.fetchWalletTokens(wallet.adapter as unknown as Wallet) } @@ -21,11 +29,10 @@ const WalletListener = () => { if (connected) { onConnect() } - }, [wallet, connected]) + }, [wallet, connected, mangoAccounts.length]) useEffect(() => { const setStore = mangoStore.getState().set - if (disconnecting) { setStore((state) => { state.mangoAccount.current = undefined diff --git a/pages/index.tsx b/pages/index.tsx index 42db1e86..5de017be 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -209,7 +209,7 @@ const Index: NextPage = () => { ) : null ) : ( -
+
)}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 1b7fb6e3..c838ae72 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -23,10 +23,12 @@ "connect": "Connect", "connect-helper": "Connect to get started", "create-account": "Create Account", + "creating-account": "Creating Account...", "cumulative-interest-value": "Cumulative Interest Value", "daily-volume": "24h Volume", "date": "Date", "deposit": "Deposit", + "deposit-rate": "Deposit Rate (APR)", "deposit-value": "Deposit Value", "disconnect": "Disconnect", "edit-account": "Edit Account", @@ -38,6 +40,7 @@ "governance": "Governance", "health": "Health", "health-impact": "Health Impact", + "insufficient-sol": "The Solana blockchain requires 0.00757 SOL to create a Mango Account. This covers rent for your account and will be refunded if you close your account.", "interest-earned": "Interest Earned", "language": "Language", "learn": "Learn", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 2a6527ef..339e9a35 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -23,10 +23,12 @@ "connect": "Connect", "connect-helper": "Connect to get started", "create-account": "Create Account", + "creating-account": "Creating Account...", "cumulative-interest-value": "Cumulative Interest Value", "daily-volume": "24h Volume", "date": "Date", "deposit": "Deposit", + "deposit-rate": "Deposit Rate (APR)", "deposit-value": "Deposit Value", "disconnect": "Disconnect", "edit-account": "Edit Account", @@ -38,6 +40,7 @@ "governance": "Governance", "health": "Health", "health-impact": "Health Impact", + "insufficient-sol": "The Solana blockchain requires 0.00757 SOL to create a Mango Account. This covers rent for your account and will be refunded if you close your account.", "interest-earned": "Interest Earned", "language": "Language", "learn": "Learn", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 0509ebc0..c7831d24 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -23,10 +23,12 @@ "connect": "Connect", "connect-helper": "Connect to get started", "create-account": "Create Account", + "creating-account": "Creating Account...", "cumulative-interest-value": "Cumulative Interest Value", "daily-volume": "24h Volume", "date": "Date", "deposit": "Deposit", + "deposit-rate": "Deposit Rate (APR)", "deposit-value": "Deposit Value", "disconnect": "Disconnect", "edit-account": "Edit Account", @@ -38,6 +40,7 @@ "governance": "Governance", "health": "Health", "health-impact": "Health Impact", + "insufficient-sol": "The Solana blockchain requires 0.00757 SOL to create a Mango Account. This covers rent for your account and will be refunded if you close your account.", "interest-earned": "Interest Earned", "language": "Language", "learn": "Learn", diff --git a/public/locales/zh_tw/common.json b/public/locales/zh_tw/common.json index f75f5cf9..a960c914 100644 --- a/public/locales/zh_tw/common.json +++ b/public/locales/zh_tw/common.json @@ -23,10 +23,12 @@ "connect": "Connect", "connect-helper": "Connect to get started", "create-account": "Create Account", + "creating-account": "Creating Account...", "cumulative-interest-value": "Cumulative Interest Value", "daily-volume": "24h Volume", "date": "Date", "deposit": "Deposit", + "deposit-rate": "Deposit Rate (APR)", "deposit-value": "Deposit Value", "disconnect": "Disconnect", "edit-account": "Edit Account", @@ -38,6 +40,7 @@ "governance": "Governance", "health": "Health", "health-impact": "Health Impact", + "insufficient-sol": "The Solana blockchain requires 0.00757 SOL to create a Mango Account. This covers rent for your account and will be refunded if you close your account.", "interest-earned": "Interest Earned", "language": "Language", "learn": "Learn", diff --git a/store/state.ts b/store/state.ts index 5dd8b235..8769bfb0 100644 --- a/store/state.ts +++ b/store/state.ts @@ -134,7 +134,7 @@ export type MangoStore = { ) => Promise fetchCoingeckoPrices: () => Promise fetchGroup: () => Promise - fetchMangoAccount: (wallet: Wallet) => Promise + fetchMangoAccount: (wallet: Wallet, accountNumber?: number) => Promise fetchMangoAccounts: (wallet: Wallet) => Promise fetchNfts: (connection: Connection, walletPk: PublicKey) => void fetchProfilePicture: (wallet: Wallet) => void @@ -161,7 +161,7 @@ const mangoStore = create( jupiterTokens: [], mangoAccount: { current: undefined, - loading: false, + loading: true, stats: { interestTotals: { data: [], loading: false }, performance: { data: [], loading: false }, @@ -315,7 +315,7 @@ const mangoStore = create( console.error('Error fetching group', e) } }, - fetchMangoAccount: async (wallet) => { + fetchMangoAccount: async (wallet, accountNumber) => { const set = get().set try { const group = get().group @@ -340,15 +340,10 @@ const mangoStore = create( } ) - set((state) => { - state.mangoAccount.loading = true - }) - - const mangoAccount = await client.getOrCreateMangoAccount( + const mangoAccount = await client.getMangoAccountForOwner( group, wallet.publicKey, - 0, - 'Account' + accountNumber || 0 ) // let orders = await client.getSerum3Orders( @@ -356,8 +351,9 @@ const mangoStore = create( // SERUM3_PROGRAM_ID['devnet'], // 'BTC/USDC' // ) - - await mangoAccount.reloadAccountData(client, group) + if (mangoAccount) { + await mangoAccount.reloadAccountData(client, group) + } set((state) => { state.client = client state.mangoAccount.current = mangoAccount