Skip to content

Commit

Permalink
fixup! WIP: design
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-sanderson committed Nov 29, 2024
1 parent cc67d7b commit 01eb8b4
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 118 deletions.
54 changes: 28 additions & 26 deletions packages/suite/src/actions/bluetooth/bluetoothActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,44 @@ import { createAction } from '@reduxjs/toolkit';

import { ElectronBluetoothDevice } from '@trezor/suite-desktop-api';

import {
BluetoothScanStatus,
DeviceBluetoothStatus,
} from '../../reducers/bluetooth/bluetoothReducer';

export const BLUETOOTH_PREFIX = '@suite/bluetooth';

export const bluetoothAdapterEventAction = createAction(
`${BLUETOOTH_PREFIX}/adapter-event`,
({ isPowered }: { isPowered: boolean }) => ({
payload: {
isPowered,
},
}),
({ isPowered }: { isPowered: boolean }) => ({ payload: { isPowered } }),
);

export const bluetoothSelectDeviceEventAction = createAction(
`${BLUETOOTH_PREFIX}/select-device-event`,
({ devices }: { devices: ElectronBluetoothDevice[] }) => ({
payload: {
devices,
},
}),
export const bluetoothDeviceListUpdate = createAction(
`${BLUETOOTH_PREFIX}/device-list-update`,
({ devices }: { devices: ElectronBluetoothDevice[] }) => ({ payload: { devices } }),
);

export const bluetoothConnectDeviceEventAction = createAction(
`${BLUETOOTH_PREFIX}/connect-device-event`,
({ device, phase }: { device: ElectronBluetoothDevice; phase: string }) => ({
payload: {
device,
phase,
},
`${BLUETOOTH_PREFIX}/device-connection-status`,
({ connectionStatus }: { connectionStatus: DeviceBluetoothStatus }) => ({
payload: { connectionStatus },
}),
);

export const bluetoothPairDeviceEventAction = createAction(
`${BLUETOOTH_PREFIX}/pair-device-event`,
({ paired, pin }: { paired: boolean; pin: string }) => ({
payload: {
paired,
pin,
},
}),
export const bluetoothSelectDeviceAction = createAction(
`${BLUETOOTH_PREFIX}/select-device`,
({ uuid }: { uuid: string | undefined }) => ({ payload: { uuid } }),
);

export const bluetoothScanStatusAction = createAction(
`${BLUETOOTH_PREFIX}/scan-status`,
({ status }: { status: BluetoothScanStatus }) => ({ payload: { status } }),
);

export const allBluetoothActions = {
bluetoothAdapterEventAction,
bluetoothDeviceListUpdate,
bluetoothConnectDeviceEventAction,
bluetoothSelectDeviceAction,
bluetoothScanStatusAction,
};
97 changes: 52 additions & 45 deletions packages/suite/src/components/suite/bluetooth/BluetoothConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';

import { desktopApi, ElectronBluetoothDevice } from '@trezor/suite-desktop-api';
import { desktopApi } from '@trezor/suite-desktop-api';
import TrezorConnect from '@trezor/connect';
import { Card, ElevationUp, Column } from '@trezor/components';
import { spacings } from '@trezor/theme';
Expand All @@ -12,51 +12,52 @@ import { BluetoothVersionNotCompatible } from './errors/BluetoothVersionNotCompa
import { BluetoothTips } from './BluetoothTips';
import { BluetoothScanHeader } from './BluetoothScanHeader';
import { BluetoothScanFooter } from './BluetoothScanFooter';
import { useDispatch } from '../../../hooks/suite';
import { useDispatch, useSelector } from '../../../hooks/suite';
import { BluetoothSelectedDevice } from './BluetoothSelectedDevice';
import {
DeviceBluetoothStatus,
FakeScanStatus,
} from '../../../reducers/bluetooth/bluetoothReducer';
bluetoothAdapterEventAction,
bluetoothConnectDeviceEventAction,
bluetoothDeviceListUpdate,
bluetoothScanStatusAction,
bluetoothSelectDeviceAction,
} from '../../../actions/bluetooth/bluetoothActions';
import {
selectBluetoothDeviceList,
selectBluetoothEnabled,
selectBluetoothScanStatus,
selectBluetoothSelectedDevice,
} from '../../../reducers/bluetooth/bluetoothSelectors';

const FAKE_SCAN_TIMEOUT = 30_000;
const SCAN_TIMEOUT = 30_000;

type BluetoothConnectProps = {
onClose: () => void;
};

export const BluetoothConnect = ({ onClose }: BluetoothConnectProps) => {
const [isBluetoothEnabled, setBluetoothEnabled] = useState(true); // Intentionally by default true so UI wont flicker
const [fakeScanStatus, setFakeScanStatus] = useState<FakeScanStatus>('running');
const [selectedDeviceStatus, setSelectedDeviceStatus] = useState<DeviceBluetoothStatus>({
type: 'found',
uuid: 'TODO',
});
const [deviceList, setDeviceList] = useState<ElectronBluetoothDevice[]>([]);
const [selectedDevice, setSelectedDevice] = useState<ElectronBluetoothDevice | undefined>(
undefined,
);

const dispatch = useDispatch();

const isBluetoothEnabled = useSelector(selectBluetoothEnabled);
const scanStatus = useSelector(selectBluetoothScanStatus);
const selectedDevice = useSelector(selectBluetoothSelectedDevice);
const deviceList = useSelector(selectBluetoothDeviceList);

console.log('selectedDevice', selectedDevice);

useEffect(() => {
desktopApi.on('bluetooth/adapter-event', isPowered => {
console.warn('bluetooth/adapter-event', isPowered);
setBluetoothEnabled(isPowered);
if (!isPowered) {
setDeviceList([]);
}
dispatch(bluetoothAdapterEventAction({ isPowered }));
});

// Todo: rename to something more like: `update-device-list`
desktopApi.on('bluetooth/device-list-update', list => {
console.warn('bluetooth/device-list-update', list);
setDeviceList(list);
desktopApi.on('bluetooth/device-list-update', devices => {
console.warn('bluetooth/device-list-update', devices);
dispatch(bluetoothDeviceListUpdate({ devices }));
});

desktopApi.on('bluetooth/device-connection-status', event => {
console.warn('bluetooth/device-connection-status', event);
setSelectedDeviceStatus(event);
desktopApi.on('bluetooth/device-connection-status', connectionStatus => {
console.warn('bluetooth/device-connection-status', connectionStatus);
dispatch(bluetoothConnectDeviceEventAction({ connectionStatus }));
});

desktopApi.bluetoothRequestDevice();
Expand All @@ -67,34 +68,37 @@ export const BluetoothConnect = ({ onClose }: BluetoothConnectProps) => {
desktopApi.removeAllListeners('bluetooth/device-connection-status');
desktopApi.bluetoothStopScan();
};
}, []);
}, [dispatch]);

useEffect(() => {
setTimeout(() => {
setFakeScanStatus('done');
}, FAKE_SCAN_TIMEOUT);
}, []);
dispatch(bluetoothScanStatusAction({ status: 'done' }));
}, SCAN_TIMEOUT);
}, [dispatch]);

const onReScanClick = () => {
setFakeScanStatus('running');
dispatch(bluetoothScanStatusAction({ status: 'running' }));

setTimeout(() => {
setFakeScanStatus('done');
}, FAKE_SCAN_TIMEOUT);
dispatch(bluetoothScanStatusAction({ status: 'done' }));
}, SCAN_TIMEOUT);
};

