Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/address book #96

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions packages/renderer/src/components/UI/modals/AddressBook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {useEffect, useState} from 'react';
import Button from '../Button';
import {getAddresses, removeAddress, saveNewAddress, trimMiddle} from '/@/tools';
import {toast} from 'react-toastify';
import Input from '../input/Input';

export default function AddressBook({
editable,
selectable,
onSelect,
}: {
editable?: boolean;
selectable?: boolean;
onSelect?: (address: string) => void;
}) {
const [showAdd, setShowAdd] = useState(false);
const [address, setAddress] = useState('');
const [alias, setAlias] = useState('');
const [addressBook, setAddressBook] = useState([]);

useEffect(() => {
readFromStorage();
}, []);

const readFromStorage = () => {
const addresses = getAddresses();
setAddressBook(addresses);
};

const saveAddress = () => {
if (!address) {
return toast.error('Please enter address and alias');
}
saveNewAddress(address, alias);
setShowAdd(false);
readFromStorage();
};

const deleteAddress = (address: string) => {
removeAddress(address);
readFromStorage();
};

return (
<div style={{width: '500px'}}>
<div className="w-100">
<div className="flex flex-col flex-vertical-center">
<h1 className="mb-0">Address book</h1>
<div className="divider w-100" />
</div>
</div>
<div className="address-book-container">
{addressBook.length > 0 ? (
addressBook.map(({address, alias}) => (
<div
key={address}
className="address-book-item"
>
<div className="flex flex-row justify-between ">
<div className="flex flex-col">
<p className="mb-0">{alias}</p>
<p className="small-text">{trimMiddle(address, 30)}</p>
</div>
{selectable && (
<Button
text="Select"
onClick={() => {
onSelect && onSelect(address);
}}
/>
)}
{editable && (
<div className="flex flex-row gap-2">
<Button
text="Delete"
onClick={() => deleteAddress(address)}
/>
</div>
)}
</div>
</div>
))
) : (
<div className="address-book-item">
<div>No addresses</div>
</div>
)}
</div>
{editable ? (
showAdd ? (
<div className="address-book-item">
<p>Address</p>
<Input
type="text"
value={address}
inputHandler={e => {
setAddress(e.target.value);
}}
/>
<p>Alias</p>
<Input
type="text"
value={alias}
inputHandler={e => {
setAlias(e.target.value);
}}
/>
<Button
style="primary"
text="Save"
onClick={saveAddress}
/>
</div>
) : (
<Button
style="primary"
text="Add new"
onClick={() => setShowAdd(true)}
/>
)
) : null}
</div>
);
}
46 changes: 34 additions & 12 deletions packages/renderer/src/components/UI/modals/CustomDelegation.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
import { useState } from 'react';
import { Row, Col } from 'react-bootstrap';
import { ArrowLeft, ArrowRight } from 'react-feather';
import {useState} from 'react';
import {Row, Col} from 'react-bootstrap';
import {ArrowLeft, ArrowRight} from 'react-feather';
import Button from '../Button';
import Input from '../input/Input';
import {ModalContainer} from './ModalContainer';
import AddressBook from './AddressBook';

interface IProps {
closeModal: () => void;
confirmCustomDelegate: (customDelegate: string) => void;
}

