import { Fragment, useContext, useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Divider from '@mui/material/Divider'; import ListItemText from '@mui/material/ListItemText'; import ListItemAvatar from '@mui/material/ListItemAvatar'; import Avatar from '@mui/material/Avatar'; import Typography from '@mui/material/Typography'; import { Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Input, useTheme, } from '@mui/material'; import { CustomButton } from './styles/App-styles'; import { useDropzone } from 'react-dropzone'; import EditIcon from '@mui/icons-material/Edit'; import { Label } from './components/Group/AddGroup'; import { Spacer } from './common/Spacer'; import { getWallets, storeWallets, walletVersion } from './background'; import { useModal } from './common/useModal'; import PhraseWallet from './utils/generateWallet/phrase-wallet'; import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet'; import { crypto } from './constants/decryptWallet'; import { LoadingButton } from '@mui/lab'; import { PasswordField } from './components'; import { HtmlTooltip } from './components/NotAuthenticated'; import { QORTAL_APP_CONTEXT } from './App'; import { useTranslation } from 'react-i18next'; const parsefilenameQortal = (filename) => { return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename; }; export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [wallets, setWallets] = useState([]); const [isLoading, setIsLoading] = useState(true); const [seedValue, setSeedValue] = useState(''); const [seedName, setSeedName] = useState(''); const [seedError, setSeedError] = useState(''); const { hasSeenGettingStarted } = useContext(QORTAL_APP_CONTEXT); const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ accept: { 'application/json': ['.json'], // Only accept JSON files }, onDrop: async (acceptedFiles) => { const files: any = acceptedFiles; let importedWallets: any = []; for (const file of files) { try { const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onabort = () => reject('File reading was aborted'); reader.onerror = () => reject('File reading has failed'); reader.onload = () => { // Resolve the promise with the reader result when reading completes resolve(reader.result); }; // Read the file as text reader.readAsText(file); }); if (typeof fileContents !== 'string') continue; const parsedData = JSON.parse(fileContents); importedWallets.push({ ...parsedData, filename: file?.name }); } catch (error) { console.error(error); } } const uniqueInitialMap = new Map(); // Only add a message if it doesn't already exist in the Map importedWallets.forEach((wallet) => { if (!wallet?.address0) return; if (!uniqueInitialMap.has(wallet?.address0)) { uniqueInitialMap.set(wallet?.address0, wallet); } }); const data = Array.from(uniqueInitialMap.values()); if (data && data?.length > 0) { const uniqueNewWallets = data.filter( (newWallet) => !wallets.some( (existingWallet) => existingWallet?.address0 === newWallet?.address0 ) ); setWallets([...wallets, ...uniqueNewWallets]); } }, }); const updateWalletItem = (idx, wallet) => { setWallets((prev) => { let copyPrev = [...prev]; if (wallet === null) { copyPrev.splice(idx, 1); // Use splice to remove the item return copyPrev; } else { copyPrev[idx] = wallet; // Update the wallet at the specified index return copyPrev; } }); }; const handleSetSeedValue = async () => { try { setIsOpenSeedModal(true); const { seedValue, seedName, password } = await show({ message: '', publishFee: '', }); setIsLoadingEncryptSeed(true); const res = await decryptStoredWalletFromSeedPhrase(seedValue); const wallet2 = new PhraseWallet(res, walletVersion); const wallet = await wallet2.generateSaveWalletData( password, crypto.kdfThreads, () => {} ); if (wallet?.address0) { setWallets([ ...wallets, { ...wallet, name: seedName, }, ]); setIsOpenSeedModal(false); setSeedValue(''); setSeedName(''); setPassword(''); setSeedError(''); } else { setSeedError( t('auth:message.error.account_creation', { postProcess: 'capitalizeFirstChar', }) ); } } catch (error) { setSeedError( error?.message || t('auth:message.error.account_creation', { postProcess: 'capitalizeFirstChar', }) ); } finally { setIsLoadingEncryptSeed(false); } }; const selectedWalletFunc = (wallet) => { setRawWallet(wallet); setExtState('wallet-dropped'); }; useEffect(() => { setIsLoading(true); getWallets() .then((res) => { if (res && Array.isArray(res)) { setWallets(res); } setIsLoading(false); }) .catch((error) => { console.error(error); setIsLoading(false); }); }, []); useEffect(() => { if (!isLoading && wallets && Array.isArray(wallets)) { storeWallets(wallets); } }, [wallets, isLoading]); if (isLoading) return null; return (
{wallets?.length === 0 || !wallets ? ( <> {t('auth:message.generic.no_account', { postProcess: 'capitalizeFirstChar', })} ) : ( <> {t('auth:message.generic.your_accounts', { postProcess: 'capitalizeFirstChar', })} )} {rawWallet && ( {t('auth:account.selected', { postProcess: 'capitalizeFirstChar', })} : {rawWallet?.name && {rawWallet.name}} {rawWallet?.address0 && ( {rawWallet?.address0} )} )} {wallets?.length > 0 && ( {wallets?.map((wallet, idx) => { return ( <> ); })} )} {t('auth:tips.existing_account', { postProcess: 'capitalizeFirstChar', })} } > {t('auth:action.add.seed_phrase', { postProcess: 'capitalizeFirstChar', })} {t('auth:tips.additional_wallet', { postProcess: 'capitalizeFirstChar', })} } > {t('auth:action.add.account', { postProcess: 'capitalizeFirstChar', })} { if (e.key === 'Enter' && seedValue && seedName && password) { onOk({ seedValue, seedName, password }); } }} > {t('auth:message.generic.type_seed', { postProcess: 'capitalizeFirstChar', })} setSeedName(e.target.value)} /> setSeedValue(e.target.value)} autoComplete="off" sx={{ width: '100%', }} /> setPassword(e.target.value)} autoComplete="off" sx={{ width: '100%', }} /> { if (!seedValue || !seedName || !password) return; onOk({ seedValue, seedName, password }); }} autoFocus > {t('core:action.add', { postProcess: 'capitalizeFirstChar', })} {seedError}
); }; const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { const [name, setName] = useState(''); const [note, setNote] = useState(''); const [isEdit, setIsEdit] = useState(false); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); useEffect(() => { if (wallet?.name) { setName(wallet.name); } if (wallet?.note) { setNote(wallet.note); } }, [wallet]); return ( <> { setSelectedWallet(wallet); }} sx={{ width: '100%', padding: '10px', }} > {wallet?.address0} {wallet?.note} {t('core:action.login', { postProcess: 'capitalizeFirstChar', })} } /> { e.stopPropagation(); setIsEdit(true); }} edge="end" aria-label={t('core:action.edit', { postProcess: 'capitalizeFirstChar', })} > {isEdit && ( setName(e.target.value)} sx={{ width: '100%', }} /> setNote(e.target.value)} inputProps={{ maxLength: 100, }} sx={{ width: '100%', }} /> )} ); };