const onSelect = async (uuid: string) => {
console.log('selecting....', uuid);

setSelectedDevice(deviceList.find(d => d.uuid === uuid));
setSelectedDeviceStatus({ type: 'pairing', uuid });
dispatch(bluetoothSelectDeviceAction({ uuid }));

const result = await desktopApi.bluetoothConnectDevice(uuid);

console.log('result', result);

if (!result.success) {
setSelectedDeviceStatus({ type: 'error', uuid });
dispatch(
bluetoothConnectDeviceEventAction({
connectionStatus: { type: 'error', uuid, error: result.error },
}),
);
dispatch(
notificationsActions.addToast({
type: 'error',
Expand All @@ -104,7 +108,11 @@ export const BluetoothConnect = ({ onClose }: BluetoothConnectProps) => {
} else {
// Todo: What to do with error in this flow? UI-Wise

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

// WAIT for connect event, TODO: figure out better way
const closePopupAfterConnection = () => {
Expand All @@ -127,15 +135,14 @@ export const BluetoothConnect = ({ onClose }: BluetoothConnectProps) => {
}

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

if (selectedDevice !== undefined) {
return (
<BluetoothSelectedDevice
device={selectedDevice}
status={selectedDeviceStatus}
onCancel={() => setSelectedDevice(undefined)}
onCancel={() => dispatch(bluetoothSelectDeviceAction({ uuid: undefined }))}
/>
);
}
Expand Down Expand Up @@ -171,7 +178,7 @@ export const BluetoothConnect = ({ onClose }: BluetoothConnectProps) => {
<BluetoothScanFooter
onReScanClick={onReScanClick}
numberOfDevices={deviceList.length}
fakeScanStatus={fakeScanStatus}
scanStatus={scanStatus}
/>
</Column>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Card, Column, SkeletonRectangle, Row } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { ElectronBluetoothDevice } from '@trezor/suite-desktop-api';

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

type BluetoothDeviceListProps = {
deviceList: ElectronBluetoothDevice[];
deviceList: BluetoothDeviceState[];
onSelect: (uuid: string) => void;
isScanning: boolean;
};
Expand All @@ -31,9 +31,9 @@ export const BluetoothDeviceList = ({
<Column gap={spacings.md} alignItems="stretch">
{deviceList.map(d => (
<BluetoothDeviceItem
key={d.uuid}
device={d}
onClick={() => onSelect(d.uuid)}
key={d.device.uuid}
device={d.device}
onClick={() => onSelect(d.device.uuid)}
connecting={false}
lastSeenTimestamp={0}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ import { Text } from '@trezor/components';
import { spacings } from '@trezor/theme';

import { NotTrezorYouAreLookingFor } from './NotTrezorYouAreLookingFor';
import { FakeScanStatus } from './types';
import { BluetoothScanStatus } from '../../../reducers/bluetooth/bluetoothReducer';

type BluetoothScanFooterProps = {
onReScanClick: () => void;
fakeScanStatus: FakeScanStatus;
scanStatus: BluetoothScanStatus;
numberOfDevices: number;
};

export const BluetoothScanFooter = ({
onReScanClick,
fakeScanStatus,
scanStatus,
numberOfDevices,
}: BluetoothScanFooterProps) => {
if (fakeScanStatus === 'running') {
if (scanStatus === 'running') {
return (
<Text typographyStyle="hint" variant="tertiary" margin={{ horizontal: spacings.md }}>
Make sure your TS7 is on and in pairing mode (hold power button)
</Text>
);
}

if (fakeScanStatus === 'done' && numberOfDevices > 0) {
if (scanStatus === 'done' && numberOfDevices > 0) {
return <NotTrezorYouAreLookingFor onReScanClick={onReScanClick} />;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { ReactNode } from 'react';

import { ElectronBluetoothDevice } from '@trezor/suite-desktop-api';
import { Card, ElevationContext, Icon, Row, Spinner, Text } from '@trezor/components';
import { spacings } from '@trezor/theme';

import { BluetoothDevice } from './BluetoothDevice';
import { DeviceBluetoothStatus, DeviceBluetoothStatusType } from './types';
import { BluetoothPairingPin } from './BluetoothPairingPin';
import {
BluetoothDeviceState,
DeviceBluetoothStatusType,
} from '../../../reducers/bluetooth/bluetoothReducer';

export type BluetoothSelectedDeviceProps = {
device: ElectronBluetoothDevice;
status: DeviceBluetoothStatus;
device: BluetoothDeviceState;
onCancel: () => void;
};

Expand All @@ -21,11 +22,7 @@ const PairingComponent = () => (
</Row>
);

export const BluetoothSelectedDevice = ({
device,
status,
onCancel,
}: BluetoothSelectedDeviceProps) => {
export const BluetoothSelectedDevice = ({ device, onCancel }: BluetoothSelectedDeviceProps) => {
const map: Record<DeviceBluetoothStatusType, (() => ReactNode) | null> = {
found: null,
error: () => (
Expand All @@ -47,11 +44,11 @@ export const BluetoothSelectedDevice = ({
),
};

if (status.type === 'pairing' && (status.pin?.length ?? 0) > 0) {
if (device.status.type === 'pairing' && (device.status.pin?.length ?? 0) > 0) {
return (
<BluetoothPairingPin
device={device}
pairingPin={status.pin}
device={device.device}
pairingPin={device.status.pin}
onCancel={onCancel}
onConfirm={() => {}}
/>
Expand All @@ -62,8 +59,8 @@ export const BluetoothSelectedDevice = ({
<ElevationContext baseElevation={0}>
<Card minWidth="470px">
<Row gap={spacings.md} alignItems="center" justifyContent="stretch">
<BluetoothDevice device={device} flex="1" />
{map[status.type]?.()}
<BluetoothDevice device={device.device} flex="1" />
{map[device.status.type]?.()}
</Row>
</Card>
</ElevationContext>
Expand Down
Loading

0 comments on commit 01eb8b4

Please sign in to comment.