feat: cargo project at root

This commit is contained in:
binarybaron 2024-08-08 00:49:04 +02:00
parent aa0c0623ca
commit 709a2820c4
No known key found for this signature in database
GPG key ID: 99B75D3E1476A26E
313 changed files with 1 additions and 740 deletions

View file

@ -0,0 +1,75 @@
import { makeStyles, Box, Typography, Chip, Tooltip } from '@material-ui/core';
import { VerifiedUser } from '@material-ui/icons';
import { satsToBtc, secondsToDays } from 'utils/conversionUtils';
import { ExtendedProviderStatus } from 'models/apiModel';
import {
MoneroBitcoinExchangeRate,
SatsAmount,
} from 'renderer/components/other/Units';
const useStyles = makeStyles((theme) => ({
content: {
flex: 1,
'& *': {
lineBreak: 'anywhere',
},
},
chipsOuter: {
display: 'flex',
marginTop: theme.spacing(1),
gap: theme.spacing(0.5),
flexWrap: 'wrap',
},
}));
export default function ProviderInfo({
provider,
}: {
provider: ExtendedProviderStatus;
}) {
const classes = useStyles();
return (
<Box className={classes.content}>
<Typography color="textSecondary" gutterBottom>
Swap Provider
</Typography>
<Typography variant="h5" component="h2">
{provider.multiAddr}
</Typography>
<Typography color="textSecondary" gutterBottom>
{provider.peerId.substring(0, 8)}...{provider.peerId.slice(-8)}
</Typography>
<Typography variant="caption">
Exchange rate:{' '}
<MoneroBitcoinExchangeRate rate={satsToBtc(provider.price)} />
<br />
Minimum swap amount: <SatsAmount amount={provider.minSwapAmount} />
<br />
Maximum swap amount: <SatsAmount amount={provider.maxSwapAmount} />
</Typography>
<Box className={classes.chipsOuter}>
<Chip label={provider.testnet ? 'Testnet' : 'Mainnet'} />
{provider.uptime && (
<Tooltip title="A high uptime indicates reliability. Providers with low uptime may be unreliable and cause swaps to take longer to complete or fail entirely.">
<Chip label={`${Math.round(provider.uptime * 100)} % uptime`} />
</Tooltip>
)}
{provider.age ? (
<Chip
label={`Went online ${Math.round(secondsToDays(provider.age))} ${
provider.age === 1 ? 'day' : 'days'
} ago`}
/>
) : (
<Chip label="Discovered via rendezvous point" />
)}
{provider.recommended === true && (
<Tooltip title="This provider has shown to be exceptionally reliable">
<Chip label="Recommended" icon={<VerifiedUser />} color="primary" />
</Tooltip>
)}
</Box>
</Box>
);
}

View file

@ -0,0 +1,129 @@
import {
Avatar,
List,
ListItem,
ListItemAvatar,
ListItemText,
DialogTitle,
Dialog,
DialogActions,
Button,
DialogContent,
makeStyles,
CircularProgress,
} from '@material-ui/core';
import AddIcon from '@material-ui/icons/Add';
import { useState } from 'react';
import SearchIcon from '@material-ui/icons/Search';
import { ExtendedProviderStatus } from 'models/apiModel';
import {
useAllProviders,
useAppDispatch,
useIsRpcEndpointBusy,
} from 'store/hooks';
import { setSelectedProvider } from 'store/features/providersSlice';
import { RpcMethod } from 'models/rpcModel';
import ProviderSubmitDialog from './ProviderSubmitDialog';
import ListSellersDialog from '../listSellers/ListSellersDialog';
import ProviderInfo from './ProviderInfo';
const useStyles = makeStyles({
dialogContent: {
padding: 0,
},
});
type ProviderSelectDialogProps = {
open: boolean;
onClose: () => void;
};
export function ProviderSubmitDialogOpenButton() {
const [open, setOpen] = useState(false);
return (
<ListItem
autoFocus
button
onClick={() => {
// Prevents background from being clicked and reopening dialog
if (!open) {
setOpen(true);
}
}}
>
<ProviderSubmitDialog open={open} onClose={() => setOpen(false)} />
<ListItemAvatar>
<Avatar>
<AddIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Add a new provider to public registry" />
</ListItem>
);
}
export function ListSellersDialogOpenButton() {
const [open, setOpen] = useState(false);
const running = useIsRpcEndpointBusy(RpcMethod.LIST_SELLERS);
return (
<ListItem
autoFocus
button
disabled={running}
onClick={() => {
// Prevents background from being clicked and reopening dialog
if (!open) {
setOpen(true);
}
}}
>
<ListSellersDialog open={open} onClose={() => setOpen(false)} />
<ListItemAvatar>
<Avatar>{running ? <CircularProgress /> : <SearchIcon />}</Avatar>
</ListItemAvatar>
<ListItemText primary="Discover providers by connecting to a rendezvous point" />
</ListItem>
);
}
export default function ProviderListDialog({
open,
onClose,
}: ProviderSelectDialogProps) {
const classes = useStyles();
const providers = useAllProviders();
const dispatch = useAppDispatch();
function handleProviderChange(provider: ExtendedProviderStatus) {
dispatch(setSelectedProvider(provider));
onClose();
}
return (
<Dialog onClose={onClose} open={open}>
<DialogTitle>Select a swap provider</DialogTitle>
<DialogContent className={classes.dialogContent} dividers>
<List>
{providers.map((provider) => (
<ListItem
button
onClick={() => handleProviderChange(provider)}
key={provider.peerId}
>
<ProviderInfo provider={provider} key={provider.peerId} />
</ListItem>
))}
<ListSellersDialogOpenButton />
<ProviderSubmitDialogOpenButton />
</List>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
</DialogActions>
</Dialog>
);
}

