From 5d088a67c1d1bae7abd5cc1e7ac4e1a15e8ee4db Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 23 Feb 2025 22:59:22 -0400 Subject: [PATCH 01/33] REF: Wallet tranaction header animation --- screen/wallets/WalletTransactions.tsx | 195 +++++++++++--------------- 1 file changed, 84 insertions(+), 111 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 758edaa1f..f579ee540 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -5,7 +5,6 @@ import { Alert, Dimensions, findNodeHandle, - FlatList, I18nManager, InteractionManager, LayoutAnimation, @@ -14,7 +13,7 @@ import { StyleSheet, Text, View, - RefreshControl, + Animated, } from 'react-native'; import { Icon } from '@rneui/themed'; import * as BlueElectrum from '../../blue_modules/BlueElectrum'; @@ -38,7 +37,6 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; import { Transaction, TWallet } from '../../class/wallets/types'; import getWalletTransactionsOptions from '../../navigation/helpers/getWalletTransactionsOptions'; -import { presentWalletExportReminder } from '../../helpers/presentWalletExportReminder'; import selectWallet from '../../helpers/select-wallet'; import assert from 'assert'; import useMenuElements from '../../hooks/useMenuElements'; @@ -46,7 +44,6 @@ import { useSettings } from '../../hooks/context/useSettings'; import { getClipboardContent } from '../../blue_modules/clipboard'; import HandOffComponent from '../../components/HandOffComponent'; import { HandOffActivityType } from '../../components/types'; -import WalletGradient from '../../class/wallet-gradient'; const buttonFontSize = PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22 @@ -56,6 +53,8 @@ const buttonFontSize = type WalletTransactionsProps = NativeStackScreenProps; type RouteProps = RouteProp; type TransactionListItem = Transaction & { type: 'transaction' | 'header' }; +const HEADER_HEIGHT = 210; + const WalletTransactions: React.FC = ({ route }) => { const { wallets, saveToDisk, setSelectedWalletID } = useStorage(); const { setReloadTransactionsMenuActionFunction } = useMenuElements(); @@ -74,6 +73,12 @@ const WalletTransactions: React.FC = ({ route }) => { const [lastFetchTimestamp, setLastFetchTimestamp] = useState(() => wallet?._lastTxFetch || 0); const [fetchFailures, setFetchFailures] = useState(0); const MAX_FAILURES = 3; + const scrollY = useRef(new Animated.Value(0)).current; + const headerTranslate = scrollY.interpolate({ + inputRange: [0, HEADER_HEIGHT], + outputRange: [0, -HEADER_HEIGHT], + extrapolate: 'clamp', + }); const stylesHook = StyleSheet.create({ listHeaderText: { @@ -279,9 +284,11 @@ const WalletTransactions: React.FC = ({ route }) => { walletID, }, }); + } else if (wallet?.type === MultisigHDWallet.type) { + navigateToViewEditCosigners(); } }, - [name, navigate, onWalletSelect, walletID, wallets], + [name, navigate, navigateToViewEditCosigners, onWalletSelect, wallet?.type, walletID, wallets], ); const getItemLayout = (_: any, index: number) => ({ @@ -296,7 +303,6 @@ const WalletTransactions: React.FC = ({ route }) => { }, [getTransactions, limit]); const renderItem = useCallback( - // eslint-disable-next-line react/no-unused-prop-types ({ item }: { item: Transaction }) => { return ; }, @@ -400,7 +406,8 @@ const WalletTransactions: React.FC = ({ route }) => { task.cancel(); setReloadTransactionsMenuActionFunction(() => {}); }; - }, [setReloadTransactionsMenuActionFunction, refreshTransactions]), + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []), ); const [balance, setBalance] = useState(wallet ? wallet.getBalance() : 0); @@ -433,97 +440,67 @@ const WalletTransactions: React.FC = ({ route }) => { [navigation, wallet, walletBalance, setOptions, route], ); - const ListHeaderComponent = useCallback( - () => - wallet ? ( - <> - { - wallet.preferredBalanceUnit = selectedUnit; - await saveToDisk(); - }} - unit={wallet.preferredBalanceUnit} - onWalletBalanceVisibilityChange={async isShouldBeVisible => { - const isBiometricsEnabled = await isBiometricUseCapableAndEnabled(); - if (wallet.hideBalance && isBiometricsEnabled) { - const unlocked = await unlockWithBiometrics(); - if (!unlocked) throw new Error('Biometrics failed'); - } - wallet.hideBalance = isShouldBeVisible; - await saveToDisk(); - }} - onManageFundsPressed={id => { - if (wallet.type === MultisigHDWallet.type) { - navigateToViewEditCosigners(); - } else if (wallet.type === LightningCustodianWallet.type) { - if (wallet.getUserHasSavedExport()) { - if (!id) return; - onManageFundsPressed(id); - } else { - presentWalletExportReminder() - .then(async () => { - if (!id) return; - wallet.setUserHasSavedExport(true); - await saveToDisk(); - onManageFundsPressed(id); - }) - .catch(() => { - navigate('WalletExportRoot', { - screen: 'WalletExport', - params: { - walletID, - }, - }); - }); - } - } - }} - /> - <> - - - {loc.transactions.list_title} - - - - {wallet.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && ( - { - wallet.isWatchOnlyWarningVisible = false; - LayoutAnimation.configureNext(LayoutAnimation.Presets.linear); - saveToDisk(); - }} - /> - )} - - - - ) : undefined, - [ - wallet, - colors.background, - stylesHook.listHeaderText, - saveToDisk, - isBiometricUseCapableAndEnabled, - navigateToViewEditCosigners, - onManageFundsPressed, - navigate, - walletID, - ], + const refreshProps = useMemo( + () => (!isDesktop && !isElectrumDisabled ? { onRefresh: refreshTransactions, refreshing: isLoading } : {}), + [isElectrumDisabled, isLoading, refreshTransactions], + ); + + // Extracted named callbacks + const handleWalletUnitChange = useCallback( + async (selectedUnit: any) => { + if (wallet) { + wallet.preferredBalanceUnit = selectedUnit; + await saveToDisk(); + } + }, + [wallet, saveToDisk], + ); + + const handleWalletBalanceVisibilityChange = useCallback( + async (isShouldBeVisible: boolean) => { + if (wallet) { + const isBiometricsEnabled = await isBiometricUseCapableAndEnabled(); + if (wallet.hideBalance && isBiometricsEnabled) { + const unlocked = await unlockWithBiometrics(); + if (!unlocked) throw new Error('Biometrics failed'); + } + wallet.hideBalance = isShouldBeVisible; + await saveToDisk(); + } + }, + [wallet, saveToDisk, isBiometricUseCapableAndEnabled], ); return ( - - {/* The color of the refresh indicator. Temporary hack */} - - - + + + {wallet ? ( + + ) : undefined} + + + {loc.transactions.list_title} + + + {wallet?.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && ( + { + wallet.isWatchOnlyWarningVisible = false; + LayoutAnimation.configureNext(LayoutAnimation.Presets.linear); + saveToDisk(); + }} + /> + )} + + + + getItemLayout={getItemLayout} updateCellsBatchingPeriod={30} onEndReachedThreshold={0.3} @@ -534,14 +511,12 @@ const WalletTransactions: React.FC = ({ route }) => { keyExtractor={_keyExtractor} renderItem={renderItem} initialNumToRender={10} + contentInset={{ top: HEADER_HEIGHT }} removeClippedSubviews contentContainerStyle={{ backgroundColor: colors.background }} - contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} maxToRenderPerBatch={15} - onScroll={handleScroll} + onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} - stickyHeaderHiddenOnScroll - ListHeaderComponent={ListHeaderComponent} ListEmptyComponent={ @@ -550,11 +525,7 @@ const WalletTransactions: React.FC = ({ route }) => { {isLightning() && {loc.wallets.list_empty_txs2_lightning}} } - refreshControl={ - !isDesktop && !isElectrumDisabled ? ( - refreshTransactions(true)} tintColor={colors.msSuccessCheck} /> - ) : undefined - } + {...refreshProps} /> {wallet?.allowReceive() && ( @@ -603,20 +574,22 @@ const WalletTransactions: React.FC = ({ route }) => { export default WalletTransactions; const styles = StyleSheet.create({ + container: { flex: 1 }, flex: { flex: 1 }, scrollViewContent: { flex: 1, justifyContent: 'center', paddingHorizontal: 16, paddingBottom: 500 }, activityIndicator: { marginVertical: 20 }, listHeaderTextRow: { flex: 1, margin: 16, flexDirection: 'row', justifyContent: 'space-between' }, listHeaderText: { marginTop: 8, marginBottom: 8, fontWeight: 'bold', fontSize: 24 }, - refreshIndicatorBackground: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - height: 140, - }, emptyTxs: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', marginVertical: 16 }, emptyTxsLightning: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', fontWeight: '600' }, sendIcon: { transform: [{ rotate: I18nManager.isRTL ? '-225deg' : '225deg' }] }, receiveIcon: { transform: [{ rotate: I18nManager.isRTL ? '45deg' : '-45deg' }] }, + stickyHeader: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: HEADER_HEIGHT, + zIndex: 1, + }, }); From 5b20ac352e1328f1d340c5a1c7a6e9e70bb00754 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 24 Feb 2025 00:26:15 -0400 Subject: [PATCH 02/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index f579ee540..3193d7ece 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -14,6 +14,7 @@ import { Text, View, Animated, + RefreshControl, } from 'react-native'; import { Icon } from '@rneui/themed'; import * as BlueElectrum from '../../blue_modules/BlueElectrum'; @@ -303,6 +304,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [getTransactions, limit]); const renderItem = useCallback( + // eslint-disable-next-line react/no-unused-prop-types ({ item }: { item: Transaction }) => { return ; }, @@ -440,11 +442,6 @@ const WalletTransactions: React.FC = ({ route }) => { [navigation, wallet, walletBalance, setOptions, route], ); - const refreshProps = useMemo( - () => (!isDesktop && !isElectrumDisabled ? { onRefresh: refreshTransactions, refreshing: isLoading } : {}), - [isElectrumDisabled, isLoading, refreshTransactions], - ); - // Extracted named callbacks const handleWalletUnitChange = useCallback( async (selectedUnit: any) => { @@ -478,11 +475,11 @@ const WalletTransactions: React.FC = ({ route }) => { - ) : undefined} + ) : null} {loc.transactions.list_title} @@ -518,14 +515,22 @@ const WalletTransactions: React.FC = ({ route }) => { onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} ListEmptyComponent={ - + {(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1} {isLightning() && {loc.wallets.list_empty_txs2_lightning}} } - {...refreshProps} + refreshControl={ + !isElectrumDisabled && !isDesktop ? ( + refreshTransactions(true)} progressViewOffset={HEADER_HEIGHT} /> + ) : undefined + } /> {wallet?.allowReceive() && ( @@ -576,7 +581,7 @@ export default WalletTransactions; const styles = StyleSheet.create({ container: { flex: 1 }, flex: { flex: 1 }, - scrollViewContent: { flex: 1, justifyContent: 'center', paddingHorizontal: 16, paddingBottom: 500 }, + scrollViewContent: { paddingHorizontal: 16, paddingTop: HEADER_HEIGHT }, activityIndicator: { marginVertical: 20 }, listHeaderTextRow: { flex: 1, margin: 16, flexDirection: 'row', justifyContent: 'space-between' }, listHeaderText: { marginTop: 8, marginBottom: 8, fontWeight: 'bold', fontSize: 24 }, @@ -589,7 +594,7 @@ const styles = StyleSheet.create({ top: 0, left: 0, right: 0, - height: HEADER_HEIGHT, + minHeight: HEADER_HEIGHT, zIndex: 1, }, }); From 2bb7b0c53fe3002c9fa2e9defdc36656a5b606f5 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 24 Feb 2025 02:39:28 -0400 Subject: [PATCH 03/33] REF: Wallet carousel item animation to use new helperds --- components/WalletsCarousel.tsx | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/components/WalletsCarousel.tsx b/components/WalletsCarousel.tsx index 0b6ebbe00..e1ebd0c91 100644 --- a/components/WalletsCarousel.tsx +++ b/components/WalletsCarousel.tsx @@ -187,32 +187,32 @@ export const WalletCarouselItem: React.FC = React.memo( const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82; const { isLargeScreen } = useIsLargeScreen(); + // Memoize springConfig for stable dependency + const springConfig = React.useMemo(() => ({ useNativeDriver: true, tension: 100 }), []); + const animateScale = useCallback( + (toValue: number, callback?: () => void) => { + Animated.spring(scaleValue, { toValue, ...springConfig }).start(callback); + }, + [scaleValue, springConfig], + ); + const onPressedIn = useCallback(() => { if (animationsEnabled) { - Animated.spring(scaleValue, { - toValue: 0.95, - useNativeDriver: true, - tension: 100, - }).start(); + animateScale(0.95); } if (onPressIn) onPressIn(); - }, [scaleValue, animationsEnabled, onPressIn]); + }, [animateScale, animationsEnabled, onPressIn]); const onPressedOut = useCallback(() => { if (animationsEnabled) { - Animated.spring(scaleValue, { - toValue: 1.0, - useNativeDriver: true, - tension: 100, - }).start(); + animateScale(1.0); } if (onPressOut) onPressOut(); - }, [scaleValue, animationsEnabled, onPressOut]); + }, [animateScale, animationsEnabled, onPressOut]); const handlePress = useCallback(() => { - onPressedOut(); onPress(item); - }, [item, onPress, onPressedOut]); + }, [item, onPress]); const opacity = isSelectedWallet === false ? 0.5 : 1.0; let image; @@ -254,6 +254,8 @@ export const WalletCarouselItem: React.FC = React.memo( if (handleLongPress) handleLongPress(); }} onPress={handlePress} + delayHoverIn={0} + delayHoverOut={0} > @@ -421,6 +423,7 @@ const WalletsCarousel = forwardRef((props showsHorizontalScrollIndicator={false} initialNumToRender={10} scrollEnabled={scrollEnabled} + keyboardShouldPersistTaps="handled" ListHeaderComponent={ListHeaderComponent} style={{ minHeight: sliderHeight + 12 }} onScrollToIndexFailed={onScrollToIndexFailed} From 3423730a416a13d0699d7a3b9c60496c0278d261 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 24 Feb 2025 02:39:51 -0400 Subject: [PATCH 04/33] Update WalletsCarousel.tsx --- components/WalletsCarousel.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/WalletsCarousel.tsx b/components/WalletsCarousel.tsx index e1ebd0c91..b9037e414 100644 --- a/components/WalletsCarousel.tsx +++ b/components/WalletsCarousel.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; +import React, { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react'; import { Animated, FlatList, @@ -187,8 +187,7 @@ export const WalletCarouselItem: React.FC = React.memo( const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82; const { isLargeScreen } = useIsLargeScreen(); - // Memoize springConfig for stable dependency - const springConfig = React.useMemo(() => ({ useNativeDriver: true, tension: 100 }), []); + const springConfig = useMemo(() => ({ useNativeDriver: true, tension: 100 }), []); const animateScale = useCallback( (toValue: number, callback?: () => void) => { Animated.spring(scaleValue, { toValue, ...springConfig }).start(callback); From 00dcc251427494af5d53770cd07f17a096b40b1f Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Fri, 28 Feb 2025 18:19:33 -0400 Subject: [PATCH 05/33] wip --- hooks/useExtendedNavigation.ts | 2 +- navigation/DetailViewScreensStack.tsx | 6 +-- navigation/DetailViewStackParamList.ts | 2 +- navigation/ViewEditMultisigCosignersStack.tsx | 48 ------------------- screen/wallets/ViewEditMultisigCosigners.tsx | 29 +++++++---- screen/wallets/WalletDetails.tsx | 7 +-- screen/wallets/WalletTransactions.tsx | 10 ++-- 7 files changed, 31 insertions(+), 73 deletions(-) delete mode 100644 navigation/ViewEditMultisigCosignersStack.tsx diff --git a/hooks/useExtendedNavigation.ts b/hooks/useExtendedNavigation.ts index 72c6fcec1..039f09bd4 100644 --- a/hooks/useExtendedNavigation.ts +++ b/hooks/useExtendedNavigation.ts @@ -10,7 +10,7 @@ import { useCallback, useMemo } from 'react'; const requiresBiometrics = [ 'WalletExportRoot', 'WalletXpubRoot', - 'ViewEditMultisigCosignersRoot', + 'ViewEditMultisigCosigners', 'ExportMultisigCoordinationSetupRoot', ]; diff --git a/navigation/DetailViewScreensStack.tsx b/navigation/DetailViewScreensStack.tsx index affe7cc19..f17ce097a 100644 --- a/navigation/DetailViewScreensStack.tsx +++ b/navigation/DetailViewScreensStack.tsx @@ -35,7 +35,6 @@ import ReceiveDetailsStackRoot from './ReceiveDetailsStack'; import ScanLndInvoiceRoot from './ScanLndInvoiceStack'; import SendDetailsStack from './SendDetailsStack'; import SignVerifyStackRoot from './SignVerifyStack'; -import ViewEditMultisigCosignersStackRoot from './ViewEditMultisigCosignersStack'; import WalletExportStack from './WalletExportStack'; import WalletXpubStackRoot from './WalletXpubStack'; import SettingsButton from '../components/icons/SettingsButton'; @@ -66,6 +65,7 @@ import ToolsScreen from '../screen/settings/tools'; import SettingsPrivacy from '../screen/settings/SettingsPrivacy'; import { ScanQRCodeComponent } from './LazyLoadScanQRCodeStack'; import { useIsLargeScreen } from '../hooks/useIsLargeScreen'; +import { ViewEditMultisigCosignersComponent } from './LazyLoadViewEditMultisigCosignersStack'; const DetailViewStackScreensStack = () => { const theme = useTheme(); @@ -342,8 +342,8 @@ const DetailViewStackScreensStack = () => { /> diff --git a/navigation/DetailViewStackParamList.ts b/navigation/DetailViewStackParamList.ts index 497f6cc4d..c5d4b1980 100644 --- a/navigation/DetailViewStackParamList.ts +++ b/navigation/DetailViewStackParamList.ts @@ -79,7 +79,7 @@ export type DetailViewStackParamList = { ReleaseNotes: undefined; ToolsScreen: undefined; SettingsPrivacy: undefined; - ViewEditMultisigCosignersRoot: { walletID: string; cosigners: string[] }; + ViewEditMultisigCosigners: { walletID: string; cosigners: string[]; onBarScanned?: string }; WalletXpubRoot: undefined; SignVerifyRoot: { screen: 'SignVerify'; diff --git a/navigation/ViewEditMultisigCosignersStack.tsx b/navigation/ViewEditMultisigCosignersStack.tsx deleted file mode 100644 index 6e7d42aec..000000000 --- a/navigation/ViewEditMultisigCosignersStack.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import React from 'react'; - -import navigationStyle from '../components/navigationStyle'; -import { useTheme } from '../components/themes'; -import loc from '../loc'; -import { ViewEditMultisigCosignersComponent } from './LazyLoadViewEditMultisigCosignersStack'; -import { ScanQRCodeComponent } from './LazyLoadScanQRCodeStack'; -import { ScanQRCodeParamList } from './DetailViewStackParamList'; - -export type ViewEditMultisigCosignersStackParamList = { - ViewEditMultisigCosigners: { - walletID: string; - onBarScanned?: string; - }; - ScanQRCode: ScanQRCodeParamList; -}; - -const Stack = createNativeStackNavigator(); - -const ViewEditMultisigCosignersStackRoot = () => { - const theme = useTheme(); - - return ( - - - - - ); -}; - -export default ViewEditMultisigCosignersStackRoot; diff --git a/screen/wallets/ViewEditMultisigCosigners.tsx b/screen/wallets/ViewEditMultisigCosigners.tsx index 2119961a3..72322f242 100644 --- a/screen/wallets/ViewEditMultisigCosigners.tsx +++ b/screen/wallets/ViewEditMultisigCosigners.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { RouteProp, useFocusEffect, useRoute, usePreventRemove, CommonActions } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useRoute, usePreventRemove, StackActions } from '@react-navigation/native'; import { ActivityIndicator, Alert, @@ -18,7 +18,15 @@ import { import { Badge, Icon } from '@rneui/themed'; import { isDesktop } from '../../blue_modules/environment'; import { encodeUR } from '../../blue_modules/ur'; -import { BlueCard, BlueFormMultiInput, BlueLoading, BlueSpacing10, BlueSpacing20, BlueTextCentered } from '../../BlueComponents'; +import { + BlueCard, + BlueFormMultiInput, + BlueLoading, + BlueSpacing10, + BlueSpacing20, + BlueSpacing40, + BlueTextCentered, +} from '../../BlueComponents'; import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class'; import presentAlert from '../../components/Alert'; import BottomModal, { BottomModalHandle } from '../../components/BottomModal'; @@ -40,14 +48,14 @@ import { useStorage } from '../../hooks/context/useStorage'; import ToolTipMenu from '../../components/TooltipMenu'; import { CommonToolTipActions } from '../../typings/CommonToolTipActions'; import { useSettings } from '../../hooks/context/useSettings'; -import { ViewEditMultisigCosignersStackParamList } from '../../navigation/ViewEditMultisigCosignersStack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import SafeArea from '../../components/SafeArea'; import { TWallet } from '../../class/wallets/types'; import { AddressInputScanButton } from '../../components/AddressInputScanButton'; +import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; -type RouteParams = RouteProp; -type NavigationProp = NativeStackNavigationProp; +type RouteParams = RouteProp; +type NavigationProp = NativeStackNavigationProp; const ViewEditMultisigCosigners: React.FC = () => { const hasLoaded = useRef(false); @@ -169,9 +177,11 @@ const ViewEditMultisigCosigners: React.FC = () => { setIsSaveButtonDisabled(true); setWalletsWithNewOrder(newWallets); setTimeout(() => { - dispatch( - CommonActions.navigate({ name: 'WalletTransactions', params: { walletID: wallet.getID(), walletType: MultisigHDWallet.type } }), - ); + const popTo = StackActions.popTo('WalletTransactions', { + walletID, + walletType: wallet.type, + }); + dispatch(popTo); }, 500); }, 100); }; @@ -560,6 +570,7 @@ const ViewEditMultisigCosigners: React.FC = () => { {!isLoading && ( <> + { await provideMnemonicsModalRef.current?.dismiss(); @@ -568,7 +579,7 @@ const ViewEditMultisigCosigners: React.FC = () => { type="link" onChangeText={setImportText} /> - + )} diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 71634f798..04ba8eac0 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -305,11 +305,8 @@ const WalletDetails: React.FC = () => { }); }; const navigateToViewEditCosigners = () => { - navigate('ViewEditMultisigCosignersRoot', { - screen: 'ViewEditMultisigCosigners', - params: { - walletID, - }, + navigate('ViewEditMultisigCosigners', { + walletID, }); }; const navigateToXPub = () => diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 25503205b..6fd5a1b58 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -54,7 +54,7 @@ const buttonFontSize = type WalletTransactionsProps = NativeStackScreenProps; type RouteProps = RouteProp; type TransactionListItem = Transaction & { type: 'transaction' | 'header' }; -const HEADER_HEIGHT = 210; +const HEADER_HEIGHT = 240; const WalletTransactions: React.FC = ({ route }) => { const { wallets, saveToDisk, setSelectedWalletID } = useStorage(); @@ -262,11 +262,8 @@ const WalletTransactions: React.FC = ({ route }) => { ); const navigateToViewEditCosigners = useCallback(() => { - navigate('ViewEditMultisigCosignersRoot', { - screen: 'ViewEditMultisigCosigners', - params: { - walletID, - }, + navigate('ViewEditMultisigCosigners', { + walletID, }); }, [navigate, walletID]); @@ -516,6 +513,7 @@ const WalletTransactions: React.FC = ({ route }) => { style={[styles.flex, { backgroundColor: colors.background }]} contentContainerStyle={styles.scrollViewContent} centerContent + testID="TransactionsListEmpty" > {(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1} From bbe4449dd930ed3eb09f83509b6d359bfbb86509 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Fri, 28 Feb 2025 21:27:30 -0400 Subject: [PATCH 06/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 6fd5a1b58..ed314aaaa 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -54,7 +54,7 @@ const buttonFontSize = type WalletTransactionsProps = NativeStackScreenProps; type RouteProps = RouteProp; type TransactionListItem = Transaction & { type: 'transaction' | 'header' }; -const HEADER_HEIGHT = 240; +const HEADER_HEIGHT = 190; const WalletTransactions: React.FC = ({ route }) => { const { wallets, saveToDisk, setSelectedWalletID } = useStorage(); @@ -502,6 +502,7 @@ const WalletTransactions: React.FC = ({ route }) => { renderItem={renderItem} initialNumToRender={10} contentInset={{ top: HEADER_HEIGHT }} + contentOffset={{ y: -HEADER_HEIGHT, x: 0 }} removeClippedSubviews testID="TransactionsListView" contentContainerStyle={{ backgroundColor: colors.background }} @@ -576,10 +577,10 @@ export default WalletTransactions; const styles = StyleSheet.create({ container: { flex: 1 }, flex: { flex: 1 }, - scrollViewContent: { paddingHorizontal: 16, paddingTop: HEADER_HEIGHT }, + scrollViewContent: { paddingHorizontal: 16 }, activityIndicator: { marginVertical: 20 }, - listHeaderTextRow: { flex: 1, margin: 16, flexDirection: 'row', justifyContent: 'space-between' }, - listHeaderText: { marginTop: 8, marginBottom: 8, fontWeight: 'bold', fontSize: 24 }, + listHeaderTextRow: { padding: 16, flexDirection: 'row' }, + listHeaderText: { fontWeight: 'bold', fontSize: 24 }, emptyTxs: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', marginVertical: 16 }, emptyTxsLightning: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', fontWeight: '600' }, sendIcon: { transform: [{ rotate: I18nManager.isRTL ? '-225deg' : '225deg' }] }, From 2f3cf1b4e99c74ffecf763cc9301e1489f1ea607 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 12:14:52 -0400 Subject: [PATCH 07/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index ed314aaaa..50a5b0609 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -501,11 +501,9 @@ const WalletTransactions: React.FC = ({ route }) => { keyExtractor={_keyExtractor} renderItem={renderItem} initialNumToRender={10} - contentInset={{ top: HEADER_HEIGHT }} - contentOffset={{ y: -HEADER_HEIGHT, x: 0 }} removeClippedSubviews testID="TransactionsListView" - contentContainerStyle={{ backgroundColor: colors.background }} + contentContainerStyle={{ backgroundColor: colors.background, marginTop: HEADER_HEIGHT }} maxToRenderPerBatch={15} onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} From 96e553f3d5cc68beb7761580bdd2f78c61f05ef6 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 13:21:50 -0400 Subject: [PATCH 08/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 50a5b0609..38349c3f4 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -575,7 +575,7 @@ export default WalletTransactions; const styles = StyleSheet.create({ container: { flex: 1 }, flex: { flex: 1 }, - scrollViewContent: { paddingHorizontal: 16 }, + scrollViewContent: { paddingHorizontal: 16, marginTop: HEADER_HEIGHT }, activityIndicator: { marginVertical: 20 }, listHeaderTextRow: { padding: 16, flexDirection: 'row' }, listHeaderText: { fontWeight: 'bold', fontSize: 24 }, From 05491387ffbcc90007816d6d8f6596d7f545723b Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 14:36:42 -0400 Subject: [PATCH 09/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 38349c3f4..aacafe2b6 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -1,4 +1,4 @@ -import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useRoute, useIsFocused } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ActivityIndicator, @@ -80,6 +80,8 @@ const WalletTransactions: React.FC = ({ route }) => { outputRange: [0, -HEADER_HEIGHT], extrapolate: 'clamp', }); + const flatListRef = useRef(null); + const isFocused = useIsFocused(); const stylesHook = StyleSheet.create({ listHeaderText: { @@ -93,6 +95,21 @@ const WalletTransactions: React.FC = ({ route }) => { }, [route, setOptions]), ); + // Reset header position when navigating away from screen + useFocusEffect( + useCallback(() => { + return () => { + // This will run when screen loses focus + scrollY.setValue(0); + // Reset FlatList scroll position to top + if (flatListRef.current) { + flatListRef.current.scrollToOffset({ offset: 0, animated: false }); + } + setOptions(getWalletTransactionsOptions({ route })); + }; + }, [scrollY, setOptions, route]), + ); + const onBarCodeRead = useCallback( (ret?: { data?: any }) => { if (!isLoading) { @@ -421,7 +438,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [wallet, wallet?.hideBalance, wallet?.preferredBalanceUnit, balance]); const handleScroll = useCallback( - (event: any) => { + (event: any) => { const offsetY = event.nativeEvent.contentOffset.y; const combinedHeight = 180; if (offsetY < combinedHeight) { @@ -491,6 +508,7 @@ const WalletTransactions: React.FC = ({ route }) => { + ref={flatListRef} getItemLayout={getItemLayout} updateCellsBatchingPeriod={30} onEndReachedThreshold={0.3} From a65776933dc0ca5a50f2b5309602f41c13184e59 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 15:06:47 -0400 Subject: [PATCH 10/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index aacafe2b6..adec07551 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -1,4 +1,4 @@ -import { RouteProp, useFocusEffect, useRoute, useIsFocused } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ActivityIndicator, @@ -81,7 +81,6 @@ const WalletTransactions: React.FC = ({ route }) => { extrapolate: 'clamp', }); const flatListRef = useRef(null); - const isFocused = useIsFocused(); const stylesHook = StyleSheet.create({ listHeaderText: { From 88b82747585aacf95d6cbd4e69e66dded0a1a392 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 15:06:57 -0400 Subject: [PATCH 11/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index adec07551..6f8d9cd3e 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -437,7 +437,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [wallet, wallet?.hideBalance, wallet?.preferredBalanceUnit, balance]); const handleScroll = useCallback( - (event: any) => { + (event: any) => { const offsetY = event.nativeEvent.contentOffset.y; const combinedHeight = 180; if (offsetY < combinedHeight) { From af8d7d3477dc43173f08f319dcd646295f194989 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 21:24:09 -0400 Subject: [PATCH 12/33] Revert "Update WalletTransactions.tsx" This reverts commit 88b82747585aacf95d6cbd4e69e66dded0a1a392. --- screen/wallets/WalletTransactions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 6f8d9cd3e..adec07551 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -437,7 +437,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [wallet, wallet?.hideBalance, wallet?.preferredBalanceUnit, balance]); const handleScroll = useCallback( - (event: any) => { + (event: any) => { const offsetY = event.nativeEvent.contentOffset.y; const combinedHeight = 180; if (offsetY < combinedHeight) { From fe795e648bd828fb65753b286ac6ae3d8c590b20 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 21:24:12 -0400 Subject: [PATCH 13/33] Revert "Update WalletTransactions.tsx" This reverts commit a65776933dc0ca5a50f2b5309602f41c13184e59. --- screen/wallets/WalletTransactions.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index adec07551..aacafe2b6 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -1,4 +1,4 @@ -import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useRoute, useIsFocused } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ActivityIndicator, @@ -81,6 +81,7 @@ const WalletTransactions: React.FC = ({ route }) => { extrapolate: 'clamp', }); const flatListRef = useRef(null); + const isFocused = useIsFocused(); const stylesHook = StyleSheet.create({ listHeaderText: { From 3504d0dc30453e5bca780556c103983922d8501c Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 21:24:15 -0400 Subject: [PATCH 14/33] Revert "Update WalletTransactions.tsx" This reverts commit 05491387ffbcc90007816d6d8f6596d7f545723b. --- screen/wallets/WalletTransactions.tsx | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index aacafe2b6..38349c3f4 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -1,4 +1,4 @@ -import { RouteProp, useFocusEffect, useRoute, useIsFocused } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ActivityIndicator, @@ -80,8 +80,6 @@ const WalletTransactions: React.FC = ({ route }) => { outputRange: [0, -HEADER_HEIGHT], extrapolate: 'clamp', }); - const flatListRef = useRef(null); - const isFocused = useIsFocused(); const stylesHook = StyleSheet.create({ listHeaderText: { @@ -95,21 +93,6 @@ const WalletTransactions: React.FC = ({ route }) => { }, [route, setOptions]), ); - // Reset header position when navigating away from screen - useFocusEffect( - useCallback(() => { - return () => { - // This will run when screen loses focus - scrollY.setValue(0); - // Reset FlatList scroll position to top - if (flatListRef.current) { - flatListRef.current.scrollToOffset({ offset: 0, animated: false }); - } - setOptions(getWalletTransactionsOptions({ route })); - }; - }, [scrollY, setOptions, route]), - ); - const onBarCodeRead = useCallback( (ret?: { data?: any }) => { if (!isLoading) { @@ -438,7 +421,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [wallet, wallet?.hideBalance, wallet?.preferredBalanceUnit, balance]); const handleScroll = useCallback( - (event: any) => { + (event: any) => { const offsetY = event.nativeEvent.contentOffset.y; const combinedHeight = 180; if (offsetY < combinedHeight) { @@ -508,7 +491,6 @@ const WalletTransactions: React.FC = ({ route }) => { - ref={flatListRef} getItemLayout={getItemLayout} updateCellsBatchingPeriod={30} onEndReachedThreshold={0.3} From 5e4d58b2078743147720993182ef5cc368a05991 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 21:40:46 -0400 Subject: [PATCH 15/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 81 +++++++++++++++++---------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 38349c3f4..53f1a3d02 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -15,6 +15,7 @@ import { View, Animated, RefreshControl, + LayoutChangeEvent, } from 'react-native'; import { Icon } from '@rneui/themed'; import * as BlueElectrum from '../../blue_modules/BlueElectrum'; @@ -54,7 +55,6 @@ const buttonFontSize = type WalletTransactionsProps = NativeStackScreenProps; type RouteProps = RouteProp; type TransactionListItem = Transaction & { type: 'transaction' | 'header' }; -const HEADER_HEIGHT = 190; const WalletTransactions: React.FC = ({ route }) => { const { wallets, saveToDisk, setSelectedWalletID } = useStorage(); @@ -75,9 +75,11 @@ const WalletTransactions: React.FC = ({ route }) => { const [fetchFailures, setFetchFailures] = useState(0); const MAX_FAILURES = 3; const scrollY = useRef(new Animated.Value(0)).current; + const [headerHeight, setHeaderHeight] = useState(0); + const headerTranslate = scrollY.interpolate({ - inputRange: [0, HEADER_HEIGHT], - outputRange: [0, -HEADER_HEIGHT], + inputRange: [0, headerHeight], + outputRange: [0, -headerHeight], extrapolate: 'clamp', }); @@ -423,8 +425,9 @@ const WalletTransactions: React.FC = ({ route }) => { const handleScroll = useCallback( (event: any) => { const offsetY = event.nativeEvent.contentOffset.y; - const combinedHeight = 180; - if (offsetY < combinedHeight) { + // Use the measured header height to determine when to show/hide the header title + const threshold = headerHeight * 0.75; + if (offsetY < threshold) { setOptions({ ...getWalletTransactionsOptions({ route }), headerTitle: undefined }); } else { navigation.setOptions({ @@ -432,7 +435,7 @@ const WalletTransactions: React.FC = ({ route }) => { }); } }, - [navigation, wallet, walletBalance, setOptions, route], + [navigation, wallet, walletBalance, setOptions, route, headerHeight], ); // Extracted named callbacks @@ -461,9 +464,14 @@ const WalletTransactions: React.FC = ({ route }) => { [wallet, saveToDisk, isBiometricUseCapableAndEnabled], ); - return ( - - + const handleHeaderLayout = useCallback((event: LayoutChangeEvent) => { + const { height } = event.nativeEvent.layout; + setHeaderHeight(height); + }, []); + + const stickyHeader = useMemo(() => { + return ( + {wallet ? ( = ({ route }) => { onManageFundsPressed={onManageFundsPressed} /> ) : null} - - - {loc.transactions.list_title} - - - {wallet?.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && ( - { - wallet.isWatchOnlyWarningVisible = false; - LayoutAnimation.configureNext(LayoutAnimation.Presets.linear); - saveToDisk(); - }} - /> - )} - - + ); + }, [handleWalletBalanceVisibilityChange, handleWalletUnitChange, headerTranslate, onManageFundsPressed, wallet, handleHeaderLayout]); + + const renderHeader = useCallback(() => { + return ( + + + {loc.transactions.list_title} + + + {wallet?.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && ( + { + wallet.isWatchOnlyWarningVisible = false; + LayoutAnimation.configureNext(LayoutAnimation.Presets.linear); + saveToDisk(); + }} + /> + )} + + + ); + }, [colors.background, stylesHook.listHeaderText, wallet, saveToDisk]); + + return ( + + {stickyHeader} getItemLayout={getItemLayout} updateCellsBatchingPeriod={30} onEndReachedThreshold={0.3} + ListHeaderComponent={renderHeader} onEndReached={loadMoreTransactions} ListFooterComponent={renderListFooterComponent} data={getTransactions(limit)} @@ -503,14 +523,16 @@ const WalletTransactions: React.FC = ({ route }) => { initialNumToRender={10} removeClippedSubviews testID="TransactionsListView" - contentContainerStyle={{ backgroundColor: colors.background, marginTop: HEADER_HEIGHT }} + contentInsetAdjustmentBehavior="automatic" + automaticallyAdjustContentInsets + contentContainerStyle={{ backgroundColor: colors.background, marginTop: headerHeight }} maxToRenderPerBatch={15} onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} ListEmptyComponent={ @@ -522,7 +544,7 @@ const WalletTransactions: React.FC = ({ route }) => { } refreshControl={ !isElectrumDisabled && !isDesktop ? ( - refreshTransactions(true)} progressViewOffset={HEADER_HEIGHT} /> + refreshTransactions(true)} progressViewOffset={headerHeight} /> ) : undefined } /> @@ -575,7 +597,7 @@ export default WalletTransactions; const styles = StyleSheet.create({ container: { flex: 1 }, flex: { flex: 1 }, - scrollViewContent: { paddingHorizontal: 16, marginTop: HEADER_HEIGHT }, + scrollViewContent: { paddingHorizontal: 16 }, activityIndicator: { marginVertical: 20 }, listHeaderTextRow: { padding: 16, flexDirection: 'row' }, listHeaderText: { fontWeight: 'bold', fontSize: 24 }, @@ -588,7 +610,6 @@ const styles = StyleSheet.create({ top: 0, left: 0, right: 0, - minHeight: HEADER_HEIGHT, zIndex: 1, }, }); From c1ae300254ee38a78caf2cf4c8f6306bebcd4c0c Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 22:09:53 -0400 Subject: [PATCH 16/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 53f1a3d02..01f9a3278 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -16,6 +16,7 @@ import { Animated, RefreshControl, LayoutChangeEvent, + Platform, } from 'react-native'; import { Icon } from '@rneui/themed'; import * as BlueElectrum from '../../blue_modules/BlueElectrum'; @@ -532,7 +533,7 @@ const WalletTransactions: React.FC = ({ route }) => { ListEmptyComponent={ From a62a21b28ba806674b8de66b8c40e429299fb906 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 1 Mar 2025 23:32:18 -0400 Subject: [PATCH 17/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 01f9a3278..8bd8d31ff 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -9,14 +9,12 @@ import { InteractionManager, LayoutAnimation, PixelRatio, - ScrollView, StyleSheet, Text, View, Animated, RefreshControl, LayoutChangeEvent, - Platform, } from 'react-native'; import { Icon } from '@rneui/themed'; import * as BlueElectrum from '../../blue_modules/BlueElectrum'; @@ -531,17 +529,12 @@ const WalletTransactions: React.FC = ({ route }) => { onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} ListEmptyComponent={ - + {(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1} {isLightning() && {loc.wallets.list_empty_txs2_lightning}} - + } refreshControl={ !isElectrumDisabled && !isDesktop ? ( @@ -598,7 +591,6 @@ export default WalletTransactions; const styles = StyleSheet.create({ container: { flex: 1 }, flex: { flex: 1 }, - scrollViewContent: { paddingHorizontal: 16 }, activityIndicator: { marginVertical: 20 }, listHeaderTextRow: { padding: 16, flexDirection: 'row' }, listHeaderText: { fontWeight: 'bold', fontSize: 24 }, From 863ac46bc8024262c38a16ddf95df1215de792c5 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 00:02:05 -0400 Subject: [PATCH 18/33] wip --- fastlane/Fastfile | 58 +++++++++++++++++++++++++++++++++------------- fastlane/Matchfile | 50 +++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 9b236b387..c286273d2 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -238,26 +238,52 @@ platform :ios do desc "Synchronize certificates and provisioning profiles" lane :setup_provisioning_profiles do + required_vars = ["GIT_ACCESS_TOKEN", "GIT_URL", "ITC_TEAM_ID", "ITC_TEAM_NAME", "KEYCHAIN_PASSWORD"] + ensure_env_vars(required_vars) + UI.message("Setting up provisioning profiles...") - - platform = "ios" - + + max_retries = 3 + # Iterate over app identifiers to fetch provisioning profiles app_identifiers.each do |app_identifier| - match( - git_basic_authorization: ENV["GIT_ACCESS_TOKEN"], - git_url: ENV["GIT_URL"], - type: "appstore", - clone_branch_directly: true, # Skip if the branch already exists - platform: platform, - app_identifier: app_identifier, - team_id: ENV["ITC_TEAM_ID"], - team_name: ENV["ITC_TEAM_NAME"], - readonly: true, - keychain_name: "temp_keychain", - keychain_password: ENV["KEYCHAIN_PASSWORD"] - ) + retries = 0 + success = false + + while !success && retries < max_retries + begin + UI.message("Fetching provisioning profile for #{app_identifier} (attempt #{retries + 1}/#{max_retries})...") + match( + git_basic_authorization: ENV["GIT_ACCESS_TOKEN"], + git_url: ENV["GIT_URL"], + type: "appstore", + clone_branch_directly: true, + platform: "ios", + app_identifier: app_identifier, + team_id: ENV["ITC_TEAM_ID"], + team_name: ENV["ITC_TEAM_NAME"], + readonly: true, + keychain_name: "temp_keychain", + keychain_password: ENV["KEYCHAIN_PASSWORD"] + ) + success = true + log_success("Successfully fetched provisioning profile for #{app_identifier}") + rescue => e + retries += 1 + if retries < max_retries + wait_time = 10 * retries # Exponential backoff + log_error("Failed to fetch provisioning profile for #{app_identifier}: #{e.message}") + UI.message("Retrying in #{wait_time} seconds...") + sleep(wait_time) + else + log_error("Failed to fetch provisioning profile for #{app_identifier} after #{max_retries} attempts: #{e.message}") + raise e + end + end + end end + + log_success("All provisioning profiles set up") end desc "Fetch development certificates and provisioning profiles for Mac Catalyst" diff --git a/fastlane/Matchfile b/fastlane/Matchfile index ba2f5652b..9772ca474 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -3,33 +3,39 @@ # URL of the Git repository to store the certificates git_url(ENV["GIT_URL"]) -# Define the type of match to run, could be one of 'appstore', 'adhoc', 'development', or 'enterprise'. -# For example, use 'appstore' for App Store builds, 'adhoc' for Ad Hoc distribution, -# 'development' for development builds, and 'enterprise' for In-House (enterprise) distribution. -type("appstore") +# Define the type of match to run +# Default to "appstore" but can be overridden +type(ENV["MATCH_TYPE"] || "appstore") -app_identifier(["io.bluewallet.bluewallet", "io.bluewallet.bluewallet.watch", "io.bluewallet.bluewallet.watch.extension", "io.bluewallet.bluewallet.Stickers", "io.bluewallet.bluewallet.MarketWidget"]) # Replace with your app identifiers +# App identifiers for all BlueWallet apps +app_identifier([ + "io.bluewallet.bluewallet", + "io.bluewallet.bluewallet.watch", + "io.bluewallet.bluewallet.watch.extension", + "io.bluewallet.bluewallet.Stickers", + "io.bluewallet.bluewallet.MarketWidget" +]) -# List of app identifiers to create provisioning profiles for. -# Replace with your app's bundle identifier(s). - -# Your Apple Developer account email address. +# Your Apple Developer account email address username(ENV["APPLE_ID"]) -# The ID of your Apple Developer team if you're part of multiple teams +# The ID of your Apple Developer team team_id(ENV["ITC_TEAM_ID"]) -# Set this to true if match should only read existing certificates and profiles -# and not create new ones. -readonly(true) +# Set readonly based on environment (default to true for safety) +# Set to false explicitly when new profiles need to be created +readonly(ENV["MATCH_READONLY"] == "false" ? false : true) -# Optional: The Git branch that is used for match. -# Default is 'master'. - -# Optional: Path to a specific SSH key to be used by match. -# Only needed if you're using a private repository and match needs to use SSH keys for authentication. -# ssh_key("/path/to/your/private/key") - -# Optional: Define the platform to use, can be 'ios', 'macos', or 'tvos'. -# For React Native projects, you'll typically use 'ios'. +# Define the platform to use platform("ios") + +# Git basic authentication through access token +# This is useful for CI/CD environments where SSH keys aren't available +git_basic_authorization(ENV["GIT_ACCESS_TOKEN"]) + +# Storage mode (git by default) +storage_mode("git") + +# Optional: The Git branch that is used for match +# Default is 'master' +# branch("main") From aa695f2705908297fd1c4ef0d04b9debb5c81ec7 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 01:09:40 -0400 Subject: [PATCH 19/33] Update Fastfile --- fastlane/Fastfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c286273d2..70baba57f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -192,6 +192,20 @@ end # =========================== platform :ios do + # Helper methods + def ensure_env_vars(vars) + vars.each do |var| + UI.user_error!("#{var} environment variable is missing") if ENV[var].nil? || ENV[var].empty? + end + end + + def log_success(message) + UI.success("✅ #{message}") + end + + def log_error(message) + UI.error("❌ #{message}") + end desc "Register new devices from a file" lane :register_devices_from_txt do From 758c2acf3a5421138da14b75f13f4dd84cf791bd Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 01:11:33 -0400 Subject: [PATCH 20/33] Revert "Update Fastfile" This reverts commit aa695f2705908297fd1c4ef0d04b9debb5c81ec7. --- fastlane/Fastfile | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 70baba57f..c286273d2 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -192,20 +192,6 @@ end # =========================== platform :ios do - # Helper methods - def ensure_env_vars(vars) - vars.each do |var| - UI.user_error!("#{var} environment variable is missing") if ENV[var].nil? || ENV[var].empty? - end - end - - def log_success(message) - UI.success("✅ #{message}") - end - - def log_error(message) - UI.error("❌ #{message}") - end desc "Register new devices from a file" lane :register_devices_from_txt do From 0449965ef53151a71e375ffa9b4190621b158d3c Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 01:11:36 -0400 Subject: [PATCH 21/33] Revert "wip" This reverts commit 863ac46bc8024262c38a16ddf95df1215de792c5. --- fastlane/Fastfile | 58 +++++++++++++--------------------------------- fastlane/Matchfile | 50 ++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 70 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c286273d2..9b236b387 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -238,52 +238,26 @@ platform :ios do desc "Synchronize certificates and provisioning profiles" lane :setup_provisioning_profiles do - required_vars = ["GIT_ACCESS_TOKEN", "GIT_URL", "ITC_TEAM_ID", "ITC_TEAM_NAME", "KEYCHAIN_PASSWORD"] - ensure_env_vars(required_vars) - UI.message("Setting up provisioning profiles...") - - max_retries = 3 - + + platform = "ios" + # Iterate over app identifiers to fetch provisioning profiles app_identifiers.each do |app_identifier| - retries = 0 - success = false - - while !success && retries < max_retries - begin - UI.message("Fetching provisioning profile for #{app_identifier} (attempt #{retries + 1}/#{max_retries})...") - match( - git_basic_authorization: ENV["GIT_ACCESS_TOKEN"], - git_url: ENV["GIT_URL"], - type: "appstore", - clone_branch_directly: true, - platform: "ios", - app_identifier: app_identifier, - team_id: ENV["ITC_TEAM_ID"], - team_name: ENV["ITC_TEAM_NAME"], - readonly: true, - keychain_name: "temp_keychain", - keychain_password: ENV["KEYCHAIN_PASSWORD"] - ) - success = true - log_success("Successfully fetched provisioning profile for #{app_identifier}") - rescue => e - retries += 1 - if retries < max_retries - wait_time = 10 * retries # Exponential backoff - log_error("Failed to fetch provisioning profile for #{app_identifier}: #{e.message}") - UI.message("Retrying in #{wait_time} seconds...") - sleep(wait_time) - else - log_error("Failed to fetch provisioning profile for #{app_identifier} after #{max_retries} attempts: #{e.message}") - raise e - end - end - end + match( + git_basic_authorization: ENV["GIT_ACCESS_TOKEN"], + git_url: ENV["GIT_URL"], + type: "appstore", + clone_branch_directly: true, # Skip if the branch already exists + platform: platform, + app_identifier: app_identifier, + team_id: ENV["ITC_TEAM_ID"], + team_name: ENV["ITC_TEAM_NAME"], + readonly: true, + keychain_name: "temp_keychain", + keychain_password: ENV["KEYCHAIN_PASSWORD"] + ) end - - log_success("All provisioning profiles set up") end desc "Fetch development certificates and provisioning profiles for Mac Catalyst" diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 9772ca474..ba2f5652b 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -3,39 +3,33 @@ # URL of the Git repository to store the certificates git_url(ENV["GIT_URL"]) -# Define the type of match to run -# Default to "appstore" but can be overridden -type(ENV["MATCH_TYPE"] || "appstore") +# Define the type of match to run, could be one of 'appstore', 'adhoc', 'development', or 'enterprise'. +# For example, use 'appstore' for App Store builds, 'adhoc' for Ad Hoc distribution, +# 'development' for development builds, and 'enterprise' for In-House (enterprise) distribution. +type("appstore") -# App identifiers for all BlueWallet apps -app_identifier([ - "io.bluewallet.bluewallet", - "io.bluewallet.bluewallet.watch", - "io.bluewallet.bluewallet.watch.extension", - "io.bluewallet.bluewallet.Stickers", - "io.bluewallet.bluewallet.MarketWidget" -]) +app_identifier(["io.bluewallet.bluewallet", "io.bluewallet.bluewallet.watch", "io.bluewallet.bluewallet.watch.extension", "io.bluewallet.bluewallet.Stickers", "io.bluewallet.bluewallet.MarketWidget"]) # Replace with your app identifiers -# Your Apple Developer account email address +# List of app identifiers to create provisioning profiles for. +# Replace with your app's bundle identifier(s). + +# Your Apple Developer account email address. username(ENV["APPLE_ID"]) -# The ID of your Apple Developer team +# The ID of your Apple Developer team if you're part of multiple teams team_id(ENV["ITC_TEAM_ID"]) -# Set readonly based on environment (default to true for safety) -# Set to false explicitly when new profiles need to be created -readonly(ENV["MATCH_READONLY"] == "false" ? false : true) +# Set this to true if match should only read existing certificates and profiles +# and not create new ones. +readonly(true) -# Define the platform to use +# Optional: The Git branch that is used for match. +# Default is 'master'. + +# Optional: Path to a specific SSH key to be used by match. +# Only needed if you're using a private repository and match needs to use SSH keys for authentication. +# ssh_key("/path/to/your/private/key") + +# Optional: Define the platform to use, can be 'ios', 'macos', or 'tvos'. +# For React Native projects, you'll typically use 'ios'. platform("ios") - -# Git basic authentication through access token -# This is useful for CI/CD environments where SSH keys aren't available -git_basic_authorization(ENV["GIT_ACCESS_TOKEN"]) - -# Storage mode (git by default) -storage_mode("git") - -# Optional: The Git branch that is used for match -# Default is 'master' -# branch("main") From 898443f3a58ed3c68caf6955a83e0fa940e4793a Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 04:51:07 -0400 Subject: [PATCH 22/33] Update TransactionsNavigationHeader.tsx --- components/TransactionsNavigationHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/TransactionsNavigationHeader.tsx b/components/TransactionsNavigationHeader.tsx index 6722ad601..80f09684e 100644 --- a/components/TransactionsNavigationHeader.tsx +++ b/components/TransactionsNavigationHeader.tsx @@ -170,7 +170,7 @@ const TransactionsNavigationHeader: React.FC > - + {wallet.getLabel()} From 2376ef8be9b9b140630e8291b3b44bc32d35c9a6 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 18:03:07 -0400 Subject: [PATCH 23/33] Update WalletDetails.tsx --- screen/wallets/WalletDetails.tsx | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 04ba8eac0..7fc2a9ec4 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -369,10 +369,12 @@ const WalletDetails: React.FC = () => { if (wallet.type === HDSegwitBech32Wallet.type) { wallet._txs_by_external_index = {}; wallet._txs_by_internal_index = {}; - presentAlert({ message: msg }); - wallet._balances_by_external_index = {}; wallet._balances_by_internal_index = {}; + wallet._utxo = []; + if ('transactions' in wallet) { + wallet.transactions = []; + } wallet._lastTxFetch = 0; wallet._lastBalanceFetch = 0; } @@ -383,17 +385,30 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._txs_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._txs_by_internal_index = {}; - // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_internal_index = {}; // @ts-expect-error: Need to fix later + wallet._hdWalletInstance._utxo = []; + // @ts-expect-error: Need to fix later + if ('transactions' in wallet._hdWalletInstance) { + // @ts-expect-error: Need to fix later + wallet._hdWalletInstance.transactions = []; + } + // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastTxFetch = 0; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastBalanceFetch = 0; - presentAlert({ message: msg }); } + + // Save changes to disk + await saveToDisk(); + presentAlert({ message: msg }); + + // Force refresh on UI + setHideTransactionsInWalletsList(!hideTransactionsInWalletsList); + setTimeout(() => setHideTransactionsInWalletsList(hideTransactionsInWalletsList), 500); }; const walletNameTextInputOnBlur = useCallback(async () => { From a6d66574cd6f10e3de0a9a8489494dcbff726ca8 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 18:03:17 -0400 Subject: [PATCH 24/33] Update WalletDetails.tsx --- screen/wallets/WalletDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 7fc2a9ec4..67afa4162 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -405,7 +405,7 @@ const WalletDetails: React.FC = () => { // Save changes to disk await saveToDisk(); presentAlert({ message: msg }); - + // Force refresh on UI setHideTransactionsInWalletsList(!hideTransactionsInWalletsList); setTimeout(() => setHideTransactionsInWalletsList(hideTransactionsInWalletsList), 500); From 63a3c6153471040ee7a0d7eabe7562a3a5a365c2 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 18:22:58 -0400 Subject: [PATCH 25/33] Revert "Update WalletDetails.tsx" This reverts commit a6d66574cd6f10e3de0a9a8489494dcbff726ca8. --- screen/wallets/WalletDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 67afa4162..7fc2a9ec4 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -405,7 +405,7 @@ const WalletDetails: React.FC = () => { // Save changes to disk await saveToDisk(); presentAlert({ message: msg }); - + // Force refresh on UI setHideTransactionsInWalletsList(!hideTransactionsInWalletsList); setTimeout(() => setHideTransactionsInWalletsList(hideTransactionsInWalletsList), 500); From e3fcbbb713fd1c0ce11e9f09f3fb9de85760b3cb Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 18:23:00 -0400 Subject: [PATCH 26/33] Revert "Update WalletDetails.tsx" This reverts commit 2376ef8be9b9b140630e8291b3b44bc32d35c9a6. --- screen/wallets/WalletDetails.tsx | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 7fc2a9ec4..04ba8eac0 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -369,12 +369,10 @@ const WalletDetails: React.FC = () => { if (wallet.type === HDSegwitBech32Wallet.type) { wallet._txs_by_external_index = {}; wallet._txs_by_internal_index = {}; + presentAlert({ message: msg }); + wallet._balances_by_external_index = {}; wallet._balances_by_internal_index = {}; - wallet._utxo = []; - if ('transactions' in wallet) { - wallet.transactions = []; - } wallet._lastTxFetch = 0; wallet._lastBalanceFetch = 0; } @@ -385,30 +383,17 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._txs_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._txs_by_internal_index = {}; + // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_internal_index = {}; // @ts-expect-error: Need to fix later - wallet._hdWalletInstance._utxo = []; - // @ts-expect-error: Need to fix later - if ('transactions' in wallet._hdWalletInstance) { - // @ts-expect-error: Need to fix later - wallet._hdWalletInstance.transactions = []; - } - // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastTxFetch = 0; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastBalanceFetch = 0; + presentAlert({ message: msg }); } - - // Save changes to disk - await saveToDisk(); - presentAlert({ message: msg }); - - // Force refresh on UI - setHideTransactionsInWalletsList(!hideTransactionsInWalletsList); - setTimeout(() => setHideTransactionsInWalletsList(hideTransactionsInWalletsList), 500); }; const walletNameTextInputOnBlur = useCallback(async () => { From 4b249edaa41d1c61dd8c1e6a988917760f5c4eb7 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 18:43:40 -0400 Subject: [PATCH 27/33] Update WalletDetails.tsx --- screen/wallets/WalletDetails.tsx | 37 +++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index 04ba8eac0..e863e48be 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -37,7 +37,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; import loc, { formatBalanceWithoutSuffix } from '../../loc'; import { BitcoinUnit, Chain } from '../../models/bitcoinUnits'; import { useStorage } from '../../hooks/context/useStorage'; -import { useFocusEffect, useRoute, RouteProp, usePreventRemove } from '@react-navigation/native'; +import { useFocusEffect, useRoute, RouteProp, usePreventRemove, CommonActions } from '@react-navigation/native'; import { LightningTransaction, Transaction, TWallet } from '../../class/wallets/types'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; import HeaderMenuButton from '../../components/HeaderMenuButton'; @@ -66,7 +66,7 @@ const WalletDetails: React.FC = () => { const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState( wallet.getHideTransactionsInWalletsList ? !wallet.getHideTransactionsInWalletsList() : true, ); - const { setOptions, navigate } = useExtendedNavigation(); + const { setOptions, navigate, dispatch } = useExtendedNavigation(); const { colors } = useTheme(); const [walletName, setWalletName] = useState(wallet.getLabel()); @@ -365,16 +365,16 @@ const WalletDetails: React.FC = () => { if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1); setBackdoorPressed(0); const msg = 'Transactions & balances purged. Pls go to main screen and back to rerender screen'; + let wasPurged = false; if (wallet.type === HDSegwitBech32Wallet.type) { wallet._txs_by_external_index = {}; wallet._txs_by_internal_index = {}; - presentAlert({ message: msg }); - wallet._balances_by_external_index = {}; wallet._balances_by_internal_index = {}; wallet._lastTxFetch = 0; wallet._lastBalanceFetch = 0; + wasPurged = true; } // @ts-expect-error: Need to fix later @@ -383,7 +383,6 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._txs_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._txs_by_internal_index = {}; - // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_external_index = {}; // @ts-expect-error: Need to fix later @@ -392,6 +391,34 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._lastTxFetch = 0; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastBalanceFetch = 0; + wasPurged = true; + } + + if (wasPurged) { + await saveToDisk(); + + // Find the WalletTransactions screen in the navigation state and reset just that screen. + // It can be multiple WalletTransactions screen. + dispatch(state => { + // Find the route that contains 'WalletTransactions' in the navigation stack + const routes = state.routes.map(route => { + if (route.name === 'WalletTransactions' && (route.params as { walletID: string })?.walletID === walletID) { + // Reset this specific route with the same params to force a refresh + return { + ...route, + key: `WalletTransactions-${walletID}-${Date.now()}`, // Force new key to ensure fresh mount + }; + } + return route; + }); + + return CommonActions.reset({ + ...state, + routes, + index: state.index, + }); + }); + presentAlert({ message: msg }); } }; From 957455478013ec86938c279aebc24f4d1a673d61 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 20:43:19 -0400 Subject: [PATCH 28/33] Update WalletDetails.tsx --- screen/wallets/WalletDetails.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/screen/wallets/WalletDetails.tsx b/screen/wallets/WalletDetails.tsx index e863e48be..5779e7d1d 100644 --- a/screen/wallets/WalletDetails.tsx +++ b/screen/wallets/WalletDetails.tsx @@ -309,6 +309,7 @@ const WalletDetails: React.FC = () => { walletID, }); }; + const navigateToXPub = () => navigate('WalletXpubRoot', { screen: 'WalletXpub', @@ -365,16 +366,16 @@ const WalletDetails: React.FC = () => { if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1); setBackdoorPressed(0); const msg = 'Transactions & balances purged. Pls go to main screen and back to rerender screen'; - let wasPurged = false; if (wallet.type === HDSegwitBech32Wallet.type) { wallet._txs_by_external_index = {}; wallet._txs_by_internal_index = {}; + presentAlert({ message: msg }); + wallet._balances_by_external_index = {}; wallet._balances_by_internal_index = {}; wallet._lastTxFetch = 0; wallet._lastBalanceFetch = 0; - wasPurged = true; } // @ts-expect-error: Need to fix later @@ -383,6 +384,7 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._txs_by_external_index = {}; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._txs_by_internal_index = {}; + // @ts-expect-error: Need to fix later wallet._hdWalletInstance._balances_by_external_index = {}; // @ts-expect-error: Need to fix later @@ -391,12 +393,6 @@ const WalletDetails: React.FC = () => { wallet._hdWalletInstance._lastTxFetch = 0; // @ts-expect-error: Need to fix later wallet._hdWalletInstance._lastBalanceFetch = 0; - wasPurged = true; - } - - if (wasPurged) { - await saveToDisk(); - // Find the WalletTransactions screen in the navigation state and reset just that screen. // It can be multiple WalletTransactions screen. dispatch(state => { From 3f82cb44496bac8cb7ea9a1a8a4f723f474df9b3 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sun, 2 Mar 2025 21:46:28 -0400 Subject: [PATCH 29/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 28 +++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 8bd8d31ff..bcc17fe5a 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -468,22 +468,6 @@ const WalletTransactions: React.FC = ({ route }) => { setHeaderHeight(height); }, []); - const stickyHeader = useMemo(() => { - return ( - - {wallet ? ( - - ) : null} - - ); - }, [handleWalletBalanceVisibilityChange, handleWalletUnitChange, headerTranslate, onManageFundsPressed, wallet, handleHeaderLayout]); - const renderHeader = useCallback(() => { return ( @@ -507,7 +491,17 @@ const WalletTransactions: React.FC = ({ route }) => { return ( - {stickyHeader} + + {wallet ? ( + + ) : null} + getItemLayout={getItemLayout} updateCellsBatchingPeriod={30} From 0bdfc6fa85711dc22a1f4cdd8d5ed47c92e79560 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 3 Mar 2025 01:17:43 -0400 Subject: [PATCH 30/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index bcc17fe5a..e0e323f97 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -514,7 +514,6 @@ const WalletTransactions: React.FC = ({ route }) => { keyExtractor={_keyExtractor} renderItem={renderItem} initialNumToRender={10} - removeClippedSubviews testID="TransactionsListView" contentInsetAdjustmentBehavior="automatic" automaticallyAdjustContentInsets From ec2bc5e6273318390bdc2494635f4b9a86c06ad6 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 3 Mar 2025 03:30:19 -0400 Subject: [PATCH 31/33] wip --- screen/wallets/WalletTransactions.tsx | 14 ++++++-------- tests/e2e/bluewallet2.spec.js | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index e0e323f97..74f1fe8c2 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -13,7 +13,6 @@ import { Text, View, Animated, - RefreshControl, LayoutChangeEvent, } from 'react-native'; import { Icon } from '@rneui/themed'; @@ -298,7 +297,6 @@ const WalletTransactions: React.FC = ({ route }) => { }); const renderItem = useCallback( - // eslint-disable-next-line react/no-unused-prop-types ({ item }: { item: Transaction }) => { return ; }, @@ -468,6 +466,9 @@ const WalletTransactions: React.FC = ({ route }) => { setHeaderHeight(height); }, []); + const refreshProps = + !isDesktop && !isElectrumDisabled ? { onRefresh: refreshTransactions, progressViewOffset: headerHeight, refreshing: isLoading } : {}; + const renderHeader = useCallback(() => { return ( @@ -517,7 +518,8 @@ const WalletTransactions: React.FC = ({ route }) => { testID="TransactionsListView" contentInsetAdjustmentBehavior="automatic" automaticallyAdjustContentInsets - contentContainerStyle={{ backgroundColor: colors.background, marginTop: headerHeight }} + automaticallyAdjustsScrollIndicatorInsets + contentContainerStyle={{ backgroundColor: colors.background, paddingTop: headerHeight }} maxToRenderPerBatch={15} onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })} scrollEventThrottle={16} @@ -529,11 +531,7 @@ const WalletTransactions: React.FC = ({ route }) => { {isLightning() && {loc.wallets.list_empty_txs2_lightning}} } - refreshControl={ - !isElectrumDisabled && !isDesktop ? ( - refreshTransactions(true)} progressViewOffset={headerHeight} /> - ) : undefined - } + {...refreshProps} /> {wallet?.allowReceive() && ( diff --git a/tests/e2e/bluewallet2.spec.js b/tests/e2e/bluewallet2.spec.js index ca084683f..7bdda1197 100644 --- a/tests/e2e/bluewallet2.spec.js +++ b/tests/e2e/bluewallet2.spec.js @@ -758,7 +758,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await waitForId('TransactionsListEmpty'); assert.strictEqual(await countElements('TransactionListItem'), 0); - await element(by.id('TransactionsListView')).swipe('down', 'slow'); // pul-to-refresh + await element(by.id('TransactionsListView')).swipe('down', 'slow', 0.5, 0.5); // pul-to-refresh // asserting balance and txs loaded: await waitForText('0.00105526'); // the wait inside allows network request to propagate From e519360d89f6ee1c446a0e2d58b5903f7251b136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Mon, 3 Mar 2025 04:25:38 -0400 Subject: [PATCH 32/33] Update bluewallet2.spec.js --- tests/e2e/bluewallet2.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/bluewallet2.spec.js b/tests/e2e/bluewallet2.spec.js index 7bdda1197..89b8a9e8a 100644 --- a/tests/e2e/bluewallet2.spec.js +++ b/tests/e2e/bluewallet2.spec.js @@ -758,7 +758,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await waitForId('TransactionsListEmpty'); assert.strictEqual(await countElements('TransactionListItem'), 0); - await element(by.id('TransactionsListView')).swipe('down', 'slow', 0.5, 0.5); // pul-to-refresh + await element(by.id('TransactionsListView')).swipe('down', 'slow', 0.5, 0.3); // pul-to-refresh // asserting balance and txs loaded: await waitForText('0.00105526'); // the wait inside allows network request to propagate From b81879e9bd23d2ddaa86e26795946a5ecd5451db Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Mon, 3 Mar 2025 14:59:49 -0400 Subject: [PATCH 33/33] Update WalletTransactions.tsx --- screen/wallets/WalletTransactions.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index a671acbc0..479ea3a89 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -493,7 +493,7 @@ const WalletTransactions: React.FC = ({ route }) => { }, [colors.background, stylesHook.listHeaderText, wallet, saveToDisk]); return ( - + {wallet ? ( = ({ route }) => { keyExtractor={_keyExtractor} renderItem={renderItem} initialNumToRender={10} + style={{ marginTop: headerHeight }} removeClippedSubviews contentContainerStyle={{ backgroundColor: colors.background }} contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} @@ -524,6 +525,7 @@ const WalletTransactions: React.FC = ({ route }) => { testID="TransactionsListView" onScroll={handleScroll} scrollEventThrottle={16} + {...refreshProps} ListEmptyComponent={ @@ -577,7 +579,7 @@ const WalletTransactions: React.FC = ({ route }) => { url={`https://www.blockonomics.co/#/search?q=${wallet.getXpub()}`} /> ) : null} - + ); };