Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec86657a15 | ||
|
|
d06566f0cc | ||
|
|
fd653588b7 | ||
|
|
d663a13cbd | ||
|
|
ad6b7de090 | ||
|
|
978acedc4c |
@ -1,9 +1,7 @@
|
||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
||||
import { StyleSheet, ViewStyle, ActivityIndicator, Platform, Animated, View, Text, Pressable } from 'react-native';
|
||||
import { StyleSheet, ViewStyle, ActivityIndicator, Platform, Animated, View } from 'react-native';
|
||||
import { useLocale } from '@react-navigation/native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import { ExtendedTransaction, LightningTransaction, Transaction, TWallet } from '../class/wallets/types';
|
||||
import loc from '../loc';
|
||||
import { TransactionListItem } from './TransactionListItem';
|
||||
import { useTheme } from './themes';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
@ -16,6 +14,7 @@ import { MultisigHDWallet } from '../class/wallets/multisig-hd-wallet';
|
||||
import { AbstractHDElectrumWallet } from '../class/wallets/abstract-hd-electrum-wallet';
|
||||
import { WatchOnlyWallet } from '../class/wallets/watch-only-wallet';
|
||||
import WalletListItem from './WalletListItem';
|
||||
import SwipeableWalletRow from './SwipeableWalletRow';
|
||||
|
||||
const getHdElectrumWallet = (wallet: TWallet): AbstractHDElectrumWallet | undefined => {
|
||||
const w: unknown = wallet;
|
||||
@ -60,6 +59,7 @@ interface ManageWalletsListItemProps {
|
||||
item: Item;
|
||||
isDraggingDisabled: boolean;
|
||||
handleToggleHideBalance: (wallet: TWallet) => void;
|
||||
handleChangeWalletUnit: (wallet: TWallet) => void;
|
||||
state: { wallets: TWallet[]; searchQuery: string; isSearchFocused?: boolean };
|
||||
navigateToWallet: (wallet: TWallet) => void;
|
||||
navigateToAddress: (address: string, walletID: string) => void;
|
||||
@ -81,6 +81,7 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
isPlaceHolder = false,
|
||||
navigateToWallet,
|
||||
navigateToAddress,
|
||||
handleChangeWalletUnit,
|
||||
renderHighlightedText,
|
||||
onPressIn,
|
||||
onPressOut,
|
||||
@ -94,7 +95,8 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const prevIsActive = useRef(isActive);
|
||||
const swipeableRef = useRef<Swipeable | null>(null);
|
||||
const rowScale = useRef(new Animated.Value(1)).current;
|
||||
const swipeableRef = useRef<import('react-native-gesture-handler').Swipeable | null>(null);
|
||||
const swipeInProgressRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -104,7 +106,17 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
prevIsActive.current = isActive;
|
||||
}, [isActive]);
|
||||
|
||||
useEffect(() => {
|
||||
Animated.spring(rowScale, {
|
||||
toValue: isActive ? 1.04 : 1,
|
||||
friction: 7,
|
||||
tension: 90,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}, [isActive, rowScale]);
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
if (globalDragActive) return;
|
||||
if (swipeInProgressRef.current) return;
|
||||
if (item.type === ItemType.WalletSection) {
|
||||
setIsLoading(true);
|
||||
@ -113,9 +125,10 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
} else if (item.type === ItemType.AddressSection) {
|
||||
navigateToAddress(item.data.address, item.data.walletID);
|
||||
}
|
||||
}, [item, navigateToWallet, navigateToAddress]);
|
||||
}, [globalDragActive, item, navigateToWallet, navigateToAddress]);
|
||||
|
||||
const startDrag = useCallback(() => {
|
||||
if (globalDragActive || isDraggingDisabled) return;
|
||||
if (swipeInProgressRef.current) {
|
||||
swipeableRef.current?.close?.();
|
||||
return;
|
||||
@ -124,7 +137,7 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
if (drag) {
|
||||
drag();
|
||||
}
|
||||
}, [drag]);
|
||||
}, [drag, globalDragActive, isDraggingDisabled]);
|
||||
|
||||
if (isLoading) {
|
||||
return <ActivityIndicator size="large" color={colors.brandingColor} />;
|
||||
@ -140,26 +153,15 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
|
||||
const onToggle = () => {
|
||||
handleToggleHideBalance(wallet);
|
||||
swipeableRef.current?.close?.();
|
||||
};
|
||||
|
||||
const renderRightActions = () => (
|
||||
<View style={styles.rightActionsContainer}>
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
styles.rightAction,
|
||||
{ backgroundColor: colors.buttonBackgroundColor },
|
||||
pressed && styles.rightActionPressed,
|
||||
]}
|
||||
onPress={onToggle}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<Text style={[styles.rightActionText, { color: colors.buttonTextColor }]}>
|
||||
{isHidden ? loc.wallets.swipe_balance_show : loc.wallets.swipe_balance_hide}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
const onChangeUnit = () => {
|
||||
handleChangeWalletUnit(wallet);
|
||||
};
|
||||
|
||||
const onSwipeStateChange = (isSwipeInProgress: boolean) => {
|
||||
swipeInProgressRef.current = isSwipeInProgress;
|
||||
};
|
||||
|
||||
const content = (
|
||||
<WalletListItem
|
||||
@ -178,29 +180,20 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
/>
|
||||
);
|
||||
|
||||
if (!canSwipe) return content;
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={r => {
|
||||
swipeableRef.current = r;
|
||||
}}
|
||||
onSwipeableWillOpen={() => {
|
||||
swipeInProgressRef.current = true;
|
||||
}}
|
||||
onSwipeableWillClose={() => {
|
||||
swipeInProgressRef.current = false;
|
||||
}}
|
||||
onSwipeableClose={() => {
|
||||
swipeInProgressRef.current = false;
|
||||
}}
|
||||
renderRightActions={renderRightActions}
|
||||
friction={2}
|
||||
rightThreshold={40}
|
||||
overshootRight={false}
|
||||
>
|
||||
{content}
|
||||
</Swipeable>
|
||||
<Animated.View style={[styles.dragAnimatedRow, { transform: [{ scale: rowScale }] }]}>
|
||||
<SwipeableWalletRow
|
||||
ref={swipeableRef}
|
||||
enabled={canSwipe}
|
||||
isHidden={isHidden}
|
||||
currentUnit={wallet.getPreferredBalanceUnit()}
|
||||
onToggleHideBalance={onToggle}
|
||||
onChangeUnit={onChangeUnit}
|
||||
onSwipeStateChange={onSwipeStateChange}
|
||||
>
|
||||
{content}
|
||||
</SwipeableWalletRow>
|
||||
</Animated.View>
|
||||
);
|
||||
} else if (item.type === ItemType.TransactionSection && item.data) {
|
||||
try {
|
||||
@ -411,27 +404,13 @@ const WalletGroupComponent: React.FC<WalletGroupProps> = ({
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
dragAnimatedRow: {
|
||||
width: '100%',
|
||||
},
|
||||
itemDivider: {
|
||||
height: 1,
|
||||
width: '100%',
|
||||
},
|
||||
rightActionsContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
rightAction: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 18,
|
||||
height: '100%',
|
||||
},
|
||||
rightActionPressed: {
|
||||
opacity: 0.85,
|
||||
},
|
||||
rightActionText: {
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
|
||||
export { WalletGroupComponent };
|
||||
|
||||
186
components/SwipeableWalletRow.tsx
Normal file
186
components/SwipeableWalletRow.tsx
Normal file
@ -0,0 +1,186 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { Animated, Pressable, StyleSheet, Text } from 'react-native';
|
||||
import { Swipeable } from 'react-native-gesture-handler';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../blue_modules/hapticFeedback';
|
||||
import { useTheme } from './themes';
|
||||
import loc from '../loc';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import { FiatUnit } from '../models/fiatUnit';
|
||||
import { useSettings } from '../hooks/context/useSettings';
|
||||
import Icon from './Icon';
|
||||
|
||||
/** Width of each swipe-action button — matches iOS Mail / Contacts style. */
|
||||
const ACTION_WIDTH = 80;
|
||||
|
||||
interface SwipeableWalletRowProps {
|
||||
children: React.ReactNode;
|
||||
/** When false the row renders children without any swipe wrapper (during drag / active). */
|
||||
enabled: boolean;
|
||||
isHidden: boolean;
|
||||
currentUnit: BitcoinUnit;
|
||||
onToggleHideBalance: () => void;
|
||||
onChangeUnit: () => void;
|
||||
onSwipeStateChange?: (isSwipeInProgress: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* SwipeableWalletRow wraps a wallet list item and reveals iOS-style action
|
||||
* buttons on left-swipe:
|
||||
* [ Change Unit ] [ Hide / Show ]
|
||||
*
|
||||
* The reveal animation matches the native iOS feel:
|
||||
* - The whole panel translates in sync with the user's finger via a single
|
||||
* translateX derived from `dragX`.
|
||||
* - No per-button stagger (that's what causes the "non-native" look).
|
||||
* - Buttons have a fixed width so the panel has a predictable size.
|
||||
*/
|
||||
const SwipeableWalletRow = React.forwardRef<Swipeable, SwipeableWalletRowProps>(
|
||||
({ children, enabled, isHidden, currentUnit, onToggleHideBalance, onChangeUnit, onSwipeStateChange }, ref) => {
|
||||
const { colors } = useTheme();
|
||||
const { preferredFiatCurrency } = useSettings();
|
||||
const internalRef = useRef<Swipeable | null>(null);
|
||||
|
||||
// Forward the ref while keeping a local copy for `close()`.
|
||||
const setRef = useCallback(
|
||||
(r: Swipeable | null) => {
|
||||
internalRef.current = r;
|
||||
if (typeof ref === 'function') {
|
||||
ref(r);
|
||||
} else if (ref) {
|
||||
(ref as React.MutableRefObject<Swipeable | null>).current = r;
|
||||
}
|
||||
},
|
||||
[ref],
|
||||
);
|
||||
|
||||
const close = useCallback(() => {
|
||||
internalRef.current?.close();
|
||||
}, []);
|
||||
|
||||
const handleHideBalance = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
onToggleHideBalance();
|
||||
close();
|
||||
}, [onToggleHideBalance, close]);
|
||||
|
||||
const handleChangeUnit = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
onChangeUnit();
|
||||
close();
|
||||
}, [onChangeUnit, close]);
|
||||
|
||||
const getUnitLabel = useCallback(() => {
|
||||
if (currentUnit === BitcoinUnit.BTC) return loc.units.BTC;
|
||||
if (currentUnit === BitcoinUnit.SATS) return loc.units.sats;
|
||||
return preferredFiatCurrency?.endPointKey ?? FiatUnit.USD;
|
||||
}, [currentUnit, preferredFiatCurrency]);
|
||||
|
||||
/**
|
||||
* iOS-style reveal: the whole panel slides in together.
|
||||
* `progress` goes from 0 → 1 as the row opens, so we translate
|
||||
* each button from its fully-hidden offset → 0.
|
||||
*/
|
||||
const renderRightActions = useCallback(
|
||||
(progress: Animated.AnimatedInterpolation<number>) => {
|
||||
const totalWidth = ACTION_WIDTH * 2;
|
||||
|
||||
const panelTranslate = progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [totalWidth, 0],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.actionsContainer, { transform: [{ translateX: panelTranslate }] }]}>
|
||||
{/* Change Unit */}
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
styles.actionButton,
|
||||
{ backgroundColor: colors.changeBackground },
|
||||
pressed && styles.actionButtonPressed,
|
||||
]}
|
||||
onPress={handleChangeUnit}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc.wallets.swipe_change_unit}
|
||||
>
|
||||
<Icon name="arrows-rotate" type="font-awesome-6" size={14} color={colors.changeText} />
|
||||
<Text style={[styles.actionText, { color: colors.changeText }]}>{getUnitLabel()}</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* Hide / Show Balance — rightmost, matches iOS "destructive" slot */}
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
styles.actionButton,
|
||||
{ backgroundColor: colors.buttonBackgroundColor },
|
||||
pressed && styles.actionButtonPressed,
|
||||
]}
|
||||
onPress={handleHideBalance}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={isHidden ? loc.wallets.swipe_balance_show : loc.wallets.swipe_balance_hide}
|
||||
>
|
||||
<Icon name={isHidden ? 'eye' : 'eye-slash'} type="font-awesome" size={14} color={colors.buttonTextColor} />
|
||||
<Text style={[styles.actionText, { color: colors.buttonTextColor }]}>
|
||||
{isHidden ? loc.wallets.swipe_balance_show : loc.wallets.swipe_balance_hide}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</Animated.View>
|
||||
);
|
||||
},
|
||||
[colors, getUnitLabel, handleChangeUnit, handleHideBalance, isHidden],
|
||||
);
|
||||
|
||||
if (!enabled) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={setRef}
|
||||
renderRightActions={renderRightActions}
|
||||
// friction=1 gives a 1:1 feel with the finger — most iOS-like.
|
||||
friction={1}
|
||||
rightThreshold={ACTION_WIDTH / 2}
|
||||
// Allow pulling past action width with elastic resistance.
|
||||
overshootRight
|
||||
overshootFriction={10}
|
||||
onSwipeableWillOpen={() => {
|
||||
onSwipeStateChange?.(true);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactLight);
|
||||
}}
|
||||
onSwipeableWillClose={() => {
|
||||
onSwipeStateChange?.(false);
|
||||
}}
|
||||
onSwipeableClose={() => {
|
||||
onSwipeStateChange?.(false);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Swipeable>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SwipeableWalletRow.displayName = 'SwipeableWalletRow';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
actionsContainer: {
|
||||
flexDirection: 'row',
|
||||
width: ACTION_WIDTH * 2,
|
||||
},
|
||||
actionButton: {
|
||||
width: ACTION_WIDTH,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 3,
|
||||
},
|
||||
actionButtonPressed: {
|
||||
opacity: 0.8,
|
||||
},
|
||||
actionText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default SwipeableWalletRow;
|
||||
@ -2806,7 +2806,7 @@ SPEC CHECKSUMS:
|
||||
BVLinearGradient: cb006ba232a1f3e4f341bb62c42d1098c284da70
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
FBLazyVector: 24e62c765683b8d89006a88a2c8f5cf019f0074d
|
||||
hermes-engine: 4ed74710a31e8e31f20356c641eab1d8f7d54595
|
||||
hermes-engine: 86cdbf283775c54dc008895c3eacd24a1f2a40b4
|
||||
lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3
|
||||
lottie-react-native: 615e5f4651bee144ea991ad8e900630b6b3daf5d
|
||||
RCTDeprecation: a4c521821fab57cbb125b36effe84d897d0dfa12
|
||||
@ -2817,7 +2817,7 @@ SPEC CHECKSUMS:
|
||||
React: e2dc35338068bbd299c66f043ae0d7f25de8499e
|
||||
React-callinvoker: 28b25d21b124c26cebaea713ba7d801b9351dc48
|
||||
React-Core: 02ed7d2ffb70437bdf2aba074a13078a7b0b9ff0
|
||||
React-Core-prebuilt: 3445f1028d9b206cd45c8bbb7e2427ee891f810e
|
||||
React-Core-prebuilt: 9e875134f667c471ab68bf9edf1661fa11b86540
|
||||
React-CoreModules: b3a5a42dadcde3b5d47b325bd912eb2ced89e146
|
||||
React-cxxreact: fe8f88dda044e5905e99a00f41b7a874c3908716
|
||||
React-debug: 92944dc4d89f56d640e75498266cbde557a48189
|
||||
@ -2898,7 +2898,7 @@ SPEC CHECKSUMS:
|
||||
ReactCodegen: 1bd7f2174582b0e142f8671735b5c906c08b72ea
|
||||
ReactCommon: 7dfc3250793bf36cf221096ff59e1179e13eef7f
|
||||
ReactNativeCameraKit: 5974256fc608631c1c812710cd98abe95dae0f88
|
||||
ReactNativeDependencies: 75299c281f422106c723e79dc1f6ce7ef03241be
|
||||
ReactNativeDependencies: 0a5c93845772e4b1c5ad065c59a859518b13a6b7
|
||||
RealmJS: 1c37c6bdfe060f4caa0f9175aa0eedb962622ee1
|
||||
RNCAsyncStorage: 2ad919e88b8bc2cd80e8697ce66d04d006743283
|
||||
RNCClipboard: 715fa7c6c8366f17d00f05a439ee7488f390fa5f
|
||||
|
||||
@ -448,6 +448,7 @@
|
||||
"wallets": "Wallets",
|
||||
"swipe_balance_hide": "Hide",
|
||||
"swipe_balance_show": "Show",
|
||||
"swipe_change_unit": "Unit",
|
||||
"drag_to_reorder": "Drag to reorder",
|
||||
"clear_search": "Clear search",
|
||||
"details_type": "Type",
|
||||
|
||||
@ -25,6 +25,7 @@ import { ExtendedTransaction, LightningTransaction, Transaction, TWallet } from
|
||||
import useBounceAnimation from '../../hooks/useBounceAnimation';
|
||||
import DraggableFlatList, { RenderItemParams, DragEndParams } from 'react-native-draggable-flatlist';
|
||||
import { ItemType, AddressItemData } from '../../models/itemTypes';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import ManageWalletsListItem, { WalletGroupComponent } from '../../components/ManageWalletsListItem';
|
||||
import HighlightedText from '../../components/HighlightedText';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
@ -144,6 +145,8 @@ const ManageWallets: React.FC = () => {
|
||||
const [noResultsOpacity] = useState(new Animated.Value(0));
|
||||
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [interactionLockActive, setInteractionLockActive] = useState(false);
|
||||
const interactionLockTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const debouncedSearch = useCallback((text: string) => {
|
||||
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
||||
@ -161,6 +164,7 @@ const ManageWallets: React.FC = () => {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
||||
if (interactionLockTimerRef.current) clearTimeout(interactionLockTimerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -414,7 +418,10 @@ const ManageWallets: React.FC = () => {
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.close}
|
||||
style={styles.button}
|
||||
onPress={goBack}
|
||||
onPress={() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
goBack();
|
||||
}}
|
||||
testID="NavigationCloseButton"
|
||||
>
|
||||
<Image source={closeImage} />
|
||||
@ -427,8 +434,14 @@ const ManageWallets: React.FC = () => {
|
||||
const searchBarOptions = {
|
||||
hideWhenScrolling: false,
|
||||
onChangeText: (event: { nativeEvent: { text: any } }) => debouncedSearch(event.nativeEvent.text),
|
||||
onClear: () => debouncedSearch(''),
|
||||
onFocus: () => dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: true }),
|
||||
onClear: () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
debouncedSearch('');
|
||||
},
|
||||
onFocus: () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: true });
|
||||
},
|
||||
onBlur: () => dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: false }),
|
||||
placeholder: loc.wallets.manage_wallets_search_placeholder,
|
||||
};
|
||||
@ -496,6 +509,30 @@ const ManageWallets: React.FC = () => {
|
||||
[state.walletsCopy, setWalletsWithNewOrder],
|
||||
);
|
||||
|
||||
const handleChangeWalletUnit = useCallback(
|
||||
(wallet: TWallet) => {
|
||||
const current = wallet.getPreferredBalanceUnit();
|
||||
let next: BitcoinUnit;
|
||||
if (current === BitcoinUnit.BTC) {
|
||||
next = BitcoinUnit.SATS;
|
||||
} else if (current === BitcoinUnit.SATS) {
|
||||
next = BitcoinUnit.LOCAL_CURRENCY;
|
||||
} else {
|
||||
next = BitcoinUnit.BTC;
|
||||
}
|
||||
const walletID = wallet.getID();
|
||||
const updatedWallets = deepCopyWallets(state.walletsCopy).map(w => {
|
||||
if (w.getID() === walletID) {
|
||||
w.setPreferredBalanceUnit(next);
|
||||
}
|
||||
return w;
|
||||
});
|
||||
setWalletsWithNewOrder(updatedWallets);
|
||||
dispatch({ type: SAVE_CHANGES, payload: updatedWallets });
|
||||
},
|
||||
[state.walletsCopy, setWalletsWithNewOrder],
|
||||
);
|
||||
|
||||
const renderListItem = useCallback(
|
||||
(item: Item, drag: (() => void) | undefined, isActive: boolean) => {
|
||||
const compatibleState = {
|
||||
@ -521,20 +558,22 @@ const ManageWallets: React.FC = () => {
|
||||
return (
|
||||
<ManageWalletsListItem
|
||||
item={item}
|
||||
isDraggingDisabled={isDragDisabled}
|
||||
isDraggingDisabled={isDragDisabled || interactionLockActive}
|
||||
handleToggleHideBalance={handleToggleHideBalance}
|
||||
handleChangeWalletUnit={handleChangeWalletUnit}
|
||||
state={compatibleState}
|
||||
navigateToWallet={navigateToWallet}
|
||||
navigateToAddress={navigateToAddress}
|
||||
renderHighlightedText={renderHighlightedText}
|
||||
isActive={isActive}
|
||||
drag={isDragDisabled ? undefined : drag}
|
||||
globalDragActive={dragging}
|
||||
globalDragActive={dragging || interactionLockActive}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[
|
||||
handleToggleHideBalance,
|
||||
handleChangeWalletUnit,
|
||||
state.walletsCopy,
|
||||
state.searchQuery,
|
||||
state.isSearchFocused,
|
||||
@ -542,6 +581,7 @@ const ManageWallets: React.FC = () => {
|
||||
navigateToAddress,
|
||||
renderHighlightedText,
|
||||
dragging,
|
||||
interactionLockActive,
|
||||
isDragDisabled,
|
||||
],
|
||||
);
|
||||
@ -618,6 +658,7 @@ const ManageWallets: React.FC = () => {
|
||||
style={({ pressed }) => [styles.clearSearchButton, stylesHook.clearSearchButton, pressed && styles.clearSearchButtonPressed]}
|
||||
android_ripple={{ color: colors.buttonDisabledTextColor, borderless: false }}
|
||||
onPress={() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
dispatch({ type: SET_SEARCH_QUERY, payload: '' });
|
||||
dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: false });
|
||||
}}
|
||||
@ -660,10 +701,22 @@ const ManageWallets: React.FC = () => {
|
||||
renderItem={renderDraggableItem}
|
||||
containerStyle={styles.listContainer}
|
||||
onDragBegin={() => {
|
||||
if (interactionLockTimerRef.current) {
|
||||
clearTimeout(interactionLockTimerRef.current);
|
||||
interactionLockTimerRef.current = null;
|
||||
}
|
||||
setInteractionLockActive(false);
|
||||
setDragging(true);
|
||||
}}
|
||||
onDragEnd={({ from, to, data }: DragEndParams<Item>) => {
|
||||
setDragging(false);
|
||||
setInteractionLockActive(true);
|
||||
if (interactionLockTimerRef.current) {
|
||||
clearTimeout(interactionLockTimerRef.current);
|
||||
}
|
||||
interactionLockTimerRef.current = setTimeout(() => {
|
||||
setInteractionLockActive(false);
|
||||
}, 180);
|
||||
|
||||
if (state.searchQuery.length > 0 || state.isSearchFocused) {
|
||||
return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user