Skip to content

Commit c0c5442

Browse files
committed
frontend: convert bitbox01 to functional typescript
1 parent 487c890 commit c0c5442

File tree

2 files changed

+111
-113
lines changed

2 files changed

+111
-113
lines changed
Lines changed: 107 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/**
22
* Copyright 2018 Shift Devices AG
3+
* Copyright 2025 Shift Crypto AG
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
56
* you may not use this file except in compliance with the License.
@@ -14,22 +15,20 @@
1415
* limitations under the License.
1516
*/
1617

17-
import { Component } from 'react';
18-
import { AppUpgradeRequired } from '../../../components/appupgraderequired';
19-
import { apiGet } from '../../../utils/request';
20-
import { apiWebsocket } from '../../../utils/websocket';
21-
import Unlock from './unlock';
18+
import { useState, useEffect, useCallback } from 'react';
19+
import { AppUpgradeRequired } from '@/components/appupgraderequired';
20+
import { apiGet } from '@/utils/request';
21+
import { apiWebsocket } from '@/utils/websocket';
22+
import { Unlock } from './unlock';
2223
import Bootloader from './upgrade/bootloader';
2324
import RequireUpgrade from './upgrade/require_upgrade';
2425
import Goal from './setup/goal';
2526
import { SecurityInformation } from './setup/security-information';
26-
import SeedCreateNew from './setup/seed-create-new';
27+
import { SeedCreateNew } from './setup/seed-create-new';
2728
import SeedRestore from './setup/seed-restore';
2829
import { Initialize } from './setup/initialize';
2930
import Success from './setup/success';
3031
import Settings from './settings/settings';
31-
import { withTranslation } from 'react-i18next';
32-
import { AppContext } from '../../../contexts/AppContext';
3332

