ADD: BottomModal header prop

This commit is contained in:
Marcos Rodriguez Velez 2024-08-23 13:40:27 -04:00
parent e1746c6fb0
commit 31f897d537
3 changed files with 130 additions and 95 deletions

View File

@ -1,6 +1,8 @@
import React, { forwardRef, useImperativeHandle, useRef, ReactElement, ComponentType, JSXElementConstructor, ReactNode } from 'react';
import React, { forwardRef, useImperativeHandle, useRef, ReactElement, ComponentType } from 'react';
import { SheetSize, SizeInfo, TrueSheet, TrueSheetProps } from '@lodev09/react-native-true-sheet';
import { Keyboard, StyleSheet, View, TouchableOpacity, Platform, Image } from 'react-native';
import { Keyboard, StyleSheet, View, TouchableOpacity, Platform, PlatformColor, GestureResponderEvent } from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import SaveFileButton from './SaveFileButton';
interface BottomModalProps extends TrueSheetProps {
children?: React.ReactNode;
@ -12,32 +14,21 @@ interface BottomModalProps extends TrueSheetProps {
onPresent?: () => void;
onSizeChange?: (size: SizeInfo) => void;
showCloseButton?: boolean;
shareContent?: BottomModalShareContent;
shareButtonOnPress?: (event: GestureResponderEvent) => void;
header?: ReactElement | ComponentType<any> | null;
}
type BottomModalShareContent = {
fileName: string;
fileContent: string;
};
export interface BottomModalHandle {
present: () => Promise<void>;
dismiss: () => Promise<void>;
}
const styles = StyleSheet.create({
footerContainer: {
alignItems: 'center',
justifyContent: 'center',
},
buttonContainer: {
position: 'absolute',
backgroundColor: 'lightgray',
justifyContent: 'center',
alignItems: 'center',
width: 30,
height: 30,
borderRadius: 15,
top: 20,
right: 20,
zIndex: 10,
},
});
const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
(
{
@ -46,9 +37,12 @@ const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
onSizeChange,
showCloseButton = true,
isGrabberVisible = true,
shareContent,
shareButtonOnPress,
sizes = ['auto'],
footer,
footerDefaultMargins,
header,
children,
...props
},
@ -79,21 +73,54 @@ const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
trueSheetRef.current?.dismiss();
};
const renderTopRightButton = () =>
showCloseButton ? (
<TouchableOpacity style={styles.buttonContainer} onPress={dismiss} testID="ModalDoneButton">
<Image source={require('../img/close.png')} width={20} height={20} />
</TouchableOpacity>
) : null;
const renderTopRightButton = () => {
const buttons = [];
if (shareContent || shareButtonOnPress) {
if (shareContent) {
buttons.push(
<SaveFileButton
style={styles.topRightButton}
fileContent={shareContent.fileContent}
fileName={shareContent.fileName}
testID="ModalShareButton"
>
<Ionicons name="share" size={20} color={PlatformColor('lightText')} />
</SaveFileButton>,
);
} else if (shareButtonOnPress) {
buttons.push(
<TouchableOpacity style={styles.topRightButton} onPress={shareButtonOnPress} testID="ModalShareButton">
<Ionicons name="share" size={20} color={PlatformColor('lightText')} />
</TouchableOpacity>,
);
}
}
if (showCloseButton) {
buttons.push(
<TouchableOpacity style={styles.topRightButton} onPress={dismiss} testID="ModalDoneButton">
<Ionicons name="close" size={20} color={PlatformColor('lightText')} />
</TouchableOpacity>,
);
}
return <View style={styles.topRightButtonContainer}>{buttons}</View>;
};
const renderFooter = (): ReactElement<any, string | JSXElementConstructor<any>> | ComponentType<unknown> | undefined => {
const renderHeader = () => {
return (
<View style={styles.headerContainer}>
<View style={styles.headerContent}>{typeof header === 'function' ? <header /> : header}</View>
{renderTopRightButton()}
</View>
);
};
const renderFooter = (): ReactElement | undefined => {
// Footer is not working correctly on Android yet.
if (!footer) return undefined;
if (React.isValidElement(footer)) {
return footerDefaultMargins ? <View style={styles.footerContainer}>{footer}</View> : footer;
} else if (typeof footer === 'function') {
// Render the footer component dynamically
const FooterComponent = footer as ComponentType<any>;
return <FooterComponent />;
}
@ -101,7 +128,7 @@ const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
return undefined;
};
const FooterComponent = Platform.OS !== 'android' && renderFooter();
const FooterComponent = renderFooter();
return (
<TrueSheet
@ -112,16 +139,51 @@ const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
onPresent={onPresent}
onSizeChange={onSizeChange}
grabber={isGrabberVisible}
// Footer is not working correctly on Android yet.
FooterComponent={FooterComponent as ReactElement}
FooterComponent={FooterComponent}
{...props}
>
{children}
{renderTopRightButton()}
{Platform.OS === 'android' && (renderFooter() as ReactNode)}
{renderHeader()}
<View style={styles.childrenContainer}>{children}</View>
{Platform.OS === 'android' && FooterComponent}
</TrueSheet>
);
},
);
export default BottomModal;
const styles = StyleSheet.create({
footerContainer: {
alignItems: 'center',
justifyContent: 'center',
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 16,
paddingHorizontal: 16,
marginBottom: 16,
minHeight: 22,
},
headerContent: {
flex: 1,
justifyContent: 'center',
minHeight: 22,
},
topRightButton: {
backgroundColor: PlatformColor('systemGray2'),
justifyContent: 'center',
alignItems: 'center',
width: 30,
height: 30,
borderRadius: 15,
marginLeft: 22,
},
topRightButtonContainer: {
flexDirection: 'row',
alignItems: 'center',
},
childrenContainer: {
marginTop: 0,
},
});

