From 7e9409bbef6d7f74c015257a87e30dbbfed7ccd1 Mon Sep 17 00:00:00 2001 From: Fouad Date: Wed, 6 Jul 2022 21:08:40 +0100 Subject: [PATCH] Feature/send with simulated fee (#1402) * add simulate send function * extract loading modal into its own component * move appbar component into folder * add additional prop for making modal list item text bold * create new send UI and stories * remove old send page * use simulated fee in send request * use new confirmation modal component --- nym-wallet/.storybook/mocks/tauri/index.js | 22 +++- .../src/components/{ => AppBar}/AppBar.tsx | 30 ++--- nym-wallet/src/components/AppBar/index.ts | 1 + .../Delegation/DelegationActions.tsx | 3 +- .../components/Delegation/DelegationModal.tsx | 16 +-- .../src/components/Modals/LoadingModal.tsx | 25 ++++ .../src/components/Modals/ModalListItem.tsx | 13 ++- nym-wallet/src/components/Nav.tsx | 109 ++++++++++-------- .../src/components/Rewards/CompoundModal.tsx | 3 +- .../src/components/Rewards/RedeemModal.tsx | 1 - .../components/Send/SendDetails.stories.tsx | 48 ++++++++ .../src/components/Send/SendDetailsModal.tsx | 43 +++++++ .../src/components/Send/SendErrorModal.tsx | 24 ++++ .../src/components/Send/SendInputModal.tsx | 70 +++++++++++ nym-wallet/src/components/Send/SendModal.tsx | 93 +++++++++++++++ .../src/components/Send/SendSuccessModal.tsx | 19 +++ nym-wallet/src/components/Send/index.tsx | 11 ++ nym-wallet/src/components/Send/types.ts | 4 + nym-wallet/src/context/delegations.tsx | 2 +- nym-wallet/src/context/main.tsx | 9 +- nym-wallet/src/context/mocks/main.tsx | 2 + nym-wallet/src/pages/delegation/index.tsx | 2 +- nym-wallet/src/requests/actions.ts | 4 +- nym-wallet/src/requests/simulate.ts | 7 +- nym-wallet/src/routes/app.tsx | 5 +- .../types/src/types/rust/FeeDetails.ts | 6 +- ts-packages/types/src/types/rust/index.ts | 1 + 27 files changed, 464 insertions(+), 109 deletions(-) rename nym-wallet/src/components/{ => AppBar}/AppBar.tsx (65%) create mode 100644 nym-wallet/src/components/AppBar/index.ts create mode 100644 nym-wallet/src/components/Modals/LoadingModal.tsx create mode 100644 nym-wallet/src/components/Send/SendDetails.stories.tsx create mode 100644 nym-wallet/src/components/Send/SendDetailsModal.tsx create mode 100644 nym-wallet/src/components/Send/SendErrorModal.tsx create mode 100644 nym-wallet/src/components/Send/SendInputModal.tsx create mode 100644 nym-wallet/src/components/Send/SendModal.tsx create mode 100644 nym-wallet/src/components/Send/SendSuccessModal.tsx create mode 100644 nym-wallet/src/components/Send/index.tsx create mode 100644 nym-wallet/src/components/Send/types.ts diff --git a/nym-wallet/.storybook/mocks/tauri/index.js b/nym-wallet/.storybook/mocks/tauri/index.js index 0cfa49e7ac..10ebffe42f 100644 --- a/nym-wallet/.storybook/mocks/tauri/index.js +++ b/nym-wallet/.storybook/mocks/tauri/index.js @@ -4,7 +4,7 @@ */ module.exports = { invoke: (operation, args) => { - switch(operation) { + switch (operation) { case 'get_balance': { return { amount: { @@ -12,20 +12,30 @@ module.exports = { denom: 'NYMT', }, printable_balance: '100 NYMT', - } + }; } case 'delegate_to_mixnode': { - return ({ + return { logs_json: '[]', data_json: '{}', transaction_hash: '12345', - }); + }; + } + case 'simulate_send': { + return { + amount: { + amount: '0.01', + denom: 'NYM', + }, + }; } } - console.error(`Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_wallet/.storybook/mocks/tauri.js" if you need. The default response is "void".`); + console.error( + `Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_wallet/.storybook/mocks/tauri.js" if you need. The default response is "void".`, + ); return new Promise((resolve, reject) => { reject(new Error(`Tauri operation ${operation} not available in storybook.`)); }); }, -} \ No newline at end of file +}; diff --git a/nym-wallet/src/components/AppBar.tsx b/nym-wallet/src/components/AppBar/AppBar.tsx similarity index 65% rename from nym-wallet/src/components/AppBar.tsx rename to nym-wallet/src/components/AppBar/AppBar.tsx index b747950ef9..48c22c8ee8 100644 --- a/nym-wallet/src/components/AppBar.tsx +++ b/nym-wallet/src/components/AppBar/AppBar.tsx @@ -1,17 +1,16 @@ import React, { useContext } from 'react'; -import { AppBar as MuiAppBar, Grid, IconButton, Toolbar } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; import { Logout } from '@mui/icons-material'; import TerminalIcon from '@mui/icons-material/Terminal'; -import { AppContext } from '../context/main'; -import { NetworkSelector } from './NetworkSelector'; -import { Node as NodeIcon } from '../svg-icons/node'; -import { MultiAccounts } from './Accounts'; -import { config } from '../config'; +import { AppBar as MuiAppBar, Grid, IconButton, Toolbar } from '@mui/material'; +import { Node } from 'src/svg-icons/node'; +import { config } from '../../config'; +import { AppContext } from '../../context/main'; +import { MultiAccounts } from '../Accounts'; +import { NetworkSelector } from '../NetworkSelector'; export const AppBar = () => { - const { logOut, handleShowTerminal, appEnv, handleShowSettings, showSettings } = useContext(AppContext); - const navigate = useNavigate(); + const { showSettings, handleShowTerminal, appEnv, handleShowSettings, logOut } = useContext(AppContext); + return ( @@ -24,7 +23,7 @@ export const AppBar = () => { - + {(appEnv?.SHOW_TERMINAL || config.IS_DEV_MODE) && ( @@ -38,18 +37,11 @@ export const AppBar = () => { sx={{ color: showSettings ? 'primary.main' : 'nym.background.dark' }} size="small" > - + - { - await logOut(); - navigate('/'); - }} - sx={{ color: 'nym.background.dark' }} - > + diff --git a/nym-wallet/src/components/AppBar/index.ts b/nym-wallet/src/components/AppBar/index.ts new file mode 100644 index 0000000000..60f2f0e90b --- /dev/null +++ b/nym-wallet/src/components/AppBar/index.ts @@ -0,0 +1 @@ +export * from './AppBar'; diff --git a/nym-wallet/src/components/Delegation/DelegationActions.tsx b/nym-wallet/src/components/Delegation/DelegationActions.tsx index 7c27eafed2..7a25a93f88 100644 --- a/nym-wallet/src/components/Delegation/DelegationActions.tsx +++ b/nym-wallet/src/components/Delegation/DelegationActions.tsx @@ -98,9 +98,8 @@ export const DelegationsActionsMenu: React.FC<{ onActionClick?: (action: DelegationListItemActions) => void; isPending?: DelegationEventKind; disableRedeemingRewards?: boolean; - disableDelegateMore?: boolean; disableCompoundRewards?: boolean; -}> = ({ disableRedeemingRewards, disableDelegateMore, disableCompoundRewards, onActionClick, isPending }) => { +}> = ({ disableRedeemingRewards, disableCompoundRewards, onActionClick, isPending }) => { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { diff --git a/nym-wallet/src/components/Delegation/DelegationModal.tsx b/nym-wallet/src/components/Delegation/DelegationModal.tsx index aec11bcf2a..9197150409 100644 --- a/nym-wallet/src/components/Delegation/DelegationModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegationModal.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { Box, Button, CircularProgress, Modal, Stack, Typography } from '@mui/material'; +import { Box, Button, Modal, Typography } from '@mui/material'; import { Link } from '@nymproject/react/link/Link'; import { modalStyle } from '../Modals/styles'; +import { LoadingModal } from '../Modals/LoadingModal'; export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound'; @@ -42,18 +43,7 @@ export const DelegationModal: React.FC< onClose?: () => void; } > = ({ status, action, message, recipient, balance, balanceVested, transactions, open, onClose, children }) => { - if (status === 'loading') { - return ( - - - - - Please wait... - - - - ); - } + if (status === 'loading') return ; if (status === 'error') { return ( diff --git a/nym-wallet/src/components/Modals/LoadingModal.tsx b/nym-wallet/src/components/Modals/LoadingModal.tsx new file mode 100644 index 0000000000..2a1cdceb1b --- /dev/null +++ b/nym-wallet/src/components/Modals/LoadingModal.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Box, CircularProgress, Modal, Stack, Typography } from '@mui/material'; + +const modalStyle = { + position: 'absolute' as 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: 500, + bgcolor: 'background.paper', + boxShadow: 24, + borderRadius: '16px', + p: 4, +}; + +export const LoadingModal = () => ( + + + + + Please wait... + + + +); diff --git a/nym-wallet/src/components/Modals/ModalListItem.tsx b/nym-wallet/src/components/Modals/ModalListItem.tsx index a416375200..8d0ba9568b 100644 --- a/nym-wallet/src/components/Modals/ModalListItem.tsx +++ b/nym-wallet/src/components/Modals/ModalListItem.tsx @@ -6,12 +6,17 @@ export const ModalListItem: React.FC<{ label: string; divider?: boolean; hidden?: boolean; - value: string | React.ReactNode; -}> = ({ label, value, hidden, divider }) => ( + strong?: boolean; + value: React.ReactNode; +}> = ({ label, value, hidden, divider, strong }) => ( diff --git a/nym-wallet/src/components/Nav.tsx b/nym-wallet/src/components/Nav.tsx index d3ab7eef74..18c43fba7b 100644 --- a/nym-wallet/src/components/Nav.tsx +++ b/nym-wallet/src/components/Nav.tsx @@ -1,58 +1,67 @@ -import React, { useContext } from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import React, { useState, useContext } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'; import { AccountBalanceWalletOutlined, ArrowBack, ArrowForward, Description, Settings } from '@mui/icons-material'; import { AppContext } from '../context/main'; import { Bond, Delegate, Unbond } from '../svg-icons'; -const routesSchema = [ - { - label: 'Balance', - route: '/balance', - Icon: AccountBalanceWalletOutlined, - }, - { - label: 'Send', - route: '/send', - Icon: ArrowForward, - }, - { - label: 'Receive', - route: '/receive', - Icon: ArrowBack, - }, - { - label: 'Bond', - route: '/bond', - Icon: Bond, - }, - { - label: 'Unbond', - route: '/unbond', - Icon: Unbond, - }, - { - label: 'Delegation', - route: '/delegation', - Icon: Delegate, - }, - { - label: 'Docs', - route: '/docs', - Icon: Description, - mode: 'dev', - }, - { - label: 'Admin', - route: '/admin', - Icon: Settings, - mode: 'admin', - }, -]; - export const Nav = () => { const location = useLocation(); - const { isAdminAddress } = useContext(AppContext); + const navigate = useNavigate(); + + const { isAdminAddress, handleShowSendModal } = useContext(AppContext); + + const [routesSchema] = useState([ + { + label: 'Balance', + route: '/balance', + Icon: AccountBalanceWalletOutlined, + onClick: () => navigate('/balance'), + }, + { + label: 'Send', + Icon: ArrowForward, + onClick: handleShowSendModal, + }, + { + label: 'Receive', + route: '/receive', + Icon: ArrowBack, + onClick: () => navigate('/receive'), + }, + { + label: 'Bond', + route: '/bond', + Icon: Bond, + onClick: () => navigate('/bond'), + }, + { + label: 'Unbond', + route: '/unbond', + Icon: Unbond, + onClick: () => navigate('/unbond'), + }, + { + label: 'Delegation', + route: '/delegation', + Icon: Delegate, + onClick: () => navigate('/delegation'), + }, + { + label: 'Docs', + route: '/admin', + Icon: Description, + mode: 'dev', + onClick: () => navigate('/docs'), + }, + { + label: 'Admin', + route: '/admin', + Icon: Settings, + mode: 'admin', + onClick: () => navigate('/admin'), + }, + ]); return (
{ return false; } }) - .map(({ Icon, route, label }) => ( - + .map(({ Icon, onClick, label, route }) => ( + void; identityKey: string; amount: number; - minimum?: number; currency: string; message: string; usesVestingTokens: boolean; diff --git a/nym-wallet/src/components/Rewards/RedeemModal.tsx b/nym-wallet/src/components/Rewards/RedeemModal.tsx index 0bb317c093..c496df68fd 100644 --- a/nym-wallet/src/components/Rewards/RedeemModal.tsx +++ b/nym-wallet/src/components/Rewards/RedeemModal.tsx @@ -14,7 +14,6 @@ export const RedeemModal: React.FC<{ onOk?: (identityKey: string, fee?: FeeDetails) => void; identityKey: string; amount: number; - minimum?: number; currency: string; message: string; usesVestingTokens: boolean; diff --git a/nym-wallet/src/components/Send/SendDetails.stories.tsx b/nym-wallet/src/components/Send/SendDetails.stories.tsx new file mode 100644 index 0000000000..6d87990ce6 --- /dev/null +++ b/nym-wallet/src/components/Send/SendDetails.stories.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { ComponentMeta } from '@storybook/react'; +import { MockMainContextProvider } from 'src/context/mocks/main'; +import { SendDetailsModal } from './SendDetailsModal'; +import { SendSuccessModal } from './SendSuccessModal'; +import { SendErrorModal } from './SendErrorModal'; +import { SendInputModal } from './SendInputModal'; +import { Send } from '.'; + +export default { + title: 'Send/Components', + component: SendDetailsModal, +} as ComponentMeta; + +export const SendInput = () => ( + {}} + onClose={() => {}} + onAddressChange={() => {}} + onAmountChange={() => {}} + /> +); + +export const SendDetails = () => ( + {}} + onSend={() => {}} + onClose={() => {}} + /> +); + +export const SendSuccess = () => ( + {}} /> +); + +export const SendError = () => {}} />; + +export const SendFlow = () => ( + + + +); diff --git a/nym-wallet/src/components/Send/SendDetailsModal.tsx b/nym-wallet/src/components/Send/SendDetailsModal.tsx new file mode 100644 index 0000000000..2219b734b0 --- /dev/null +++ b/nym-wallet/src/components/Send/SendDetailsModal.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Stack } from '@mui/material'; +import { FeeDetails, MajorCurrencyAmount } from '@nymproject/types'; +import { SimpleModal } from '../Modals/SimpleModal'; +import { ModalListItem } from '../Modals/ModalListItem'; + +export const SendDetailsModal = ({ + amount, + toAddress, + fromAddress, + fee, + onClose, + onPrev, + onSend, +}: { + fromAddress?: string; + toAddress: string; + fee?: FeeDetails; + amount?: MajorCurrencyAmount; + onClose: () => void; + onPrev: () => void; + onSend: (data: { val: MajorCurrencyAmount; to: string }) => void; +}) => ( + amount && onSend({ val: amount, to: toAddress })} + onBack={onPrev} + > + + + + + + + +); diff --git a/nym-wallet/src/components/Send/SendErrorModal.tsx b/nym-wallet/src/components/Send/SendErrorModal.tsx new file mode 100644 index 0000000000..3578a0acf6 --- /dev/null +++ b/nym-wallet/src/components/Send/SendErrorModal.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { SimpleModal } from '../Modals/SimpleModal'; + +export const SendErrorModal = ({ onClose }: { onClose: () => void }) => ( + onClose()} + header="Send" + subHeader="An error occurred while sending. Please try again" + okLabel="Close" + sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }} + headerStyles={{ + width: '100%', + mb: 3, + textAlign: 'center', + color: 'error.main', + fontSize: 16, + textTransform: 'capitalize', + }} + subHeaderStyles={{ textAlign: 'center', color: 'text.primary', fontSize: 14, fontWeight: 400 }} + /> +); diff --git a/nym-wallet/src/components/Send/SendInputModal.tsx b/nym-wallet/src/components/Send/SendInputModal.tsx new file mode 100644 index 0000000000..cc0149658d --- /dev/null +++ b/nym-wallet/src/components/Send/SendInputModal.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState } from 'react'; +import { Stack, TextField, Typography } from '@mui/material'; +import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; +import { MajorCurrencyAmount } from '@nymproject/types'; +import { validateAmount } from 'src/utils'; +import { SimpleModal } from '../Modals/SimpleModal'; +import { ModalListItem } from '../Modals/ModalListItem'; + +export const SendInputModal = ({ + fromAddress, + toAddress, + amount, + balance, + error, + onNext, + onClose, + onAmountChange, + onAddressChange, +}: { + fromAddress?: string; + toAddress: string; + amount?: MajorCurrencyAmount; + balance?: string; + error?: string; + onNext: () => void; + onClose: () => void; + onAmountChange: (value: MajorCurrencyAmount) => void; + onAddressChange: (value: string) => void; +}) => { + const [isValid, setIsValid] = useState(false); + + const validate = async (value: MajorCurrencyAmount) => { + const isValidAmount = await validateAmount(value.amount, '0'); + setIsValid(isValidAmount); + }; + + useEffect(() => { + if (amount) validate(amount); + }, []); + + return ( + onNext()} okDisabled={!isValid}> + + onAddressChange(e.target.value)} + value={toAddress} + /> + { + onAmountChange(value); + validate(value); + }} + initialValue={amount?.amount} + /> + + {error} + + + + + + Est. fee for this transaction will be show on the next page + + + ); +}; diff --git a/nym-wallet/src/components/Send/SendModal.tsx b/nym-wallet/src/components/Send/SendModal.tsx new file mode 100644 index 0000000000..1f5a1b3b3f --- /dev/null +++ b/nym-wallet/src/components/Send/SendModal.tsx @@ -0,0 +1,93 @@ +import React, { useContext, useState } from 'react'; +import { MajorCurrencyAmount } from '@nymproject/types'; +import { AppContext, urls } from 'src/context'; +import { useGetFee } from 'src/hooks/useGetFee'; +import { send } from 'src/requests'; +import { Console } from 'src/utils/console'; +import { simulateSend } from 'src/requests/simulate'; +import { LoadingModal } from '../Modals/LoadingModal'; +import { SendDetailsModal } from './SendDetailsModal'; +import { SendErrorModal } from './SendErrorModal'; +import { SendInputModal } from './SendInputModal'; +import { SendSuccessModal } from './SendSuccessModal'; +import { TTransactionDetails } from './types'; + +export const SendModal = ({ onClose }: { onClose: () => void }) => { + const [toAddress, setToAddress] = useState(''); + const [amount, setAmount] = useState(); + const [modal, setModal] = useState<'send' | 'send details'>('send'); + const [error, setError] = useState(); + const [sendError, setSendError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [txDetails, setTxDetails] = useState(); + + const { clientDetails, userBalance, network } = useContext(AppContext); + const { fee, getFee } = useGetFee(); + + const handleOnNext = async () => { + if (amount) { + setIsLoading(true); + setError(undefined); + try { + await getFee(simulateSend, { address: toAddress, amount }); + setModal('send details'); + } catch (e) { + setError(e as string); + } finally { + setIsLoading(false); + } + } else { + setError('An amount is required'); + } + }; + + const handleSend = async ({ val, to }: { val: MajorCurrencyAmount; to: string }) => { + setIsLoading(true); + setError(undefined); + try { + const txResponse = await send({ amount: val, address: to, memo: '', fee: fee?.fee }); + setTxDetails({ + amount: `${amount?.amount} ${clientDetails?.denom}`, + txUrl: `${urls(network).blockExplorer}/transaction/${txResponse.tx_hash}`, + }); + } catch (e) { + Console.error(e as string); + setSendError(true); + } finally { + setIsLoading(false); + } + }; + + if (isLoading) return ; + + if (sendError) return ; + + if (txDetails) return ; + + if (modal === 'send details') + return ( + setModal('send')} + onSend={handleSend} + /> + ); + + return ( + setAmount(value)} + onAddressChange={(value) => setToAddress(value)} + /> + ); +}; diff --git a/nym-wallet/src/components/Send/SendSuccessModal.tsx b/nym-wallet/src/components/Send/SendSuccessModal.tsx new file mode 100644 index 0000000000..ff0786c567 --- /dev/null +++ b/nym-wallet/src/components/Send/SendSuccessModal.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Stack, Typography } from '@mui/material'; +import { Link } from '@nymproject/react/link/Link'; +import { TTransactionDetails } from './types'; +import { ConfirmationModal } from '../Modals/ConfirmationModal'; + +export const SendSuccessModal = ({ txDetails, onClose }: { txDetails: TTransactionDetails; onClose: () => void }) => ( + + + You sent + {txDetails && ( + <> + {txDetails.amount} + + + )} + + +); diff --git a/nym-wallet/src/components/Send/index.tsx b/nym-wallet/src/components/Send/index.tsx new file mode 100644 index 0000000000..2ccc172778 --- /dev/null +++ b/nym-wallet/src/components/Send/index.tsx @@ -0,0 +1,11 @@ +import React, { useContext } from 'react'; +import { AppContext } from 'src/context'; +import { SendModal } from './SendModal'; + +export const Send = () => { + const { showSendModal, handleShowSendModal } = useContext(AppContext); + + if (showSendModal) return ; + + return null; +}; diff --git a/nym-wallet/src/components/Send/types.ts b/nym-wallet/src/components/Send/types.ts new file mode 100644 index 0000000000..a863ab6a99 --- /dev/null +++ b/nym-wallet/src/components/Send/types.ts @@ -0,0 +1,4 @@ +export type TTransactionDetails = { + amount: string; + txUrl: string; +}; diff --git a/nym-wallet/src/context/delegations.tsx b/nym-wallet/src/context/delegations.tsx index 166c66c8bc..48435e6c67 100644 --- a/nym-wallet/src/context/delegations.tsx +++ b/nym-wallet/src/context/delegations.tsx @@ -64,7 +64,7 @@ export const DelegationContextProvider: FC<{ try { let tx; - if (tokenPool === 'locked') tx = await vestingDelegateToMixnode(data); + if (tokenPool === 'locked') tx = await vestingDelegateToMixnode({ ...data, fee }); else tx = await delegateToMixnode(data); return tx; diff --git a/nym-wallet/src/context/main.tsx b/nym-wallet/src/context/main.tsx index b6fcda2513..65949935ce 100644 --- a/nym-wallet/src/context/main.tsx +++ b/nym-wallet/src/context/main.tsx @@ -46,7 +46,9 @@ export type TAppContext = { error?: string; loginType?: TLoginType; showSettings: boolean; - handleShowSettings?: () => void; + showSendModal: boolean; + handleShowSettings: () => void; + handleShowSendModal: () => void; setIsLoading: (isLoading: boolean) => void; setError: (value?: string) => void; switchNetwork: (network: Network) => void; @@ -76,6 +78,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { const [appVersion, setAppVersion] = useState(); const [isAdminAddress, setIsAdminAddress] = useState(false); const [showSettings, setShowSettings] = useState(false); + const [showSendModal, setShowSendModal] = useState(false); const userBalance = useGetBalance(clientDetails); const navigate = useNavigate(); @@ -211,6 +214,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { const handleShowTerminal = () => setShowTerminal((show) => !show); const switchNetwork = (_network: Network) => setNetwork(_network); const handleShowSettings = () => setShowSettings((show) => !show); + const handleShowSendModal = () => setShowSendModal((show) => !show); const memoizedValue = useMemo( () => ({ @@ -240,6 +244,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { logOut, onAccountChange, handleShowSettings, + showSendModal, + handleShowSendModal, }), [ appVersion, @@ -257,6 +263,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { storedAccounts, showTerminal, showSettings, + showSendModal, ], ); diff --git a/nym-wallet/src/context/mocks/main.tsx b/nym-wallet/src/context/mocks/main.tsx index 672b3e0123..92af066bb5 100644 --- a/nym-wallet/src/context/mocks/main.tsx +++ b/nym-wallet/src/context/mocks/main.tsx @@ -37,6 +37,7 @@ export const MockMainContextProvider: FC<{}> = ({ children }) => { showAdmin: false, showTerminal: false, showSettings: false, + showSendModal: true, network: 'SANDBOX', loginType: 'mnemonic', setIsLoading: () => undefined, @@ -50,6 +51,7 @@ export const MockMainContextProvider: FC<{}> = ({ children }) => { logOut: () => undefined, onAccountChange: () => undefined, handleShowSettings: () => undefined, + handleShowSendModal: () => undefined, }), [], ); diff --git a/nym-wallet/src/pages/delegation/index.tsx b/nym-wallet/src/pages/delegation/index.tsx index bfaf58df6c..61ffc28047 100644 --- a/nym-wallet/src/pages/delegation/index.tsx +++ b/nym-wallet/src/pages/delegation/index.tsx @@ -410,7 +410,7 @@ export const Delegation: FC = () => { open={Boolean(saturationError)} onClose={() => setSaturationError(undefined)} header={`Node saturation: ${Math.round(saturationError.saturation * 100000) / 1000}%`} - subHeader={'This node is over saturated. Choose a new mix node to delegate to and start compounding rewards.'} + subHeader="This node is over saturated. Choose a new mix node to delegate to and start compounding rewards." /> )} diff --git a/nym-wallet/src/requests/actions.ts b/nym-wallet/src/requests/actions.ts index b84c44c703..629d61884b 100644 --- a/nym-wallet/src/requests/actions.ts +++ b/nym-wallet/src/requests/actions.ts @@ -1,4 +1,4 @@ -import { MajorCurrencyAmount, SendTxResult, TransactionExecuteResult } from '@nymproject/types'; +import { Fee, MajorCurrencyAmount, SendTxResult, TransactionExecuteResult } from '@nymproject/types'; import { EnumNodeType, TBondArgs, TBondGatewayArgs, TBondMixNodeArgs } from '../types'; import { invokeWrapper } from './wrapper'; @@ -15,7 +15,7 @@ export const unbondMixNode = async () => invokeWrapper export const updateMixnode = async (profitMarginPercent: number) => invokeWrapper('update_mixnode', { profitMarginPercent }); -export const send = async (args: { amount: MajorCurrencyAmount; address: string; memo: string }) => +export const send = async (args: { amount: MajorCurrencyAmount; address: string; memo: string; fee?: Fee }) => invokeWrapper('send', args); export const unbond = async (type: EnumNodeType) => { diff --git a/nym-wallet/src/requests/simulate.ts b/nym-wallet/src/requests/simulate.ts index a9b41b41e7..31dcaf4ef8 100644 --- a/nym-wallet/src/requests/simulate.ts +++ b/nym-wallet/src/requests/simulate.ts @@ -50,5 +50,8 @@ export const simulateVestingUnbondMixnode = async (args: any) => export const simulateVestingUpdateMixnode = async (args: any) => invokeWrapper('simulate_vesting_update_mixnode', args); -export const simulateWithdrawVestedCoins = async ({ amount }: { amount: MajorCurrencyAmount }) => - invokeWrapper('simulate_withdraw_vested_coins', { amount }); +export const simulateWithdrawVestedCoins = async (args: any) => + invokeWrapper('simulate_withdraw_vested_coins', args); + +export const simulateSend = async ({ address, amount }: { address: string; amount: MajorCurrencyAmount }) => + invokeWrapper('simulate_send', { address, amount }); diff --git a/nym-wallet/src/routes/app.tsx b/nym-wallet/src/routes/app.tsx index b1a8ae7750..19f4c769e4 100644 --- a/nym-wallet/src/routes/app.tsx +++ b/nym-wallet/src/routes/app.tsx @@ -2,15 +2,16 @@ import React from 'react'; import { Route, Routes } from 'react-router-dom'; import { ApplicationLayout } from 'src/layouts'; import { Terminal } from 'src/pages/terminal'; -import { Bond, Balance, InternalDocs, Receive, Send, Unbond, DelegationPage, Admin, Settings } from '../pages'; +import { Send } from 'src/components/Send'; +import { Bond, Balance, InternalDocs, Receive, Unbond, DelegationPage, Admin, Settings } from '../pages'; export const AppRoutes = () => ( + } /> - } /> } /> } /> } /> diff --git a/ts-packages/types/src/types/rust/FeeDetails.ts b/ts-packages/types/src/types/rust/FeeDetails.ts index 234734f6a8..17d9631bc9 100644 --- a/ts-packages/types/src/types/rust/FeeDetails.ts +++ b/ts-packages/types/src/types/rust/FeeDetails.ts @@ -1,4 +1,4 @@ -import type { Fee } from "./Fee"; -import type { MajorCurrencyAmount } from "./Currency"; +import type { Fee } from './Fee'; +import type { MajorCurrencyAmount } from './Currency'; -export type FeeDetails = { amount: MajorCurrencyAmount | null, fee: Fee }; \ No newline at end of file +export type FeeDetails = { amount: MajorCurrencyAmount | null; fee: Fee }; diff --git a/ts-packages/types/src/types/rust/index.ts b/ts-packages/types/src/types/rust/index.ts index b0e7264ca8..1be7a06002 100644 --- a/ts-packages/types/src/types/rust/index.ts +++ b/ts-packages/types/src/types/rust/index.ts @@ -13,6 +13,7 @@ export * from './DelegationRecord'; export * from './DelegationResult'; export * from './DelegationSummaryResponse'; export * from './DelegationWithEverything'; +export * from './Fee'; export * from './FeeDetails'; export * from './Gas'; export * from './GasInfo';