Merge pull request #7465 from BlueWallet/mana

REF: Manage Wallet to a different better package
This commit is contained in:
GLaDOS 2025-01-07 17:21:51 +00:00 committed by GitHub
commit 5e1b8b1c4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 98 additions and 110 deletions

View file

@ -6,6 +6,7 @@ import { WalletCarouselItem } from './WalletsCarousel';
import { TransactionListItem } from './TransactionListItem';
import { useTheme } from './themes';
import { BitcoinUnit } from '../models/bitcoinUnits';
import { TouchableOpacityWrapper } from './ListItem';
enum ItemType {
WalletSection = 'wallet',
@ -29,11 +30,14 @@ interface ManageWalletsListItemProps {
isDraggingDisabled: boolean;
drag?: () => void;
isPlaceHolder?: boolean;
onPressIn?: () => void;
onPressOut?: () => void;
state: { wallets: TWallet[]; searchQuery: string };
navigateToWallet: (wallet: TWallet) => void;
renderHighlightedText: (text: string, query: string) => JSX.Element;
handleDeleteWallet: (wallet: TWallet) => void;
handleToggleHideBalance: (wallet: TWallet) => void;
isActive?: boolean;
}
interface SwipeContentProps {
@ -67,6 +71,9 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
renderHighlightedText,
handleDeleteWallet,
handleToggleHideBalance,
onPressIn,
onPressOut,
isActive,
}) => {
const { colors } = useTheme();
@ -110,6 +117,10 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
containerStyle={{ backgroundColor: colors.background }}
leftContent={leftContent}
rightContent={rightContent}
Component={TouchableOpacityWrapper}
onPressOut={onPressOut}
onPressIn={onPressIn}
style={isActive ? styles.activeItem : undefined}
>
<ListItem.Content
style={{
@ -121,6 +132,8 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
item={item.data}
handleLongPress={isDraggingDisabled ? undefined : drag}
onPress={onPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
animationsEnabled={false}
searchQuery={state.searchQuery}
isPlaceHolder={isPlaceHolder}
@ -164,6 +177,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
backgroundColor: 'red',
},
activeItem: {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
},
});
export { ManageWalletsListItem, LeftSwipeContent, RightSwipeContent };

View file

@ -172,6 +172,8 @@ interface WalletCarouselItemProps {
searchQuery?: string;
renderHighlightedText?: (text: string, query: string) => JSX.Element;
animationsEnabled?: boolean;
onPressIn?: () => void;
onPressOut?: () => void;
}
export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
@ -186,6 +188,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
renderHighlightedText,
animationsEnabled = true,
isPlaceHolder = false,
onPressIn,
onPressOut,
}) => {
const scaleValue = useRef(new Animated.Value(1.0)).current;
const { colors } = useTheme();
@ -203,7 +207,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
tension: 100,
}).start();
}
}, [scaleValue, animationsEnabled]);
if (onPressIn) onPressIn();
}, [scaleValue, animationsEnabled, onPressIn]);
const onPressedOut = useCallback(() => {
if (animationsEnabled) {
@ -214,7 +219,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
tension: 100,
}).start();
}
}, [scaleValue, animationsEnabled]);
if (onPressOut) onPressOut();
}, [scaleValue, animationsEnabled, onPressOut]);
const handlePress = useCallback(() => {
onPressedOut();

View file

@ -162,7 +162,7 @@
B4D0B2682C1DED67006B6B1B /* ReceiveMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2672C1DED67006B6B1B /* ReceiveMethod.swift */; };
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
B4EFF73B2C3F6C5E0095D655 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EFF73A2C3F6C5E0095D655 /* MockData.swift */; };
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -427,7 +427,7 @@
files = (
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */,
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */,
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */,
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
17CDA0718F42DB2CE856C872 /* libPods-BlueWallet.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;

16
package-lock.json generated
View file

@ -68,7 +68,7 @@
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
"react-native-device-info": "13.2.0",
"react-native-document-picker": "9.3.1",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3a61627",
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
"react-native-fs": "2.20.0",
"react-native-gesture-handler": "2.21.2",
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",
@ -20615,17 +20615,13 @@
}
}
},
"node_modules/react-native-draggable-flatlist": {
"version": "4.0.1",
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-draggable-flatlist.git#3a61627474a4e35198ae961310c77fb305507509",
"node_modules/react-native-draglist": {
"version": "3.8.0",
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-draglist.git#a4af02fec803b75508a8136e35eca564bbb1d644",
"license": "MIT",
"dependencies": {
"@babel/preset-typescript": "^7.17.12"
},
"peerDependencies": {
"react-native": ">=0.64.0",
"react-native-gesture-handler": ">=2.0.0",
"react-native-reanimated": ">=2.8.0"
"react": ">=17.0.1",
"react-native": ">=0.64.0"
}
},
"node_modules/react-native-fs": {

View file

@ -132,7 +132,7 @@
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
"react-native-device-info": "13.2.0",
"react-native-document-picker": "9.3.1",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3a61627",
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
"react-native-fs": "2.20.0",
"react-native-gesture-handler": "2.21.2",
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",

View file

@ -1,13 +1,5 @@
import React, { useEffect, useLayoutEffect, useReducer, useCallback, useMemo, useRef } from 'react';
import { StyleSheet, TouchableOpacity, Image, Text, Alert, I18nManager, Animated, LayoutAnimation } from 'react-native';
import {
NestableScrollContainer,
ScaleDecorator,
OpacityDecorator,
NestableDraggableFlatList,
RenderItem,
// @ts-expect-error: react-native-draggable-flatlist is not typed
} from 'react-native-draggable-flatlist';
import React, { useEffect, useLayoutEffect, useReducer, useCallback, useMemo, useRef, useState } from 'react';
import { StyleSheet, TouchableOpacity, Image, Text, Alert, I18nManager, Animated, LayoutAnimation, FlatList } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
@ -25,6 +17,7 @@ import prompt from '../../helpers/prompt';
import HeaderRightButton from '../../components/HeaderRightButton';
import { ManageWalletsListItem } from '../../components/ManageWalletsListItem';
import { useSettings } from '../../hooks/context/useSettings';
import DragList, { DragListRenderItemInfo } from 'react-native-draglist';
enum ItemType {
WalletSection = 'wallet',
@ -206,21 +199,24 @@ const ManageWallets: React.FC = () => {
color: colors.foregroundColor,
},
};
const [data, setData] = useState(state.tempOrder);
const listRef = useRef<FlatList<Item> | null>(null);
useEffect(() => {
dispatch({
type: SET_INITIAL_ORDER,
payload: { wallets: walletsRef.current, txMetadata },
});
setData(state.tempOrder);
}, [state.tempOrder]);
useEffect(() => {
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: walletsRef.current, txMetadata } });
}, [txMetadata]);
useEffect(() => {
if (debouncedSearchQuery) {
dispatch({ type: SET_FILTERED_ORDER, payload: debouncedSearchQuery });
} else {
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: walletsRef.current, txMetadata } });
dispatch({ type: SET_TEMP_ORDER, payload: state.order });
}
}, [debouncedSearchQuery, txMetadata]);
}, [debouncedSearchQuery, state.order]);
const handleClose = useCallback(() => {
if (state.searchQuery.length === 0 && !state.isSearchFocused) {
@ -244,6 +240,7 @@ const ManageWallets: React.FC = () => {
dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: false });
}
}, [goBack, setWalletsWithNewOrder, state.searchQuery, state.isSearchFocused, state.tempOrder, navigation]);
const hasUnsavedChanges = useMemo(() => {
return JSON.stringify(walletsRef.current) !== JSON.stringify(state.tempOrder.map(item => item.data));
}, [state.tempOrder]);
@ -319,6 +316,14 @@ const ManageWallets: React.FC = () => {
}, [hasUnsavedChanges, navigation, setIsDrawerShouldHide]),
);
// Ensure the listener is re-added every time there are unsaved changes
useEffect(() => {
if (beforeRemoveListenerRef.current) {
navigation.removeListener('beforeRemove', beforeRemoveListenerRef.current);
navigation.addListener('beforeRemove', beforeRemoveListenerRef.current);
}
}, [hasUnsavedChanges, navigation]);
const renderHighlightedText = useCallback(
(text: string, query: string) => {
const parts = text.split(new RegExp(`(${query})`, 'gi'));
@ -425,60 +430,43 @@ const ManageWallets: React.FC = () => {
},
[goBack, navigate],
);
const renderWalletItem = useCallback(
({ item, drag, isActive }: RenderItem<Item>) => (
<ScaleDecorator drag={drag} activeScale={1.1}>
<OpacityDecorator activeOpacity={0.5}>
<ManageWalletsListItem
item={item}
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
drag={drag}
state={state}
navigateToWallet={navigateToWallet}
renderHighlightedText={renderHighlightedText}
handleDeleteWallet={handleDeleteWallet}
handleToggleHideBalance={handleToggleHideBalance}
/>
</OpacityDecorator>
</ScaleDecorator>
),
const renderItem = useCallback(
(info: DragListRenderItemInfo<Item>) => {
const { item, onDragStart, onDragEnd, isActive } = info;
return (
<ManageWalletsListItem
item={item}
onPressIn={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragStart}
onPressOut={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragEnd}
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
state={state}
navigateToWallet={navigateToWallet}
renderHighlightedText={renderHighlightedText}
handleDeleteWallet={handleDeleteWallet}
handleToggleHideBalance={handleToggleHideBalance}
isActive={isActive}
drag={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragStart}
/>
);
},
[state, navigateToWallet, renderHighlightedText, handleDeleteWallet, handleToggleHideBalance],
);
const renderPlaceholder = useCallback(
({ item, drag, isActive }: RenderItem<Item>) => (
<ManageWalletsListItem
item={item}
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
state={state}
navigateToWallet={navigateToWallet}
renderHighlightedText={renderHighlightedText}
isPlaceHolder
handleDeleteWallet={handleDeleteWallet}
handleToggleHideBalance={handleToggleHideBalance}
/>
),
[handleDeleteWallet, handleToggleHideBalance, navigateToWallet, renderHighlightedText, state],
);
const onChangeOrder = useCallback(() => {
triggerHapticFeedback(HapticFeedbackTypes.ImpactMedium);
}, []);
const onDragBegin = useCallback(() => {
triggerHapticFeedback(HapticFeedbackTypes.Selection);
}, []);
const onRelease = useCallback(() => {
triggerHapticFeedback(HapticFeedbackTypes.ImpactLight);
}, []);
const onDragEnd = useCallback(
({ data }: { data: Item[] }) => {
const updatedWallets = data.filter((item): item is WalletItem => item.type === ItemType.WalletSection).map(item => item.data);
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: updatedWallets, txMetadata: state.txMetadata } });
const onReordered = useCallback(
(fromIndex: number, toIndex: number) => {
const copy = [...state.order];
const removed = copy.splice(fromIndex, 1);
copy.splice(toIndex, 0, removed[0]);
dispatch({ type: SET_TEMP_ORDER, payload: copy });
dispatch({
type: SET_INITIAL_ORDER,
payload: {
wallets: copy.filter(item => item.type === ItemType.WalletSection).map(item => item.data as TWallet),
txMetadata: state.txMetadata,
},
});
},
[state.txMetadata],
[state.order, state.txMetadata],
);
const keyExtractor = useCallback((item: Item, index: number) => index.toString(), []);
@ -499,39 +487,21 @@ const ManageWallets: React.FC = () => {
return (
<GestureHandlerRootView style={[{ backgroundColor: colors.background }, styles.root]}>
<NestableScrollContainer contentInsetAdjustmentBehavior="automatic" automaticallyAdjustContentInsets scrollEnabled>
<>
{renderHeader}
<NestableDraggableFlatList
data={state.tempOrder.filter((item): item is WalletItem => item.type === ItemType.WalletSection)}
extraData={state.tempOrder}
keyExtractor={keyExtractor}
renderItem={renderWalletItem}
onChangeOrder={onChangeOrder}
onDragBegin={onDragBegin}
onPlaceholderIndexChange={onChangeOrder}
onRelease={onRelease}
delayLongPress={150}
useNativeDriver={true}
dragItemOverflow
autoscrollThreshold={1}
renderPlaceholder={renderPlaceholder}
autoscrollSpeed={0.5}
contentInsetAdjustmentBehavior="automatic"
<DragList
automaticallyAdjustContentInsets
onDragEnd={onDragEnd}
containerStyle={styles.root}
/>
<NestableDraggableFlatList
data={state.tempOrder.filter((item): item is TransactionItem => item.type === ItemType.TransactionSection)}
keyExtractor={keyExtractor}
renderItem={renderWalletItem}
dragItemOverflow
containerStyle={styles.root}
automaticallyAdjustKeyboardInsets
automaticallyAdjustsScrollIndicatorInsets
contentInsetAdjustmentBehavior="automatic"
automaticallyAdjustContentInsets
useNativeDriver={true}
data={data}
containerStyle={[{ backgroundColor: colors.background }, styles.root]}
keyExtractor={keyExtractor}
onReordered={onReordered}
renderItem={renderItem}
ref={listRef}
/>
</NestableScrollContainer>
</>
</GestureHandlerRootView>
);
};