Skip to content

Commit

Permalink
chore(suite): refactor connect popup to store state in redux
Browse files Browse the repository at this point in the history
  • Loading branch information
martykan committed Feb 11, 2025
1 parent 549f6da commit 25b6741
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,54 +1,54 @@
import { connectPopupActions, selectConnectPopupCall } from '@suite-common/connect-popup';
import { H2, NewModal, Paragraph } from '@trezor/components';
import { ERRORS } from '@trezor/connect';
import { spacings } from '@trezor/theme';

import { Translation } from 'src/components/suite';
import { useDispatch, useSelector } from 'src/hooks/suite';

interface ConnectPopupModalProps {
method: string;
processName?: string;
origin?: string;
onConfirm: () => void;
onCancel: () => void;
}
export const ConnectPopupModal = () => {
const dispatch = useDispatch();
const popupCall = useSelector(selectConnectPopupCall);
if (!popupCall) return null;

export const ConnectPopupModal = ({
method,
processName,
origin,
onConfirm,
onCancel,
}: ConnectPopupModalProps) => (
<NewModal
onCancel={onCancel}
iconName="plugs"
variant="primary"
bottomContent={
<>
<NewModal.Button variant="tertiary" onClick={onCancel}>
<Translation id="TR_CANCEL" />
</NewModal.Button>
<NewModal.Button variant="primary" onClick={onConfirm}>
<Translation id="TR_CONFIRM" />
</NewModal.Button>
</>
}
heading={<Translation id="TR_TREZOR_CONNECT" />}
>
<H2>{method}</H2>
const { method, processName, origin } = popupCall;
const onConfirm = () => dispatch(connectPopupActions.approveCall());
const onCancel = () =>
dispatch(connectPopupActions.rejectCall(ERRORS.TypedError('Method_Cancel')));

{processName && (
<Paragraph margin={{ top: spacings.xs }}>
<Translation id="TR_CONNECT_MODAL_PROCESS" /> <strong>{processName}</strong>
</Paragraph>
)}
{origin && (
<Paragraph>
<Translation id="TR_CONNECT_MODAL_WEB_ORIGIN" /> <strong>{origin}</strong>
</Paragraph>
)}
return (
<NewModal
onCancel={onCancel}
iconName="plugs"
variant="primary"
bottomContent={
<>
<NewModal.Button variant="tertiary" onClick={onCancel}>
<Translation id="TR_CANCEL" />
</NewModal.Button>
<NewModal.Button variant="primary" onClick={onConfirm}>
<Translation id="TR_CONFIRM" />
</NewModal.Button>
</>
}
heading={<Translation id="TR_TREZOR_CONNECT" />}
>
<H2>{method}</H2>

<Paragraph variant="tertiary" margin={{ top: spacings.xs }}>
<Translation id="TR_CONNECT_MODAL_REQUEST_DESCRIPTION" />
</Paragraph>
</NewModal>
);
{processName && (
<Paragraph margin={{ top: spacings.xs }}>
<Translation id="TR_CONNECT_MODAL_PROCESS" /> <strong>{processName}</strong>
</Paragraph>
)}
{origin && (
<Paragraph>
<Translation id="TR_CONNECT_MODAL_WEB_ORIGIN" /> <strong>{origin}</strong>
</Paragraph>
)}

<Paragraph variant="tertiary" margin={{ top: spacings.xs }}>
<Translation id="TR_CONNECT_MODAL_REQUEST_DESCRIPTION" />
</Paragraph>
</NewModal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,7 @@ export const UserContextModal = ({
case 'passphrase-mismatch-warning':
return <PassphraseMismatchModal onCancel={onCancel} />;
case 'connect-popup':
return (
<ConnectPopupModal
onCancel={payload.onCancel}
onConfirm={payload.onConfirm}
method={payload.method}
origin={payload.origin}
processName={payload.processName}
/>
);
return <ConnectPopupModal />;
case 'walletconnect-proposal':
return <WalletConnectProposalModal eventId={payload.eventId} />;
default:
Expand Down
2 changes: 2 additions & 0 deletions packages/suite/src/middlewares/wallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
prepareStakeMiddleware,
} from '@suite-common/wallet-core';
import { prepareTokenDefinitionsMiddleware } from '@suite-common/token-definitions';
import { prepareConnectPopupMiddleware } from '@suite-common/connect-popup';
import { prepareWalletConnectMiddleware } from '@suite-common/walletconnect';

import { extraDependencies } from 'src/support/extraDependencies';
Expand All @@ -31,5 +32,6 @@ export default [
tradingMiddleware,
coinjoinMiddleware,
replaceByFeeErrorMiddleware,
prepareConnectPopupMiddleware(extraDependencies),
prepareWalletConnectMiddleware(extraDependencies),
];
3 changes: 3 additions & 0 deletions packages/suite/src/reducers/wallet/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { combineReducers } from 'redux';

import { prepareConnectPopupReducer } from '@suite-common/connect-popup';
import {
feesReducer,
prepareAccountsReducer,
Expand Down Expand Up @@ -31,6 +32,7 @@ export const fiatRatesReducer = prepareFiatRatesReducer(extraDependencies);
export const discoveryReducer = prepareDiscoveryReducer(extraDependencies);
export const stakeReducer = prepareStakeReducer(extraDependencies);
export const sendFormReducer = prepareSendFormReducer(extraDependencies);
export const connectPopupReducer = prepareConnectPopupReducer(extraDependencies);
export const walletConnectReducer = prepareWalletConnectReducer(extraDependencies);

const WalletReducers = combineReducers({
Expand All @@ -51,6 +53,7 @@ const WalletReducers = combineReducers({
cardanoStaking: cardanoStakingReducer,
coinjoin: coinjoinReducer,
stake: stakeReducer,
connectPopup: connectPopupReducer,
walletConnect: walletConnectReducer,
});

Expand Down
7 changes: 6 additions & 1 deletion packages/suite/src/types/wallet/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { connectPopupActions } from '@suite-common/connect-popup';
import { tokenDefinitionsActions } from '@suite-common/token-definitions/src/tokenDefinitionsActions';
import {
accountsActions,
Expand Down Expand Up @@ -61,6 +62,9 @@ type BlockchainAction = ReturnType<(typeof blockchainActions)[keyof typeof block
type DiscoveryAction = ReturnType<(typeof discoveryActions)[keyof typeof discoveryActions]>;
type StakeAction = ReturnType<(typeof stakeActions)[keyof typeof stakeActions]>;
type SendFormAction = ReturnType<(typeof sendFormActions)[keyof typeof sendFormActions]>;
type ConnectPopupAction = ReturnType<
(typeof connectPopupActions)[keyof typeof connectPopupActions]
>;

export type WalletAction =
| TokenDefinitionsAction
Expand All @@ -81,4 +85,5 @@ export type WalletAction =
| CoinjoinAccountAction
| CoinjoinClientAction
| AccountsAction
| StakeAction;
| StakeAction
| ConnectPopupAction;
21 changes: 21 additions & 0 deletions suite-common/connect-popup/src/connectPopupActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createAction } from '@reduxjs/toolkit';

import { ConnectPopupCall } from './connectPopupTypes';

export const ACTION_PREFIX = '@suite-common/connect-popup';

const initiateCall = createAction(`${ACTION_PREFIX}/initiateCall`, (payload: ConnectPopupCall) => ({
payload,
}));

const approveCall = createAction(`${ACTION_PREFIX}/approveCall`);

const rejectCall = createAction(`${ACTION_PREFIX}/rejectCall`, (payload: Error) => ({
payload,
}));

export const connectPopupActions = {
initiateCall,
approveCall,
rejectCall,
} as const;
21 changes: 21 additions & 0 deletions suite-common/connect-popup/src/connectPopupMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createMiddlewareWithExtraDeps } from '@suite-common/redux-utils';

import { connectPopupActions } from './connectPopupActions';

export const prepareConnectPopupMiddleware = createMiddlewareWithExtraDeps(
async (action, { dispatch, next, extra }) => {
await next(action);

if (connectPopupActions.initiateCall.match(action)) {
dispatch(extra.actions.openModal({ type: 'connect-popup' }));
}
if (
connectPopupActions.approveCall.match(action) ||
connectPopupActions.rejectCall.match(action)
) {
dispatch(extra.actions.onModalCancel());
}

return action;
},
);
37 changes: 37 additions & 0 deletions suite-common/connect-popup/src/connectPopupReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createReducerWithExtraDeps } from '@suite-common/redux-utils';

import { connectPopupActions } from './connectPopupActions';
import { ConnectPopupCall } from './connectPopupTypes';

export type ConnectPopupState = {
activeCall?: ConnectPopupCall;
};

type ConnectPopupStateRootState = {
wallet: { connectPopup: ConnectPopupState };
};

const connectPopupInitialState: ConnectPopupState = {
activeCall: undefined,
};

export const prepareConnectPopupReducer = createReducerWithExtraDeps(
connectPopupInitialState,
(builder, _extra) => {
builder
.addCase(connectPopupActions.initiateCall, (state, { payload }) => {
state.activeCall = payload;
})
.addCase(connectPopupActions.approveCall, state => {
state?.activeCall?.confirmation.resolve();
state.activeCall = undefined;
})
.addCase(connectPopupActions.rejectCall, (state, { payload }) => {
state?.activeCall?.confirmation.reject(payload);
state.activeCall = undefined;
});
},
);

export const selectConnectPopupCall = (state: ConnectPopupStateRootState) =>
state.wallet.connectPopup.activeCall;
28 changes: 11 additions & 17 deletions suite-common/connect-popup/src/connectPopupThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import TrezorConnect, { CallMethodParams, CallMethodResponse, ERRORS } from '@tr
import { serializeError } from '@trezor/connect/src/constants/errors';
import { createDeferred } from '@trezor/utils';

import { connectPopupActions } from './connectPopupActions';

const CONNECT_POPUP_MODULE = '@common/connect-popup';

type ConnectPopupCallThunkResponse<M extends keyof typeof TrezorConnect> = Promise<{
Expand All @@ -29,15 +31,6 @@ export const connectPopupCallThunkInner = createThunk<
`${CONNECT_POPUP_MODULE}/callThunk`,
async ({ id, method, payload, processName, origin }, { dispatch, getState, extra }) => {
try {
const device = selectSelectedDevice(getState());

if (!device) {
console.error('Device not found');

// TODO: wait for device selection and continue
throw ERRORS.TypedError('Device_NotFound');
}

// @ts-expect-error: method is dynamic
const methodInfo = await TrezorConnect[method]({
...payload,
Expand All @@ -50,17 +43,19 @@ export const connectPopupCallThunkInner = createThunk<
const confirmation = createDeferred();
dispatch(extra.actions.lockDevice(true));
dispatch(
extra.actions.openModal({
type: 'connect-popup',
onCancel: () => confirmation.reject(ERRORS.TypedError('Method_Cancel')),
onConfirm: () => confirmation.resolve(),
connectPopupActions.initiateCall({
method: methodInfo.payload.info,
processName,
origin,
confirmation,
}),
);
await confirmation.promise;
dispatch(extra.actions.lockDevice(false));

const device = selectSelectedDevice(getState());
if (!device) {
throw ERRORS.TypedError('Device_NotFound');
}

// @ts-expect-error: method is dynamic
const response = await TrezorConnect[method]({
Expand All @@ -72,21 +67,20 @@ export const connectPopupCallThunkInner = createThunk<
...payload,
});

dispatch(extra.actions.onModalCancel());

return {
...response,
id,
};
} catch (error) {
console.error('connectPopupCallThunk', error);
dispatch(extra.actions.onModalCancel());

return {
success: false,
payload: serializeError(error),
id,
};
} finally {
dispatch(extra.actions.lockDevice(false));
}
},
);
Expand Down
8 changes: 8 additions & 0 deletions suite-common/connect-popup/src/connectPopupTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Deferred } from '@trezor/utils';

export type ConnectPopupCall = {
method: string;
processName?: string;
origin?: string;
confirmation: Deferred<void>;
};
3 changes: 3 additions & 0 deletions suite-common/connect-popup/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export * from './connectPopupActions';
export * from './connectPopupThunks';
export * from './connectPopupMiddleware';
export * from './connectPopupReducer';
5 changes: 0 additions & 5 deletions suite-common/suite-types/src/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,6 @@ export type UserContextPayload =
}
| {
type: 'connect-popup';
onConfirm: () => void;
onCancel: () => void;
method: string;
processName?: string;
origin?: string;
}
| {
type: 'walletconnect-proposal';
Expand Down

0 comments on commit 25b6741

Please sign in to comment.