View File

@ -1,5 +1,5 @@
import React, { ReactNode, useCallback } from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import { StyleProp, TouchableOpacityProps, ViewStyle } from 'react-native';
import * as fs from '../blue_modules/fs';
import loc from '../loc';
@ -7,7 +7,7 @@ import { ActionIcons } from '../typings/ActionIcons';
import ToolTipMenu from './TooltipMenu';
import { Action } from './types';
interface SaveFileButtonProps {
interface SaveFileButtonProps extends TouchableOpacityProps {
fileName: string;
fileContent: string;
children?: ReactNode;
@ -57,6 +57,7 @@ const SaveFileButton: React.FC<SaveFileButtonProps> = ({
actions={actions}
onPressMenuItem={handlePressMenuItem}
buttonStyle={style as ViewStyle} // Type assertion to match ViewStyle
{...{ children }}
>
{children}
</ToolTipMenu>

View File

@ -37,8 +37,6 @@ import MultipleStepsListItem, {
MultipleStepsListItemDashType,
} from '../../components/MultipleStepsListItem';
import QRCodeComponent from '../../components/QRCodeComponent';
import SaveFileButton from '../../components/SaveFileButton';
import { SquareButton } from '../../components/SquareButton';
import SquareEnumeratedWords, { SquareEnumeratedWordsContentAlign } from '../../components/SquareEnumeratedWords';
import { useTheme } from '../../components/themes';
import prompt from '../../helpers/prompt';
@ -91,9 +89,6 @@ const ViewEditMultisigCosigners: React.FC = () => {
textDestination: {
color: colors.foregroundColor,
},
exportButton: {
backgroundColor: colors.buttonDisabledBackgroundColor,
},
vaultKeyText: {
color: colors.alternativeTextColor,
},
@ -159,10 +154,6 @@ const ViewEditMultisigCosigners: React.FC = () => {
}, [isSaveButtonDisabled, addListener, dispatch]),
);
const saveFileButtonAfterOnPress = () => {
shareModalRef.current?.dismiss();
};
const onSave = async () => {
dismissAllModals();
if (!wallet) {
@ -225,32 +216,24 @@ const ViewEditMultisigCosigners: React.FC = () => {
return (
<BottomModal
ref={mnemonicsModalRef}
footerDefaultMargins
backgroundColor={colors.elevated}
contentContainerStyle={styles.newKeyModalContent}
footer={
<>
<Button
title={loc.multisig.share}
onPress={() => {
shareModalRef.current?.present();
}}
/>
<BlueSpacing20 />
</>
shareButtonOnPress={() => {
shareModalRef.current?.present();
}}
header={
<View style={styles.itemKeyUnprovidedWrapper}>
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
<Icon size={24} name="check" type="ionicons" color={colors.msSuccessCheck} />
</View>
<View style={styles.vaultKeyTextWrapper}>
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>
{loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex })}
</Text>
</View>
</View>
}
>
<View style={styles.itemKeyUnprovidedWrapper}>
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
<Icon size={24} name="check" type="ionicons" color={colors.msSuccessCheck} />
</View>
<View style={styles.vaultKeyTextWrapper}>
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>
{loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex })}
</Text>
</View>
</View>
<BlueSpacing20 />
{vaultKeyData.xpub.length > 1 && (
<>
<Text style={[styles.textDestination, stylesHook.textDestination]}>{loc._.wallet_key}</Text>
@ -568,23 +551,15 @@ const ViewEditMultisigCosigners: React.FC = () => {
onClose={hideShareModal}
contentContainerStyle={[styles.modalContent, styles.alignItemsCenter, styles.shareModalHeight]}
backgroundColor={colors.elevated}
footerDefaultMargins
footer={
<SaveFileButton
style={[styles.exportButton, stylesHook.exportButton]}
fileContent={exportString}
fileName={exportFilename}
afterOnPress={saveFileButtonAfterOnPress}
>
<SquareButton title={loc.multisig.share} />
</SaveFileButton>
}
shareContent={{ fileName: exportFilename, fileContent: exportString }}
>
<Text style={[styles.headerText, stylesHook.textDestination]}>
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
</Text>
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
<BlueSpacing20 />
<View style={styles.alignItemsCenter}>
<Text style={[styles.headerText, stylesHook.textDestination]}>
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
</Text>
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
<BlueSpacing20 />
</View>
</BottomModal>
);
};
@ -637,6 +612,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
renderItem={_renderKeyItem}
automaticallyAdjustKeyboardInsets
contentInsetAdjustmentBehavior="automatic"
contentContainerStyle={styles.contentContainerStyle}
automaticallyAdjustContentInsets
keyExtractor={(_item, index) => `${index}`}
/>
@ -656,14 +632,16 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'space-between',
},
itemKeyUnprovidedWrapper: { flexDirection: 'row', paddingTop: 16 },
itemKeyUnprovidedWrapper: { flexDirection: 'row', paddingTop: 22 },
textDestination: { fontWeight: '600' },
vaultKeyText: { fontSize: 18, fontWeight: 'bold' },
vaultKeyTextWrapper: { justifyContent: 'center', alignItems: 'center', paddingLeft: 16 },
newKeyModalContent: {
paddingHorizontal: 22,
paddingTop: 32,
minHeight: 370,
minHeight: 320,
},
contentContainerStyle: {
padding: 16,
},
modalContent: {
padding: 22,
@ -676,16 +654,10 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
exportButton: {
height: 48,
borderRadius: 8,
justifyContent: 'center',
paddingHorizontal: 16,
marginBottom: 32,
},
headerText: { fontSize: 15, color: '#13244D' },
alignItemsCenter: { alignItems: 'center', justifyContent: 'space-between' },
shareModalHeight: { minHeight: 450 },
shareModalHeight: { minHeight: 370 },
tipKeys: {
fontSize: 15,
fontWeight: '600',