Skip to content

Commit

Permalink
feat(connect): dispatch call in progress event - needed for immediate…
Browse files Browse the repository at this point in the history
… feedback in suite when called from outside
  • Loading branch information
mroz22 committed Oct 4, 2024
1 parent a864485 commit 8da4a49
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 47 deletions.
6 changes: 4 additions & 2 deletions packages/connect/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,6 @@ const onCall = async (context: CoreContext, message: IFrameCallMessage) => {
'onCall: message.id or message.payload is missing',
);
}

const {
uiPromises,
callMethods,
Expand Down Expand Up @@ -531,6 +530,8 @@ const onCallDevice = async (
const responseID = message.id;
const { origin, env, useCoreInPopup } = DataManager.getSettings();

sendCoreMessage(createUiMessage(UI.CALL_IN_PROGRESS, { value: true }));

if (!deviceList.isConnected() && !deviceList.pendingConnection()) {
// transport is missing try to initialize it once again
deviceList.init();
Expand Down Expand Up @@ -570,7 +571,6 @@ const onCallDevice = async (
const device = tempDevice;

method.setDevice(device);

// find pending calls to this device
const previousCall = callMethods.filter(
call => call && call !== method && call.devicePath === method.devicePath,
Expand Down Expand Up @@ -721,6 +721,8 @@ const onCallDevice = async (
if (!useCoreInPopup) {
sendCoreMessage(response);
}

sendCoreMessage(createUiMessage(UI.CALL_IN_PROGRESS, { value: false }));
}
}
};
Expand Down
14 changes: 13 additions & 1 deletion packages/connect/src/events/ui-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const UI_REQUEST = {
FIRMWARE_NOT_COMPATIBLE: 'ui-device_firmware_not_compatible',
FIRMWARE_NOT_INSTALLED: 'ui-device_firmware_not_installed',
FIRMWARE_PROGRESS: 'ui-firmware-progress',
CALL_IN_PROGRESS: 'ui-call_in_progress',

/** connect is waiting for device to be automatically reconnected */
FIRMWARE_RECONNECT: 'ui-firmware_reconnect',
Expand Down Expand Up @@ -157,6 +158,16 @@ export interface UiRequestSetOperation {
payload: string;
}

// todo: is it request? not really. at least naming, ( if not category) is misleading here
export interface UiRequestCallInProgress {
type: typeof UI_REQUEST.CALL_IN_PROGRESS;
payload: {
// wrapped into object to make it extendable in the future
// context might be useful, such as device, method name etc.
value: boolean;
};
}

export interface UiRequestPermission {
type: typeof UI_REQUEST.REQUEST_PERMISSION;
payload: {
Expand Down Expand Up @@ -292,7 +303,8 @@ export type UiEvent =
| FirmwareException
| FirmwareReconnect
| UiRequestAddressValidation
| UiRequestSetOperation;
| UiRequestSetOperation
| UiRequestCallInProgress;

export type UiEventMessage = UiEvent & { event: typeof UI_EVENT };

Expand Down
3 changes: 2 additions & 1 deletion packages/suite/src/support/extraDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
TokenDefinitionsState,
buildTokenDefinitionsFromStorage,
} from '@suite-common/token-definitions';
import { selectSuiteSettings } from '../reducers/suite/suiteReducer';
import { selectSuiteSettings, selectIsDeviceLocked } from '../reducers/suite/suiteReducer';
import { addWalletThunk, openSwitchDeviceDialog } from 'src/actions/wallet/addWalletThunk';

const connectSrc = resolveStaticPath('connect/');
Expand Down Expand Up @@ -91,6 +91,7 @@ export const extraDependencies: ExtraDependencies = {
selectAddressDisplayType: (state: AppState) => state.suite.settings.addressDisplayType,
selectSelectedAccountStatus: (state: AppState) => state.wallet.selectedAccount.status,
selectSuiteSettings,
selectIsDeviceLocked,
},
actions: {
setAccountAddMetadata: metadataActions.setAccountAdd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ describe('TrezorConnect Actions', () => {
it('Wrapped method', async () => {
testMocks.setTrezorConnectFixtures();
await store.dispatch(connectInitThunk());
await testMocks.getTrezorConnectMock().getFeatures();
const { getFeatures, emitTestEvent } = testMocks.getTrezorConnectMock();
await getFeatures();
emitTestEvent(UI_EVENT, { type: 'ui-call_in_progress', payload: { value: true } });

const actions = store.getActions();
// check actions in reversed order
expect(actions.pop()).toEqual({
type: extraDependenciesMock.actions.lockDevice.type,
payload: false,
});
expect(actions.pop()).toEqual({ type: 'ui-call_in_progress', payload: { value: true } });
expect(actions.pop()).toEqual({
type: extraDependenciesMock.actions.lockDevice.type,
payload: true,
Expand Down
55 changes: 17 additions & 38 deletions suite-common/connect-init/src/connectInitThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const connectInitThunk = createThunk(
selectEnabledNetworks,
selectIsPendingTransportEvent,
selectDebugSettings,
selectIsDeviceLocked,
},
actions: { lockDevice },
utils: { connectInitSettings },
Expand All @@ -45,7 +46,9 @@ export const connectInitThunk = createThunk(
});

TrezorConnect.on(UI_EVENT, ({ event: _, ...action }) => {
// dispatch event as action
if (action.type === 'ui-call_in_progress') {
dispatch(lockDevice(action.payload.value));
}
dispatch(action);
});

Expand All @@ -61,50 +64,26 @@ export const connectInitThunk = createThunk(

const synchronize = getSynchronize();

const wrappedMethods: Array<keyof typeof TrezorConnect> = [
'applySettings',
'authenticateDevice',
'authorizeCoinjoin',
'backupDevice',
'cancelCoinjoinAuthorization',
'cardanoGetAddress',
'cardanoGetPublicKey',
'cardanoSignTransaction',
'changePin',
'checkFirmwareAuthenticity',
'cipherKeyValue',
'ethereumGetAddress',
'ethereumSignTransaction',
'getAddress',
'getDeviceState',
'getFeatures',
'getOwnershipProof',
'getPublicKey',
'pushTransaction',
'rebootToBootloader',
'recoveryDevice',
'resetDevice',
'rippleGetAddress',
'rippleSignTransaction',
'setBusy',
'showDeviceTutorial',
'signTransaction',
'solanaGetAddress',
'solanaSignTransaction',
'unlockPath',
'wipeDevice',
] as const;
const wrappedMethods = (
Object.keys(TrezorConnect) as Array<keyof typeof TrezorConnect>
).filter(method => {
return (
['on', 'off', 'removeAllListeners', 'uiResponse', 'dispose', 'init'].indexOf(
method,
) === -1
);
});

wrappedMethods.forEach(key => {
// typescript complains about params and return type, need to be "any"
const original: any = TrezorConnect[key];
if (!original) return;
(TrezorConnect[key] as any) = async (params: any) => {
dispatch(lockDevice(true));
const result = await synchronize(() => original(params));
dispatch(lockDevice(false));
if (selectIsDeviceLocked(getState())) {
return await synchronize(() => original(params));
}

return result;
return original(params);
};
});

Expand Down
1 change: 1 addition & 0 deletions suite-common/redux-utils/src/extraDependenciesType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export type ExtraDependencies = {
selectSuiteSettings: SuiteCompatibleSelector<{
defaultWalletLoading: WalletType;
}>;
selectIsDeviceLocked: SuiteCompatibleSelector<boolean>;
};
// You should only use ActionCreatorWithPayload from redux-toolkit!
// That means you will need to convert actual action creators in packages/suite to use createAction from redux-toolkit,
Expand Down
1 change: 1 addition & 0 deletions suite-common/test-utils/src/extraDependenciesMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export const extraDependenciesMock: ExtraDependencies = {
selectSuiteSettings: mockSelector('selectSuiteSettings', {
defaultWalletLoading: 'standard',
}),
selectIsDeviceLocked: mockSelector('selectIsDeviceLocked', false),
},
actions: {
setAccountAddMetadata: mockAction('setAccountAddMetadata'),
Expand Down

0 comments on commit 8da4a49

Please sign in to comment.