Skip to content

Commit c6d52e8

Browse files
committed
Merge branch 'frontend-bt-connectionstatus-styling'
2 parents 6d7592e + 0a10373 commit c6d52e8

File tree

4 files changed

+49
-28
lines changed

4 files changed

+49
-28
lines changed

frontends/web/src/api/bluetooth.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,21 @@ export type TPeripheral = {
2121
identifier: string;
2222
name: string;
2323
} & (
24-
| {
25-
connectionState: 'discovered' | 'connecting' | 'connected';
26-
}
27-
| {
28-
connectionState: 'error';
29-
connectionError: string;
30-
}
24+
{
25+
connectionState: 'discovered' | 'connecting' | 'connected';
26+
} | {
27+
connectionState: 'error';
28+
connectionError: string;
29+
}
3130
);
3231

33-
export type TState = {
32+
type TBluetoothState = {
3433
bluetoothAvailable: boolean;
3534
scanning: boolean;
3635
peripherals: TPeripheral[];
3736
};
3837

39-
export const getState = (): Promise<TState> => {
38+
export const getState = (): Promise<TBluetoothState> => {
4039
return apiGet('bluetooth/state');
4140
};
4241

@@ -45,7 +44,7 @@ export const connect = (identifier: string): Promise<void> => {
4544
};
4645

4746
export const syncState = (
48-
cb: (state: TState) => void
47+
cb: (state: TBluetoothState) => void
4948
): TUnsubscribe => {
5049
return subscribeEndpoint('bluetooth/state', cb);
5150
};

frontends/web/src/components/actionable-item/actionable-item.tsx

+8-9
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,35 @@ type TProps = {
2222
className?: string;
2323
disabled?: boolean;
2424
children: ReactNode;
25+
icon?: ReactNode;
2526
onClick?: () => void;
2627
}
2728

2829
export const ActionableItem = ({
2930
className = '',
3031
disabled,
3132
children,
33+
icon,
3234
onClick,
3335
}: TProps) => {
3436
const notButton = disabled || onClick === undefined;
3537

36-
const content = (
37-
<div className={styles.content}>
38-
{children}
39-
<ChevronRightDark />
40-
</div>
41-
);
42-
4338
return (
4439
<>
4540
{notButton ? (
4641
<div className={`${styles.container} ${className}`}>
47-
{content}
42+
{children}
43+
{icon && icon}
4844
</div>
4945
) : (
5046
<button
5147
type="button"
5248
className={`${styles.container} ${styles.isButton} ${className}`}
5349
onClick={onClick}>
54-
{content}
50+
{children}
51+
{icon ? icon : (
52+
<ChevronRightDark />
53+
)}
5554
</button>
5655
)}
5756
</>

frontends/web/src/components/bluetooth/bluetooth.tsx

+30-9
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,71 @@
1616

1717
import { useTranslation } from 'react-i18next';
1818
import { useSync } from '@/hooks/api';
19-
import { connect, getState, syncState } from '@/api/bluetooth';
19+
import { connect, getState, syncState, TPeripheral } from '@/api/bluetooth';
2020
import { runningInIOS } from '@/utils/env';
21+
import { Status } from '@/components/status/status';
2122
import { ActionableItem } from '@/components/actionable-item/actionable-item';
2223
import { Badge } from '@/components/badge/badge';
24+
import { HorizontallyCenteredSpinner, SpinnerRingAnimated } from '@/components/spinner/SpinnerAnimation';
2325
import styles from './bluetooth.module.css';
2426

27+
const isConnectedOrConnecting = (peripheral: TPeripheral) => {
28+
return peripheral.connectionState === 'connecting' || peripheral.connectionState === 'connected';
29+
};
30+
2531
const _Bluetooth = () => {
2632
const { t } = useTranslation();
2733
const state = useSync(getState, syncState);
2834
if (!state) {
2935
return null;
3036
}
3137
if (!state.bluetoothAvailable) {
32-
return <>Please turn on Bluetooth</>;
38+
return (
39+
<Status type="warning">
40+
{t('bluetooth.enable')}
41+
</Status>
42+
);
3343
}
44+
const hasConnection = state.peripherals.some(isConnectedOrConnecting);
3445
return (
3546
<>
3647
<div className={styles.label}>
3748
{t('bluetooth.select')}
3849
</div>
3950
<div className={styles.container}>
40-
{ state.scanning ? 'scanning' : null }
4151
{state.peripherals.map(peripheral => {
52+
const onClick = !hasConnection ? () => connect(peripheral.identifier) : undefined;
53+
const connectingIcon = peripheral.connectionState === 'connecting' ? (
54+
<SpinnerRingAnimated />
55+
) : undefined;
4256
return (
4357
<ActionableItem
4458
key={peripheral.identifier}
45-
onClick={() => connect(peripheral.identifier)}>
59+
icon={connectingIcon}
60+
onClick={onClick}>
4661
<span>
4762
{ peripheral.name !== '' ? peripheral.name : peripheral.identifier }
4863
{' '}
64+
{ peripheral.connectionState === 'connected' ? (
65+
<Badge type="success">
66+
{t('bluetooth.connected')}
67+
</Badge>
68+
) : null }
4969
{ peripheral.connectionState === 'error' ? (
5070
<Badge type="danger">
51-
{t('bluetooth.connectionFailed')}
71+
<span style={{ whiteSpace: 'wrap' }}>
72+
{peripheral.connectionError}
73+
</span>
5274
</Badge>
5375
) : null }
54-
{ peripheral.connectionState === 'error' ? (
55-
<p>{ peripheral.connectionError }</p>
56-
) : peripheral.connectionState }
5776
</span>
58-
5977
</ActionableItem>
6078
);
6179
})}
6280
</div>
81+
{state.scanning && (
82+
<HorizontallyCenteredSpinner />
83+
)}
6384
</>
6485
);
6586
};

frontends/web/src/locales/en/app.json

+2
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,9 @@
367367
"button": "Blink"
368368
},
369369
"bluetooth": {
370+
"connected": "connected",
370371
"connectionFailed": "failed",
372+
"enable": "Please turn on Bluetooth",
371373
"select": "Select your BitBox"
372374
},
373375
"bootloader": {

0 commit comments

Comments
 (0)