diff --git a/nym-wallet/src/components/ActionsMenu.tsx b/nym-wallet/src/components/ActionsMenu.tsx new file mode 100644 index 0000000000..1c1a6a2664 --- /dev/null +++ b/nym-wallet/src/components/ActionsMenu.tsx @@ -0,0 +1,44 @@ +import React, { useRef } from 'react'; +import { MoreVertSharp } from '@mui/icons-material'; +import { IconButton, ListItemIcon, ListItemText, Menu, MenuItem } from '@mui/material'; + +export const ActionsMenu: React.FC<{ open: boolean; onOpen: () => void; onClose: () => void }> = ({ + children, + open, + onOpen, + onClose, +}) => { + const anchorEl: any = useRef(); + + return ( + <> + + + + + {children} + + + ); +}; + +export const ActionsMenuItem = ({ + title, + description, + onClick, + Icon, + disabled, +}: { + title: string; + description?: string; + onClick?: () => void; + Icon?: React.ReactNode; + disabled?: boolean; +}) => { + return ( + + {Icon} + + + ); +}; diff --git a/nym-wallet/src/components/Delegation/DelegationActions.tsx b/nym-wallet/src/components/Delegation/DelegationActions.tsx index 6a325c3309..e851b6ea00 100644 --- a/nym-wallet/src/components/Delegation/DelegationActions.tsx +++ b/nym-wallet/src/components/Delegation/DelegationActions.tsx @@ -1,19 +1,8 @@ -import React from 'react'; -import { - Box, - Button, - IconButton, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - Stack, - Tooltip, - Typography, -} from '@mui/material'; -import { MoreVertSharp } from '@mui/icons-material'; +import React, { useState } from 'react'; +import { Box, Button, ListItemIcon, ListItemText, MenuItem, Stack, Tooltip, Typography } from '@mui/material'; import { DelegationEventKind } from '@nymproject/types'; import { Delegate, Undelegate } from '../../svg-icons'; +import { ActionsMenu, ActionsMenuItem } from '../ActionsMenu'; import { DelegateListItemPending } from './types'; export type DelegationListItemActions = 'delegate' | 'undelegate' | 'redeem' | 'compound'; @@ -100,17 +89,14 @@ export const DelegationsActionsMenu: React.FC<{ disableRedeemingRewards?: boolean; disableCompoundRewards?: boolean; }> = ({ disableRedeemingRewards, disableCompoundRewards, onActionClick, isPending }) => { - const [anchorEl, setAnchorEl] = React.useState(null); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; + const [isOpen, setIsOpen] = useState(false); - const handleClose = () => setAnchorEl(null); + const handleOpenMenu = () => setIsOpen(true); + const handleOnClose = () => setIsOpen(false); const handleActionSelect = (action: DelegationListItemActions) => { - handleClose(); onActionClick?.(action); + handleOnClose(); }; if (isPending) { @@ -126,37 +112,28 @@ export const DelegationsActionsMenu: React.FC<{ } return ( - <> - - - - - } - onClick={() => handleActionSelect?.('delegate')} - /> - } - onClick={() => handleActionSelect?.('undelegate')} - disabled={false} - /> - R} - onClick={() => handleActionSelect?.('redeem')} - disabled={disableRedeemingRewards} - /> - C} - onClick={() => handleActionSelect?.('compound')} - disabled={disableCompoundRewards} - /> - - + + } onClick={() => handleActionSelect('delegate')} /> + } + onClick={() => handleActionSelect('undelegate')} + disabled={false} + /> + R} + onClick={() => handleActionSelect('redeem')} + disabled={disableRedeemingRewards} + /> + C} + onClick={() => handleActionSelect('compound')} + disabled={disableCompoundRewards} + /> + ); }; diff --git a/nym-wallet/src/pages/bonding/components/NodeMenu.tsx b/nym-wallet/src/pages/bonding/components/NodeMenu.tsx index 1a44359767..bfc2822276 100644 --- a/nym-wallet/src/pages/bonding/components/NodeMenu.tsx +++ b/nym-wallet/src/pages/bonding/components/NodeMenu.tsx @@ -1,9 +1,9 @@ -import * as React from 'react'; -import { IconButton, Menu, MenuItem, Stack, Typography } from '@mui/material'; -import { MoreVert } from '@mui/icons-material'; -import { useEffect } from 'react'; -import { MixnodeFlow } from '../mixnode/types'; +import React, { useState } from 'react'; +import { Typography } from '@mui/material'; +import { ActionsMenu, ActionsMenuItem } from 'src/components/ActionsMenu'; +import { Bond as BondIcon, Unbond as UnbondIcon } from '../../../svg-icons'; import { GatewayFlow } from '../gateway/types'; +import { MixnodeFlow } from '../mixnode/types'; interface Item { label: string; @@ -12,72 +12,42 @@ interface Item { description?: string; } -interface Props { - onFlowChange: (flow: MixnodeFlow | GatewayFlow) => void; - items: Item[]; - onOpen: (open: boolean) => void; -} +const NodeMenu = ({ onFlowChange }: { onFlowChange: (flow: MixnodeFlow) => void }) => { + const [isOpen, setIsOpen] = useState(false); -const NodeMenu = ({ onFlowChange, items, onOpen }: Props) => { - const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); - const menuOpen = Boolean(menuAnchorEl); + const handleOpen = () => setIsOpen(true); + const handleClose = () => setIsOpen(false); - useEffect(() => { - onOpen(menuOpen); - }, [menuOpen]); - - const handleMenuClose = () => { - setMenuAnchorEl(null); - }; - - const onClick = (flow: MixnodeFlow | GatewayFlow) => { + const handleActionClick = (flow: MixnodeFlow | GatewayFlow) => { onFlowChange(flow); - handleMenuClose(); + handleClose(); }; return ( - <> - setMenuAnchorEl(event.currentTarget)} - aria-controls={menuOpen ? 'node-menu' : undefined} - aria-haspopup="true" - aria-expanded={menuOpen ? 'true' : undefined} - disableTouchRipple - disableFocusRipple - disableRipple - > - - - - {items.map(({ label, flow, icon, description }) => ( - onClick(flow)} key={flow} sx={{ px: 1.6 }} disableRipple> - - - {icon} - - - {label} - - {description} - - - - - ))} - - + + } + onClick={() => handleActionClick('bondMore')} + /> + } + onClick={() => handleActionClick('unbond')} + /> + C} + description="Add operator rewards to bond" + onClick={() => handleActionClick('compound')} + /> + R} + description="Add your rewards to bonding pool" + onClick={() => handleActionClick('redeem')} + /> + ); }; diff --git a/nym-wallet/src/pages/bonding/components/NodeTable.tsx b/nym-wallet/src/pages/bonding/components/NodeTable.tsx index 8cbbbee2e8..34cb305b11 100644 --- a/nym-wallet/src/pages/bonding/components/NodeTable.tsx +++ b/nym-wallet/src/pages/bonding/components/NodeTable.tsx @@ -40,7 +40,7 @@ const CellHeader = ({ children, tooltip, sx, size, align, color }: TableHeader) ); export type Header = Omit & { header?: React.ReactNode; id: string }; -export type Cell = Omit & { cell: React.ReactNode; id: string }; +export type Cell = Omit & { cell: React.ReactNode; id?: string }; export interface TableProps { headers: Header[]; diff --git a/nym-wallet/src/pages/bonding/gateway/GatewayCard.tsx b/nym-wallet/src/pages/bonding/gateway/GatewayCard.tsx index 441a39261e..8c34590600 100644 --- a/nym-wallet/src/pages/bonding/gateway/GatewayCard.tsx +++ b/nym-wallet/src/pages/bonding/gateway/GatewayCard.tsx @@ -1,13 +1,11 @@ -import React, { useMemo, useState } from 'react'; -import { useTheme } from '@mui/material/styles'; -import EditIcon from '@mui/icons-material/Edit'; -import { BondedGateway } from '../../../context'; -import { NodeTable, Cell, Header, NodeMenu } from '../components'; -import { GatewayFlow } from './types'; -import Unbond from '../unbond'; -import { NymCard } from 'src/components'; +import { useState } from 'react'; import { Stack, Typography } from '@mui/material'; +import { NymCard } from 'src/components'; import { IdentityKey } from 'src/components/IdentityKey'; +import { BondedGateway } from '../../../context'; +import { Cell, Header, NodeMenu, NodeTable } from '../components'; +import Unbond from '../unbond'; +import { GatewayFlow } from './types'; const headers: Header[] = [ { @@ -29,36 +27,24 @@ const headers: Header[] = [ const GatewayCard = ({ gateway }: { gateway: BondedGateway }) => { const { ip, bond } = gateway; const [flow, setFlow] = useState(null); - const [nodeMenuOpen, setNodeMenuOpen] = useState(false); - const theme = useTheme(); - const cells: Cell[] = useMemo( - () => [ - { - cell: ip, - id: 'ip-cell', - sx: { pl: 0 }, - }, - { - cell: `${bond.amount} ${bond.denom}`, - id: 'bond-cell', - }, - { - cell: ( - setFlow(newFlow as GatewayFlow)} - onOpen={(open) => setNodeMenuOpen(open)} - items={[{ label: 'Unbond', flow: 'unbond', icon: }]} - /> - ), - id: 'menu-button-cell', - align: 'center', - size: 'small', - sx: { backgroundColor: nodeMenuOpen ? '#FB6E4E0D' : undefined, px: 0 }, - }, - ], - [gateway, theme, nodeMenuOpen], - ); + const cells: Cell[] = [ + { + cell: ip, + id: 'ip-cell', + sx: { pl: 0 }, + }, + { + cell: `${bond.amount} ${bond.denom}`, + id: 'bond-cell', + }, + { + cell: setFlow(newFlow as GatewayFlow)} />, + id: 'menu-button-cell', + align: 'center', + }, + ]; + return ( { const { stake, bond, stakeSaturation, profitMargin, nodeRewards, operatorRewards, delegators } = mixnode; const [flow, setFlow] = useState(null); - const [nodeMenuOpen, setNodeMenuOpen] = useState(false); const theme = useTheme(); - const cells: Cell[] = useMemo( - () => [ - { - cell: `${stake.amount} ${stake.denom}`, - id: 'stake-cell', - sx: { pl: 0 }, - }, - { - cell: `${bond.amount} ${bond.denom}`, - id: 'bond-cell', - }, - { - cell: `${stakeSaturation}%`, - id: 'stake-saturation-cell', - color: stakeSaturation > 100 ? theme.palette.nym.nymWallet.selectionChance.underModerate : undefined, - }, - { - cell: `${profitMargin}%`, - id: 'pm-cell', - }, - { - cell: `${nodeRewards.amount} ${nodeRewards.denom}`, - id: 'node-rewards-cell', - }, - { - cell: `${operatorRewards.amount} ${operatorRewards.denom}`, - id: 'operator-rewards-cell', - }, - { - cell: delegators, - id: 'delegators-cell', - }, - { - cell: ( - setFlow(newFlow as MixnodeFlow)} - onOpen={(open) => setNodeMenuOpen(open)} - items={[ - { label: 'Bond more', flow: 'bondMore', icon: }, - { label: 'Unbond', flow: 'unbond', icon: }, - { - label: 'Compound rewards', - flow: 'compound', - icon: C, - description: 'Add operator rewards to bond', - }, - { - label: 'Redeem rewards', - flow: 'redeem', - icon: R, - description: 'Add your rewards to bonding pool', - }, - ]} - /> - ), - id: 'menu-button-cell', - align: 'center', - size: 'small', - sx: { backgroundColor: nodeMenuOpen ? '#FB6E4E0D' : undefined, px: 0 }, - }, - ], - [mixnode, theme, nodeMenuOpen], - ); + const cells: Cell[] = [ + { + cell: `${stake.amount} ${stake.denom}`, + id: 'stake-cell', + sx: { pl: 0 }, + }, + { + cell: `${bond.amount} ${bond.denom}`, + id: 'bond-cell', + }, + { + cell: `${stakeSaturation}%`, + id: 'stake-saturation-cell', + color: stakeSaturation > 100 ? theme.palette.nym.nymWallet.selectionChance.underModerate : undefined, + }, + { + cell: `${profitMargin}%`, + id: 'pm-cell', + }, + { + cell: `${nodeRewards.amount} ${nodeRewards.denom}`, + id: 'node-rewards-cell', + }, + { + cell: `${operatorRewards.amount} ${operatorRewards.denom}`, + id: 'operator-rewards-cell', + }, + { + cell: delegators, + id: 'delegators-cell', + }, + { + cell: setFlow(newFlow)} />, + }, + ]; + return (