mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 01:40:12 +01:00
ADD: BottomModal header prop
This commit is contained in:
parent
e1746c6fb0
commit
31f897d537
@ -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,
|
||||
},
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user