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.ts';
import { useDropzone } from 'react-dropzone';
import EditIcon from '@mui/icons-material/Edit';
import { Label } from './Group/AddGroup.tsx';
import { Spacer } from '../common/Spacer.tsx';
import {
getWallets,
storeWallets,
walletVersion,
} from '../background/background.ts';
import { useModal } from '../hooks/useModal.tsx';
import PhraseWallet from '../utils/generateWallet/phrase-wallet.ts';
import { decryptStoredWalletFromSeedPhrase } from '../utils/decryptWallet.ts';
import { crypto } from '../constants/decryptWallet.ts';
import { LoadingButton } from '@mui/lab';
import { PasswordField } from './index.ts';
import { HtmlTooltip } from './NotAuthenticated.tsx';
import { QORTAL_APP_CONTEXT } from '../App.tsx';
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',
'question',
'tutorial',
]);
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'); // TODO translate
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',
})}
);
};
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',
'question',
'tutorial',
]);
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%',
}}
/>
)}
>
);
};