mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-13 11:09:20 +01:00
Merge pull request #7631 from BlueWallet/headr
REF: Wallet tranaction header animation
This commit is contained in:
commit
28e2e343b8
9 changed files with 160 additions and 196 deletions
|
@ -170,7 +170,7 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
|
||||||
>
|
>
|
||||||
<Image source={imageSource} style={styles.chainIcon} />
|
<Image source={imageSource} style={styles.chainIcon} />
|
||||||
|
|
||||||
<Text testID="WalletLabel" numberOfLines={1} style={styles.walletLabel} selectable>
|
<Text testID="WalletLabel" numberOfLines={2} style={styles.walletLabel} selectable>
|
||||||
{wallet.getLabel()}
|
{wallet.getLabel()}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.walletBalanceAndUnitContainer}>
|
<View style={styles.walletBalanceAndUnitContainer}>
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { useCallback, useMemo } from 'react';
|
||||||
const requiresBiometrics = [
|
const requiresBiometrics = [
|
||||||
'WalletExportRoot',
|
'WalletExportRoot',
|
||||||
'WalletXpubRoot',
|
'WalletXpubRoot',
|
||||||
'ViewEditMultisigCosignersRoot',
|
'ViewEditMultisigCosigners',
|
||||||
'ExportMultisigCoordinationSetupRoot',
|
'ExportMultisigCoordinationSetupRoot',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ import ReceiveDetailsStackRoot from './ReceiveDetailsStack';
|
||||||
import ScanLndInvoiceRoot from './ScanLndInvoiceStack';
|
import ScanLndInvoiceRoot from './ScanLndInvoiceStack';
|
||||||
import SendDetailsStack from './SendDetailsStack';
|
import SendDetailsStack from './SendDetailsStack';
|
||||||
import SignVerifyStackRoot from './SignVerifyStack';
|
import SignVerifyStackRoot from './SignVerifyStack';
|
||||||
import ViewEditMultisigCosignersStackRoot from './ViewEditMultisigCosignersStack';
|
|
||||||
import WalletExportStack from './WalletExportStack';
|
import WalletExportStack from './WalletExportStack';
|
||||||
import WalletXpubStackRoot from './WalletXpubStack';
|
import WalletXpubStackRoot from './WalletXpubStack';
|
||||||
import SettingsButton from '../components/icons/SettingsButton';
|
import SettingsButton from '../components/icons/SettingsButton';
|
||||||
|
@ -66,6 +65,7 @@ import ToolsScreen from '../screen/settings/tools';
|
||||||
import SettingsPrivacy from '../screen/settings/SettingsPrivacy';
|
import SettingsPrivacy from '../screen/settings/SettingsPrivacy';
|
||||||
import { ScanQRCodeComponent } from './LazyLoadScanQRCodeStack';
|
import { ScanQRCodeComponent } from './LazyLoadScanQRCodeStack';
|
||||||
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
|
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
|
||||||
|
import { ViewEditMultisigCosignersComponent } from './LazyLoadViewEditMultisigCosignersStack';
|
||||||
|
|
||||||
const DetailViewStackScreensStack = () => {
|
const DetailViewStackScreensStack = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -342,8 +342,8 @@ const DetailViewStackScreensStack = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DetailViewStack.Screen
|
<DetailViewStack.Screen
|
||||||
name="ViewEditMultisigCosignersRoot"
|
name="ViewEditMultisigCosigners"
|
||||||
component={ViewEditMultisigCosignersStackRoot}
|
component={ViewEditMultisigCosignersComponent}
|
||||||
options={{ ...NavigationDefaultOptions, ...StatusBarLightOptions, gestureEnabled: false, fullScreenGestureEnabled: false }}
|
options={{ ...NavigationDefaultOptions, ...StatusBarLightOptions, gestureEnabled: false, fullScreenGestureEnabled: false }}
|
||||||
initialParams={{ walletID: undefined, cosigners: undefined }}
|
initialParams={{ walletID: undefined, cosigners: undefined }}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -79,7 +79,7 @@ export type DetailViewStackParamList = {
|
||||||
ReleaseNotes: undefined;
|
ReleaseNotes: undefined;
|
||||||
ToolsScreen: undefined;
|
ToolsScreen: undefined;
|
||||||
SettingsPrivacy: undefined;
|
SettingsPrivacy: undefined;
|
||||||
ViewEditMultisigCosignersRoot: { walletID: string; cosigners: string[] };
|
ViewEditMultisigCosigners: { walletID: string; cosigners: string[]; onBarScanned?: string };
|
||||||
WalletXpubRoot: undefined;
|
WalletXpubRoot: undefined;
|
||||||
SignVerifyRoot: {
|
SignVerifyRoot: {
|
||||||
screen: 'SignVerify';
|
screen: 'SignVerify';
|
||||||
|
|
|
@ -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<ViewEditMultisigCosignersStackParamList>();
|
|
||||||
|
|
||||||
const ViewEditMultisigCosignersStackRoot = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
|
||||||
<Stack.Screen
|
|
||||||
name="ViewEditMultisigCosigners"
|
|
||||||
component={ViewEditMultisigCosignersComponent}
|
|
||||||
options={navigationStyle({
|
|
||||||
headerBackVisible: false,
|
|
||||||
title: loc.multisig.manage_keys,
|
|
||||||
})(theme)}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="ScanQRCode"
|
|
||||||
component={ScanQRCodeComponent}
|
|
||||||
options={navigationStyle({
|
|
||||||
headerShown: false,
|
|
||||||
statusBarHidden: true,
|
|
||||||
presentation: 'fullScreenModal',
|
|
||||||
headerShadowVisible: false,
|
|
||||||
})(theme)}
|
|
||||||
/>
|
|
||||||
</Stack.Navigator>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ViewEditMultisigCosignersStackRoot;
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
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 {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Alert,
|
Alert,
|
||||||
|
@ -18,7 +18,15 @@ import {
|
||||||
import { Badge, Icon } from '@rneui/themed';
|
import { Badge, Icon } from '@rneui/themed';
|
||||||
import { isDesktop } from '../../blue_modules/environment';
|
import { isDesktop } from '../../blue_modules/environment';
|
||||||
import { encodeUR } from '../../blue_modules/ur';
|
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 { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class';
|
||||||
import presentAlert from '../../components/Alert';
|
import presentAlert from '../../components/Alert';
|
||||||
import BottomModal, { BottomModalHandle } from '../../components/BottomModal';
|
import BottomModal, { BottomModalHandle } from '../../components/BottomModal';
|
||||||
|
@ -40,14 +48,14 @@ import { useStorage } from '../../hooks/context/useStorage';
|
||||||
import ToolTipMenu from '../../components/TooltipMenu';
|
import ToolTipMenu from '../../components/TooltipMenu';
|
||||||
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||||
import { useSettings } from '../../hooks/context/useSettings';
|
import { useSettings } from '../../hooks/context/useSettings';
|
||||||
import { ViewEditMultisigCosignersStackParamList } from '../../navigation/ViewEditMultisigCosignersStack';
|
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
import SafeArea from '../../components/SafeArea';
|
import SafeArea from '../../components/SafeArea';
|
||||||
import { TWallet } from '../../class/wallets/types';
|
import { TWallet } from '../../class/wallets/types';
|
||||||
import { AddressInputScanButton } from '../../components/AddressInputScanButton';
|
import { AddressInputScanButton } from '../../components/AddressInputScanButton';
|
||||||
|
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||||
|
|
||||||
type RouteParams = RouteProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
type RouteParams = RouteProp<DetailViewStackParamList, 'ViewEditMultisigCosigners'>;
|
||||||
type NavigationProp = NativeStackNavigationProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
type NavigationProp = NativeStackNavigationProp<DetailViewStackParamList, 'ViewEditMultisigCosigners'>;
|
||||||
|
|
||||||
const ViewEditMultisigCosigners: React.FC = () => {
|
const ViewEditMultisigCosigners: React.FC = () => {
|
||||||
const hasLoaded = useRef(false);
|
const hasLoaded = useRef(false);
|
||||||
|
@ -169,9 +177,11 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||||
setIsSaveButtonDisabled(true);
|
setIsSaveButtonDisabled(true);
|
||||||
setWalletsWithNewOrder(newWallets);
|
setWalletsWithNewOrder(newWallets);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dispatch(
|
const popTo = StackActions.popTo('WalletTransactions', {
|
||||||
CommonActions.navigate({ name: 'WalletTransactions', params: { walletID: wallet.getID(), walletType: MultisigHDWallet.type } }),
|
walletID,
|
||||||
);
|
walletType: wallet.type,
|
||||||
|
});
|
||||||
|
dispatch(popTo);
|
||||||
}, 500);
|
}, 500);
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
@ -560,6 +570,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||||
|
|
||||||
{!isLoading && (
|
{!isLoading && (
|
||||||
<>
|
<>
|
||||||
|
<BlueSpacing40 />
|
||||||
<AddressInputScanButton
|
<AddressInputScanButton
|
||||||
beforePress={async () => {
|
beforePress={async () => {
|
||||||
await provideMnemonicsModalRef.current?.dismiss();
|
await provideMnemonicsModalRef.current?.dismiss();
|
||||||
|
@ -568,7 +579,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||||
type="link"
|
type="link"
|
||||||
onChangeText={setImportText}
|
onChangeText={setImportText}
|
||||||
/>
|
/>
|
||||||
<BlueSpacing20 />
|
<BlueSpacing40 />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -37,7 +37,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||||
import { useStorage } from '../../hooks/context/useStorage';
|
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 { LightningTransaction, Transaction, TWallet } from '../../class/wallets/types';
|
||||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||||
|
@ -66,7 +66,7 @@ const WalletDetails: React.FC = () => {
|
||||||
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState<boolean>(
|
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState<boolean>(
|
||||||
wallet.getHideTransactionsInWalletsList ? !wallet.getHideTransactionsInWalletsList() : true,
|
wallet.getHideTransactionsInWalletsList ? !wallet.getHideTransactionsInWalletsList() : true,
|
||||||
);
|
);
|
||||||
const { setOptions, navigate } = useExtendedNavigation();
|
const { setOptions, navigate, dispatch } = useExtendedNavigation();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const [walletName, setWalletName] = useState<string>(wallet.getLabel());
|
const [walletName, setWalletName] = useState<string>(wallet.getLabel());
|
||||||
|
|
||||||
|
@ -305,13 +305,11 @@ const WalletDetails: React.FC = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const navigateToViewEditCosigners = () => {
|
const navigateToViewEditCosigners = () => {
|
||||||
navigate('ViewEditMultisigCosignersRoot', {
|
navigate('ViewEditMultisigCosigners', {
|
||||||
screen: 'ViewEditMultisigCosigners',
|
walletID,
|
||||||
params: {
|
|
||||||
walletID,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToXPub = () =>
|
const navigateToXPub = () =>
|
||||||
navigate('WalletXpubRoot', {
|
navigate('WalletXpubRoot', {
|
||||||
screen: 'WalletXpub',
|
screen: 'WalletXpub',
|
||||||
|
@ -395,6 +393,28 @@ const WalletDetails: React.FC = () => {
|
||||||
wallet._hdWalletInstance._lastTxFetch = 0;
|
wallet._hdWalletInstance._lastTxFetch = 0;
|
||||||
// @ts-expect-error: Need to fix later
|
// @ts-expect-error: Need to fix later
|
||||||
wallet._hdWalletInstance._lastBalanceFetch = 0;
|
wallet._hdWalletInstance._lastBalanceFetch = 0;
|
||||||
|
// 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 });
|
presentAlert({ message: msg });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,16 +5,15 @@ import {
|
||||||
Alert,
|
Alert,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
findNodeHandle,
|
findNodeHandle,
|
||||||
FlatList,
|
|
||||||
I18nManager,
|
I18nManager,
|
||||||
InteractionManager,
|
InteractionManager,
|
||||||
LayoutAnimation,
|
LayoutAnimation,
|
||||||
PixelRatio,
|
PixelRatio,
|
||||||
ScrollView,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
RefreshControl,
|
Animated,
|
||||||
|
LayoutChangeEvent,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { Icon } from '@rneui/themed';
|
import { Icon } from '@rneui/themed';
|
||||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||||
|
@ -38,7 +37,6 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||||
import { Transaction, TWallet } from '../../class/wallets/types';
|
import { Transaction, TWallet } from '../../class/wallets/types';
|
||||||
import getWalletTransactionsOptions from '../../navigation/helpers/getWalletTransactionsOptions';
|
import getWalletTransactionsOptions from '../../navigation/helpers/getWalletTransactionsOptions';
|
||||||
import { presentWalletExportReminder } from '../../helpers/presentWalletExportReminder';
|
|
||||||
import selectWallet from '../../helpers/select-wallet';
|
import selectWallet from '../../helpers/select-wallet';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import useMenuElements from '../../hooks/useMenuElements';
|
import useMenuElements from '../../hooks/useMenuElements';
|
||||||
|
@ -46,7 +44,6 @@ import { useSettings } from '../../hooks/context/useSettings';
|
||||||
import { getClipboardContent } from '../../blue_modules/clipboard';
|
import { getClipboardContent } from '../../blue_modules/clipboard';
|
||||||
import HandOffComponent from '../../components/HandOffComponent';
|
import HandOffComponent from '../../components/HandOffComponent';
|
||||||
import { HandOffActivityType } from '../../components/types';
|
import { HandOffActivityType } from '../../components/types';
|
||||||
import WalletGradient from '../../class/wallet-gradient';
|
|
||||||
|
|
||||||
const buttonFontSize =
|
const buttonFontSize =
|
||||||
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||||
|
@ -56,6 +53,7 @@ const buttonFontSize =
|
||||||
type WalletTransactionsProps = NativeStackScreenProps<DetailViewStackParamList, 'WalletTransactions'>;
|
type WalletTransactionsProps = NativeStackScreenProps<DetailViewStackParamList, 'WalletTransactions'>;
|
||||||
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletTransactions'>;
|
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletTransactions'>;
|
||||||
type TransactionListItem = Transaction & { type: 'transaction' | 'header' };
|
type TransactionListItem = Transaction & { type: 'transaction' | 'header' };
|
||||||
|
|
||||||
const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
const { wallets, saveToDisk, setSelectedWalletID } = useStorage();
|
const { wallets, saveToDisk, setSelectedWalletID } = useStorage();
|
||||||
const { setReloadTransactionsMenuActionFunction } = useMenuElements();
|
const { setReloadTransactionsMenuActionFunction } = useMenuElements();
|
||||||
|
@ -74,6 +72,14 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
const [lastFetchTimestamp, setLastFetchTimestamp] = useState(() => wallet?._lastTxFetch || 0);
|
const [lastFetchTimestamp, setLastFetchTimestamp] = useState(() => wallet?._lastTxFetch || 0);
|
||||||
const [fetchFailures, setFetchFailures] = useState(0);
|
const [fetchFailures, setFetchFailures] = useState(0);
|
||||||
const MAX_FAILURES = 3;
|
const MAX_FAILURES = 3;
|
||||||
|
const scrollY = useRef(new Animated.Value(0)).current;
|
||||||
|
const [headerHeight, setHeaderHeight] = useState(0);
|
||||||
|
|
||||||
|
const headerTranslate = scrollY.interpolate({
|
||||||
|
inputRange: [0, headerHeight],
|
||||||
|
outputRange: [0, -headerHeight],
|
||||||
|
extrapolate: 'clamp',
|
||||||
|
});
|
||||||
|
|
||||||
const stylesHook = StyleSheet.create({
|
const stylesHook = StyleSheet.create({
|
||||||
listHeaderText: {
|
listHeaderText: {
|
||||||
|
@ -256,11 +262,8 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const navigateToViewEditCosigners = useCallback(() => {
|
const navigateToViewEditCosigners = useCallback(() => {
|
||||||
navigate('ViewEditMultisigCosignersRoot', {
|
navigate('ViewEditMultisigCosigners', {
|
||||||
screen: 'ViewEditMultisigCosigners',
|
walletID,
|
||||||
params: {
|
|
||||||
walletID,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}, [navigate, walletID]);
|
}, [navigate, walletID]);
|
||||||
|
|
||||||
|
@ -280,9 +283,11 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
walletID,
|
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) => ({
|
const getItemLayout = (_: any, index: number) => ({
|
||||||
|
@ -397,7 +402,8 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
console.debug('Next screen is focused, clearing reloadTransactionsMenuActionFunction');
|
console.debug('Next screen is focused, clearing reloadTransactionsMenuActionFunction');
|
||||||
setReloadTransactionsMenuActionFunction(() => {});
|
setReloadTransactionsMenuActionFunction(() => {});
|
||||||
};
|
};
|
||||||
}, [setReloadTransactionsMenuActionFunction, refreshTransactions]),
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [balance, setBalance] = useState(wallet ? wallet.getBalance() : 0);
|
const [balance, setBalance] = useState(wallet ? wallet.getBalance() : 0);
|
||||||
|
@ -418,8 +424,9 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
const handleScroll = useCallback(
|
const handleScroll = useCallback(
|
||||||
(event: any) => {
|
(event: any) => {
|
||||||
const offsetY = event.nativeEvent.contentOffset.y;
|
const offsetY = event.nativeEvent.contentOffset.y;
|
||||||
const combinedHeight = 180;
|
// Use the measured header height to determine when to show/hide the header title
|
||||||
if (offsetY < combinedHeight) {
|
const threshold = headerHeight * 0.75;
|
||||||
|
if (offsetY < threshold) {
|
||||||
setOptions({ ...getWalletTransactionsOptions({ route }), headerTitle: undefined });
|
setOptions({ ...getWalletTransactionsOptions({ route }), headerTitle: undefined });
|
||||||
} else {
|
} else {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
|
@ -427,104 +434,82 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[navigation, wallet, walletBalance, setOptions, route],
|
[navigation, wallet, walletBalance, setOptions, route, headerHeight],
|
||||||
);
|
);
|
||||||
|
|
||||||
const ListHeaderComponent = useCallback(
|
// Extracted named callbacks
|
||||||
() =>
|
const handleWalletUnitChange = useCallback(
|
||||||
wallet ? (
|
async (selectedUnit: any) => {
|
||||||
<>
|
if (wallet) {
|
||||||
<TransactionsNavigationHeader
|
wallet.preferredBalanceUnit = selectedUnit;
|
||||||
wallet={wallet}
|
await saveToDisk();
|
||||||
onWalletUnitChange={async selectedUnit => {
|
}
|
||||||
wallet.preferredBalanceUnit = selectedUnit;
|
},
|
||||||
await saveToDisk();
|
[wallet, 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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<>
|
|
||||||
<View style={[styles.flex, { backgroundColor: colors.background }]}>
|
|
||||||
<View style={styles.listHeaderTextRow}>
|
|
||||||
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={{ backgroundColor: colors.background }}>
|
|
||||||
{wallet.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && (
|
|
||||||
<WatchOnlyWarning
|
|
||||||
handleDismiss={() => {
|
|
||||||
wallet.isWatchOnlyWarningVisible = false;
|
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
|
|
||||||
saveToDisk();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
</>
|
|
||||||
) : undefined,
|
|
||||||
[
|
|
||||||
wallet,
|
|
||||||
colors.background,
|
|
||||||
stylesHook.listHeaderText,
|
|
||||||
saveToDisk,
|
|
||||||
isBiometricUseCapableAndEnabled,
|
|
||||||
navigateToViewEditCosigners,
|
|
||||||
onManageFundsPressed,
|
|
||||||
navigate,
|
|
||||||
walletID,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleHeaderLayout = useCallback((event: LayoutChangeEvent) => {
|
||||||
|
const { height } = event.nativeEvent.layout;
|
||||||
|
setHeaderHeight(height);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const refreshProps =
|
||||||
|
!isDesktop && !isElectrumDisabled ? { onRefresh: refreshTransactions, progressViewOffset: headerHeight, refreshing: isLoading } : {};
|
||||||
|
|
||||||
|
const renderHeader = useCallback(() => {
|
||||||
|
return (
|
||||||
|
<View style={{ backgroundColor: colors.background }}>
|
||||||
|
<View style={styles.listHeaderTextRow}>
|
||||||
|
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={{ backgroundColor: colors.background }}>
|
||||||
|
{wallet?.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && (
|
||||||
|
<WatchOnlyWarning
|
||||||
|
handleDismiss={() => {
|
||||||
|
wallet.isWatchOnlyWarningVisible = false;
|
||||||
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
|
||||||
|
saveToDisk();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}, [colors.background, stylesHook.listHeaderText, wallet, saveToDisk]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.flex, { backgroundColor: colors.background }]}>
|
<Animated.View style={styles.container}>
|
||||||
{/* The color of the refresh indicator. Temporary hack */}
|
<Animated.View style={[styles.stickyHeader, { transform: [{ translateY: headerTranslate }] }]} onLayout={handleHeaderLayout}>
|
||||||
<View
|
{wallet ? (
|
||||||
style={[
|
<TransactionsNavigationHeader
|
||||||
styles.refreshIndicatorBackground,
|
wallet={wallet}
|
||||||
{ backgroundColor: wallet ? WalletGradient.headerColorFor(wallet.type) : colors.background },
|
onWalletUnitChange={handleWalletUnitChange}
|
||||||
]}
|
unit={wallet.preferredBalanceUnit}
|
||||||
testID="TransactionsListView"
|
onWalletBalanceVisibilityChange={handleWalletBalanceVisibilityChange}
|
||||||
/>
|
onManageFundsPressed={onManageFundsPressed}
|
||||||
|
/>
|
||||||
<FlatList<Transaction>
|
) : null}
|
||||||
|
</Animated.View>
|
||||||
|
<Animated.FlatList<Transaction>
|
||||||
getItemLayout={getItemLayout}
|
getItemLayout={getItemLayout}
|
||||||
updateCellsBatchingPeriod={50}
|
updateCellsBatchingPeriod={50}
|
||||||
onEndReachedThreshold={0.3}
|
onEndReachedThreshold={0.3}
|
||||||
|
ListHeaderComponent={renderHeader}
|
||||||
onEndReached={loadMoreTransactions}
|
onEndReached={loadMoreTransactions}
|
||||||
ListFooterComponent={renderListFooterComponent}
|
ListFooterComponent={renderListFooterComponent}
|
||||||
data={getTransactions(limit)}
|
data={getTransactions(limit)}
|
||||||
|
@ -532,26 +517,22 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
keyExtractor={_keyExtractor}
|
keyExtractor={_keyExtractor}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
initialNumToRender={10}
|
initialNumToRender={10}
|
||||||
|
style={{ marginTop: headerHeight }}
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
contentContainerStyle={{ backgroundColor: colors.background }}
|
contentContainerStyle={{ backgroundColor: colors.background }}
|
||||||
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
|
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
|
||||||
maxToRenderPerBatch={10}
|
maxToRenderPerBatch={10}
|
||||||
|
testID="TransactionsListView"
|
||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
scrollEventThrottle={16}
|
scrollEventThrottle={16}
|
||||||
stickyHeaderHiddenOnScroll
|
{...refreshProps}
|
||||||
ListHeaderComponent={ListHeaderComponent}
|
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
<ScrollView style={[styles.flex, { backgroundColor: colors.background }]} contentContainerStyle={styles.scrollViewContent}>
|
<View style={[styles.flex, { backgroundColor: colors.background }]} testID="TransactionsListEmpty">
|
||||||
<Text numberOfLines={0} style={styles.emptyTxs} testID="TransactionsListEmpty">
|
<Text numberOfLines={0} style={styles.emptyTxs}>
|
||||||
{(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
|
{(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
|
||||||
</Text>
|
</Text>
|
||||||
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
|
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
|
||||||
</ScrollView>
|
</View>
|
||||||
}
|
|
||||||
refreshControl={
|
|
||||||
!isDesktop && !isElectrumDisabled ? (
|
|
||||||
<RefreshControl refreshing={isLoading} onRefresh={() => refreshTransactions(true)} tintColor={colors.msSuccessCheck} />
|
|
||||||
) : undefined
|
|
||||||
}
|
}
|
||||||
windowSize={15}
|
windowSize={15}
|
||||||
maintainVisibleContentPosition={{
|
maintainVisibleContentPosition={{
|
||||||
|
@ -598,27 +579,27 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||||
url={`https://www.blockonomics.co/#/search?q=${wallet.getXpub()}`}
|
url={`https://www.blockonomics.co/#/search?q=${wallet.getXpub()}`}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WalletTransactions;
|
export default WalletTransactions;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1 },
|
||||||
flex: { flex: 1 },
|
flex: { flex: 1 },
|
||||||
scrollViewContent: { flex: 1, justifyContent: 'center', paddingHorizontal: 16, paddingBottom: 500 },
|
|
||||||
activityIndicator: { marginVertical: 20 },
|
activityIndicator: { marginVertical: 20 },
|
||||||
listHeaderTextRow: { flex: 1, margin: 16, flexDirection: 'row', justifyContent: 'space-between' },
|
listHeaderTextRow: { padding: 16, flexDirection: 'row' },
|
||||||
listHeaderText: { marginTop: 8, marginBottom: 8, fontWeight: 'bold', fontSize: 24 },
|
listHeaderText: { fontWeight: 'bold', fontSize: 24 },
|
||||||
refreshIndicatorBackground: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
height: 140,
|
|
||||||
},
|
|
||||||
emptyTxs: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', marginVertical: 16 },
|
emptyTxs: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', marginVertical: 16 },
|
||||||
emptyTxsLightning: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', fontWeight: '600' },
|
emptyTxsLightning: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', fontWeight: '600' },
|
||||||
sendIcon: { transform: [{ rotate: I18nManager.isRTL ? '-225deg' : '225deg' }] },
|
sendIcon: { transform: [{ rotate: I18nManager.isRTL ? '-225deg' : '225deg' }] },
|
||||||
receiveIcon: { transform: [{ rotate: I18nManager.isRTL ? '45deg' : '-45deg' }] },
|
receiveIcon: { transform: [{ rotate: I18nManager.isRTL ? '45deg' : '-45deg' }] },
|
||||||
|
stickyHeader: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -758,7 +758,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||||
await waitForId('TransactionsListEmpty');
|
await waitForId('TransactionsListEmpty');
|
||||||
assert.strictEqual(await countElements('TransactionListItem'), 0);
|
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.3); // pul-to-refresh
|
||||||
|
|
||||||
// asserting balance and txs loaded:
|
// asserting balance and txs loaded:
|
||||||
await waitForText('0.00105526'); // the wait inside allows network request to propagate
|
await waitForText('0.00105526'); // the wait inside allows network request to propagate
|
||||||
|
|
Loading…
Add table
Reference in a new issue