Merge pull request #6248 from BlueWallet/multisig

FIX: Multisig Forget alerts where visible after closing modal (macOS)
This commit is contained in:
GLaDOS 2024-03-09 09:42:41 +00:00 committed by GitHub
commit fc221e9c56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 116 additions and 55 deletions

View file

@ -1,9 +1,11 @@
import React from 'react';
import React, { useRef } from 'react';
import { View, StyleSheet, Text, TouchableOpacity, ActivityIndicator } from 'react-native';
import { View, StyleSheet, Text, TouchableOpacity, ActivityIndicator, findNodeHandle } from 'react-native';
import PropTypes from 'prop-types';
import { Icon } from 'react-native-elements';
import { useTheme } from './themes';
import ActionSheetOptions from '../screen/ActionSheet.common';
import ActionSheet from '../screen/ActionSheet';
export const MultipleStepsListItemDashType = Object.freeze({ none: 0, top: 1, bottom: 2, topAndBottom: 3 });
export const MultipleStepsListItemButtohType = Object.freeze({ partial: 0, full: 1 });
@ -15,6 +17,8 @@ const MultipleStepsListItem = props => {
circledText = '',
leftText = '',
checked = false,
useActionSheet = false,
actionSheetOptions = null, // Default to null or appropriate default
} = props;
const stylesHook = StyleSheet.create({
provideKeyButton: {
@ -36,6 +40,29 @@ const MultipleStepsListItem = props => {
color: colors.alternativeTextColor,
},
});
const selfRef = useRef(null); // Create a ref for the component itself
const handleOnPressForActionSheet = () => {
if (useActionSheet && actionSheetOptions) {
// Clone options to modify them
let modifiedOptions = { ...actionSheetOptions };
// Use 'selfRef' if the component uses its own ref, or 'ref' if it's using forwarded ref
const anchor = findNodeHandle(selfRef.current);
if (anchor) {
// Attach the anchor only if it exists
modifiedOptions = { ...modifiedOptions, anchor };
}
ActionSheet.showActionSheetWithOptions(modifiedOptions, buttonIndex => {
// Call the original onPress function, if provided, and not cancelled
if (buttonIndex !== -1 && props.button.onPress) {
props.button.onPress(buttonIndex);
}
});
}
};
const renderDashes = () => {
switch (dashes) {
@ -106,11 +133,12 @@ const MultipleStepsListItem = props => {
{props.button.buttonType === undefined ||
(props.button.buttonType === MultipleStepsListItemButtohType.full && (
<TouchableOpacity
ref={useActionSheet ? selfRef : null}
testID={props.button.testID}
accessibilityRole="button"
disabled={props.button.disabled}
style={[styles.provideKeyButton, stylesHook.provideKeyButton, buttonOpacity]}
onPress={props.button.onPress}
onPress={useActionSheet ? handleOnPressForActionSheet : props.button.onPress}
>
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText]}>{props.button.text}</Text>
</TouchableOpacity>
@ -157,6 +185,8 @@ MultipleStepsListItem.propTypes = {
checked: PropTypes.bool,
leftText: PropTypes.string,
showActivityIndicator: PropTypes.bool,
useActionSheet: PropTypes.bool,
actionSheetOptions: PropTypes.shape(ActionSheetOptions),
dashes: PropTypes.number,
button: PropTypes.shape({
text: PropTypes.string,

View file

@ -0,0 +1,11 @@
// ActionSheet.common.ts
export interface ActionSheetOptions {
title?: string;
message?: string;
options: string[]; // Array of button labels.
destructiveButtonIndex?: number;
cancelButtonIndex?: number;
anchor?: number;
}
export type CompletionCallback = (buttonIndex: number) => void;

View file

@ -1,10 +0,0 @@
import { ActionSheetIOS, InteractionManager } from 'react-native';
export default class ActionSheet {
static showActionSheetWithOptions(options, completion) {
ActionSheetIOS.dismissActionSheet();
InteractionManager.runAfterInteractions(() => {
ActionSheetIOS.showActionSheetWithOptions(options, completion);
});
}
}

15
screen/ActionSheet.ios.ts Normal file
View file

@ -0,0 +1,15 @@
// ActionSheet.ios.ts
import { ActionSheetIOS, InteractionManager } from 'react-native';
import { ActionSheetOptions, CompletionCallback } from './ActionSheet.common';
export default class ActionSheet {
static showActionSheetWithOptions(options: ActionSheetOptions, completion: CompletionCallback): void {
InteractionManager.runAfterInteractions(() => {
const iosOptions = {
...options,
anchor: options.anchor,
};
ActionSheetIOS.showActionSheetWithOptions(iosOptions, completion);
});
}
}

View file

@ -1,9 +0,0 @@
import { Alert, InteractionManager } from 'react-native';
export default class ActionSheet {
static showActionSheetWithOptions(options) {
InteractionManager.runAfterInteractions(() => {
Alert.alert(options.title, options.message, options.buttons, { cancelable: true });
});
}
}

26
screen/ActionSheet.ts Normal file
View file

@ -0,0 +1,26 @@
// ActionSheet.ts
import { Alert, InteractionManager } from 'react-native';
import { ActionSheetOptions, CompletionCallback } from './ActionSheet.common';
export default class ActionSheet {
static showActionSheetWithOptions(options: ActionSheetOptions, completion: CompletionCallback): void {
InteractionManager.runAfterInteractions(() => {
const alertOptions = options.options.map((option, index) => {
let style: 'default' | 'cancel' | 'destructive' = 'default';
if (index === options.destructiveButtonIndex) {
style = 'destructive';
} else if (index === options.cancelButtonIndex) {
style = 'cancel';
}
return {
text: option,
onPress: () => completion(index),
style,
};
});
Alert.alert(options.title || '', options.message || '', alertOptions, { cancelable: !!options.cancelButtonIndex });
});
}
}

View file

@ -3,7 +3,6 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack';
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import {
ActivityIndicator,
Alert,
FlatList,
InteractionManager,
Keyboard,
@ -48,6 +47,7 @@ import { useTheme } from '../../components/themes';
import { scanQrHelper } from '../../helpers/scan-qr';
import usePrivacy from '../../hooks/usePrivacy';
import loc from '../../loc';
import { isDesktop } from '../../blue_modules/environment';
const fs = require('../../blue_modules/fs');
const prompt = require('../../helpers/prompt');
@ -357,45 +357,43 @@ const ViewEditMultisigCosigners = ({ route }: Props) => {
dashes={MultipleStepsListItemDashType.topAndBottom}
/>
)}
{/* destructiveButtonIndex and cancelButtonIndex are different numbers on Mac Catalyst and mobile */}
<MultipleStepsListItem
useActionSheet
actionSheetOptions={{
options: isDesktop ? [loc.multisig.confirm, loc._.cancel] : ['', loc.multisig.confirm, loc._.cancel],
title: loc._.seed,
message: loc.multisig.are_you_sure_seed_will_be_lost,
cancelButtonIndex: isDesktop ? 1 : 0,
destructiveButtonIndex: isDesktop ? 0 : 1,
}}
showActivityIndicator={vaultKeyData.keyIndex === el.index + 1 && vaultKeyData.isLoading}
dashes={el.index === length - 1 ? MultipleStepsListItemDashType.top : MultipleStepsListItemDashType.topAndBottom}
button={{
text: loc.multisig.forget_this_seed,
disabled: vaultKeyData.isLoading,
buttonType: MultipleStepsListItemButtohType.full,
onPress: () => {
Alert.alert(
loc._.seed,
loc.multisig.are_you_sure_seed_will_be_lost,
[
{
text: loc._.ok,
onPress: () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setVaultKeyData({
...vaultKeyData,
isLoading: true,
keyIndex: el.index + 1,
});
setTimeout(
() =>
xpubInsteadOfSeed(el.index + 1).finally(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setVaultKeyData({
...vaultKeyData,
isLoading: false,
keyIndex: el.index + 1,
});
}),
100,
);
},
style: 'destructive',
},
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' },
],
{ cancelable: false },
onPress: buttonIndex => {
if ((isDesktop && buttonIndex === 1) || (!isDesktop && buttonIndex === 2)) return;
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setVaultKeyData({
...vaultKeyData,
isLoading: true,
keyIndex: el.index + 1,
});
setTimeout(
() =>
xpubInsteadOfSeed(el.index + 1).finally(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setVaultKeyData({
...vaultKeyData,
isLoading: false,
keyIndex: el.index + 1,
});
}),
100,
);
},
}}