Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract reducers to common package #17090

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
aa34e30
feat: move reducers into seperate common package, create shared state…
peter-sanderson Feb 19, 2025
c0009aa
feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 19, 2025
e1e8026
feat: add tests for bluetoothRedcer and devicesSecondTime
peter-sanderson Feb 20, 2025
2ebe1bc
feat: extract functionality and add test for it
peter-sanderson Feb 20, 2025
6a30f1f
fixup! feat: extract functionality and add test for it
peter-sanderson Feb 21, 2025
20fcc71
fixup! feat: move reducers into seperate common package, create share…
peter-sanderson Feb 21, 2025
1d22b88
fixup! feat: extract functionality and add test for it
peter-sanderson Feb 21, 2025
eb34bdb
fixup! feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 21, 2025
c9d03f1
fixup! feat: move reducers into seperate common package, create share…
peter-sanderson Feb 21, 2025
9dcc1e1
fixup! feat: move reducers into seperate common package, create share…
peter-sanderson Feb 21, 2025
e0b0c76
fixup! feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 21, 2025
fe0378a
fixup! feat: move reducers into seperate common package, create share…
peter-sanderson Feb 21, 2025
1c10d6c
fixup! feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 24, 2025
f7c8ca8
feat: Hide Bluetooth devices that are not advertising more then 30 se…
peter-sanderson Feb 24, 2025
ef80901
fix(suite): fix inability to select device (silent return) when one d…
peter-sanderson Feb 24, 2025
1a79397
fixup! feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 24, 2025
cd31be2
fixup! feat: implement suite-common bluetooth code into Suite
peter-sanderson Feb 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions packages/suite/src/actions/bluetooth/bluetoothActions.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { BLUETOOTH_PREFIX } from '@suite-common/bluetooth';
import { createThunk } from '@suite-common/redux-utils';
import { bluetoothIpc } from '@trezor/transport-bluetooth';

import { BLUETOOTH_PREFIX } from './bluetoothActions';

type ThunkResponse = ReturnType<typeof bluetoothIpc.connectDevice>;

export const bluetoothConnectDeviceThunk = createThunk<ThunkResponse, { id: string }, void>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BLUETOOTH_PREFIX, bluetoothScanStatusAction } from '@suite-common/bluetooth';
import { createThunk } from '@suite-common/redux-utils';
import { bluetoothIpc } from '@trezor/transport-bluetooth';

import { BLUETOOTH_PREFIX } from './bluetoothActions';

