mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 21:35:21 +01:00
ADD: Swipeable
This commit is contained in:
parent
485b0e3492
commit
14cded25bf
@ -1,11 +1,12 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ActivityIndicator, I18nManager, Pressable, PressableProps, StyleSheet, Switch, TouchableOpacity } from 'react-native';
|
||||
import { Avatar, ListItem as RNElementsListItem } from '@rneui/themed'; // Replace with actual import paths
|
||||
import { Avatar, ListItem as RNElementsListItem, Button } from '@rneui/themed'; // Replace with actual import paths
|
||||
|
||||
import { useTheme } from './themes';
|
||||
|
||||
// Update the type for the props
|
||||
interface ListItemProps {
|
||||
swipeable?: boolean;
|
||||
rightIcon?: any;
|
||||
leftAvatar?: React.JSX.Element;
|
||||
containerStyle?: object;
|
||||
@ -15,7 +16,7 @@ interface ListItemProps {
|
||||
testID?: string;
|
||||
onPress?: () => void;
|
||||
onLongPress?: () => void;
|
||||
hideChevron?: boolean;
|
||||
onDeletePressed?: () => void;
|
||||
disabled?: boolean;
|
||||
switch?: object; // Define more specific type if needed
|
||||
leftIcon?: any; // Define more specific type if needed
|
||||
@ -28,6 +29,8 @@ interface ListItemProps {
|
||||
chevron?: boolean;
|
||||
checkmark?: boolean;
|
||||
subtitleProps?: object;
|
||||
swipeableLeftContent?: React.ReactNode;
|
||||
swipeableRightContent?: React.ReactNode;
|
||||
}
|
||||
|
||||
export class PressableWrapper extends React.Component<PressableProps> {
|
||||
@ -42,81 +45,155 @@ export class TouchableOpacityWrapper extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = React.memo(props => {
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
title: {
|
||||
color: props.disabled ? colors.buttonDisabledTextColor : colors.foregroundColor,
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
subtitle: {
|
||||
flexWrap: 'wrap',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
color: colors.alternativeTextColor,
|
||||
fontWeight: '400',
|
||||
fontSize: 14,
|
||||
},
|
||||
rightTitleContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
containerStyle: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
});
|
||||
// Define Swipeable Button Components
|
||||
const DefaultRightContent: React.FC<{ reset: () => void; onDeletePressed?: () => void }> = ({ reset, onDeletePressed }) => (
|
||||
<Button
|
||||
title="Delete"
|
||||
onPress={() => {
|
||||
reset();
|
||||
onDeletePressed?.();
|
||||
}}
|
||||
icon={{ name: 'delete', color: 'white' }}
|
||||
buttonStyle={styles.rightButton}
|
||||
/>
|
||||
);
|
||||
|
||||
const memoizedSwitchProps = useMemo(() => {
|
||||
return props.switch ? { ...props.switch } : undefined;
|
||||
}, [props.switch]);
|
||||
|
||||
return (
|
||||
<RNElementsListItem
|
||||
containerStyle={props.containerStyle ?? stylesHook.containerStyle}
|
||||
Component={props.Component ?? TouchableOpacityWrapper}
|
||||
bottomDivider={props.bottomDivider !== undefined ? props.bottomDivider : true}
|
||||
topDivider={props.topDivider !== undefined ? props.topDivider : false}
|
||||
testID={props.testID}
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
disabled={props.disabled}
|
||||
accessible={props.switch === undefined}
|
||||
>
|
||||
{props.leftIcon && <Avatar icon={props.leftIcon} />}
|
||||
{props.leftAvatar && props.leftAvatar}
|
||||
<RNElementsListItem.Content>
|
||||
<RNElementsListItem.Title style={stylesHook.title} numberOfLines={0} accessible={props.switch === undefined}>
|
||||
{props.title}
|
||||
</RNElementsListItem.Title>
|
||||
{props.subtitle && (
|
||||
<RNElementsListItem.Subtitle
|
||||
numberOfLines={props.subtitleNumberOfLines ?? 1}
|
||||
accessible={props.switch === undefined}
|
||||
style={stylesHook.subtitle}
|
||||
>
|
||||
{props.subtitle}
|
||||
</RNElementsListItem.Subtitle>
|
||||
)}
|
||||
</RNElementsListItem.Content>
|
||||
{props.rightTitle && (
|
||||
<RNElementsListItem.Content right style={stylesHook.rightTitleContainer}>
|
||||
<RNElementsListItem.Title style={props.rightTitleStyle} numberOfLines={0} right>
|
||||
{props.rightTitle}
|
||||
</RNElementsListItem.Title>
|
||||
</RNElementsListItem.Content>
|
||||
)}
|
||||
{props.isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<>
|
||||
{props.chevron && <RNElementsListItem.Chevron iconStyle={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }} />}
|
||||
{props.rightIcon && <Avatar icon={props.rightIcon} />}
|
||||
{props.switch && <Switch {...memoizedSwitchProps} accessibilityLabel={props.title} accessible accessibilityRole="switch" />}
|
||||
{props.checkmark && <RNElementsListItem.CheckBox iconType="octaicon" checkedColor="#0070FF" checkedIcon="check" checked />}
|
||||
</>
|
||||
)}
|
||||
</RNElementsListItem>
|
||||
);
|
||||
const styles = StyleSheet.create({
|
||||
rightButton: {
|
||||
minHeight: '100%',
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
});
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = React.memo(
|
||||
({
|
||||
swipeable = false,
|
||||
Component = TouchableOpacityWrapper,
|
||||
rightIcon,
|
||||
leftAvatar,
|
||||
containerStyle,
|
||||
bottomDivider = true,
|
||||
topDivider = false,
|
||||
testID,
|
||||
onPress,
|
||||
onLongPress,
|
||||
onDeletePressed,
|
||||
disabled,
|
||||
switch: switchProps,
|
||||
leftIcon,
|
||||
title,
|
||||
subtitle,
|
||||
subtitleNumberOfLines,
|
||||
rightTitle,
|
||||
rightTitleStyle,
|
||||
isLoading,
|
||||
chevron,
|
||||
checkmark,
|
||||
swipeableLeftContent,
|
||||
swipeableRightContent,
|
||||
}: ListItemProps) => {
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
title: {
|
||||
color: disabled ? colors.buttonDisabledTextColor : colors.foregroundColor,
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
subtitle: {
|
||||
flexWrap: 'wrap',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
color: colors.alternativeTextColor,
|
||||
fontWeight: '400',
|
||||
fontSize: 14,
|
||||
},
|
||||
rightTitleContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
containerStyle: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
});
|
||||
|
||||
const memoizedSwitchProps = useMemo(() => {
|
||||
return switchProps ? { ...switchProps } : undefined;
|
||||
}, [switchProps]);
|
||||
|
||||
const renderContent = () => (
|
||||
<>
|
||||
{leftIcon && <Avatar icon={leftIcon} />}
|
||||
{leftAvatar && leftAvatar}
|
||||
<RNElementsListItem.Content>
|
||||
<RNElementsListItem.Title style={stylesHook.title} numberOfLines={0} accessible={switchProps === undefined}>
|
||||
{title}
|
||||
</RNElementsListItem.Title>
|
||||
{subtitle && (
|
||||
<RNElementsListItem.Subtitle
|
||||
numberOfLines={subtitleNumberOfLines ?? 1}
|
||||
accessible={switchProps === undefined}
|
||||
style={stylesHook.subtitle}
|
||||
>
|
||||
{subtitle}
|
||||
</RNElementsListItem.Subtitle>
|
||||
)}
|
||||
</RNElementsListItem.Content>
|
||||
{rightTitle && (
|
||||
<RNElementsListItem.Content right style={stylesHook.rightTitleContainer}>
|
||||
<RNElementsListItem.Title style={rightTitleStyle} numberOfLines={0} right>
|
||||
{rightTitle}
|
||||
</RNElementsListItem.Title>
|
||||
</RNElementsListItem.Content>
|
||||
)}
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<>
|
||||
{chevron && <RNElementsListItem.Chevron iconStyle={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }} />}
|
||||
{rightIcon && <Avatar icon={rightIcon} />}
|
||||
{switchProps && <Switch {...memoizedSwitchProps} accessibilityLabel={title} accessible accessibilityRole="switch" />}
|
||||
{checkmark && <RNElementsListItem.CheckBox iconType="octaicon" checkedColor="#0070FF" checkedIcon="check" checked />}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (swipeable && !Component) {
|
||||
console.warn('Component prop is required when swipeable is true.');
|
||||
return null;
|
||||
}
|
||||
|
||||
return swipeable ? (
|
||||
<RNElementsListItem.Swipeable
|
||||
containerStyle={containerStyle ?? stylesHook.containerStyle}
|
||||
Component={Component}
|
||||
bottomDivider={bottomDivider}
|
||||
topDivider={topDivider}
|
||||
testID={testID}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
disabled={disabled}
|
||||
leftContent={swipeableLeftContent}
|
||||
rightContent={swipeableRightContent ?? <DefaultRightContent reset={() => {}} onDeletePressed={onDeletePressed} />}
|
||||
accessible={switchProps === undefined}
|
||||
>
|
||||
{renderContent()}
|
||||
</RNElementsListItem.Swipeable>
|
||||
) : (
|
||||
<RNElementsListItem
|
||||
containerStyle={containerStyle ?? stylesHook.containerStyle}
|
||||
Component={Component}
|
||||
bottomDivider={bottomDivider}
|
||||
topDivider={topDivider}
|
||||
testID={testID}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
disabled={disabled}
|
||||
accessible={switchProps === undefined}
|
||||
>
|
||||
{renderContent()}
|
||||
</RNElementsListItem>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default ListItem;
|
||||
|
@ -1323,14 +1323,13 @@ const SendDetails = () => {
|
||||
<KeyboardAvoidingView enabled={!isTablet} behavior={undefined}>
|
||||
<View style={[styles.optionsContent, stylesHook.optionsContent]}>
|
||||
{wallet?.allowBIP47() && wallet.isBIP47Enabled() && (
|
||||
<ListItem testID="InsertContactButton" title={loc.send.details_insert_contact} hideChevron onPress={handleInsertContact} />
|
||||
<ListItem testID="InsertContactButton" title={loc.send.details_insert_contact} onPress={handleInsertContact} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<ListItem
|
||||
testID="sendMaxButton"
|
||||
disabled={balance === 0 || isSendMaxUsed}
|
||||
title={loc.send.details_adv_full}
|
||||
hideChevron
|
||||
onPress={onUseAllPressed}
|
||||
/>
|
||||
)}
|
||||
@ -1342,37 +1341,31 @@ const SendDetails = () => {
|
||||
/>
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem title={loc.send.details_adv_import} hideChevron onPress={importTransaction} />
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransaction} />
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem
|
||||
testID="ImportQrTransactionButton"
|
||||
title={loc.send.details_adv_import_qr}
|
||||
hideChevron
|
||||
onPress={importQrTransaction}
|
||||
/>
|
||||
<ListItem testID="ImportQrTransactionButton" title={loc.send.details_adv_import_qr} onPress={importQrTransaction} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && isEditable && (
|
||||
<ListItem title={loc.send.details_adv_import} hideChevron onPress={importTransactionMultisig} />
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransactionMultisig} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && wallet.howManySignaturesCanWeMake() > 0 && isEditable && (
|
||||
<ListItem title={loc.multisig.co_sign_transaction} hideChevron onPress={importTransactionMultisigScanQr} />
|
||||
<ListItem title={loc.multisig.co_sign_transaction} onPress={importTransactionMultisigScanQr} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<>
|
||||
<ListItem testID="AddRecipient" title={loc.send.details_add_rec_add} hideChevron onPress={handleAddRecipient} />
|
||||
<ListItem testID="AddRecipient" title={loc.send.details_add_rec_add} onPress={handleAddRecipient} />
|
||||
<ListItem
|
||||
testID="RemoveRecipient"
|
||||
title={loc.send.details_add_rec_rem}
|
||||
hideChevron
|
||||
disabled={addresses.length < 2}
|
||||
onPress={handleRemoveRecipient}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ListItem testID="CoinControl" title={loc.cc.header} hideChevron onPress={handleCoinControl} />
|
||||
<ListItem testID="CoinControl" title={loc.cc.header} onPress={handleCoinControl} />
|
||||
{(wallet as MultisigHDWallet)?.allowCosignPsbt() && isEditable && (
|
||||
<ListItem testID="PsbtSign" title={loc.send.psbt_sign} hideChevron onPress={handlePsbtSign} />
|
||||
<ListItem testID="PsbtSign" title={loc.send.psbt_sign} onPress={handlePsbtSign} />
|
||||
)}
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
|
@ -226,7 +226,6 @@ const EncryptStorage = () => {
|
||||
</Text>
|
||||
<ListItem
|
||||
testID="EncyptedAndPasswordProtected"
|
||||
hideChevron
|
||||
title={loc.settings.encrypt_enc_and_pass}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{
|
||||
|
@ -54,7 +54,6 @@ const GeneralSettings: React.FC = () => {
|
||||
{Platform.OS === 'ios' ? (
|
||||
<>
|
||||
<ListItem
|
||||
hideChevron
|
||||
title={loc.settings.general_continuity}
|
||||
Component={PressableWrapper}
|
||||
switch={{ onValueChange: onHandOffUseEnabledChange, value: isHandOffUseEnabled }}
|
||||
|
@ -107,7 +107,6 @@ const SettingsPrivacy: React.FC = () => {
|
||||
) : null}
|
||||
|
||||
<ListItem
|
||||
hideChevron
|
||||
title={loc.settings.privacy_read_clipboard}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{
|
||||
@ -124,7 +123,6 @@ const SettingsPrivacy: React.FC = () => {
|
||||
</BlueCard>
|
||||
<BlueSpacing20 />
|
||||
<ListItem
|
||||
hideChevron
|
||||
title={loc.settings.privacy_quickactions}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{
|
||||
@ -142,7 +140,6 @@ const SettingsPrivacy: React.FC = () => {
|
||||
</BlueCard>
|
||||
|
||||
<ListItem
|
||||
hideChevron
|
||||
title={loc.settings.privacy_do_not_track}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{ onValueChange: onDoNotTrackValueChange, value: isDoNotTrackEnabled, disabled: isLoading === SettingsPrivacySection.All }}
|
||||
@ -157,7 +154,6 @@ const SettingsPrivacy: React.FC = () => {
|
||||
{loc.settings.widgets}
|
||||
</Text>
|
||||
<ListItem
|
||||
hideChevron
|
||||
title={loc.settings.total_balance}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{
|
||||
|
Loading…
Reference in New Issue
Block a user