REF: useStorage useCallback and useMemo

This commit is contained in:
Marcos Rodriguez Velez 2024-05-12 17:14:52 -04:00
parent 6766d9173f
commit fbc5609047
No known key found for this signature in database
GPG Key ID: 6030B2F48CCE86D7
4 changed files with 234 additions and 188 deletions

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { startAndDecrypt } from './start-and-decrypt';
import Notifications from '../blue_modules/notifications';
import { LegacyWallet, TTXMetadata, WatchOnlyWallet, BlueApp as BlueAppClass, TCounterpartyMetadata } from '../class';
@ -18,8 +18,8 @@ const _lastTimeTriedToRefetchWallet: { [walletID: string]: number } = {};
interface BlueStorageContextType {
wallets: TWallet[];
setWalletsWithNewOrder: (wallets: TWallet[]) => void;
txMetadata: TTXMetadata;
counterpartyMetadata: TCounterpartyMetadata;
txMetadata: React.MutableRefObject<TTXMetadata>;
counterpartyMetadata: React.MutableRefObject<TCounterpartyMetadata>;
saveToDisk: (force?: boolean) => Promise<void>;
selectedWalletID: string | undefined;
setSelectedWalletID: (walletID: string | undefined) => void;
@ -62,143 +62,8 @@ export enum WalletTransactionsStatus {
// @ts-ignore defaut value does not match the type
export const BlueStorageContext = createContext<BlueStorageContextType>(undefined);
export const BlueStorageProvider = ({ children }: { children: React.ReactNode }) => {
const [wallets, setWallets] = useState<TWallet[]>([]);
const [selectedWalletID, setSelectedWalletID] = useState<undefined | string>();
const [walletTransactionUpdateStatus, setWalletTransactionUpdateStatus] = useState<WalletTransactionsStatus | string>(
WalletTransactionsStatus.NONE,
);
const [walletsInitialized, setWalletsInitialized] = useState<boolean>(false);
const [isElectrumDisabled, setIsElectrumDisabled] = useState<boolean>(true);
const [currentSharedCosigner, setCurrentSharedCosigner] = useState<string>('');
const [reloadTransactionsMenuActionFunction, setReloadTransactionsMenuActionFunction] = useState<() => void>(() => {});
useEffect(() => {
setWallets(BlueApp.getWallets());
BlueElectrum.isDisabled().then(setIsElectrumDisabled);
if (walletsInitialized) {
BlueElectrum.connectMain();
}
}, [walletsInitialized]);
const saveToDisk = async (force: boolean = false) => {
InteractionManager.runAfterInteractions(async () => {
if (BlueApp.getWallets().length === 0 && !force) {
console.log('not saving empty wallets array');
return;
}
BlueApp.tx_metadata = txMetadata;
BlueApp.counterparty_metadata = counterpartyMetadata;
await BlueApp.saveToDisk();
setWallets([...BlueApp.getWallets()]);
txMetadata = BlueApp.tx_metadata;
counterpartyMetadata = BlueApp.counterparty_metadata;
});
};
const resetWallets = () => {
setWallets(BlueApp.getWallets());
};
const setWalletsWithNewOrder = (wlts: TWallet[]) => {
BlueApp.wallets = wlts;
saveToDisk();
};
const refreshAllWalletTransactions = async (lastSnappedTo?: number, showUpdateStatusIndicator: boolean = true) => {
let noErr = true;
try {
await BlueElectrum.waitTillConnected();
if (showUpdateStatusIndicator) {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
}
const paymentCodesStart = Date.now();
await BlueApp.fetchSenderPaymentCodes(lastSnappedTo);
const paymentCodesEnd = Date.now();
console.log('fetch payment codes took', (paymentCodesEnd - paymentCodesStart) / 1000, 'sec');
const balanceStart = +new Date();
await fetchWalletBalances(lastSnappedTo);
const balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await fetchWalletTransactions(lastSnappedTo);
const end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
} finally {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
}
if (noErr) await saveToDisk(); // caching
};
const fetchAndSaveWalletTransactions = async (walletID: string) => {
const index = wallets.findIndex(wallet => wallet.getID() === walletID);
let noErr = true;
try {
// 5sec debounce:
if (+new Date() - _lastTimeTriedToRefetchWallet[walletID] < 5000) {
console.log('re-fetch wallet happens too fast; NOP');
return;
}
_lastTimeTriedToRefetchWallet[walletID] = +new Date();
await BlueElectrum.waitTillConnected();
setWalletTransactionUpdateStatus(walletID);
const balanceStart = +new Date();
await fetchWalletBalances(index);
const balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await fetchWalletTransactions(index);
const end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
} finally {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
}
if (noErr) await saveToDisk(); // caching
};
const addWallet = (wallet: TWallet) => {
BlueApp.wallets.push(wallet);
setWallets([...BlueApp.getWallets()]);
};
const deleteWallet = (wallet: TWallet) => {
BlueApp.deleteWallet(wallet);
setWallets([...BlueApp.getWallets()]);
};
const addAndSaveWallet = async (w: TWallet) => {
if (wallets.some(i => i.getID() === w.getID())) {
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
presentAlert({ message: 'This wallet has been previously imported.' });
return;
}
const emptyWalletLabel = new LegacyWallet().getLabel();
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
if (w.getLabel() === emptyWalletLabel) w.setLabel(loc.wallets.import_imported + ' ' + w.typeReadable);
w.setUserHasSavedExport(true);
addWallet(w);
await saveToDisk();
A(A.ENUM.CREATED_WALLET);
presentAlert({
hapticFeedback: HapticFeedbackTypes.ImpactHeavy,
message: w.type === WatchOnlyWallet.type ? loc.wallets.import_success_watchonly : loc.wallets.import_success,
});
// @ts-ignore need to type notifications first
Notifications.majorTomToGroundControl(w.getAllExternalAddresses(), [], []);
// start balance fetching at the background
await w.fetchBalance();
setWallets([...BlueApp.getWallets()]);
};
let txMetadata = BlueApp.tx_metadata;
let counterpartyMetadata = BlueApp.counterparty_metadata || {}; // init
const txMetadata = useRef<TTXMetadata>(BlueApp.tx_metadata);
const counterpartyMetadata = useRef<TCounterpartyMetadata>(BlueApp.counterparty_metadata || {}); // init
const getTransactions = BlueApp.getTransactions;
const fetchWalletBalances = BlueApp.fetchWalletBalances;
const fetchWalletTransactions = BlueApp.fetchWalletTransactions;
@ -214,46 +79,227 @@ export const BlueStorageProvider = ({ children }: { children: React.ReactNode })
const getItem = BlueApp.getItem;
const setItem = BlueApp.setItem;
const value: BlueStorageContextType = {
wallets,
setWalletsWithNewOrder,
txMetadata,
counterpartyMetadata,
saveToDisk,
getTransactions,
selectedWalletID,
setSelectedWalletID,
addWallet,
deleteWallet,
currentSharedCosigner,
setSharedCosigner: setCurrentSharedCosigner,
addAndSaveWallet,
setItem,
getItem,
fetchWalletBalances,
fetchWalletTransactions,
fetchAndSaveWalletTransactions,
isStorageEncrypted,
encryptStorage,
startAndDecrypt,
cachedPassword,
getBalance,
walletsInitialized,
setWalletsInitialized,
refreshAllWalletTransactions,
sleep,
createFakeStorage,
resetWallets,
decryptStorage,
isPasswordInUse,
walletTransactionUpdateStatus,
setWalletTransactionUpdateStatus,
isElectrumDisabled,
setIsElectrumDisabled,
reloadTransactionsMenuActionFunction,
setReloadTransactionsMenuActionFunction,
const [wallets, setWallets] = useState<TWallet[]>([]);
const [selectedWalletID, setSelectedWalletID] = useState<undefined | string>();
const [walletTransactionUpdateStatus, setWalletTransactionUpdateStatus] = useState<WalletTransactionsStatus | string>(
WalletTransactionsStatus.NONE,
);
const [walletsInitialized, setWalletsInitialized] = useState<boolean>(false);
const [isElectrumDisabled, setIsElectrumDisabled] = useState<boolean>(true);
const [currentSharedCosigner, setCurrentSharedCosigner] = useState<string>('');
const [reloadTransactionsMenuActionFunction, setReloadTransactionsMenuActionFunction] = useState<() => void>(() => {});
useEffect(() => {
BlueElectrum.isDisabled().then(setIsElectrumDisabled);
if (walletsInitialized) {
setWallets(BlueApp.getWallets());
BlueElectrum.connectMain();
}
}, [walletsInitialized]);
const saveToDisk = useCallback(async (force: boolean = false) => {
InteractionManager.runAfterInteractions(async () => {
if (BlueApp.getWallets().length === 0 && !force) {
console.log('not saving empty wallets array');
return;
}
BlueApp.tx_metadata = txMetadata.current;
BlueApp.counterparty_metadata = counterpartyMetadata.current;
await BlueApp.saveToDisk();
setWallets([...BlueApp.getWallets()]);
txMetadata.current = BlueApp.tx_metadata;
counterpartyMetadata.current = BlueApp.counterparty_metadata;
});
}, []);
const resetWallets = () => {
setWallets(BlueApp.getWallets());
};
const setWalletsWithNewOrder = useCallback(
(wlts: TWallet[]) => {
BlueApp.wallets = wlts;
saveToDisk();
},
[saveToDisk],
);
const refreshAllWalletTransactions = useCallback(
async (lastSnappedTo?: number, showUpdateStatusIndicator: boolean = true) => {
InteractionManager.runAfterInteractions(async () => {
let noErr = true;
try {
await BlueElectrum.waitTillConnected();
if (showUpdateStatusIndicator) {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
}
const paymentCodesStart = Date.now();
await BlueApp.fetchSenderPaymentCodes(lastSnappedTo);
const paymentCodesEnd = Date.now();
console.log('fetch payment codes took', (paymentCodesEnd - paymentCodesStart) / 1000, 'sec');
const balanceStart = +new Date();
await fetchWalletBalances(lastSnappedTo);
const balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await fetchWalletTransactions(lastSnappedTo);
const end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
} finally {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
}
if (noErr) await saveToDisk(); // caching
});
},
[fetchWalletBalances, fetchWalletTransactions, saveToDisk],
);
const fetchAndSaveWalletTransactions = useCallback(
async (walletID: string) => {
InteractionManager.runAfterInteractions(async () => {
const index = wallets.findIndex(wallet => wallet.getID() === walletID);
let noErr = true;
try {
// 5sec debounce:
if (+new Date() - _lastTimeTriedToRefetchWallet[walletID] < 5000) {
console.log('re-fetch wallet happens too fast; NOP');
return;
}
_lastTimeTriedToRefetchWallet[walletID] = +new Date();
await BlueElectrum.waitTillConnected();
setWalletTransactionUpdateStatus(walletID);
const balanceStart = +new Date();
await fetchWalletBalances(index);
const balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await fetchWalletTransactions(index);
const end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
} finally {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
}
if (noErr) await saveToDisk(); // caching
});
},
[fetchWalletBalances, fetchWalletTransactions, saveToDisk, wallets],
);
const addWallet = useCallback((wallet: TWallet) => {
BlueApp.wallets.push(wallet);
setWallets([...BlueApp.getWallets()]);
}, []);
const deleteWallet = useCallback((wallet: TWallet) => {
BlueApp.deleteWallet(wallet);
setWallets([...BlueApp.getWallets()]);
}, []);
const addAndSaveWallet = useCallback(
async (w: TWallet) => {
if (wallets.some(i => i.getID() === w.getID())) {
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
presentAlert({ message: 'This wallet has been previously imported.' });
return;
}
const emptyWalletLabel = new LegacyWallet().getLabel();
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
if (w.getLabel() === emptyWalletLabel) w.setLabel(loc.wallets.import_imported + ' ' + w.typeReadable);
w.setUserHasSavedExport(true);
addWallet(w);
await saveToDisk();
A(A.ENUM.CREATED_WALLET);
presentAlert({
hapticFeedback: HapticFeedbackTypes.ImpactHeavy,
message: w.type === WatchOnlyWallet.type ? loc.wallets.import_success_watchonly : loc.wallets.import_success,
});
// @ts-ignore need to type notifications first
Notifications.majorTomToGroundControl(w.getAllExternalAddresses(), [], []);
// start balance fetching at the background
await w.fetchBalance();
setWallets([...BlueApp.getWallets()]);
},
[addWallet, saveToDisk, wallets],
);
const value: BlueStorageContextType = useMemo(
() => ({
wallets,
setWalletsWithNewOrder,
txMetadata,
counterpartyMetadata,
saveToDisk,
getTransactions,
selectedWalletID,
setSelectedWalletID,
addWallet,
deleteWallet,
currentSharedCosigner,
setSharedCosigner: setCurrentSharedCosigner,
addAndSaveWallet,
setItem,
getItem,
fetchWalletBalances,
fetchWalletTransactions,
fetchAndSaveWalletTransactions,
isStorageEncrypted,
encryptStorage,
startAndDecrypt,
cachedPassword,
getBalance,
walletsInitialized,
setWalletsInitialized,
refreshAllWalletTransactions,
sleep,
createFakeStorage,
resetWallets,
decryptStorage,
isPasswordInUse,
walletTransactionUpdateStatus,
setWalletTransactionUpdateStatus,
isElectrumDisabled,
setIsElectrumDisabled,
reloadTransactionsMenuActionFunction,
setReloadTransactionsMenuActionFunction,
}),
[
wallets,
setWalletsWithNewOrder,
saveToDisk,
getTransactions,
selectedWalletID,
addWallet,
deleteWallet,
currentSharedCosigner,
addAndSaveWallet,
setItem,
getItem,
fetchWalletBalances,
fetchWalletTransactions,
fetchAndSaveWalletTransactions,
isStorageEncrypted,
encryptStorage,
cachedPassword,
getBalance,
walletsInitialized,
refreshAllWalletTransactions,
sleep,
createFakeStorage,
decryptStorage,
isPasswordInUse,
walletTransactionUpdateStatus,
isElectrumDisabled,
reloadTransactionsMenuActionFunction,
],
);
return <BlueStorageContext.Provider value={value}>{children}</BlueStorageContext.Provider>;
};

View File

@ -61,9 +61,9 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
let counterparty;
if (item.counterparty) {
counterparty = counterpartyMetadata?.[item.counterparty]?.label ?? item.counterparty;
counterparty = counterpartyMetadata.current?.[item.counterparty]?.label ?? item.counterparty;
}
const txMemo = (counterparty ? `[${shortenContactName(counterparty)}] ` : '') + (txMetadata[item.hash]?.memo ?? '');
const txMemo = (counterparty ? `[${shortenContactName(counterparty)}] ` : '') + (txMetadata.current[item.hash]?.memo ?? '');
const subtitle = useMemo(() => {
let sub = Number(item.confirmations) < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
if (sub !== '') sub += ' ';

View File

@ -432,12 +432,12 @@ const TransactionStatus = () => {
};
const renderTXMetadata = () => {
if (txMetadata[tx.hash]) {
if (txMetadata[tx.hash].memo) {
if (txMetadata.current[tx.hash]) {
if (txMetadata.current[tx.hash].memo) {
return (
<View style={styles.memo}>
<Text selectable style={styles.memoText}>
{txMetadata[tx.hash].memo}
{txMetadata.current[tx.hash].memo}
</Text>
</View>
);
@ -449,7 +449,7 @@ const TransactionStatus = () => {
if (!tx.counterparty) return; // no BIP47 counterparty for this tx, return early
// theres a counterparty. lets lookup if theres an alias for him
let counterparty = counterpartyMetadata?.[tx.counterparty]?.label ?? tx.counterparty;
let counterparty = counterpartyMetadata.current?.[tx.counterparty]?.label ?? tx.counterparty;
counterparty = shortenCounterpartyName(counterparty);
return (

View File

@ -67,9 +67,9 @@ const TransactionDetails = () => {
const handleOnSaveButtonTapped = useCallback(() => {
Keyboard.dismiss();
if (!tx) return;
txMetadata[tx.hash] = { memo };
txMetadata.current[tx.hash] = { memo };
if (counterpartyLabel && paymentCode) {
counterpartyMetadata[paymentCode] = { label: counterpartyLabel };
counterpartyMetadata.current[paymentCode] = { label: counterpartyLabel };
}
saveToDisk().then(_success => {
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
@ -117,13 +117,13 @@ const TransactionDetails = () => {
// okay, this txid _was_ with someone using payment codes, so we show the label edit dialog
// and load user-defined alias for the pc if any
setCounterpartyLabel(counterpartyMetadata ? counterpartyMetadata[foundPaymentCode]?.label ?? '' : '');
setCounterpartyLabel(counterpartyMetadata ? counterpartyMetadata.current[foundPaymentCode]?.label ?? '' : '');
setIsCounterpartyLabelVisible(true);
setPaymentCode(foundPaymentCode);
}
}
setMemo(txMetadata[foundTx.hash]?.memo ?? '');
setMemo(txMetadata.current[foundTx.hash]?.memo ?? '');
setTX(foundTx);
setFrom(newFrom);
setTo(newTo);