Optimize transaction sorting (#86)

* implemented new tx sorting to prevent rerenders

* updated component to reduce re-renders

* fixing bank transactions page
This commit is contained in:
Blake Kaufman
2025-03-16 14:00:39 -04:00
committed by GitHub
parent c0b98e669d
commit b9067e3939
20 changed files with 552 additions and 431 deletions
+31 -28
View File
@@ -63,6 +63,7 @@ import {
} from './navigation/screens';
import getDeepLinkUser from './app/components/admin/homeComponents/contacts/internalComponents/getDeepLinkUser';
import {navigationRef} from './navigation/navigationService';
import {GlobalConbinedTxContextProvider} from './context-store/combinedTransactionsContext';
const Stack = createNativeStackNavigator();
@@ -70,35 +71,37 @@ function App(): JSX.Element {
return (
<GestureHandlerRootView>
<KeysContextProvider>
<AppStatusProvider>
<GlobalThemeProvider>
<GLobalNodeContextProider>
<GlobalContextProvider>
<GlobalAppDataProvider>
<POSTransactionsProvider>
<WebViewProvider>
<GlobalContactsList>
<GlobaleCashVariables>
<PushNotificationManager>
<LiquidEventProvider>
<LightningEventProvider>
{/* <Suspense
<GlobalContextProvider>
<AppStatusProvider>
<GlobalThemeProvider>
<GlobaleCashVariables>
<GLobalNodeContextProider>
<GlobalConbinedTxContextProvider>
<GlobalAppDataProvider>
<POSTransactionsProvider>
<WebViewProvider>
<GlobalContactsList>
<PushNotificationManager>
<LiquidEventProvider>
<LightningEventProvider>
{/* <Suspense
fallback={<FullLoadingScreen text={'Loading Page'} />}> */}
<ResetStack />
{/* </Suspense> */}
</LightningEventProvider>
</LiquidEventProvider>
</PushNotificationManager>
</GlobaleCashVariables>
</GlobalContactsList>
</WebViewProvider>
</POSTransactionsProvider>
</GlobalAppDataProvider>
{/* <BreezTest /> */}
</GlobalContextProvider>
</GLobalNodeContextProider>
</GlobalThemeProvider>
</AppStatusProvider>
<ResetStack />
{/* </Suspense> */}
</LightningEventProvider>
</LiquidEventProvider>
</PushNotificationManager>
</GlobalContactsList>
</WebViewProvider>
</POSTransactionsProvider>
</GlobalAppDataProvider>
{/* <BreezTest /> */}
</GlobalConbinedTxContextProvider>
</GLobalNodeContextProider>
</GlobaleCashVariables>
</GlobalThemeProvider>
</AppStatusProvider>
</GlobalContextProvider>
</KeysContextProvider>
</GestureHandlerRootView>
);
@@ -210,8 +210,8 @@ export default function GiftCardPage() {
/>
) : (
<FlatList
initialNumToRender={10}
maxToRenderPerBatch={10}
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={3}
removeClippedSubviews={true}
data={filteredGiftCards}
@@ -2,12 +2,11 @@ import {StyleSheet, View} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {ThemeText} from '../../../../functions/CustomElements';
import {useGlobaleCash} from '../../../../../context-store/eCash';
import GetThemeColors from '../../../../hooks/themeColors';
import FullLoadingScreen from '../../../../functions/CustomElements/loadingScreen';
import {useNodeContext} from '../../../../../context-store/nodeContext';
import writeAndShareFileToFilesystem from '../../../../functions/writeFileToFilesystem';
import SwipeButtonNew from '../../../../functions/CustomElements/sliderButton';
import {useGlobalTxContextProvider} from '../../../../../context-store/combinedTransactionsContext';
export default function ConfirmExportPayments({
startExport,
@@ -15,14 +14,9 @@ export default function ConfirmExportPayments({
darkModeType,
}) {
const navigate = useNavigation();
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {ecashWalletInformation} = useGlobaleCash();
const {combinedTransactions} = useGlobalTxContextProvider();
const {backgroundOffset, backgroundColor} = GetThemeColors();
const ecashTransactions = ecashWalletInformation.transactions;
const totalPayments =
nodeInformation.transactions.length +
liquidNodeInformation.transactions.length +
ecashTransactions.length;
const totalPayments = combinedTransactions.length;
const [txNumber, setTxNumber] = useState(0);
@@ -30,9 +24,6 @@ export default function ConfirmExportPayments({
async function generateCSV() {
if (!startExport) return;
try {
const lNdata = nodeInformation.transactions;
const liquidData = liquidNodeInformation.transactions;
const ecashData = ecashTransactions;
const headers = [
[
'Payment Type',
@@ -44,7 +35,7 @@ export default function ConfirmExportPayments({
],
];
const conjoinedTxList = [...liquidData, ...lNdata, ...ecashData];
const conjoinedTxList = combinedTransactions;
let formatedData = [];
for (let index = 0; index < conjoinedTxList.length; index++) {
@@ -71,7 +62,9 @@ export default function ConfirmExportPayments({
: !!tx.timestamp
? tx.feesSat
: tx.feeMsat / 1000,
).toLocaleString(),
)
.toLocaleString()
.replace(/,/g, ' '),
Math.round(
tx.type === 'ecash'
? tx.amount * (tx.paymentType === 'sent' ? -1 : 1)
@@ -5,28 +5,31 @@ import {useGlobalContextProvider} from '../../../../context-store/context';
import {GlobalThemeView, ThemeText} from '../../../functions/CustomElements';
import CustomFlatList from './homeLightning/cusomFlatlist/CustomFlatList';
import getFormattedHomepageTxs from '../../../functions/combinedTransactions';
import NavBar from './navBar';
import {NavBar} from './navBar';
import {useNavigation} from '@react-navigation/native';
import {useUpdateHomepageTransactions} from '../../../hooks/updateHomepageTransactions';
import {useGlobaleCash} from '../../../../context-store/eCash';
import {useEffect, useMemo} from 'react';
import {useTranslation} from 'react-i18next';
import {useNodeContext} from '../../../../context-store/nodeContext';
import {useAppStatus} from '../../../../context-store/appStatus';
export default function HomeLightning({tabNavigation}) {
import {useGlobalThemeContext} from '../../../../context-store/theme';
import {useGlobalTxContextProvider} from '../../../../context-store/combinedTransactionsContext';
export default function HomeLightning() {
console.log('HOME LIGHTNING PAGE');
const {theme, darkModeType, toggleTheme} = useGlobalThemeContext();
const {masterInfoObject} = useGlobalContextProvider();
const {toggleDidGetToHomepage, didGetToHomepage} = useAppStatus();
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {ecashWalletInformation} = useGlobaleCash();
const {toggleDidGetToHomepage, isConnectedToTheInternet} = useAppStatus();
const {combinedTransactions} = useGlobalTxContextProvider();
const navigate = useNavigation();
const shouldUpdateTransactions = useUpdateHomepageTransactions();
const currentTime = useUpdateHomepageTransactions();
const {t} = useTranslation();
console.log(currentTime);
const masterFailedTransactions = masterInfoObject.failedTransactions;
const enabledEcash = masterInfoObject.enabledEcash;
const homepageTxPreferance = masterInfoObject.homepageTxPreferance;
const ecashTransactions = ecashWalletInformation.transactions;
const userBalanceDenomination = masterInfoObject.userBalanceDenomination;
useEffect(() => {
toggleDidGetToHomepage(true);
@@ -34,12 +37,11 @@ export default function HomeLightning({tabNavigation}) {
const flatListData = useMemo(() => {
return getFormattedHomepageTxs({
nodeInformation,
liquidNodeInformation,
combinedTransactions,
currentTime,
homepageTxPreferance,
navigate,
frompage: 'home',
ecashTransactions,
viewAllTxText: t('wallet.see_all_txs'),
noTransactionHistoryText: t('wallet.no_transaction_history'),
todayText: t('constants.today'),
@@ -48,16 +50,20 @@ export default function HomeLightning({tabNavigation}) {
monthText: t('constants.month'),
yearText: t('constants.year'),
agoText: t('transactionLabelText.ago'),
theme,
darkModeType,
userBalanceDenomination,
});
}, [
ecashTransactions,
nodeInformation,
liquidNodeInformation,
masterFailedTransactions,
enabledEcash,
homepageTxPreferance,
shouldUpdateTransactions,
navigate,
combinedTransactions,
currentTime,
theme,
darkModeType,
userBalanceDenomination,
]);
return (
@@ -66,22 +72,20 @@ export default function HomeLightning({tabNavigation}) {
style={{overflow: 'hidden', flex: 1}}
data={flatListData}
renderItem={({item}) => item}
HeaderComponent={<NavBar />}
HeaderComponent={<NavBar theme={theme} toggleTheme={toggleTheme} />}
StickyElementComponent={
<GlobalThemeView styles={style.balanceContainer}>
<ThemeText
content={t('constants.total_balance')}
styles={{
textTransform: 'uppercase',
marginTop: nodeInformation.userBalance === 0 ? 20 : 0,
}}
/>
<UserSatAmount
nodeInformation={nodeInformation}
liquidNodeInformation={liquidNodeInformation}
ecashWalletInformation={ecashWalletInformation}
isConnectedToTheInternet={isConnectedToTheInternet}
theme={theme}
darkModeType={darkModeType}
/>
{/* {nodeInformation.userBalance != 0 && <LiquidityIndicator />} */}
</GlobalThemeView>
}
TopListElementComponent={
@@ -89,7 +93,11 @@ export default function HomeLightning({tabNavigation}) {
style={{
alignItems: 'center',
}}>
<SendRecieveBTNs tabNavigation={tabNavigation} />
<SendRecieveBTNs
theme={theme}
darkModeType={darkModeType}
isConnectedToTheInternet={isConnectedToTheInternet}
/>
</View>
}
/>
@@ -3,18 +3,27 @@ import {CENTER, COLORS, ICONS} from '../../../../constants';
import {useNavigation} from '@react-navigation/native';
import {useTranslation} from 'react-i18next';
import ThemeImage from '../../../../functions/CustomElements/themeImage';
import {useMemo} from 'react';
import {memo, useCallback, useMemo} from 'react';
import CustomSendAndRequsetBTN from '../../../../functions/CustomElements/sendRequsetCircleBTN';
import {useGlobalThemeContext} from '../../../../../context-store/theme';
import {useAppStatus} from '../../../../../context-store/appStatus';
export function SendRecieveBTNs() {
export const SendRecieveBTNs = memo(function SendRecieveBTNs({
theme,
darkModeType,
isConnectedToTheInternet,
}) {
console.log('SENd and receiv btns');
const navigate = useNavigation();
const {isConnectedToTheInternet} = useAppStatus();
const {theme, darkModeType} = useGlobalThemeContext();
const {t} = useTranslation();
const handleSettingsCheck = useCallback(() => {
try {
if (!isConnectedToTheInternet) throw new Error('Not Connected To Node');
return true;
} catch (err) {
return false;
}
}, [isConnectedToTheInternet]);
const buttonElements = useMemo(
() =>
['send', 'camera', 'receive'].map(btnType => {
@@ -22,8 +31,8 @@ export function SendRecieveBTNs() {
if (isArrow) {
return CustomSendAndRequsetBTN({
btnType,
btnFunction: async () => {
const areSettingsSet = await handleSettingsCheck();
btnFunction: () => {
const areSettingsSet = handleSettingsCheck();
if (!areSettingsSet) {
navigate.navigate('ErrorScreen', {
errorMessage: t('constants.internetError'),
@@ -53,17 +62,15 @@ export function SendRecieveBTNs() {
<TouchableOpacity
key={btnType}
onPress={() => {
(async () => {
const areSettingsSet = await handleSettingsCheck();
if (!areSettingsSet) {
navigate.navigate('ErrorScreen', {
errorMessage: t('constants.internetError'),
});
return;
}
const areSettingsSet = handleSettingsCheck();
if (!areSettingsSet) {
navigate.navigate('ErrorScreen', {
errorMessage: t('constants.internetError'),
});
return;
}
navigate.navigate('SendBTC');
})();
navigate.navigate('SendBTC');
}}>
<View
style={{
@@ -83,25 +90,17 @@ export function SendRecieveBTNs() {
</TouchableOpacity>
);
}),
[isConnectedToTheInternet, theme, darkModeType],
[
isConnectedToTheInternet,
theme,
darkModeType,
handleSettingsCheck,
navigate,
],
);
return <View style={styles.container}>{buttonElements}</View>;
async function handleSettingsCheck() {
try {
if (!isConnectedToTheInternet) throw new Error('Not Connected To Node');
return new Promise(resolve => {
resolve(true);
});
} catch (err) {
return new Promise(resolve => {
resolve(false);
});
}
}
}
});
const styles = StyleSheet.create({
container: {
@@ -2,22 +2,24 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native';
import {COLORS, SIZES} from '../../../../constants';
import {useGlobalContextProvider} from '../../../../../context-store/context';
import FormattedSatText from '../../../../functions/CustomElements/satTextDisplay';
import {useRef, useState} from 'react';
import {memo, useRef, useState} from 'react';
import handleDBStateChange from '../../../../functions/handleDBStateChange';
import Icon from '../../../../functions/CustomElements/Icon';
import {useNavigation} from '@react-navigation/native';
import {useAppStatus} from '../../../../../context-store/appStatus';
import {useGlobalThemeContext} from '../../../../../context-store/theme';
import {useNodeContext} from '../../../../../context-store/nodeContext';
import {useGlobaleCash} from '../../../../../context-store/eCash';
export function UserSatAmount({
nodeInformation,
liquidNodeInformation,
ecashWalletInformation,
export const UserSatAmount = memo(function UserSatAmount({
isConnectedToTheInternet,
theme,
darkModeType,
}) {
console.log('User sat amount container');
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {ecashWalletInformation} = useGlobaleCash();
const {masterInfoObject, toggleMasterInfoObject, setMasterInfoObject} =
useGlobalContextProvider();
const {isConnectedToTheInternet} = useAppStatus();
const {darkModeType, theme} = useGlobalThemeContext();
const eCashBalance = ecashWalletInformation.balance;
const saveTimeoutRef = useRef(null);
const navigate = useNavigation();
@@ -102,7 +104,7 @@ export function UserSatAmount({
)}
</TouchableOpacity>
);
}
});
const styles = StyleSheet.create({
balanceContainer: {
@@ -3,13 +3,11 @@ import {CENTER, ICONS} from '../../../constants';
import {useNavigation} from '@react-navigation/native';
import {WINDOWWIDTH} from '../../../constants/theme';
import ThemeImage from '../../../functions/CustomElements/themeImage';
import {useGlobalThemeContext} from '../../../../context-store/theme';
import {memo} from 'react';
export default function NavBar() {
export const NavBar = memo(function NavBar({theme, toggleTheme}) {
console.log('NAV BAR PAGE');
const navigate = useNavigation();
const {theme, toggleTheme} = useGlobalThemeContext();
return (
<View style={[styles.topBar]}>
@@ -39,7 +37,7 @@ export default function NavBar() {
</TouchableOpacity>
</View>
);
}
});
const styles = StyleSheet.create({
topBar: {
width: WINDOWWIDTH,
@@ -4,34 +4,65 @@ import {useNavigation} from '@react-navigation/native';
import {GlobalThemeView, ThemeText} from '../../../../functions/CustomElements';
import getFormattedHomepageTxs from '../../../../functions/combinedTransactions';
import FormattedSatText from '../../../../functions/CustomElements/satTextDisplay';
import {useGlobaleCash} from '../../../../../context-store/eCash';
import {useTranslation} from 'react-i18next';
import CustomButton from '../../../../functions/CustomElements/button';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {ANDROIDSAFEAREA, CENTER} from '../../../../constants/styles';
import {useWebView} from '../../../../../context-store/webViewContext';
import CustomSettingsTopBar from '../../../../functions/CustomElements/settingsTopBar';
import {useNodeContext} from '../../../../../context-store/nodeContext';
import {useAppStatus} from '../../../../../context-store/appStatus';
import {INSET_WINDOW_WIDTH} from '../../../../constants/theme';
import GetThemeColors from '../../../../hooks/themeColors';
import {useUpdateHomepageTransactions} from '../../../../hooks/updateHomepageTransactions';
import {useGlobalContextProvider} from '../../../../../context-store/context';
import {useGlobalThemeContext} from '../../../../../context-store/theme';
import {useEffect, useState} from 'react';
import FullLoadingScreen from '../../../../functions/CustomElements/loadingScreen';
export default function LiquidWallet() {
const {isConnectedToTheInternet} = useAppStatus();
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {ecashWalletInformation} = useGlobaleCash();
const {liquidNodeInformation} = useNodeContext();
const {theme, darkModeType} = useGlobalThemeContext();
const {masterInfoObject} = useGlobalContextProvider();
const navigate = useNavigation();
const {t} = useTranslation();
const insets = useSafeAreaInsets();
const {autoChannelRebalanceIDs} = useWebView();
const {backgroundColor} = GetThemeColors();
const ecashTransactions = ecashWalletInformation.transactions;
const [txs, setTxs] = useState([]);
const currentTime = useUpdateHomepageTransactions();
const userBalanceDenomination = masterInfoObject.userBalanceDenomination;
const bottomPadding = Platform.select({
ios: insets.bottom,
android: ANDROIDSAFEAREA,
});
useEffect(() => {
const formattedTxs = getFormattedHomepageTxs({
currentTime,
liquidNodeInformation,
navigate,
isBankPage: true,
noTransactionHistoryText: t('wallet.no_transaction_history'),
todayText: t('constants.today'),
yesterdayText: t('constants.yesterday'),
dayText: t('constants.day'),
monthText: t('constants.month'),
yearText: t('constants.year'),
agoText: t('transactionLabelText.ago'),
theme,
darkModeType,
userBalanceDenomination,
});
setTxs(formattedTxs);
}, [
currentTime,
liquidNodeInformation,
navigate,
theme,
darkModeType,
userBalanceDenomination,
]);
return (
<GlobalThemeView useStandardWidth={true} styles={styles.container}>
<CustomSettingsTopBar
@@ -52,44 +83,32 @@ export default function LiquidWallet() {
navigate.navigate('LiquidSettingsPage');
}}
/>
<FlatList
style={{flex: 1, width: '100%'}}
showsVerticalScrollIndicator={false}
stickyHeaderIndices={[1]} // Selects only the second item
ListHeaderComponent={<View style={{marginTop: 25}} />}
data={[
<View
style={{...styles.stickyHeader, backgroundColor: backgroundColor}}>
<ThemeText content={'Balance'} styles={styles.amountText} />
<FormattedSatText
styles={{...styles.valueText}}
balance={liquidNodeInformation.userBalance}
/>
</View>, // Dummy item to shift indices
...getFormattedHomepageTxs({
nodeInformation,
liquidNodeInformation,
navigate,
isBankPage: true,
ecashTransactions,
noTransactionHistoryText: t('wallet.no_transaction_history'),
todayText: t('constants.today'),
yesterdayText: t('constants.yesterday'),
dayText: t('constants.day'),
monthText: t('constants.month'),
yearText: t('constants.year'),
agoText: t('transactionLabelText.ago'),
autoChannelRebalanceIDs,
}),
]}
renderItem={
({item, index}) => item // Skip dummy item
}
ListFooterComponent={
<View style={{width: '100%', height: bottomPadding + 60}} />
}
/>
<View
key={'balance'}
style={{
...styles.stickyHeader,
backgroundColor: backgroundColor,
}}>
<ThemeText content={'Balance'} styles={styles.amountText} />
<FormattedSatText
styles={{...styles.valueText}}
balance={liquidNodeInformation.userBalance}
/>
</View>
{!txs.length ? (
<FullLoadingScreen />
) : (
<FlatList
style={{flex: 1, width: '100%'}}
contentContainerStyle={{paddingBottom: bottomPadding + 60}}
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={3}
showsVerticalScrollIndicator={false}
data={txs}
renderItem={({item, index}) => item}
/>
)}
<CustomButton
buttonStyles={{
+1 -1
View File
@@ -26,7 +26,7 @@ const VALID_URL_REGEX =
const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const VALID_USERNAME_REGEX = /^(?=.*\p{L})[\p{L}\p{N}_]+$/u;
const BITCOIN_SATS_ICON = '';
const BITCOIN_SATS_ICON = '\u20BF';
const HIDDEN_BALANCE_TEXT = `* * * * *`;
const ECASH_TX_STORAGE_KEY = 'CASHU_TRANSACTIONS';
+24 -88
View File
@@ -3,7 +3,6 @@ import {
BLITZ_DEFAULT_PAYMENT_DESCRIPTION,
CENTER,
COLORS,
FONT,
HIDDEN_BALANCE_TEXT,
ICONS,
SIZES,
@@ -12,19 +11,17 @@ import {ThemeText} from './CustomElements';
import FormattedSatText from './CustomElements/satTextDisplay';
import {useTranslation} from 'react-i18next';
import Icon from './CustomElements/Icon';
import {useGlobalThemeContext} from '../../context-store/theme';
import {useGlobalContextProvider} from '../../context-store/context';
import {useMemo} from 'react';
import MinHeap from './minHeap';
import {memo, useMemo} from 'react';
import {mergeArrays} from './mergeArrays';
export default function getFormattedHomepageTxs({
nodeInformation,
combinedTransactions,
currentTime,
liquidNodeInformation,
homepageTxPreferance = 25,
navigate,
isBankPage,
frompage,
ecashTransactions,
viewAllTxText,
noTransactionHistoryText,
todayText,
@@ -33,23 +30,18 @@ export default function getFormattedHomepageTxs({
monthText,
yearText,
agoText,
theme,
darkModeType,
userBalanceDenomination,
}) {
console.log('re-rendering transactions');
const arr1 = [...nodeInformation.transactions];
const n1 = nodeInformation.transactions.length;
const arr2 = [...liquidNodeInformation.transactions];
const n2 = liquidNodeInformation.transactions.length;
const arr3 = [...ecashTransactions];
const n3 = ecashTransactions.length;
const arr2 = liquidNodeInformation?.transactions;
const n2 = liquidNodeInformation?.transactions?.length;
const conjoinedTxList = isBankPage
? mergeArrays({arr2, n2})
: mergeArrays({arr1, arr2, n1, n2, arr3, n3});
: combinedTransactions;
console.log('re-rendering transactions');
if (conjoinedTxList.length === 0) {
return [
@@ -64,12 +56,11 @@ export default function getFormattedHomepageTxs({
let formattedTxs = [];
let currentGroupedDate = '';
let transactionIndex = 0;
const currentTime = new Date();
while (
formattedTxs.length <
(isBankPage
? arr2.length
? n2
: frompage === 'viewAllTx'
? conjoinedTxList.length
: homepageTxPreferance) &&
@@ -104,7 +95,6 @@ export default function getFormattedHomepageTxs({
tx={currentTransaction}
currentTime={currentTime}
navigate={navigate}
nodeInformation={nodeInformation}
isLiquidPayment={isLiquidPayment}
isLightningPayment={isLightningPayment}
isEcashPayment={isEcashPayment}
@@ -113,6 +103,9 @@ export default function getFormattedHomepageTxs({
id={uniuqeIDFromTx}
isBankPage={isBankPage}
frompage={frompage}
theme={theme}
darkModeType={darkModeType}
userBalanceDenomination={userBalanceDenomination}
/>
);
@@ -175,6 +168,7 @@ export default function getFormattedHomepageTxs({
if (!isBankPage && frompage != 'viewAllTx')
formattedTxs.push(
<TouchableOpacity
key={'view_all_tx_btn'}
style={{marginBottom: 10, ...CENTER}}
onPress={() => {
navigate.navigate('ViewAllTxPage');
@@ -186,67 +180,8 @@ export default function getFormattedHomepageTxs({
return formattedTxs;
}
}
function mergeArrays({
arr1 = [],
arr2 = [],
n1 = 0,
n2 = 0,
arr3 = [],
n3 = 0,
}) {
console.log('re-ordering transaactins');
let mergedArray = [];
const minHeap = new MinHeap();
// Function to push elements into the heap
const pushToHeap = (arr, index, identifier) => {
if (arr[index]) {
const time =
arr[index].paymentTime * 1000 ||
arr[index].timestamp * 1000 ||
arr[index].time ||
Infinity;
const element = {
...arr[index],
[identifier === 'arr1'
? 'usesLightningNode'
: identifier === 'arr2'
? 'usesLiquidNode'
: 'usesEcash']: true,
source: identifier,
index,
time,
};
minHeap.add(element);
}
};
// Add first elements of each array to the heap
if (n1 > 0) pushToHeap(arr1, 0, 'arr1');
if (n2 > 0) pushToHeap(arr2, 0, 'arr2');
if (n3 > 0) pushToHeap(arr3, 0, 'arr3');
// Process heap
while (!minHeap.isEmpty()) {
const minElement = minHeap.poll();
mergedArray.push(minElement);
// Push next element from the same source array
let {source, index} = minElement;
if (source === 'arr1' && index + 1 < n1)
pushToHeap(arr1, index + 1, 'arr1');
if (source === 'arr2' && index + 1 < n2)
pushToHeap(arr2, index + 1, 'arr2');
if (source === 'arr3' && index + 1 < n3)
pushToHeap(arr3, index + 1, 'arr3');
}
return mergedArray;
}
export function UserTransaction({
export const UserTransaction = memo(function UserTransaction({
tx: transaction,
currentTime,
paymentDate,
@@ -258,9 +193,10 @@ export function UserTransaction({
navigate,
isBankPage,
frompage,
theme,
darkModeType,
userBalanceDenomination,
}) {
const {theme, darkModeType} = useGlobalThemeContext();
const {masterInfoObject} = useGlobalContextProvider();
const {t} = useTranslation();
const endDate = currentTime;
@@ -391,7 +327,7 @@ export function UserTransaction({
content={
isFailedPayment
? t('transactionLabelText.failed')
: masterInfoObject.userBalanceDenomination === 'hidden'
: userBalanceDenomination === 'hidden'
? `${HIDDEN_BALANCE_TEXT}`
: isDefaultDescription || !paymentDescription
? transaction?.paymentType !== 'receive' &&
@@ -456,7 +392,7 @@ export function UserTransaction({
}
containerStyles={{marginLeft: 'auto', marginBottom: 'auto'}}
frontText={
masterInfoObject.userBalanceDenomination !== 'hidden'
userBalanceDenomination !== 'hidden'
? transaction.paymentType === 'closed_channel'
? ''
: isLiquidPayment
@@ -489,7 +425,7 @@ export function UserTransaction({
</View>
</TouchableOpacity>
);
}
});
export function dateBanner(bannerText) {
return (
+1 -1
View File
@@ -44,7 +44,7 @@ const startLiquidUpdateInterval = (toggleLiquidNodeInformation, runCount) => {
};
// Run 2 times with 30 second interval
return runIntervalTimes(updateNodeInfo, 1000 * 30, runCount || 2);
return runIntervalTimes(updateNodeInfo, 1000 * 30, runCount);
};
export default startLiquidUpdateInterval;
+61
View File
@@ -0,0 +1,61 @@
import MaxHeap from './minHeap';
export function mergeArrays({
arr1 = [],
arr2 = [],
n1 = 0,
n2 = 0,
arr3 = [],
n3 = 0,
}) {
// console.log('re-ordering transaactins');
let mergedArray = [];
const minHeap = new MaxHeap();
// Function to push elements into the heap
const pushToHeap = (arr, index, identifier) => {
if (arr[index]) {
const time =
arr[index].paymentTime * 1000 ||
arr[index].timestamp * 1000 ||
arr[index].time ||
Infinity;
const element = {
...arr[index],
[identifier === 'arr1'
? 'usesLightningNode'
: identifier === 'arr2'
? 'usesLiquidNode'
: 'usesEcash']: true,
source: identifier,
index,
time,
};
minHeap.add(element);
}
};
// Add first elements of each array to the heap
if (n1 > 0) pushToHeap(arr1, 0, 'arr1');
if (n2 > 0) pushToHeap(arr2, 0, 'arr2');
if (n3 > 0) pushToHeap(arr3, 0, 'arr3');
// Process heap
while (!minHeap.isEmpty()) {
const minElement = minHeap.poll();
mergedArray.push(minElement);
// Push next element from the same source array
let {source, index} = minElement;
if (source === 'arr1' && index + 1 < n1)
pushToHeap(arr1, index + 1, 'arr1');
if (source === 'arr2' && index + 1 < n2)
pushToHeap(arr2, index + 1, 'arr2');
if (source === 'arr3' && index + 1 < n3)
pushToHeap(arr3, index + 1, 'arr3');
}
return mergedArray;
}
+24 -9
View File
@@ -1,23 +1,38 @@
import {useFocusEffect} from '@react-navigation/native';
import {useState, useEffect, useRef, useCallback} from 'react';
import {useState, useRef, useCallback, useMemo} from 'react';
export function useUpdateHomepageTransactions() {
const [updateTransaction, setUpdateTransaction] = useState(0);
const [minuteTick, setMinuteTick] = useState(Math.floor(Date.now() / 60000));
const intervalRef = useRef(null);
useFocusEffect(
useCallback(() => {
console.log('Starting homepage interval');
const homepageUpdateInterval = setInterval(() => {
setUpdateTransaction(prev => (prev = prev + 1));
console.log('Starting stable time interval');
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
setMinuteTick(Math.floor(Date.now() / 60000));
intervalRef.current = setInterval(() => {
setMinuteTick(Math.floor(Date.now() / 60000));
}, 60000);
setUpdateTransaction(prev => (prev = prev + 1));
return () => {
console.log('clearing homepage interval');
clearInterval(homepageUpdateInterval);
console.log('Clearing stable time interval');
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, []),
);
return updateTransaction;
const stableTime = useMemo(
() => new Date(minuteTick * 60000).getTime(),
[minuteTick],
);
return stableTime;
}
+20 -20
View File
@@ -11,7 +11,7 @@ import {
import {Back_BTN} from '../../../components/login';
import {retrieveData, storeData} from '../../../functions';
import {CENTER, COLORS, FONT, SIZES} from '../../../constants';
import {useEffect, useRef, useState} from 'react';
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import isValidMnemonic from '../../../functions/isValidMnemonic';
import {useTranslation} from 'react-i18next';
import {GlobalThemeView, ThemeText} from '../../../functions/CustomElements';
@@ -56,24 +56,27 @@ export default function RestoreWallet({
key11: '',
key12: '',
});
function handleInputElement(text, keyNumber) {
const handleInputElement = useCallback((text, keyNumber) => {
setInputedKey(prev => ({...prev, [`key${keyNumber}`]: text}));
}
}, []);
function handleFocus(keyNumber) {
const handleFocus = useCallback(keyNumber => {
setCurrentFocused(keyNumber); // Update the current focused key
}
function handleSubmit(keyNumber) {
if (keyNumber < 12) {
const nextKey = keyNumber + 1;
keyRefs.current[nextKey]?.focus(); // Focus the next input
} else {
keyRefs.current[12]?.blur(); // Blur the last input
}
}
}, []);
function createInputKeys() {
const handleSubmit = useCallback(
keyNumber => {
if (keyNumber < 12) {
const nextKey = keyNumber + 1;
keyRefs.current[nextKey]?.focus(); // Focus the next input
} else {
keyRefs.current[12]?.blur(); // Blur the last input
}
},
[keyRefs],
);
const inputKeys = useMemo(() => {
let keyRows = [];
let keyItem = [];
NUMARRAY.forEach(item => {
@@ -82,7 +85,6 @@ export default function RestoreWallet({
key={item}
style={{
...styles.seedItem,
backgroundColor: theme
? COLORS.darkModeBackgroundOffset
: COLORS.darkModeText,
@@ -112,9 +114,7 @@ export default function RestoreWallet({
}
});
return keyRows;
}
const keyElements = createInputKeys();
}, [handleSubmit, theme, inputedKey, keyRefs]);
useEffect(() => {
const keyboardDidHideListener = Keyboard.addListener(
@@ -168,7 +168,7 @@ export default function RestoreWallet({
showsVerticalScrollIndicator={false}
style={styles.contentContainer}
contentContainerStyle={{paddingBottom: 10}}>
{keyElements}
{inputKeys}
</ScrollView>
{params && !currentFocused && (
<CustomButton
+6 -3
View File
@@ -432,7 +432,7 @@ export default function ConnectingToNodeLoadingScreen({
'CHEKCING RETRY LOGIC',
);
if (didRestoreWallet && !payments.length) {
if (didRestoreWallet) {
console.log('RETRYING LIQUID INFORMATION, LOADING....');
await new Promise(resolve => setTimeout(resolve, 10000));
console.log('FINISHED WAITING');
@@ -458,12 +458,14 @@ export default function ConnectingToNodeLoadingScreen({
};
}
toggleLiquidNodeInformation(liquidNodeObject);
toggleLiquidNodeInformation({
...liquidNodeObject,
didConnectToNode: true,
});
return liquidNodeObject;
} catch (err) {
console.log(err, 'LIQUID INFORMATION ERROR');
return new Promise(resolve => {
resolve(false);
});
@@ -489,6 +491,7 @@ export default function ConnectingToNodeLoadingScreen({
balance: balance || 0,
transactions: transactions,
proofs: storedProofs,
didConnectToNode: true,
};
toggleEcashWalletInformation(ecashWalletData);
toggleMintList(mintList);
+60 -90
View File
@@ -1,101 +1,90 @@
import {useNavigation} from '@react-navigation/native';
import {
FlatList,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {CENTER, COLORS, ICONS, SIZES} from '../../constants';
import {FlatList, Platform, View} from 'react-native';
import {ICONS} from '../../constants';
import {ANDROIDSAFEAREA} from '../../constants/styles';
import {GlobalThemeView} from '../../functions/CustomElements';
import {WINDOWWIDTH} from '../../constants/theme';
import getFormattedHomepageTxs from '../../functions/combinedTransactions';
import {useGlobaleCash} from '../../../context-store/eCash';
import ThemeImage from '../../functions/CustomElements/themeImage';
import {useTranslation} from 'react-i18next';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {useGlobalThemeContext} from '../../../context-store/theme';
import {useNodeContext} from '../../../context-store/nodeContext';
import useHandleBackPressNew from '../../hooks/useHandleBackPressNew';
import CustomSettingsTopBar from '../../functions/CustomElements/settingsTopBar';
import {useUpdateHomepageTransactions} from '../../hooks/updateHomepageTransactions';
import {useGlobalContextProvider} from '../../../context-store/context';
import {useGlobalTxContextProvider} from '../../../context-store/combinedTransactionsContext';
import {useEffect, useState} from 'react';
import FullLoadingScreen from '../../functions/CustomElements/loadingScreen';
export default function ViewAllTxPage() {
const navigate = useNavigation();
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {combinedTransactions} = useGlobalTxContextProvider();
const {masterInfoObject} = useGlobalContextProvider();
const {theme, darkModeType} = useGlobalThemeContext();
const {ecashWalletInformation} = useGlobaleCash();
const ecashTransactions = ecashWalletInformation.transactions;
const [txs, setTxs] = useState([]);
const currentTime = useUpdateHomepageTransactions();
const insets = useSafeAreaInsets();
const {t} = useTranslation();
useHandleBackPressNew();
const userBalanceDenomination = masterInfoObject.userBalanceDenomination;
const bottomPadding = Platform.select({
ios: insets.bottom,
android: ANDROIDSAFEAREA,
});
return (
<GlobalThemeView styles={{paddingBottom: 0}}>
<View style={styles.globalContainer}>
<View style={styles.topBar}>
<TouchableOpacity
style={{position: 'absolute', top: 0, left: 0}}
onPress={() => {
navigate.goBack();
}}>
<ThemeImage
darkModeIcon={ICONS.smallArrowLeft}
lightModeIcon={ICONS.smallArrowLeft}
lightsOutIcon={ICONS.arrow_small_left_white}
/>
</TouchableOpacity>
<Text
style={[
styles.mainHeader,
{
color: theme ? COLORS.darkModeText : COLORS.lightModeText,
},
]}>
Transactions
</Text>
<TouchableOpacity
style={{position: 'absolute', top: 0, right: 0}}
onPress={() => {
navigate.navigate('CustomHalfModal', {
wantedContent: 'exportTransactions',
sliderHight: 0.5,
});
}}>
<ThemeImage
darkModeIcon={ICONS.share}
lightModeIcon={ICONS.share}
lightsOutIcon={ICONS.shareWhite}
/>
</TouchableOpacity>
</View>
useEffect(() => {
const formattedTxs = getFormattedHomepageTxs({
currentTime,
combinedTransactions,
navigate,
isBankPage: false,
frompage: 'viewAllTx',
noTransactionHistoryText: t('wallet.no_transaction_history'),
todayText: t('constants.today'),
yesterdayText: t('constants.yesterday'),
dayText: t('constants.day'),
monthText: t('constants.month'),
yearText: t('constants.year'),
agoText: t('transactionLabelText.ago'),
theme,
darkModeType,
userBalanceDenomination,
});
setTxs(formattedTxs);
}, [
currentTime,
combinedTransactions,
navigate,
theme,
darkModeType,
userBalanceDenomination,
]);
return (
<GlobalThemeView useStandardWidth={true} styles={{paddingBottom: 0}}>
<CustomSettingsTopBar
showLeftImage={true}
leftImageBlue={ICONS.share}
LeftImageDarkMode={ICONS.shareWhite}
label={'Transactions'}
leftImageFunction={() => {
navigate.navigate('CustomHalfModal', {
wantedContent: 'exportTransactions',
sliderHight: 0.5,
});
}}
/>
{!txs.length ? (
<FullLoadingScreen />
) : (
<FlatList
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={3}
style={{flex: 1, width: '100%'}}
showsVerticalScrollIndicator={false}
data={getFormattedHomepageTxs({
nodeInformation,
liquidNodeInformation,
navigate,
isBankPage: false,
frompage: 'viewAllTx',
ecashTransactions,
noTransactionHistoryText: t('wallet.no_transaction_history'),
todayText: t('constants.today'),
yesterdayText: t('constants.yesterday'),
dayText: t('constants.day'),
monthText: t('constants.month'),
yearText: t('constants.year'),
agoText: t('transactionLabelText.ago'),
})}
data={txs}
renderItem={({item}) => item}
ListFooterComponent={
<View
@@ -106,26 +95,7 @@ export default function ViewAllTxPage() {
/>
}
/>
</View>
)}
</GlobalThemeView>
);
}
const styles = StyleSheet.create({
globalContainer: {
flex: 1,
width: WINDOWWIDTH,
...CENTER,
},
topBar: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 10,
},
mainHeader: {
fontSize: SIZES.xLarge,
...CENTER,
},
});
@@ -0,0 +1,97 @@
import {
createContext,
useState,
useContext,
useEffect,
useMemo,
useCallback,
useRef,
} from 'react';
import {useGlobaleCash} from './eCash';
import {useGlobalContextProvider} from './context';
import {mergeArrays} from '../app/functions/mergeArrays';
import {useNodeContext} from './nodeContext';
// Initiate context
const combinedTxContextManager = createContext(null);
const GlobalConbinedTxContextProvider = ({children}) => {
const {masterInfoObject} = useGlobalContextProvider();
const {nodeInformation, liquidNodeInformation} = useNodeContext();
const {ecashWalletInformation} = useGlobaleCash();
const [combinedTransactions, setCombinedTransactions] = useState([]);
const prevDependencies = useRef({arr1, arr2, arr3, n1, n2, n3});
const didConnectToLiquidNode = liquidNodeInformation.didConnectToNode;
const didConnectToLightningNode = nodeInformation.didConnectToNode;
const didConnectToEcashNode = ecashWalletInformation.didConnectToNode;
const enabledEcash = masterInfoObject.enabledEcash;
const enabledLightning =
masterInfoObject.liquidWalletSettings?.isLightningEnabled;
const shouldStartToMergeArrays = useMemo(() => {
return (
(didConnectToLightningNode || !enabledLightning) &&
didConnectToLiquidNode &&
(didConnectToEcashNode || !enabledEcash)
);
}, [
didConnectToEcashNode,
didConnectToLightningNode,
didConnectToLiquidNode,
enabledEcash,
enabledLightning,
]);
const arr1 = nodeInformation.transactions;
const arr2 = liquidNodeInformation.transactions;
const arr3 = ecashWalletInformation.transactions;
const n1 = nodeInformation.transactions.length;
const n2 = liquidNodeInformation.transactions.length;
const n3 = ecashWalletInformation.transactions.length;
useEffect(() => {
const hasChanged =
JSON.stringify(prevDependencies.current.arr1) !== JSON.stringify(arr1) ||
JSON.stringify(prevDependencies.current.arr2) !== JSON.stringify(arr2) ||
JSON.stringify(prevDependencies.current.arr3) !== JSON.stringify(arr3) ||
prevDependencies.current.n1 !== n1 ||
prevDependencies.current.n2 !== n2 ||
prevDependencies.current.n3 !== n3;
if (!hasChanged || !shouldStartToMergeArrays) return;
console.log('re-filtering transaactins');
const txs = mergeArrays({arr1, arr2, arr3, n1, n2, n3});
setCombinedTransactions(txs);
}, [arr1, arr2, arr3, n1, n2, n3, shouldStartToMergeArrays]);
const contextValue = useMemo(
() => ({
combinedTransactions,
}),
[combinedTransactions],
);
return (
<combinedTxContextManager.Provider value={contextValue}>
{children}
</combinedTxContextManager.Provider>
);
};
function useGlobalTxContextProvider() {
const context = useContext(combinedTxContextManager);
if (!context) {
throw new Error(
'useGlobalTxContextProvider must be used within a GlobalConbinedTxContextProvider',
);
}
return context;
}
export {
combinedTxContextManager,
GlobalConbinedTxContextProvider,
useGlobalTxContextProvider,
};
+26 -56
View File
@@ -12,19 +12,13 @@ import {
} from '../app/functions/messaging/encodingAndDecodingMessages';
import {useKeysContext} from './keys';
import {sumProofsValue} from '../app/functions/eCash/proofs';
import {receivePayment} from '@breeztech/react-native-breez-sdk';
import {MintQuoteState} from '@cashu/cashu-ts';
import {LIGHTNINGAMOUNTBUFFER} from '../app/constants/math';
import {useNodeContext} from './nodeContext';
import {useAppStatus} from './appStatus';
import {
checkMintQuote,
claimUnclaimedEcashQuotes,
formatEcashTx,
getMeltQuote,
hanleEcashQuoteStorage,
mintEcash,
payLnInvoiceFromEcash,
} from '../app/functions/eCash/wallet';
import {
getAllMints,
@@ -48,11 +42,10 @@ const GlobaleCash = createContext(null);
export const GlobaleCashVariables = ({children}) => {
const {contactsPrivateKey, publicKey} = useKeysContext();
const {didGetToHomepage} = useAppStatus();
const {nodeInformation} = useNodeContext();
const countersRef = useRef({});
const [globalEcashInformation, setGlobalEcashInformation] = useState([]);
const [ecashWalletInformation, setEcashWalletInformation] = useState({
didConnectToNode: null,
balance: 0,
transactions: [],
mintURL: '',
@@ -136,40 +129,6 @@ export const GlobaleCashVariables = ({children}) => {
: [];
}, [globalEcashInformation, publicKey]);
const drainEcashBalance = async () => {
try {
if (ecashWalletInformation.balance - 5 < 1) return;
const lightningInvoice = await receivePayment({
amountMsat: (ecashWalletInformation.balance - 5) * 1000,
description: 'Auto Channel Rebalance',
});
const meltQuote = await getMeltQuote(lightningInvoice.lnInvoice.bolt11);
if (!meltQuote) throw new Error('unable to create melt quote');
const didPay = await payLnInvoiceFromEcash({
quote: meltQuote.quote,
invoice: lightningInvoice.lnInvoice.bolt11,
proofsToUse: meltQuote.proofsToUse,
description: 'Auto Channel Rebalance',
});
console.log(didPay, 'pay response in drain ecash balance');
} catch (err) {
console.log(err, 'draining ecash balance error');
}
};
useEffect(() => {
if (!didGetToHomepage) return;
if (nodeInformation.userBalance === 0) return;
if (
nodeInformation.inboundLiquidityMsat / 1000 + LIGHTNINGAMOUNTBUFFER <
ecashWalletInformation?.balance
)
return;
drainEcashBalance();
}, [didGetToHomepage]);
useEffect(() => {
function listenForPayment(event) {
const receiveEcashQuote = event?.quote;
@@ -263,21 +222,32 @@ export const GlobaleCashVariables = ({children}) => {
claimUnclaimedEcashQuotes(); //if a receive ecash timeout clears before payment is receve this will try and claim the ecash quote on the next wallet load
}, [ecashWalletInformation.mintURL]);
const memoedValues = useMemo(() => {
return {
parsedEcashInformation,
globalEcashInformation,
toggleGLobalEcashInformation,
ecashWalletInformation,
toggleEcashWalletInformation,
usersMintList,
toggleMintList,
pendingNavigation,
setPendingNavigation,
};
}, [
parsedEcashInformation,
globalEcashInformation,
toggleGLobalEcashInformation,
ecashWalletInformation,
toggleEcashWalletInformation,
usersMintList,
toggleMintList,
pendingNavigation,
setPendingNavigation,
]);
return (
<GlobaleCash.Provider
value={{
parsedEcashInformation,
globalEcashInformation,
toggleGLobalEcashInformation,
ecashWalletInformation,
toggleEcashWalletInformation,
usersMintList,
toggleMintList,
pendingNavigation,
setPendingNavigation,
}}>
{children}
</GlobaleCash.Provider>
<GlobaleCash.Provider value={memoedValues}>{children}</GlobaleCash.Provider>
);
};
+3 -1
View File
@@ -24,7 +24,7 @@ const BLOCKED_PAYMENT_CODES = [
];
// Create a context for the WebView ref
export function LiquidEventProvider({children}) {
const {toggleLiquidNodeInformation} = useNodeContext();
const {toggleLiquidNodeInformation, liquidNodeInformation} = useNodeContext();
const {didGetToHomepage} = useAppStatus();
const intervalId = useRef(null);
const debounceTimer = useRef(null);
@@ -197,7 +197,9 @@ export function LiquidEventProvider({children}) {
useEffect(() => {
if (!didGetToHomepage) return;
if (liquidNodeInformation.userBalance) return;
console.log('Makeing sure user balance is loaded');
debouncedStartInterval(0);
}, [didGetToHomepage]);
+45
View File
@@ -6,11 +6,21 @@ import {
useMemo,
useCallback,
} from 'react';
import {LIGHTNINGAMOUNTBUFFER} from '../app/constants/math';
import {useGlobaleCash} from './eCash';
import {
getMeltQuote,
payLnInvoiceFromEcash,
} from '../app/functions/eCash/wallet';
import {receivePayment} from '@breeztech/react-native-breez-sdk';
import {useAppStatus} from './appStatus';
// Initiate context
const NodeContextManager = createContext(null);
const GLobalNodeContextProider = ({children}) => {
const {didGetToHomepage} = useAppStatus();
const {ecashWalletInformation} = useGlobaleCash();
const [nodeInformation, setNodeInformation] = useState({
didConnectToNode: null,
transactions: [],
@@ -22,6 +32,7 @@ const GLobalNodeContextProider = ({children}) => {
lsp: [],
});
const [liquidNodeInformation, setLiquidNodeInformation] = useState({
didConnectToNode: null,
transactions: [],
userBalance: 0,
});
@@ -33,6 +44,40 @@ const GLobalNodeContextProider = ({children}) => {
setLiquidNodeInformation(prev => ({...prev, ...newInfo}));
}, []);
useEffect(() => {
if (!didGetToHomepage) return;
if (nodeInformation.userBalance === 0) return;
if (
nodeInformation.inboundLiquidityMsat / 1000 + LIGHTNINGAMOUNTBUFFER <
ecashWalletInformation?.balance
)
return;
drainEcashBalance();
}, [didGetToHomepage]);
const drainEcashBalance = useCallback(async () => {
try {
if (ecashWalletInformation.balance - 5 < 1) return;
const lightningInvoice = await receivePayment({
amountMsat: (ecashWalletInformation.balance - 5) * 1000,
description: 'Auto Channel Rebalance',
});
const meltQuote = await getMeltQuote(lightningInvoice.lnInvoice.bolt11);
if (!meltQuote) throw new Error('unable to create melt quote');
const didPay = await payLnInvoiceFromEcash({
quote: meltQuote.quote,
invoice: lightningInvoice.lnInvoice.bolt11,
proofsToUse: meltQuote.proofsToUse,
description: 'Auto Channel Rebalance',
});
console.log(didPay, 'pay response in drain ecash balance');
} catch (err) {
console.log(err, 'draining ecash balance error');
}
}, [ecashWalletInformation]);
const contextValue = useMemo(
() => ({
nodeInformation,