diff --git a/Navigation.tsx b/Navigation.tsx
index 1664fb202..f3703d4e0 100644
--- a/Navigation.tsx
+++ b/Navigation.tsx
@@ -27,7 +27,7 @@ import WalletsAddMultisigStep2 from './screen/wallets/addMultisigStep2';
import WalletAddresses from './screen/wallets/addresses';
import WalletDetails from './screen/wallets/details';
import WalletExport from './screen/wallets/export';
-import ExportMultisigCoordinationSetup from './screen/wallets/exportMultisigCoordinationSetup';
+import ExportMultisigCoordinationSetup from './screen/wallets/ExportMultisigCoordinationSetup';
import GenerateWord from './screen/wallets/generateWord';
import ImportWallet from './screen/wallets/import';
import ImportCustomDerivationPath from './screen/wallets/importCustomDerivationPath';
@@ -556,7 +556,12 @@ const ExportMultisigCoordinationSetupRoot = () => {
);
diff --git a/components/SquareButton.tsx b/components/SquareButton.tsx
index dbf130250..47ea994ca 100644
--- a/components/SquareButton.tsx
+++ b/components/SquareButton.tsx
@@ -5,7 +5,7 @@ import { useTheme } from './themes';
interface SquareButtonProps {
title: string;
onPress?: () => void;
- style: StyleProp;
+ style?: StyleProp;
testID?: string;
}
diff --git a/screen/wallets/ExportMultisigCoordinationSetup.tsx b/screen/wallets/ExportMultisigCoordinationSetup.tsx
new file mode 100644
index 000000000..fccb695d7
--- /dev/null
+++ b/screen/wallets/ExportMultisigCoordinationSetup.tsx
@@ -0,0 +1,200 @@
+import React, { useCallback, useContext, useMemo, useReducer, useRef } from 'react';
+import { ActivityIndicator, InteractionManager, ScrollView, StyleSheet, View } from 'react-native';
+import { useFocusEffect, useRoute, RouteProp } from '@react-navigation/native';
+import { BlueSpacing20, BlueText } from '../../BlueComponents';
+import { DynamicQRCode } from '../../components/DynamicQRCode';
+import loc from '../../loc';
+import { SquareButton } from '../../components/SquareButton';
+import { BlueStorageContext } from '../../blue_modules/storage-context';
+import { useTheme } from '../../components/themes';
+import usePrivacy from '../../hooks/usePrivacy';
+import { TWallet } from '../../class/wallets/types';
+import SaveFileButton from '../../components/SaveFileButton';
+
+type RootStackParamList = {
+ ExportMultisigCoordinationSetup: {
+ walletID: string;
+ };
+};
+
+const enum ActionType {
+ SET_LOADING = 'SET_LOADING',
+ SET_SHARE_BUTTON_TAPPED = 'SET_SHARE_BUTTON_TAPPED',
+ SET_ERROR = 'SET_ERROR',
+ SET_QR_CODE_CONTENTS = 'SET_QR_CODE_CONTENTS',
+}
+
+type State = {
+ isLoading: boolean;
+ isShareButtonTapped: boolean;
+ qrCodeContents?: string;
+ error: string | null;
+};
+
+type Action =
+ | { type: ActionType.SET_LOADING; isLoading: boolean }
+ | { type: ActionType.SET_SHARE_BUTTON_TAPPED; isShareButtonTapped: boolean }
+ | { type: ActionType.SET_ERROR; error: string | null }
+ | { type: ActionType.SET_QR_CODE_CONTENTS; qrCodeContents: string };
+
+function reducer(state: State, action: Action): State {
+ switch (action.type) {
+ case ActionType.SET_LOADING:
+ return { ...state, isLoading: action.isLoading };
+ case ActionType.SET_SHARE_BUTTON_TAPPED:
+ return { ...state, isShareButtonTapped: action.isShareButtonTapped };
+ case ActionType.SET_ERROR:
+ return { ...state, error: action.error, isLoading: false };
+ case ActionType.SET_QR_CODE_CONTENTS:
+ return { ...state, qrCodeContents: action.qrCodeContents, isLoading: false };
+ default:
+ return state;
+ }
+}
+
+const initialState: State = {
+ isLoading: true,
+ isShareButtonTapped: false,
+ qrCodeContents: undefined,
+ error: null,
+};
+
+const ExportMultisigCoordinationSetup: React.FC = () => {
+ const [state, dispatch] = useReducer(reducer, initialState);
+ const { isLoading, isShareButtonTapped, qrCodeContents } = state;
+ const { params } = useRoute>();
+ const walletID = params.walletID;
+ const { wallets } = useContext(BlueStorageContext);
+ const wallet: TWallet | undefined = wallets.find(w => w.getID() === walletID);
+ const dynamicQRCode = useRef();
+ const { colors } = useTheme();
+ const { enableBlur, disableBlur } = usePrivacy();
+ const stylesHook = StyleSheet.create({
+ scrollViewContent: {
+ backgroundColor: colors.elevated,
+ },
+ type: { color: colors.foregroundColor },
+ secret: { color: colors.foregroundColor },
+ exportButton: {
+ backgroundColor: colors.buttonDisabledBackgroundColor,
+ },
+ });
+
+ const label = useMemo(() => wallet?.getLabel(), [wallet]);
+ const xpub = useMemo(() => wallet?.getXpub(), [wallet]);
+
+ const setIsShareButtonTapped = (value: boolean) => {
+ dispatch({ type: ActionType.SET_SHARE_BUTTON_TAPPED, isShareButtonTapped: value });
+ };
+
+ useFocusEffect(
+ useCallback(() => {
+ dispatch({ type: ActionType.SET_LOADING, isLoading: true });
+
+ const task = InteractionManager.runAfterInteractions(async () => {
+ enableBlur();
+ if (wallet) {
+ try {
+ if (typeof xpub === 'string') {
+ const value = Buffer.from(xpub, 'ascii').toString('hex');
+ dispatch({ type: ActionType.SET_QR_CODE_CONTENTS, qrCodeContents: value });
+ } else {
+ throw new Error('Expected getXpub() to return a string.');
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
+ dispatch({ type: ActionType.SET_ERROR, error: errorMessage });
+ }
+ } else {
+ dispatch({ type: ActionType.SET_ERROR, error: 'Wallet not found' });
+ }
+ });
+
+ return () => {
+ task.cancel();
+ disableBlur();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [wallet]),
+ );
+
+ const exportTxtFileBeforeOnPress = async () => {
+ setIsShareButtonTapped(true);
+ dynamicQRCode.current?.stopAutoMove();
+ };
+
+ const exportTxtFileAfterOnPress = () => {
+ setIsShareButtonTapped(false);
+ dynamicQRCode.current?.startAutoMove();
+ };
+
+ const renderView = wallet ? (
+ <>
+
+ {wallet.getLabel()}
+
+
+ {qrCodeContents && }
+
+ {isShareButtonTapped ? (
+
+ ) : (
+ label &&
+ xpub && (
+
+
+
+ )
+ )}
+
+
+ {wallet.getXpub()}
+ >
+ ) : null;
+
+ return (
+
+ {isLoading ? : renderView}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ scrollViewContent: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexGrow: 1,
+ },
+ type: {
+ fontSize: 17,
+ fontWeight: '700',
+ },
+ secret: {
+ alignItems: 'center',
+ paddingHorizontal: 16,
+ fontSize: 16,
+ lineHeight: 24,
+ },
+ exportButton: {
+ height: 48,
+ borderRadius: 8,
+ flex: 1,
+ justifyContent: 'center',
+ paddingHorizontal: 16,
+ width: '80%',
+ maxWidth: 300,
+ },
+});
+
+export default ExportMultisigCoordinationSetup;
diff --git a/screen/wallets/details.js b/screen/wallets/details.js
index 7b4d687a4..484b46e09 100644
--- a/screen/wallets/details.js
+++ b/screen/wallets/details.js
@@ -281,7 +281,7 @@ const WalletDetails = () => {
navigate('ExportMultisigCoordinationSetupRoot', {
screen: 'ExportMultisigCoordinationSetup',
params: {
- walletId: wallet.getID(),
+ walletID: wallet.getID(),
},
});
};
diff --git a/screen/wallets/exportMultisigCoordinationSetup.js b/screen/wallets/exportMultisigCoordinationSetup.js
deleted file mode 100644
index b40ba8d30..000000000
--- a/screen/wallets/exportMultisigCoordinationSetup.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import React, { useCallback, useContext, useRef, useState } from 'react';
-import { ActivityIndicator, InteractionManager, ScrollView, StyleSheet, View } from 'react-native';
-import { useFocusEffect, useRoute } from '@react-navigation/native';
-import { BlueSpacing20, BlueText } from '../../BlueComponents';
-import navigationStyle from '../../components/navigationStyle';
-import { DynamicQRCode } from '../../components/DynamicQRCode';
-import loc from '../../loc';
-import { SquareButton } from '../../components/SquareButton';
-import { BlueStorageContext } from '../../blue_modules/storage-context';
-import { useTheme } from '../../components/themes';
-import SafeArea from '../../components/SafeArea';
-import usePrivacy from '../../hooks/usePrivacy';
-import SaveFileButton from '../../components/SaveFileButton';
-
-const ExportMultisigCoordinationSetup = () => {
- const walletId = useRoute().params.walletId;
- const { wallets } = useContext(BlueStorageContext);
- const wallet = wallets.find(w => w.getID() === walletId);
- const qrCodeContents = useRef();
- const dynamicQRCode = useRef();
- const [isLoading, setIsLoading] = useState(true);
- const [isShareButtonTapped, setIsShareButtonTapped] = useState(false);
- const { colors } = useTheme();
- const { enableBlur, disableBlur } = usePrivacy();
- const stylesHook = StyleSheet.create({
- loading: {
- backgroundColor: colors.elevated,
- },
- root: {
- backgroundColor: colors.elevated,
- },
- type: { color: colors.foregroundColor },
- secret: { color: colors.foregroundColor },
- exportButton: {
- backgroundColor: colors.buttonDisabledBackgroundColor,
- },
- });
-
- const exportTxtFileBeforeOnPress = async () => {
- setIsShareButtonTapped(true);
- dynamicQRCode.current?.stopAutoMove();
- };
-
- const exportTxtFileAfterOnPress = () => {
- setIsShareButtonTapped(false);
- dynamicQRCode.current?.startAutoMove();
- };
-
- useFocusEffect(
- useCallback(() => {
- enableBlur();
- const task = InteractionManager.runAfterInteractions(async () => {
- if (wallet) {
- qrCodeContents.current = Buffer.from(wallet.getXpub(), 'ascii').toString('hex');
- setIsLoading(false);
- }
- });
- return () => {
- task.cancel();
- disableBlur();
- };
- }, [disableBlur, enableBlur, wallet]),
- );
-
- return isLoading ? (
-
-
-
- ) : (
-
-
-
- {wallet.getLabel()}
-
-
-
-
- {isShareButtonTapped ? (
-
- ) : (
-
-
-
- )}
-
- {wallet.getXpub()}
-
-
- );
-};
-
-const styles = StyleSheet.create({
- loading: {
- flex: 1,
- justifyContent: 'center',
- },
- scrollViewContent: {
- alignItems: 'center',
- justifyContent: 'center',
- flexGrow: 1,
- },
- type: {
- fontSize: 17,
- fontWeight: '700',
- },
- secret: {
- alignItems: 'center',
- paddingHorizontal: 16,
- fontSize: 16,
- lineHeight: 24,
- },
- exportButton: {
- height: 48,
- borderRadius: 8,
- flex: 1,
- justifyContent: 'center',
- paddingHorizontal: 16,
- width: '80%',
- maxWidth: 300,
- },
-});
-
-ExportMultisigCoordinationSetup.navigationOptions = navigationStyle(
- {
- closeButton: true,
- headerBackVisible: false,
- statusBarStyle: 'light',
- },
- opts => ({ ...opts, title: loc.multisig.export_coordination_setup }),
-);
-
-export default ExportMultisigCoordinationSetup;