3433
const DeviceStatus = Object.freeze({
3534
BOOTLOADER: 'bootloader',
@@ -38,125 +37,125 @@ const DeviceStatus = Object.freeze({
3837
LOGGED_IN: 'logged_in',
3938
SEEDED: 'seeded',
4039
REQUIRE_FIRMWARE_UPGRADE: 'require_firmware_upgrade',
41-
REQUIRE_APP_UPGRADE: 'require_app_upgrade'
40+
REQUIRE_APP_UPGRADE: 'require_app_upgrade',
4241
});
4342

4443
const GOAL = Object.freeze({
4544
CREATE: 'create',
46-
RESTORE: 'restore'
45+
RESTORE: 'restore',
4746
});
4847

49-
class Device extends Component {
50-
static contextType = AppContext;
48+
type DeviceStatusType = (typeof DeviceStatus)[keyof typeof DeviceStatus];
49+
type GoalType = (typeof GOAL)[keyof typeof GOAL];
5150

52-
state = {
53-
firmwareVersion: null,
54-
deviceStatus: '',
55-
goal: '',
56-
success: null,
57-
};
58-
59-
componentDidMount() {
60-
this.onDeviceStatusChanged();
61-
this.unsubscribe = apiWebsocket(({ type, data, deviceID }) => {
62-
if (type === 'device' && data === 'statusChanged' && deviceID === this.getDeviceID()) {
63-
this.onDeviceStatusChanged();
64-
}
65-
});
66-
}
51+
type Props = {
52+
deviceID: string;
53+
}
6754

68-
componentWillUnmount() {
69-
if (this.unsubscribe) {
70-
this.unsubscribe();
71-
}
72-
}
55+
export const BitBox01 = ({ deviceID }: Props) => {
56+
const [deviceStatus, setDeviceStatus] = useState<DeviceStatusType | ''>('');
57+
const [goal, setGoal] = useState<GoalType | null>(null);
58+
const [success, setSuccess] = useState<boolean | null>(null);
7359

74-
onDeviceStatusChanged = () => {
75-
apiGet('devices/' + this.props.deviceID + '/status').then(deviceStatus => {
76-
this.setState({ deviceStatus });
60+
// --- Fetch device status ---
61+
const onDeviceStatusChanged = useCallback(() => {
62+
apiGet(`devices/${deviceID}/status`).then((status: DeviceStatusType) => {
63+
setDeviceStatus(status);
7764
});
78-
};
65+
}, [deviceID]);
7966

80-
getDeviceID() {
81-
return this.props.deviceID || null;
82-
}
67+
useEffect(() => {
68+
onDeviceStatusChanged();
69+
const unsubscribe = apiWebsocket((data) => {
70+
if (
71+
'type' in data // check if TEventLegacy
72+
&& data.type === 'device'
73+
&& 'data' in data
74+
&& data.data === 'statusChanged'
75+
&& data.deviceID === deviceID) {
76+
onDeviceStatusChanged();
77+
}
78+
});
8379

84-
handleCreate = () => {
85-
this.setState({ goal: GOAL.CREATE });
86-
};
80+
return () => {
81+
if (unsubscribe) {
82+
unsubscribe();
83+
}
84+
};
85+
}, [deviceID, onDeviceStatusChanged]);
8786

88-
handleRestore = () => {
89-
this.setState({ goal: GOAL.RESTORE });
90-
};
87+
const handleCreate = () => setGoal(GOAL.CREATE);
88+
const handleRestore = () => setGoal(GOAL.RESTORE);
89+
const handleBack = () => setGoal(null);
90+
const handleSuccess = () => setSuccess(true);
91+
const handleHideSuccess = () => setSuccess(null);
9192

92-
handleBack = () => {
93-
this.setState({ goal: null });
94-
};
93+
if (!deviceStatus) {
94+
return null;
95+
}
9596

96-
handleSuccess = () => {
97-
this.setState({ success: true });
98-
};
97+
if (success) {
98+
return (
99+
<Success goal={goal} handleHideSuccess={handleHideSuccess} />
100+
);
101+
}
99102

100-
render() {
101-
const {
102-
deviceID,
103-
} = this.props;
104-
const {
105-
deviceStatus,
106-
goal,
107-
success,
108-
} = this.state;
109-
if (!deviceStatus) {
110-
return null;
111-
}
112-
if (success) {
113-
return <Success goal={goal} handleHideSuccess={() => this.setState({ success: null })} />;
103+
switch (deviceStatus) {
104+
case DeviceStatus.BOOTLOADER:
105+
return (
106+
<Bootloader deviceID={deviceID} />
107+
);
108+
case DeviceStatus.REQUIRE_FIRMWARE_UPGRADE:
109+
return (
110+
<RequireUpgrade deviceID={deviceID} />
111+
);
112+
case DeviceStatus.REQUIRE_APP_UPGRADE:
113+
return (
114+
<AppUpgradeRequired />
115+
);
116+
case DeviceStatus.INITIALIZED:
117+
return (
118+
<Unlock deviceID={deviceID} />
119+
);
120+
case DeviceStatus.UNINITIALIZED:
121+
if (!goal) {
122+
return (
123+
<Goal onCreate={handleCreate} onRestore={handleRestore} />
124+
);
114125
}
115-
switch (deviceStatus) {
116-
case DeviceStatus.BOOTLOADER:
117-
return <Bootloader deviceID={deviceID} />;
118-
case DeviceStatus.REQUIRE_FIRMWARE_UPGRADE:
119-
return <RequireUpgrade deviceID={deviceID} />;
120-
case DeviceStatus.REQUIRE_APP_UPGRADE:
121-
return <AppUpgradeRequired />;
122-
case DeviceStatus.INITIALIZED:
123-
return <Unlock deviceID={deviceID} />;
124-
case DeviceStatus.UNINITIALIZED:
125-
if (!goal) {
126-
return <Goal onCreate={this.handleCreate} onRestore={this.handleRestore} />;
127-
}
126+
return (
127+
<SecurityInformation goal={goal} goBack={handleBack}>
128+
<Initialize goBack={handleBack} deviceID={deviceID} />
129+
</SecurityInformation>
130+
);
131+
case DeviceStatus.LOGGED_IN:
132+
switch (goal) {
133+
case GOAL.CREATE:
128134
return (
129-
<SecurityInformation goal={goal} goBack={this.handleBack}>
130-
<Initialize goBack={this.handleBack} deviceID={deviceID} />
131-
</SecurityInformation>
135+
<SeedCreateNew
136+
goBack={handleBack}
137+
onSuccess={handleSuccess}
138+
deviceID={deviceID}
139+
/>
140+
);
141+
case GOAL.RESTORE:
142+
return (
143+
<SeedRestore
144+
goBack={handleBack}
145+
onSuccess={handleSuccess}
146+
deviceID={deviceID}
147+
/>
132148
);
133-
case DeviceStatus.LOGGED_IN:
134-
switch (goal) {
135-
case GOAL.CREATE:
136-
return (
137-
<SeedCreateNew
138-
goBack={this.handleBack}
139-
onSuccess={this.handleSuccess}
140-
deviceID={deviceID}
141-
/>
142-
);
143-
case GOAL.RESTORE:
144-
return (
145-
<SeedRestore
146-
goBack={this.handleBack}
147-
onSuccess={this.handleSuccess}
148-
deviceID={deviceID}
149-
/>
150-
);
151-
default:
152-
return <Goal onCreate={this.handleCreate} onRestore={this.handleRestore} />;
153-
}
154-
case DeviceStatus.SEEDED:
155-
return <Settings deviceID={deviceID} />;
156149
default:
157-
return null;
150+
return (
151+
<Goal onCreate={handleCreate} onRestore={handleRestore} />
152+
);
158153
}
154+
case DeviceStatus.SEEDED:
155+
return (
156+
<Settings deviceID={deviceID} />
157+
);
158+
default:
159+
return null;
159160
}
160-
}
161-
162-
export default withTranslation()(Device);
161+
};

frontends/web/src/routes/device/deviceswitch.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/**
22
* Copyright 2018 Shift Devices AG
3+
* Copyright 2025 Shift Crypto AG
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
56
* you may not use this file except in compliance with the License.
@@ -14,8 +15,8 @@
1415
* limitations under the License.
1516
*/
1617

17-
import { TDevices } from '@/api/devices';
18-
import BitBox01 from './bitbox01/bitbox01';
18+
import type { TDevices } from '@/api/devices';
19+
import { BitBox01 } from './bitbox01/bitbox01';
1920
import { BitBox02 } from './bitbox02/bitbox02';
2021
import { BitBox02Bootloader } from '@/components/devices/bitbox02bootloader/bitbox02bootloader';
2122
import { Waiting } from './waiting';
@@ -26,7 +27,7 @@ type TProps = {
2627
hasAccounts: boolean,
2728
}
2829

29-
const DeviceSwitch = ({ deviceID, devices, hasAccounts }: TProps) => {
30+
export const DeviceSwitch = ({ deviceID, devices, hasAccounts }: TProps) => {
3031
const deviceIDs = Object.keys(devices);
3132

3233
if (deviceID === null || !deviceIDs.includes(deviceID)) {
@@ -50,5 +51,3 @@ const DeviceSwitch = ({ deviceID, devices, hasAccounts }: TProps) => {
5051
return <Waiting />;
5152
}
5253
};
53-
54-
export { DeviceSwitch };

0 commit comments

Comments
 (0)