ADD: Swipeable

This commit is contained in:
Marcos Rodriguez Velez 2024-06-12 13:30:01 -04:00
parent 485b0e3492
commit 14cded25bf
No known key found for this signature in database
GPG Key ID: 6030B2F48CCE86D7
5 changed files with 161 additions and 97 deletions

View File

@ -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;

View File

@ -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>

View File

@ -226,7 +226,6 @@ const EncryptStorage = () => {
</Text>
<ListItem
testID="EncyptedAndPasswordProtected"
hideChevron
title={loc.settings.encrypt_enc_and_pass}
Component={TouchableWithoutFeedback}
switch={{

View File

@ -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 }}

View File

@ -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={{