export const bluetoothStartScanningThunk = createThunk<void, void, void>(
`${BLUETOOTH_PREFIX}/bluetoothStartScanningThunk`,
_ => {
(_, { dispatch }) => {
dispatch(bluetoothScanStatusAction({ status: 'running' }));
// This can fail, but if there is an error we already got it from `adapter-event`
// and user is informed about it (bluetooth turned-off, ...)
bluetoothIpc.startScan();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BLUETOOTH_PREFIX, bluetoothScanStatusAction } from '@suite-common/bluetooth';
import { createThunk } from '@suite-common/redux-utils';
import { bluetoothIpc } from '@trezor/transport-bluetooth';

import { BLUETOOTH_PREFIX } from './bluetoothActions';

export const bluetoothStopScanningThunk = createThunk<void, void, void>(
`${BLUETOOTH_PREFIX}/bluetoothStopScanningThunk`,
_ => {
(_, { dispatch }) => {
dispatch(bluetoothScanStatusAction({ status: 'idle' }));
// This can fail, but there is nothing we can do about it
bluetoothIpc.stopScan();
},
Expand Down
40 changes: 30 additions & 10 deletions packages/suite/src/actions/bluetooth/initBluetoothThunk.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { createThunk } from '@suite-common/redux-utils/';
import { DeviceConnectionStatus, bluetoothIpc } from '@trezor/transport-bluetooth';
import { Without } from '@trezor/type-utils';

import {
BLUETOOTH_PREFIX,
bluetoothAdapterEventAction,
bluetoothConnectDeviceEventAction,
bluetoothDeviceListUpdate,
} from './bluetoothActions';
bluetoothKnownDevicesUpdateAction,
bluetoothNearbyDevicesUpdateAction,
selectKnownDevices,
} from '@suite-common/bluetooth';
import { createThunk } from '@suite-common/redux-utils/';
import { BluetoothDevice, DeviceConnectionStatus, bluetoothIpc } from '@trezor/transport-bluetooth';
import { Without } from '@trezor/type-utils';

import { selectSuiteFlags } from '../../reducers/suite/suiteReducer';

type DeviceConnectionStatusWithOptionalId = Without<DeviceConnectionStatus, 'id'> & {
Expand All @@ -28,9 +30,27 @@ export const initBluetoothThunk = createThunk<void, void, void>(
dispatch(bluetoothAdapterEventAction({ isPowered }));
});

bluetoothIpc.on('device-list-update', devices => {
console.warn('device-list-update', devices);
dispatch(bluetoothDeviceListUpdate({ devices }));
bluetoothIpc.on('device-list-update', nearbyDevices => {
console.warn('device-list-update', nearbyDevices);

const knownDevices = selectKnownDevices<BluetoothDevice>(getState());

console.log('nearbyDevices', nearbyDevices);

// update pairedDevices, id is changed after pairing (linux)
const remappedKnownDevices = knownDevices.map(knownDevice => {
// find devices with the same address but different id
const changed = nearbyDevices.find(
nearbyDevice =>
nearbyDevice.address === knownDevice.address &&
nearbyDevice.id !== knownDevice.id,
);

return changed ? { ...knownDevice, id: changed.id } : knownDevice;
});

dispatch(bluetoothKnownDevicesUpdateAction({ knownDevices: remappedKnownDevices }));
dispatch(bluetoothNearbyDevicesUpdateAction({ nearbyDevices }));
});

bluetoothIpc.on('device-connection-status', connectionStatus => {
Expand All @@ -49,7 +69,7 @@ export const initBluetoothThunk = createThunk<void, void, void>(
});

// TODO: this should be called after trezor/connect init?
const knownDevices = getState().bluetooth.pairedDevices;
const knownDevices = selectKnownDevices<BluetoothDevice>(getState());
await bluetoothIpc.init({ knownDevices });
},
);
4 changes: 2 additions & 2 deletions packages/suite/src/actions/suite/storageActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ export const saveCoinjoinDebugSettings = () => async (_dispatch: Dispatch, getSt

export const saveKnownDevices = () => async (_dispatch: Dispatch, getState: GetState) => {
if (!(await db.isAccessible())) return;
const { pairedDevices } = getState().bluetooth;
db.addItem('knownDevices', { bluetooth: pairedDevices }, 'devices', true);
const { knownDevices } = getState().bluetooth;
db.addItem('knownDevices', { bluetooth: knownDevices }, 'devices', true);
};

export const saveFormDraft = async (key: string, draft: FieldValues) => {
Expand Down
59 changes: 25 additions & 34 deletions packages/suite/src/components/suite/bluetooth/BluetoothConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { useCallback, useEffect, useState } from 'react';

import {
bluetoothConnectDeviceEventAction,
bluetoothScanStatusAction,
prepareSelectAllDevices,
selectAdapterStatus,
selectScanStatus,
} from '@suite-common/bluetooth';
import { notificationsActions } from '@suite-common/toast-notifications';
import { Card, Column, ElevationUp } from '@trezor/components';
import TrezorConnect from '@trezor/connect';
import { spacings } from '@trezor/theme';
import { BluetoothDevice } from '@trezor/transport-bluetooth';
import { TimerId } from '@trezor/type-utils';

import { BluetoothDeviceList } from './BluetoothDeviceList';
Expand All @@ -14,19 +21,10 @@ import { BluetoothSelectedDevice } from './BluetoothSelectedDevice';
import { BluetoothTips } from './BluetoothTips';
import { BluetoothNotEnabled } from './errors/BluetoothNotEnabled';
import { BluetoothVersionNotCompatible } from './errors/BluetoothVersionNotCompatible';
import {
bluetoothConnectDeviceEventAction,
bluetoothScanStatusAction,
} from '../../../actions/bluetooth/bluetoothActions';
import { bluetoothConnectDeviceThunk } from '../../../actions/bluetooth/bluetoothConnectDeviceThunk';
import { bluetoothStartScanningThunk } from '../../../actions/bluetooth/bluetoothStartScanningThunk';
import { bluetoothStopScanningThunk } from '../../../actions/bluetooth/bluetoothStopScanningThunk';
import { useDispatch, useSelector } from '../../../hooks/suite';
import {
selectBluetoothDeviceList,
selectBluetoothEnabled,
selectBluetoothScanStatus,
} from '../../../reducers/bluetooth/bluetoothSelectors';

const SCAN_TIMEOUT = 30_000;

Expand All @@ -35,17 +33,21 @@ type BluetoothConnectProps = {
uiMode: 'spatial' | 'card';
};

const selectAllDevices = prepareSelectAllDevices<BluetoothDevice>();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to be done to create generic selector with memoized output. Consulted with @Nodonisko


export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) => {
const dispatch = useDispatch();
const [selectedDeviceId, setSelectedDeviceId] = useState<string | null>(null);
const [scannerTimerId, setScannerTimerId] = useState<TimerId | null>(null);

const isBluetoothEnabled = useSelector(selectBluetoothEnabled);
const scanStatus = useSelector(selectBluetoothScanStatus);
const deviceList = useSelector(selectBluetoothDeviceList);
const devices = Object.values(deviceList);
const bluetoothAdapterStatus = useSelector(selectAdapterStatus);
const scanStatus = useSelector(selectScanStatus);
const devices = useSelector(selectAllDevices);

const selectedDevice = selectedDeviceId !== null ? deviceList[selectedDeviceId] ?? null : null;
const selectedDevice =
selectedDeviceId !== null
? devices.find(device => device.device.id === selectedDeviceId)
: undefined;

useEffect(() => {
dispatch(bluetoothStartScanningThunk());
Expand All @@ -64,7 +66,7 @@ export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) =>
useEffect(() => {
// Intentionally no `clearScamTimer`, this is first run and if we use this we would create infinite re-render
const timerId = setTimeout(() => {
dispatch(bluetoothScanStatusAction({ status: 'done' }));
dispatch(bluetoothScanStatusAction({ status: 'idle' }));
}, SCAN_TIMEOUT);

setScannerTimerId(timerId);
Expand All @@ -76,14 +78,13 @@ export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) =>

clearScamTimer();
const timerId = setTimeout(() => {
dispatch(bluetoothScanStatusAction({ status: 'done' }));
dispatch(bluetoothScanStatusAction({ status: 'idle' }));
}, SCAN_TIMEOUT);
setScannerTimerId(timerId);
};

const onSelect = async (id: string) => {
setSelectedDeviceId(id);

const result = await dispatch(bluetoothConnectDeviceThunk({ id })).unwrap();

if (!result.success) {
Expand All @@ -100,27 +101,16 @@ export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) =>
}),
);
} else {
// Todo: What to do with error in this flow? UI-Wise

dispatch(
bluetoothConnectDeviceEventAction({
id,
connectionStatus: { type: 'connected' },
}),
);

// WAIT for connect event, TODO: figure out better way
const closePopupAfterConnection = () => {
TrezorConnect.off('device-connect', closePopupAfterConnection);
TrezorConnect.off('device-connect_unacquired', closePopupAfterConnection);
// setSelectedDeviceStatus({ type: 'error', id }); // Todo: what here?
};
TrezorConnect.on('device-connect', closePopupAfterConnection);
TrezorConnect.on('device-connect_unacquired', closePopupAfterConnection);
}
};

if (!isBluetoothEnabled) {
if (bluetoothAdapterStatus === 'disabled') {
return <BluetoothNotEnabled onCancel={onClose} />;
}

Expand All @@ -133,16 +123,17 @@ export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) =>
console.log('selectedDevice', selectedDevice);

// This is fake, we scan for devices all the time
const isScanning = scanStatus !== 'done';
const scanFailed = devices.length === 0 && scanStatus === 'done';
const isScanning = scanStatus === 'running';
const scanFailed = devices.length === 0 && scanStatus === 'idle';

const handlePairingCancel = () => {
setSelectedDeviceId(null);
onReScanClick();
};

if (
selectedDevice !== null &&
selectedDevice !== undefined &&
selectedDevice.status !== null &&
selectedDevice.status.type === 'pairing' &&
(selectedDevice.status.pin?.length ?? 0) > 0
) {
Expand All @@ -155,7 +146,7 @@ export const BluetoothConnect = ({ onClose, uiMode }: BluetoothConnectProps) =>
);
}

if (selectedDevice !== null) {
if (selectedDevice !== undefined) {
return <BluetoothSelectedDevice device={selectedDevice} onReScanClick={onReScanClick} />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { DeviceModelInternal } from '@trezor/connect';
import { models } from '@trezor/connect/src/data/models'; // Todo: solve this import issue
import { RotateDeviceImage } from '@trezor/product-components';
import { spacings } from '@trezor/theme';
import { BluetoothDevice as BluetoothDeviceType } from '@trezor/transport-bluetooth';
import { BluetoothDevice } from '@trezor/transport-bluetooth';

type BluetoothDeviceProps = {
device: BluetoothDeviceType;
device: BluetoothDevice;
flex?: FlexProps['flex'];
margin?: FlexProps['margin'];
};
Expand All @@ -18,7 +18,7 @@ const getModelEnumFromBytesUtil = (_id: number) => DeviceModelInternal.T3W1;
// discuss final format of it
const getColorEnumFromVariantBytesUtil = (variant: number) => variant;

export const BluetoothDevice = ({ device, flex, margin }: BluetoothDeviceProps) => {
export const BluetoothDeviceComponent = ({ device, flex, margin }: BluetoothDeviceProps) => {
const model = getModelEnumFromBytesUtil(device.data[2]);
const color = getColorEnumFromVariantBytesUtil(device.data[1]);
const colorName = models[model].colors[color.toString()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Row } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { BluetoothDevice as BluetoothDeviceType } from '@trezor/transport-bluetooth';

import { BluetoothDevice } from './BluetoothDevice';
import { BluetoothDeviceComponent } from './BluetoothDeviceComponent';

type BluetoothDeviceItemProps = {
device: BluetoothDeviceType;
Expand All @@ -12,7 +12,7 @@ type BluetoothDeviceItemProps = {

export const BluetoothDeviceItem = ({ device, onClick, isDisabled }: BluetoothDeviceItemProps) => (
<Row onClick={onClick} gap={spacings.md} alignItems="stretch">
<BluetoothDevice device={device} flex="1" />
<BluetoothDeviceComponent device={device} flex="1" />
<Button
variant="primary"
size="small"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { BluetoothDeviceState } from '@suite-common/bluetooth';
import { Card, Column, Row, SkeletonRectangle } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { BluetoothDevice } from '@trezor/transport-bluetooth';

import { BluetoothDeviceItem } from './BluetoothDeviceItem';
import { BluetoothDeviceState } from '../../../reducers/bluetooth/bluetoothReducer';

type BluetoothDeviceListProps = {
deviceList: BluetoothDeviceState[];
deviceList: BluetoothDeviceState<BluetoothDevice>[];
onSelect: (id: string) => void;
isScanning: boolean;
isDisabled: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import styled from 'styled-components';

import { Card, Link, NewModal, Row, Text } from '@trezor/components';
import { spacings, spacingsPx, typography } from '@trezor/theme';
import { BluetoothDevice as BluetoothDeviceType } from '@trezor/transport-bluetooth';
import { BluetoothDevice } from '@trezor/transport-bluetooth';

import { BluetoothDevice } from './BluetoothDevice';
import { BluetoothDeviceComponent } from './BluetoothDeviceComponent';

const Pin = styled.div`
display: flex;
Expand All @@ -18,7 +18,7 @@ const Pin = styled.div`
type BluetoothPairingPinProps = {
onCancel: () => void;
pairingPin?: string;
device: BluetoothDeviceType;
device: BluetoothDevice;
};

export const BluetoothPairingPin = ({ onCancel, pairingPin, device }: BluetoothPairingPinProps) => (
Expand All @@ -40,7 +40,7 @@ export const BluetoothPairingPin = ({ onCancel, pairingPin, device }: BluetoothP
margin={{ vertical: spacings.xxl, horizontal: spacings.xxl }}
>
<Pin>{pairingPin}</Pin>
<BluetoothDevice
<BluetoothDeviceComponent
device={device}
margin={{ vertical: spacings.xxs, horizontal: spacings.xxs }}
/>
Expand Down
Loading
Loading