feat(gui): Allow to select from recently used monero addresses (#139)

* feat(gui): Allow user to select from recently used monero addresses in textfield
This commit is contained in:
binarybaron 2024-11-09 12:11:00 +01:00 committed by GitHub
parent 4867d2713f
commit bd3fca7e41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 271 additions and 78 deletions

View file

@ -1,8 +1,18 @@
import { TextField } from "@material-ui/core";
import { Box, Button, Dialog, DialogActions, DialogContent, IconButton, List, ListItem, ListItemText, TextField } from "@material-ui/core";
import { TextFieldProps } from "@material-ui/core/TextField/TextField";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { getMoneroAddresses } from "renderer/rpc";
import { isTestnet } from "store/config";
import { isXmrAddressValid } from "utils/conversionUtils";
import ImportContactsIcon from '@material-ui/icons/ImportContacts';
import TruncatedText from "../other/TruncatedText";
type MoneroAddressTextFieldProps = TextFieldProps & {
address: string;
onAddressChange: (address: string) => void;
onAddressValidityChange: (valid: boolean) => void;
helperText: string;
}
export default function MoneroAddressTextField({
address,
@ -10,30 +20,116 @@ export default function MoneroAddressTextField({
onAddressValidityChange,
helperText,
...props
}: {
address: string;
onAddressChange: (address: string) => void;
onAddressValidityChange: (valid: boolean) => void;
helperText: string;
} & TextFieldProps) {
}: MoneroAddressTextFieldProps) {
const [addresses, setAddresses] = useState<string[]>([]);
const [showDialog, setShowDialog] = useState(false);
// Validation
const placeholder = isTestnet() ? "59McWTPGc745..." : "888tNkZrPN6J...";
const errorText = isXmrAddressValid(address, isTestnet())
? null
: "Not a valid Monero address";
// Effects
useEffect(() => {
onAddressValidityChange(!errorText);
}, [address, onAddressValidityChange, errorText]);
useEffect(() => {
const fetchAddresses = async () => {
const response = await getMoneroAddresses();
setAddresses(response.addresses);
};
fetchAddresses();
}, []);
// Event handlers
const handleClose = () => setShowDialog(false);
const handleAddressSelect = (selectedAddress: string) => {
onAddressChange(selectedAddress);
handleClose();
};
return (
<TextField
value={address}
onChange={(e) => onAddressChange(e.target.value)}
error={!!errorText && address.length > 0}
helperText={address.length > 0 ? errorText || helperText : helperText}
placeholder={placeholder}
variant="outlined"
{...props}
/>
<Box>
<TextField
value={address}
onChange={(e) => onAddressChange(e.target.value)}
error={!!errorText && address.length > 0}
helperText={address.length > 0 ? errorText || helperText : helperText}
placeholder={placeholder}
variant="outlined"
InputProps={{
endAdornment: addresses?.length > 0 && (
<IconButton onClick={() => setShowDialog(true)} size="small">
<ImportContactsIcon />
</IconButton>
)
}}
{...props}
/>
<RecentlyUsedAddressesDialog
open={showDialog}
onClose={handleClose}
addresses={addresses}
onAddressSelect={handleAddressSelect}
/>
</Box>
);
}
interface RecentlyUsedAddressesDialogProps {
open: boolean;
onClose: () => void;
addresses: string[];
onAddressSelect: (address: string) => void;
}
function RecentlyUsedAddressesDialog({
open,
onClose,
addresses,
onAddressSelect
}: RecentlyUsedAddressesDialogProps) {
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="sm"
fullWidth
>
<DialogContent>
<List>
{addresses.map((addr) => (
<ListItem
button
key={addr}
onClick={() => onAddressSelect(addr)}
>
<ListItemText
primary={
<Box fontFamily="monospace">
<TruncatedText limit={40} truncateMiddle>
{addr}
</TruncatedText>
</Box>
}
secondary="Recently used as a redeem address"
/>
</ListItem>
))}
</List>
</DialogContent>
<DialogActions>
<Button
onClick={onClose}
variant="contained"
color="primary"
>
Close
</Button>
</DialogActions>
</Dialog>
);
}