export const ConfirmCustomDelegation = ({
closeModal,
confirmCustomDelegate,
}: IProps) => {
export const ConfirmCustomDelegation = ({closeModal, confirmCustomDelegate}: IProps) => {
const [customDelegate, setCustomDelegate] = useState<string>('');
const [showAddressBookModal, setShowAddressBookModal] = useState(false);

const toggleAddressBookModal = () => setShowAddressBookModal(!showAddressBookModal);

return (
<div className="min-width-500">
<div className="w-100">
<div className="flex flex-col flex-vertical-center">
<h1 className="mb-0">Custom delegation </h1>
<p className="text-center mt-1 mb-1">
Delegate to someone that is not in the list
</p>
<p className="text-center mt-1 mb-1">Delegate to someone that is not in the list</p>
<div className="divider w-100" />
</div>
</div>

<div className="align-left mt-1 mb-2 label">
<strong>Public key</strong>
<div className="flex flex-row justify-between">
<strong>Public key</strong>
<Button
className="link-button custom-delegate-button purple-text align-end no-padding"
text="Address book"
onClick={toggleAddressBookModal}
/>
</div>
</div>
<Input
inputHandler={(e) => {
value={customDelegate}
inputHandler={e => {
setCustomDelegate(e.currentTarget.value);
}}
placeholder="Insert public key"
Expand All @@ -55,6 +64,19 @@ export const ConfirmCustomDelegation = ({
/>
</Col>
</Row>
<ModalContainer
show={showAddressBookModal}
close={toggleAddressBookModal}
closeOnBackgroundClick
>
<AddressBook
selectable
onSelect={address => {
setCustomDelegate(address);
toggleAddressBookModal();
}}
/>
</ModalContainer>
</div>
);
};
20 changes: 20 additions & 0 deletions packages/renderer/src/components/UI/sidebar/NetworkSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Button from '../Button';
import {useNetworkSettingsContext} from '/@/contexts/NetworkContext';
import {useNavigate} from 'react-router-dom';
import BackupWallet from '../modals/BackupWallet';
import AddressBook from '../modals/AddressBook';
import {INetworkData} from '/@/types';
import {getPassphrase} from '/@/tools';
import {useRecoilState} from 'recoil';
Expand All @@ -31,8 +32,10 @@ export default function NetworkSettings({
* Show private key backup modal
*/
const toggleBackupModal = () => setShowBackupModal(!showBackupModal);
const toggleAddressBookModal = () => setShowAddressBookModal(!showAddressBookModal);

const [showBackupModal, setShowBackupModal] = useState(false);
const [showAddressBookModal, setShowAddressBookModal] = useState(false);
const [showModal, setShowModal] = useState(false);
const [storedPassphrase, setStoredPassphrase] = useState('');
const {settings, saveSettings, availableNetworks} = useNetworkSettingsContext();
Expand Down Expand Up @@ -99,6 +102,16 @@ export default function NetworkSettings({
<label className="text-start">Connected zkapps</label>
<ConnectedZkapps />
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-row gap-4 justify-between">
<label className="text-start">Address book</label>
<Button
onClick={toggleAddressBookModal}
text="Show"
className="link-button custom-delegate-button purple-text align-end no-padding"
/>
</div>
</div>
{!hideBackup && storedPassphrase && (
<div className="flex flex-col gap-2">
<div className="flex flex-row gap-4 justify-between">
Expand Down Expand Up @@ -158,6 +171,13 @@ export default function NetworkSettings({
<ModalContainer show={showBackupModal}>
<BackupWallet closeModal={toggleBackupModal} />
</ModalContainer>
<ModalContainer
show={showAddressBookModal}
close={toggleAddressBookModal}
closeOnBackgroundClick
>
<AddressBook editable />
</ModalContainer>
</ModalContainer>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {checkFieldsAndProceed} from './TransactionFormHelper';
import type {IBalanceData} from '../../../contexts/balance/BalanceTypes';
import Big from 'big.js';
import {ArrowRight} from 'react-feather';
import {ModalContainer} from '../../UI/modals';
import AddressBook from '../../UI/modals/AddressBook';

interface IProps {
transactionData: ITransactionData;
Expand All @@ -29,6 +31,9 @@ const TransactionForm = ({
}: IProps) => {
const [amount, setAmount] = useState<number | string>(toLongMINA(transactionData.amount));
const [fee, setFee] = useState<number | string>(toLongMINA(transactionData.fee));
const [showAddressBookModal, setShowAddressBookModal] = useState(false);

const toggleAddressBookModal = () => setShowAddressBookModal(!showAddressBookModal);

/**
* If a fee button has been selected (average or fast) or a fee has been entered from the input
Expand Down Expand Up @@ -119,7 +124,14 @@ const TransactionForm = ({
return (
<div className="mx-auto w-75">
<div className="my-5">
<h3>Recipient</h3>
<div className="flex flex-row justify-between">
<h3>Recipient</h3>
<Button
className="link-button custom-delegate-button purple-text align-end no-padding"
text="Address book"
onClick={toggleAddressBookModal}
/>
</div>
<Input
value={transactionData.receiverAddress}
placeholder="Enter address "
Expand Down Expand Up @@ -195,6 +207,19 @@ const TransactionForm = ({
/>
</div>
</div>
<ModalContainer
show={showAddressBookModal}
close={toggleAddressBookModal}
closeOnBackgroundClick
>
<AddressBook
selectable
onSelect={address => {
addressHandler(address);
toggleAddressBookModal();
}}
/>
</ModalContainer>
</div>
);
};
Expand Down
54 changes: 54 additions & 0 deletions packages/renderer/src/tools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,57 @@ export async function signTransaction(privateKey: string, params: unknown) {
}
return signResult;
}

export const saveNewAddress = (address: string, alias: string) => {
try {
const addresses = localStorage.getItem('addresses');
if (addresses) {
const addressesArray = JSON.parse(addresses);
const existingAddress = addressesArray.find((item: any) => item.address === address);
if (existingAddress) {
existingAddress.alias = alias;
} else {
addressesArray.push({address, alias});
}
localStorage.setItem('addresses', JSON.stringify(addressesArray));
} else {
localStorage.setItem('addresses', JSON.stringify([{address, alias}]));
}
} catch (error) {
console.error('Error saving new address:', error);
}
};

export const getAddresses = () => {
try {
const addresses = localStorage.getItem('addresses');
if (addresses) {
return JSON.parse(addresses);
}
return [];
} catch (error) {
console.error('Error getting addresses:', error);
return [];
}
};

export const removeAddress = (address: string) => {
try {
const addresses = localStorage.getItem('addresses');
if (addresses) {
const addressesArray = JSON.parse(addresses);
const newAddresses = addressesArray.filter((item: any) => item.address !== address);
localStorage.setItem('addresses', JSON.stringify(newAddresses));
}
} catch (error) {
console.error('Error removing address:', error);
}
};

export const clearAllAddresses = () => {
try {
localStorage.removeItem('addresses');
} catch (error) {
console.error('Error clearing addresses:', error);
}
};