View file

@ -0,0 +1,62 @@
import {
makeStyles,
Card,
CardContent,
Box,
IconButton,
} from '@material-ui/core';
import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
import { useState } from 'react';
import { useAppSelector } from 'store/hooks';
import ProviderInfo from './ProviderInfo';
import ProviderListDialog from './ProviderListDialog';
const useStyles = makeStyles({
inner: {
textAlign: 'left',
width: '100%',
height: '100%',
},
providerCard: {
width: '100%',
},
providerCardContent: {
display: 'flex',
alignItems: 'center',
},
});
export default function ProviderSelect() {
const classes = useStyles();
const [selectDialogOpen, setSelectDialogOpen] = useState(false);
const selectedProvider = useAppSelector(
(state) => state.providers.selectedProvider,
);
if (!selectedProvider) return <>No provider selected</>;
function handleSelectDialogClose() {
setSelectDialogOpen(false);
}
function handleSelectDialogOpen() {
setSelectDialogOpen(true);
}
return (
<Box>
<ProviderListDialog
open={selectDialogOpen}
onClose={handleSelectDialogClose}
/>
<Card variant="outlined" className={classes.providerCard}>
<CardContent className={classes.providerCardContent}>
<ProviderInfo provider={selectedProvider} />
<IconButton onClick={handleSelectDialogOpen} size="small">
<ArrowForwardIosIcon />
</IconButton>
</CardContent>
</Card>
</Box>
);
}

View file

@ -0,0 +1,111 @@
import { ChangeEvent, useState } from 'react';
import {
DialogTitle,
Dialog,
DialogContent,
DialogContentText,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
import { Multiaddr } from 'multiaddr';
type ProviderSubmitDialogProps = {
open: boolean;
onClose: () => void;
};
export default function ProviderSubmitDialog({
open,
onClose,
}: ProviderSubmitDialogProps) {
const [multiAddr, setMultiAddr] = useState('');
const [peerId, setPeerId] = useState('');
async function handleProviderSubmit() {
if (multiAddr && peerId) {
await fetch('https://api.unstoppableswap.net/api/submit-provider', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
multiAddr,
peerId,
}),
});
setMultiAddr('');
setPeerId('');
onClose();
}
}
function handleMultiAddrChange(event: ChangeEvent<HTMLInputElement>) {
setMultiAddr(event.target.value);
}
function handlePeerIdChange(event: ChangeEvent<HTMLInputElement>) {
setPeerId(event.target.value);
}
function getMultiAddressError(): string | null {
try {
const multiAddress = new Multiaddr(multiAddr);
if (multiAddress.protoNames().includes('p2p')) {
return 'The multi address should not contain the peer id (/p2p/)';
}
if (multiAddress.protoNames().find((name) => name.includes('onion'))) {
return 'It is currently not possible to add a provider that is only reachable via Tor';
}
return null;
} catch (e) {
return 'Not a valid multi address';
}
}
return (
<Dialog onClose={onClose} open={open}>
<DialogTitle>Submit a provider to the public registry</DialogTitle>
<DialogContent dividers>
<DialogContentText>
If the provider is valid and reachable, it will be displayed to all
other users to trade with.
</DialogContentText>
<TextField
autoFocus
margin="dense"
label="Multiaddress"
fullWidth
helperText={
getMultiAddressError() ||
'Tells the swap client where the provider can be reached'
}
value={multiAddr}
onChange={handleMultiAddrChange}
placeholder="/ip4/182.3.21.93/tcp/9939"
error={!!getMultiAddressError()}
/>
<TextField
margin="dense"
label="Peer ID"
fullWidth
helperText="Identifies the provider and allows for secure communication"
value={peerId}
onChange={handlePeerIdChange}
placeholder="12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi"
/>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button
variant="contained"
onClick={handleProviderSubmit}
disabled={!(multiAddr && peerId && !getMultiAddressError())}
color="primary"
>
Submit
</Button>
</DialogActions>
</Dialog>
);
}