diff --git a/nym-wallet/src/pages/balance/components/vesting-timeline.tsx b/nym-wallet/src/components/Balance/VestingTimeline.tsx
similarity index 98%
rename from nym-wallet/src/pages/balance/components/vesting-timeline.tsx
rename to nym-wallet/src/components/Balance/VestingTimeline.tsx
index b1a0f663ce..6ff740e33a 100644
--- a/nym-wallet/src/pages/balance/components/vesting-timeline.tsx
+++ b/nym-wallet/src/components/Balance/VestingTimeline.tsx
@@ -3,7 +3,7 @@ import React, { useContext } from 'react';
import { useTheme } from '@mui/material/styles';
import { Box, Tooltip, Typography } from '@mui/material';
import { format } from 'date-fns';
-import { AppContext } from '../../../context';
+import { AppContext } from 'src/context';
const calculateMarkerPosition = (arrLength: number, index: number) => (1 / arrLength) * 100 * index;
diff --git a/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx b/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx
new file mode 100644
index 0000000000..cd1f3ba03c
--- /dev/null
+++ b/nym-wallet/src/components/Balance/cards/TokenTransfer.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { Card, Stack, Button } from '@mui/material';
+import { ModalListItem } from 'src/components/Modals/ModalListItem';
+
+export const TokenTransfer = ({
+ onTransfer,
+ unlockedTokens,
+ unlockedRewards,
+ unlockedTransferable,
+}: {
+ userBalance?: string;
+ unlockedTokens?: string;
+ unlockedRewards?: string;
+ unlockedTransferable?: string;
+ onTransfer: () => void;
+}) => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx b/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx
new file mode 100644
index 0000000000..8eec8c7eb7
--- /dev/null
+++ b/nym-wallet/src/components/Balance/cards/VestingSchedule.tsx
@@ -0,0 +1,110 @@
+import { useContext, useState, useEffect } from 'react';
+import {
+ TableContainer,
+ Table,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableBody,
+ Box,
+ Typography,
+ TableCellProps,
+ Card,
+} from '@mui/material';
+import { Period } from '@nymproject/types';
+import { AppContext } from 'src/context';
+import { VestingTimeline } from '../VestingTimeline';
+import { Stack } from '@mui/system';
+
+const columnsHeaders: Array<{ title: string; align: TableCellProps['align'] }> = [
+ { title: 'Locked', align: 'left' },
+ { title: 'Period', align: 'left' },
+ { title: 'Unlocked', align: 'right' },
+];
+
+const vestingPeriod = (current?: Period, original?: number) => {
+ if (current === 'After') return 'Complete';
+
+ if (typeof current === 'object' && typeof original === 'number') return `${current.In + 1}/${original}`;
+
+ return 'N/A';
+};
+
+export const VestingSchedule = () => {
+ const { userBalance, clientDetails } = useContext(AppContext);
+ const [vestedPercentage, setVestedPercentage] = useState(0);
+
+ const calculatePercentage = () => {
+ const { tokenAllocation, originalVesting } = userBalance;
+ if (tokenAllocation?.vesting && tokenAllocation.vested && tokenAllocation.vested !== '0' && originalVesting) {
+ const percentage = (+tokenAllocation.vested / +originalVesting.amount.amount) * 100;
+ const rounded = percentage.toFixed(2);
+ setVestedPercentage(+rounded);
+ } else {
+ setVestedPercentage(0);
+ }
+ };
+
+ useEffect(() => {
+ calculatePercentage();
+ }, [userBalance.tokenAllocation, calculatePercentage]);
+
+ return (
+
+
+
+
+
+ {columnsHeaders.map((header) => (
+
+ {header.title}
+
+ ))}
+
+
+
+
+
+ {userBalance.tokenAllocation?.vesting || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
+ {clientDetails?.display_mix_denom.toUpperCase()}
+
+
+ {vestingPeriod(userBalance.currentVestingPeriod, userBalance.originalVesting?.number_of_periods)}
+
+
+ {userBalance.tokenAllocation?.vested || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
+ {clientDetails?.display_mix_denom.toUpperCase()}
+
+
+
+
+
+
+ Percentage
+
+
+ {vestedPercentage}%
+
+
+
+ );
+};
diff --git a/nym-wallet/src/pages/balance/components/TransferModal.tsx b/nym-wallet/src/components/Balance/modals/TransferModal.tsx
similarity index 79%
rename from nym-wallet/src/pages/balance/components/TransferModal.tsx
rename to nym-wallet/src/components/Balance/modals/TransferModal.tsx
index 02a8318064..8e2d03251d 100644
--- a/nym-wallet/src/pages/balance/components/TransferModal.tsx
+++ b/nym-wallet/src/components/Balance/modals/TransferModal.tsx
@@ -8,36 +8,22 @@ import { FeeWarning } from 'src/components/FeeWarning';
import { withdrawVestedCoins } from 'src/requests';
import { Console } from 'src/utils/console';
import { simulateWithdrawVestedCoins } from 'src/requests/simulate';
-import { SuccessModal } from './TransferModalSuccess';
-import { TResponseState, TTransactionDetails } from '../types';
+import { useGetFee } from 'src/hooks/useGetFee';
+import { SuccessModal, TTransactionDetails } from './TransferModalSuccess';
+import { TResponseState } from '../../../pages/balance/types';
export const TransferModal = ({ onClose }: { onClose: () => void }) => {
const [state, setState] = useState();
- const [fee, setFee] = useState();
+
const [tx, setTx] = useState();
const { userBalance, clientDetails, network } = useContext(AppContext);
-
- const getFee = async () => {
- if (userBalance.tokenAllocation?.spendable && clientDetails?.display_mix_denom) {
- try {
- const simulatedFee = await simulateWithdrawVestedCoins({
- amount: { amount: userBalance.tokenAllocation?.spendable, denom: clientDetails?.display_mix_denom },
- });
- setFee(simulatedFee);
- await userBalance.refreshBalances();
- } catch (e) {
- setFee({
- amount: { amount: 'n/a', denom: clientDetails?.display_mix_denom.toUpperCase() as CurrencyDenom },
- fee: { Auto: null },
- });
- Console.error(e);
- }
- }
- };
+ const { fee, getFee } = useGetFee();
useEffect(() => {
- getFee();
+ getFee(simulateWithdrawVestedCoins, {
+ amount: { amount: userBalance.tokenAllocation?.spendable, denom: clientDetails?.display_mix_denom },
+ });
}, []);
const handleTransfer = async () => {
diff --git a/nym-wallet/src/pages/balance/components/TransferModalSuccess.tsx b/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx
similarity index 92%
rename from nym-wallet/src/pages/balance/components/TransferModalSuccess.tsx
rename to nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx
index b27c679d5a..13b762bcb5 100644
--- a/nym-wallet/src/pages/balance/components/TransferModalSuccess.tsx
+++ b/nym-wallet/src/components/Balance/modals/TransferModalSuccess.tsx
@@ -2,7 +2,8 @@ import React from 'react';
import { Stack, Typography } from '@mui/material';
import { Link } from '@nymproject/react/link/Link';
import { ConfirmationModal } from 'src/components';
-import { TTransactionDetails } from '../types';
+
+export type TTransactionDetails = { amount: string; url: string };
export const SuccessModal = ({ tx, onClose }: { tx?: TTransactionDetails; onClose: () => void }) => (
{
- const { userBalance, clientDetails, network } = useContext(AppContext);
-
- useEffect(() => {
- userBalance.fetchBalance();
- }, []);
-
+export const BalanceCard = ({
+ userBalance,
+ userBalanceError,
+ network,
+ clientAddress,
+}: {
+ userBalance?: Balance;
+ userBalanceError?: string;
+ network?: Network;
+ clientAddress?: string;
+}) => {
return (
{
>
- {userBalance.error && (
+ {userBalanceError && (
- {userBalance.error}
+ {userBalanceError}
)}
- {!userBalance.error && (
+ {!userBalanceError && (
{
}}
variant="h5"
>
- {userBalance.balance?.printable_balance}
+ {userBalance?.printable_balance}
)}
{network && (
Promise;
+ fetchBalance: () => Promise;
+ onTransfer: () => Promise;
+}) => {
+ const { enqueueSnackbar, closeSnackbar } = useSnackbar();
+
+ const refreshBalances = async () => {
+ await fetchBalance();
+ await fetchTokenAllocation();
+ };
+
+ useEffect(() => {
+ closeSnackbar();
+ fetchTokenAllocation();
+ }, []);
+
+ if (!originalVesting) return null;
+
+ return (
+
+ You can use up to 10% of your locked tokens for bonding and delegating
+
+ }
+ borderless
+ data-testid="check-unvested-tokens"
+ Action={
+ {
+ await refreshBalances();
+ enqueueSnackbar('Balances updated', { variant: 'success', preventDuplicate: true });
+ }}
+ >
+
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/nym-wallet/src/pages/balance/index.tsx b/nym-wallet/src/pages/balance/index.tsx
index e214d1bddf..778ca020fc 100644
--- a/nym-wallet/src/pages/balance/index.tsx
+++ b/nym-wallet/src/pages/balance/index.tsx
@@ -1,27 +1,46 @@
-import React, { useContext, useState } from 'react';
+import React, { useContext, useEffect, useState } from 'react';
import { Box } from '@mui/material';
import { AppContext } from '../../context/main';
-import { BalanceCard } from './balance';
-import { VestingCard } from './vesting';
+import { BalanceCard } from './Balance';
+import { VestingCard } from './Vesting';
import { PageLayout } from '../../layouts';
-import { TransferModal } from './components/TransferModal';
+import { TransferModal } from '../../components/Balance/modals/TransferModal';
export const Balance = () => {
const [showTransferModal, setShowTransferModal] = useState(false);
- const { userBalance } = useContext(AppContext);
+ const { userBalance, clientDetails, network } = useContext(AppContext);
+
+ useEffect(() => {
+ userBalance.fetchBalance();
+ }, []);
const handleShowTransferModal = async () => {
await userBalance.refreshBalances();
setShowTransferModal(true);
};
+ const appendDenom = (value: string = '') => `${value} ${clientDetails?.display_mix_denom.toUpperCase()}`;
+
return (
-
-
+
+
{showTransferModal && setShowTransferModal(false)} />}
diff --git a/nym-wallet/src/pages/balance/types.ts b/nym-wallet/src/pages/balance/types.ts
index 5f88fe60b1..4c4ee67403 100644
--- a/nym-wallet/src/pages/balance/types.ts
+++ b/nym-wallet/src/pages/balance/types.ts
@@ -1,2 +1 @@
export type TResponseState = 'loading' | 'success' | 'fail';
-export type TTransactionDetails = { amount: string; url: string };
diff --git a/nym-wallet/src/pages/balance/vesting.tsx b/nym-wallet/src/pages/balance/vesting.tsx
deleted file mode 100644
index d84accf898..0000000000
--- a/nym-wallet/src/pages/balance/vesting.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-import React, { useContext, useEffect, useState } from 'react';
-import { Refresh } from '@mui/icons-material';
-import {
- Box,
- Button,
- IconButton,
- Table,
- TableBody,
- TableCell,
- TableCellProps,
- TableContainer,
- TableHead,
- TableRow,
- Typography,
-} from '@mui/material';
-import { useSnackbar } from 'notistack';
-import { NymCard } from 'src/components';
-import { AppContext } from 'src/context/main';
-import { Period } from 'src/types';
-import { VestingTimeline } from './components/vesting-timeline';
-
-const columnsHeaders: Array<{ title: string; align: TableCellProps['align'] }> = [
- { title: 'Locked', align: 'left' },
- { title: 'Period', align: 'left' },
- { title: 'Percentage Vested', align: 'left' },
- { title: 'Unlocked', align: 'right' },
-];
-
-const vestingPeriod = (current?: Period, original?: number) => {
- if (current === 'After') return 'Complete';
-
- if (typeof current === 'object' && typeof original === 'number') return `${current.In + 1}/${original}`;
-
- return 'N/A';
-};
-
-const VestingSchedule = () => {
- const { userBalance, clientDetails } = useContext(AppContext);
- const [vestedPercentage, setVestedPercentage] = useState(0);
-
- const calculatePercentage = () => {
- const { tokenAllocation, originalVesting } = userBalance;
- if (tokenAllocation?.vesting && tokenAllocation.vested && tokenAllocation.vested !== '0' && originalVesting) {
- const percentage = (+tokenAllocation.vested / +originalVesting.amount.amount) * 100;
- const rounded = percentage.toFixed(2);
- setVestedPercentage(+rounded);
- } else {
- setVestedPercentage(0);
- }
- };
-
- useEffect(() => {
- calculatePercentage();
- }, [userBalance.tokenAllocation, calculatePercentage]);
-
- return (
-
-
-
-
- {columnsHeaders.map((header) => (
- t.palette.nym.text.muted }} align={header.align}>
- {header.title}
-
- ))}
-
-
-
-
-
- {userBalance.tokenAllocation?.vesting || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
- {clientDetails?.display_mix_denom.toUpperCase()}
-
-
- {vestingPeriod(userBalance.currentVestingPeriod, userBalance.originalVesting?.number_of_periods)}
-
-
-
- {`${vestedPercentage}%`}
-
-
-
-
- {userBalance.tokenAllocation?.vested || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
- {clientDetails?.display_mix_denom.toUpperCase()}
-
-
-
-
-
- );
-};
-
-const TokenTransfer = () => {
- const { userBalance, clientDetails } = useContext(AppContext);
-
- return (
-
-
- Unlocked transferable tokens
-
-
-
- {userBalance.tokenAllocation?.spendable || 'n/a'} {clientDetails?.display_mix_denom.toUpperCase()}
-
-
- );
-};
-
-export const VestingCard = ({ onTransfer }: { onTransfer: () => Promise }) => {
- const { userBalance } = useContext(AppContext);
- const { enqueueSnackbar, closeSnackbar } = useSnackbar();
-
- const refreshBalances = async () => {
- await userBalance.fetchBalance();
- await userBalance.fetchTokenAllocation();
- };
-
- useEffect(() => {
- closeSnackbar();
- userBalance.fetchTokenAllocation();
- }, []);
-
- if (!userBalance.originalVesting) return null;
-
- return (
-
- You can use up to 10% of your locked tokens for bonding and delegating
-
- }
- borderless
- data-testid="check-unvested-tokens"
- Action={
- {
- await refreshBalances();
- enqueueSnackbar('Balances updated', { variant: 'success', preventDuplicate: true });
- }}
- >
-
-
- }
- >
-
-
-
-
-
-
- );
-};