Skip to content

Commit

Permalink
refactor(TxDescription): use reducer
Browse files Browse the repository at this point in the history
see #59
  • Loading branch information
KayBeSee committed Oct 23, 2022
1 parent 18f3b99 commit 52ef1f1
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 120 deletions.
4 changes: 3 additions & 1 deletion apps/electron/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ ipcMain.on('/account-data', async (event, config: OnChainConfig) => {
// event.reply("/account-data", accountData);

try {
const accountData = await OnchainDataProvider.getAccountData(config);
const userDataPath = app.getPath('userData');
const db = await dbConnect(userDataPath);
const accountData = await OnchainDataProvider.getAccountData(config, db);
event.reply('/account-data', accountData);
} catch (e) {
console.log(`(${config.id}) /account-data error: `, e);
Expand Down
39 changes: 34 additions & 5 deletions apps/frontend/src/context/AccountMapContext.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import React, { createContext, useReducer, useCallback, useState } from 'react';
import React, { createContext, useContext, useReducer, useCallback, useState } from 'react';
import { PlatformContext } from 'src/context';

import { accountMapReducer, ACCOUNTMAP_UPDATE, ACCOUNTMAP_SET } from 'src/reducers/accountMap';
import {
accountMapReducer,
ACCOUNTMAP_UPDATE,
ACCOUNTMAP_SET,
ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION
} from 'src/reducers/accountMap';

import { AccountMap, LilyAccount } from '@lily/types';
import { AccountMap, LilyAccount, LilyOnchainAccount } from '@lily/types';

export const AccountMapContext = createContext({
setAccountMap: (accountMap: AccountMap) => {},
updateAccountMap: (account: LilyAccount) => {},
setCurrentAccountId: (id: string) => {},
accountMap: {} as AccountMap,
currentAccount: {} as LilyAccount
currentAccount: {} as LilyAccount,
updateTransactionDescription: (
account: LilyOnchainAccount,
txid: string,
description: string
) => {}
});

export const AccountMapProvider = ({ children }: { children: React.ReactChild }) => {
const [accountMap, dispatch] = useReducer(accountMapReducer, {});
const [currentAccountId, setCurrentAccountId] = useState('satoshi');

const { platform } = useContext(PlatformContext);

const currentAccount = accountMap[currentAccountId!] || {
name: 'Loading...',
loading: true,
Expand Down Expand Up @@ -47,12 +60,28 @@ export const AccountMapProvider = ({ children }: { children: React.ReactChild })
[dispatch]
);

const updateTransactionDescription = useCallback(
async (account: LilyOnchainAccount, txid: string, description: string) => {
await platform.addTransactionDescription(txid, description);
dispatch({
type: ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION,
payload: {
accountId: account.config.id,
txid,
description
}
});
},
[dispatch]
);

const value = {
accountMap,
updateAccountMap,
setAccountMap,
currentAccount,
setCurrentAccountId
setCurrentAccountId,
updateTransactionDescription
};

return <AccountMapContext.Provider value={value}>{children}</AccountMapContext.Provider>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { useContext, useEffect, useState } from 'react';
import { useContext, useState } from 'react';
import { PencilIcon, CheckIcon } from '@heroicons/react/outline';

import { PlatformContext } from 'src/context';
import { AccountMapContext } from 'src/context';
import { LilyOnchainAccount, Transaction } from '@lily/types';

interface Props {
txid: string;
description: string;
setDescription: React.Dispatch<React.SetStateAction<string>>;
transaction: Transaction;
}

export const TransactionDescription = ({ txid, description, setDescription }: Props) => {
export const TransactionDescription = ({ transaction }: Props) => {
const [description, setDescription] = useState(transaction.description);
const [isEditing, setIsEditing] = useState(false);

const { platform } = useContext(PlatformContext);
const { updateTransactionDescription, currentAccount } = useContext(AccountMapContext);

const onSave = async () => {
await platform.addTransactionDescription(txid, description);
updateTransactionDescription(
currentAccount as LilyOnchainAccount,
transaction.txid,
description
);
setIsEditing(false);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useState } from 'react';
import moment from 'moment';
import { ChevronRightIcon } from '@heroicons/react/solid';

import { Unit, SlideOver } from 'src/components';
import { capitalize } from 'src/utils/other';

import { PlatformContext } from 'src/context';

import TransactionTypeIcon from './TransactionTypeIcon';
import TxDetailsSlideover from './TxDetailsSlideover';

Expand All @@ -18,32 +16,9 @@ interface Props {
}

const TransactionRow = ({ transaction, flat }: Props) => {
const [description, setDescription] = useState('');
const { platform } = useContext(PlatformContext);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [modalContent, setModalContent] = useState<JSX.Element | null>(null);

useEffect(() => {
const retrieveDescription = async () => {
const retrievedDescription = await platform.getTransactionDescription(transaction.txid);
if (retrievedDescription) {
setDescription(retrievedDescription.description);
}
};
retrieveDescription();
}, [modalIsOpen]);

useEffect(() => {
setModalContent(
<TxDetailsSlideover
transaction={transaction}
setOpen={setModalIsOpen}
setDescription={setDescription}
description={description}
/>
);
}, [description]);

const openInModal = (component: JSX.Element) => {
setModalIsOpen(true);
setModalContent(component);
Expand All @@ -54,14 +29,7 @@ const TransactionRow = ({ transaction, flat }: Props) => {
<button
className='block bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 w-full dark:highlight-white/10 rounded-2xl outline-none focus:ring-2 focus:ring-green-500'
onClick={() => {
openInModal(
<TxDetailsSlideover
transaction={transaction}
setOpen={setModalIsOpen}
setDescription={setDescription}
description={description}
/>
);
openInModal(<TxDetailsSlideover transaction={transaction} setOpen={setModalIsOpen} />);
}}
>
<div className='flex items-center px-4 py-4 sm:px-6'>
Expand All @@ -81,7 +49,7 @@ const TransactionRow = ({ transaction, flat }: Props) => {
</div>
<div className='sm:flex sm:justify-between flex-wrap w-full items-center px-4 truncate'>
<p className='text-left text-sm font-medium text-yellow-600 truncate'>
{description ? description : transaction.address}
{transaction.description ? transaction.description : transaction.address}
</p>
<p className='hidden sm:flex items-center text-sm text-gray-900 dark:text-gray-300'>
<Unit value={transaction.value} />{' '}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React, { useState } from 'react';
import { XIcon, ExternalLinkIcon } from '@heroicons/react/outline';

import { Unit, Price } from 'src/components';
Expand All @@ -12,11 +12,9 @@ import { Transaction } from '@lily/types';
interface Props {
transaction: Transaction;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
description: string;
setDescription: React.Dispatch<React.SetStateAction<string>>;
}

const TxDetailsSlideover = ({ transaction, setOpen, description, setDescription }: Props) => {
const TxDetailsSlideover = ({ transaction, setOpen }: Props) => {
return (
<>
<div className='flex justify-between bg-white px-5 py-4 sm:px-6 dark:bg-slate-800 border-b border-gray-700/20 dark:border-slate-500/20'>
Expand Down Expand Up @@ -58,11 +56,7 @@ const TxDetailsSlideover = ({ transaction, setOpen, description, setDescription
</p>
</div>

<TransactionDescription
txid={transaction.txid}
description={description}
setDescription={setDescription}
/>
<TransactionDescription transaction={transaction} />

<TagsSection
addresses={[
Expand Down
24 changes: 23 additions & 1 deletion apps/frontend/src/reducers/accountMap.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { AccountMapAction } from 'src/types';
import { AccountMap } from '@lily/types';
import { AccountMap, LilyOnchainAccount } from '@lily/types';

export const ACCOUNTMAP_UPDATE = 'ACCOUNTMAP_UPDATE';
export const ACCOUNTMAP_SET = 'ACCOUNTMAP_SET';
export const ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION = 'ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION';

export const accountMapReducer = (state: AccountMap, action: AccountMapAction) => {
if (action.type === ACCOUNTMAP_UPDATE) {
Expand All @@ -20,5 +21,26 @@ export const accountMapReducer = (state: AccountMap, action: AccountMapAction) =
};
}

if (action.type === ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION) {
const accountTransactions = [
...(state[action.payload.accountId] as LilyOnchainAccount).transactions
];

const index = accountTransactions.findIndex((tx) => tx.txid === action.payload.txid);

accountTransactions[index] = {
...accountTransactions[index],
description: action.payload.description
};

return {
...state,
[action.payload.accountId]: {
...state[action.payload.accountId],
transactions: accountTransactions
}
};
}

return state;
};
6 changes: 5 additions & 1 deletion apps/frontend/src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,8 @@ export type SetStatePsbt = React.Dispatch<React.SetStateAction<Psbt>>;

export type AccountMapAction =
| { type: 'ACCOUNTMAP_UPDATE'; payload: { account: LilyAccount } }
| { type: 'ACCOUNTMAP_SET'; payload: AccountMap };
| { type: 'ACCOUNTMAP_SET'; payload: AccountMap }
| {
type: 'ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION';
payload: { accountId: string; txid: string; description: string };
};
6 changes: 4 additions & 2 deletions apps/frontend/src/utils/accountMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
bip32derivation: [childPubKey.bip32derivation]
bip32derivation: [childPubKey.bip32derivation],
tags: []
};
} else {
// p2wpkh
Expand All @@ -252,7 +253,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
bip32derivation: [childPubKey.bip32derivation]
bip32derivation: [childPubKey.bip32derivation],
tags: []
};
}
};
Expand Down
24 changes: 19 additions & 5 deletions packages/shared-server/src/OnchainProviders/Electrum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import ElectrumClient, {
} from '@lily-technologies/electrum-client';
import { address as bitcoinjsAddress, crypto as bitcoinjsCrypto } from 'bitcoinjs-lib';
import { bitcoinsToSatoshis } from 'unchained-bitcoin';
import { Database } from 'sqlite';
import sqlite3 from 'sqlite3';

import { getAllLabelsForAddress } from '../sqlite';

import { OnchainBaseProvider } from '.';

Expand Down Expand Up @@ -98,23 +102,27 @@ export class ElectrumProvider extends OnchainBaseProvider {
}
}

async getAccountData(account: OnChainConfig): Promise<LilyOnchainAccount> {
async getAccountData(
account: OnChainConfig,
db: Database<sqlite3.Database, sqlite3.Statement>
): Promise<LilyOnchainAccount> {
const {
receiveAddresses,
changeAddresses,
unusedReceiveAddresses,
unusedChangeAddresses,
transactions,
utxos
} = await this.scanForAddressesAndTransactions(account, 10);
} = await this.scanForAddressesAndTransactions(account, 10, db);

const currentBalance = utxos.reduce((acum, cur) => acum + cur.value, 0);

console.log(`(${account.id}): Serializing transactions...`);
const organizedTransactions = serializeTransactions(
const organizedTransactions = await serializeTransactions(
transactions,
receiveAddresses,
changeAddresses
changeAddresses,
db
);

return {
Expand Down Expand Up @@ -176,7 +184,11 @@ export class ElectrumProvider extends OnchainBaseProvider {
return response;
}

async scanForAddressesAndTransactions(account: OnChainConfig, limitGap: number) {
async scanForAddressesAndTransactions(
account: OnChainConfig,
limitGap: number,
db: Database<sqlite3.Database, sqlite3.Statement>
) {
console.log(`(${account.id}): Deriving addresses and checking for transactions...`);
const receiveAddresses: Address[] = [];
const changeAddresses: Address[] = [];
Expand All @@ -200,11 +212,13 @@ export class ElectrumProvider extends OnchainBaseProvider {
// derive a batch of receive/change addresses
for (let i = pos; i < pos + BATCH_SIZE; i++) {
const receiveAddress = getAddressFromAccount(account, `m/0/${i}`, this.network);
receiveAddress.tags = await getAllLabelsForAddress(db, receiveAddress.address);
receiveAddress.isChange = false;
receiveAddress.isMine = true;
currentReceiveAddressBatch.push(receiveAddress);

const changeAddress = getAddressFromAccount(account, `m/1/${i}`, this.network);
changeAddress.tags = await getAllLabelsForAddress(db, changeAddress.address);
changeAddress.isChange = true;
changeAddress.isMine = true;
currentChangeAddressBatch.push(changeAddress);
Expand Down
12 changes: 9 additions & 3 deletions packages/shared-server/src/OnchainProviders/Esplora.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import axios from 'axios';
import { Network } from 'bitcoinjs-lib';
import { blockExplorerAPIURL, MAINNET, TESTNET } from 'unchained-bitcoin';
import BigNumber from 'bignumber.js';
import { Database } from 'sqlite';
import sqlite3 from 'sqlite3';

import { OnchainBaseProvider } from '.';

Expand Down Expand Up @@ -38,7 +40,10 @@ export class EsploraProvider extends OnchainBaseProvider {
}
}

async getAccountData(account: OnChainConfig): Promise<LilyOnchainAccount> {
async getAccountData(
account: OnChainConfig,
db: Database<sqlite3.Database, sqlite3.Statement>
): Promise<LilyOnchainAccount> {
const {
receiveAddresses,
changeAddresses,
Expand All @@ -48,10 +53,11 @@ export class EsploraProvider extends OnchainBaseProvider {
} = await this.scanForAddressesAndTransactions(account, 10);

console.log(`(${account.id}): Serializing transactions...`);
const organizedTransactions = serializeTransactions(
const organizedTransactions = await serializeTransactions(
transactions as Transaction[],
receiveAddresses,
changeAddresses
changeAddresses,
db
);

console.log(`(${account.id}): Getting UTXO data...`);
Expand Down
Loading

0 comments on commit 52ef1f1

Please sign in to comment.