import React, { useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Dialog, DialogContent, Divider, Grid, List, ListItemText, ListItem, ListItemIcon, Typography, IconButton, Tooltip, Link, Box, CircularProgress, Accordion, AccordionDetails, AccordionSummary, } from '@mui/material'; import { Inventory, Sell, SmartToy, Percent, PriceChange, Book, Reddit, Key, Bolt, Description, Dns, Email, Equalizer, ExpandMore, GitHub, Language, Send, Tag, Twitter, } from '@mui/icons-material'; import LinkIcon from '@mui/icons-material/Link'; import { pn } from '../../utils'; import { type Contact, type Coordinator } from '../../models'; import RobotAvatar from '../RobotAvatar'; import { AmbossIcon, BitcoinSignIcon, RoboSatsNoTextIcon, BadgeFounder, BadgeDevFund, BadgePrivacy, BadgeLoved, BadgeLimits, NostrIcon, } from '../Icons'; import { AppContext } from '../../contexts/AppContext'; import { systemClient } from '../../services/System'; import { type Badges } from '../../models/Coordinator.model'; interface Props { open: boolean; onClose: () => void; coordinator: Coordinator | null; network: 'mainnet' | 'testnet' | undefined; } const ContactButtons = ({ nostr, pgp, email, telegram, twitter, matrix, website, reddit, }: Contact): JSX.Element => { const { t } = useTranslation(); const [showMatrix, setShowMatrix] = useState(false); const [showNostr, setShowNostr] = useState(false); return ( {nostr !== undefined && ( {t('...Opening on Nostr gateway. Pubkey copied!')} {nostr} } open={showNostr} > { setShowNostr(true); setTimeout(() => window.open(`https://snort.social/p/${nostr}`, '_blank'), 1500); setTimeout(() => { setShowNostr(false); }, 10000); systemClient.copyToClipboard(nostr); }} > )} {pgp !== undefined && ( )} {email !== undefined && ( )} {telegram !== undefined && ( )} {twitter !== undefined && ( )} {reddit !== undefined && ( )} {website !== undefined && ( )} {matrix !== undefined && ( {t('Matrix channel copied! {{matrix}}', { matrix })} } open={showMatrix} > { setShowMatrix(true); setTimeout(() => { setShowMatrix(false); }, 10000); systemClient.copyToClipboard(matrix); }} > )} ); }; interface BadgesProps { badges: Badges | undefined; } const BadgesHall = ({ badges }: BadgesProps): JSX.Element => { const { t } = useTranslation(); const sxProps = { width: '3em', height: '3em', filter: 'drop-shadow(3px 3px 3px RGB(0,0,0,0.3))', }; const tooltipProps = { enterTouchDelay: 0, enterNextDelay: 2000 }; return ( {badges?.isFounder === true ? t('Founder: coordinating trades since the testnet federation.') : t('Not a federation founder')} } > {t('Development fund supporter: donates {{percent}}% to make RoboSats better.', { percent: badges?.donatesToDevFund, })} } > = 20 ? undefined : 'grayscale(100%)' }} > {badges?.hasGoodOpSec === true ? t( 'Good OpSec: the coordinator follows best practices to protect his and your privacy.', ) : t('The privacy practices of this coordinator could improve')} } > {badges?.robotsLove === true ? t('Loved by robots: receives positive comments by robots over the internet.') : t( 'The coordinator does not seem to receive exceptional love from robots over the internet', )} } > {badges?.hasLargeLimits === true ? t('Large limits: the coordinator has large trade limits.') : t('Does not have large trade limits.')} } > ); }; const CoordinatorDialog = ({ open = false, onClose, coordinator, network }: Props): JSX.Element => { const { t } = useTranslation(); const { clientVersion, page, hostUrl } = useContext(AppContext); const [expanded, setExpanded] = useState<'summary' | 'stats' | 'policies' | undefined>(undefined); const listItemProps = { sx: { maxHeight: '3em', width: '100%' } }; const coordinatorVersion = `v${coordinator?.info?.version?.major ?? '?'}.${ coordinator?.info?.version?.minor ?? '?' }.${coordinator?.info?.version?.patch ?? '?'}`; return ( {String(coordinator?.longAlias)} {String(coordinator?.motto)} {['create'].includes(page) && ( <> {(coordinator?.info?.maker_fee ?? 0 * 100).toFixed(3)}% {(coordinator?.info?.taker_fee ?? 0 * 100).toFixed(3)}% )} {coordinator?.mainnetNodesPubkeys?.[0] !== undefined && network === 'mainnet' ? ( {`${coordinator?.mainnetNodesPubkeys?.[0].slice(0, 12)}... (AMBOSS)`} ) : ( <> )} {coordinator?.testnetNodesPubkeys?.[0] !== undefined && network === 'testnet' ? ( {`${coordinator?.testnetNodesPubkeys[0].slice(0, 12)}... (1ML)`} ) : ( <> )} {coordinator?.loadingInfo === true ? ( ) : coordinator?.info !== undefined ? ( {coordinator?.policies !== undefined && ( { setExpanded(expanded === 'policies' ? undefined : 'policies'); }} > }> {t('Policies')} {Object.keys(coordinator?.policies).map((key, index) => ( {index + 1} ))} )} { setExpanded(expanded === 'summary' ? undefined : 'summary'); }} > }> {t('Summary')} {(coordinator?.info?.maker_fee * 100).toFixed(3)}% {(coordinator?.info?.taker_fee * 100).toFixed(3)}% { setExpanded(expanded === 'stats' ? undefined : 'stats'); }} > }> {t('Stats for Nerds')} {coordinator?.info?.lnd_version !== undefined && ( )} {Boolean(coordinator?.info?.cln_version) && ( )} {coordinator?.info?.network === 'testnet' ? ( {`${coordinator?.info?.node_id.slice(0, 12)}... (1ML)`} ) : ( {`${coordinator?.info?.node_id.slice(0, 12)}... (AMBOSS)`} )} {`${coordinator?.info?.robosats_running_commit_hash.slice(0, 12)}...`}
{pn(parseFloat(coordinator?.info?.last_day_volume).toFixed(8))}
{pn(parseFloat(coordinator?.info?.lifetime_volume).toFixed(8))}
) : ( {t('Coordinator offline')} )}
); }; export default CoordinatorDialog;