firmware/bootloaderInstructions: Refactor.

- Use steps for the connection procedure
- Use categories (To do: consider making numbering consecutive)
- Show hub bootloader device name explicitly
- List official app to use for recovery (make V2/V3 explicit)
- Copy edit

Also fix the layout (class) of the installation panel to make it consistent with the style of the recovery panel.
This commit is contained in:
Laurens Valk
2022-12-15 14:59:36 -06:00
committed by David Lechner
parent 560da418e0
commit 8aa82eee46
8 changed files with 114 additions and 58 deletions
+6
View File
@@ -57,6 +57,12 @@ export const pybricksCopyright = 'Copyright (c) 2020-2023 The Pybricks Authors';
/** The LEGO name with registered trademark symbol. */
export const legoRegisteredTrademark = 'LEGO®';
/** The LEGO Education SPIKE name with registered trademark symbol. */
export const legoEducationSpikeRegisteredTrademark = 'LEGO® Education™ SPIKE™';
/** The LEGO MINDSTORMS name with registered trademark symbol. */
export const legoMindstormsRegisteredTrademark = 'LEGO® MINDSTORMS®';
// https://www.lego.com/en-us/legal/notices-and-policies/fair-play/
/** LEGO "fair play" disclaimer */
export const legoDisclaimer =
@@ -43,8 +43,21 @@ type BootloaderInstructionsProps = {
* @default false
*/
recovery?: boolean;
/**
* Flash button name that can be referenced in the instructions.
*/
flashButtonText: string;
};
const bootloaderDeviceNameMap: ReadonlyMap<Hub, string> = new Map([
[Hub.City, 'LEGO Bootloader'],
[Hub.Essential, 'LEGO Technic Small Hub in DFU Mode'],
[Hub.Inventor, 'LEGO Technic Large Hub in DFU Mode'],
[Hub.Move, 'LEGO Bootloader'],
[Hub.Prime, 'LEGO Technic Large Hub in DFU Mode'],
[Hub.Technic, 'LEGO Bootloader'],
]);
const videoFileMap: ReadonlyMap<Hub, string> = new Map([
[Hub.City, cityHubMp4],
[Hub.Essential, essentialHubMp4],
@@ -86,10 +99,10 @@ const recoveryMetadataFileMap: ReadonlyMap<Hub, string> = new Map([
*/
const BootloaderInstructions: React.VoidFunctionComponent<
BootloaderInstructionsProps
> = ({ hubType, recovery }) => {
> = ({ hubType, recovery, flashButtonText }) => {
const i18n = useI18n();
const { button, light, lightPattern } = useMemo(() => {
const { button, light, lightPattern, flashButton } = useMemo(() => {
return {
button: i18n.translate(
hubHasBluetoothButton(hubType) ? 'button.bluetooth' : 'button.power',
@@ -102,6 +115,7 @@ const BootloaderInstructions: React.VoidFunctionComponent<
? 'lightPattern.bluetooth'
: 'lightPattern.status',
),
flashButton: flashButtonText,
};
}, [i18n, hubType]);
@@ -192,38 +206,31 @@ const BootloaderInstructions: React.VoidFunctionComponent<
<div className="pb-spacer" />
<p>{i18n.translate('prepare.start')}</p>
<p>
<strong>{i18n.translate('prepare.start')}</strong>
</p>
<ol>
{hubHasUSB(hubType) ? (
<>
<li>{i18n.translate('prepare.usb')}</li>
<li>{i18n.translate('prepare.turnOff')}</li>
</>
<li>
{i18n.translate(
hubHasUSB(hubType) ? 'prepare.usb' : 'prepare.batteries',
)}
</li>
<li>{i18n.translate('prepare.turnOff')}</li>
{/* For non-usb recovery, show step about official app */}
{recovery && !hubHasUSB(hubType) ? (
<li>
{i18n.translate('prepare.app', {
lego: legoRegisteredTrademark,
})}
</li>
) : (
<>
{recovery ? (
<>
<li>
{i18n.translate('prepare.batteries')}{' '}
{i18n.translate('prepare.turnOff')}
</li>
<li>
{i18n.translate('prepare.app', {
lego: legoRegisteredTrademark,
})}
</li>
</>
) : (
<>
<li>{i18n.translate('prepare.batteries')}</li>
<li>{i18n.translate('prepare.turnOff')}</li>
</>
)}
</>
<></>
)}
</ol>
<p>{i18n.translate('step.start')}</p>
<ol start={3}>
<p>
<strong>{i18n.translate('step.start')}</strong>
</p>
<ol>
{/* City hub has power issues and requires disconnecting motors/sensors */}
{hubType === Hub.City && (
<li
@@ -310,6 +317,36 @@ const BootloaderInstructions: React.VoidFunctionComponent<
</li>
)}
</ol>
{hubHasUSB(hubType) || (!hubHasUSB(hubType) && !recovery) ? (
<>
<p>
<strong>{i18n.translate('connect.start')}</strong>
</p>
<ol>
<li>
{i18n.translate('connect.clickConnectAndFlash', {
flashButton: <strong>{flashButton}</strong>,
})}
</li>
<li>
{i18n.translate('connect.selectDevice', {
deviceName: (
<strong>
{bootloaderDeviceNameMap.get(hubType)}
</strong>
),
connectButton: (
<strong>
{i18n.translate('connect.connectButton')}
</strong>
),
})}
</li>
</ol>
</>
) : (
<></>
)}
</>
);
};
@@ -5,7 +5,7 @@
"learnMore": "Learn more."
},
"prepare": {
"start": "Now place the hub in update mode. To prepare:",
"start": "Preparation:",
"batteries": "Make sure the hub has fresh batteries.",
"turnOff": "Make sure the hub is turned off.",
"usb": "Disconnect the USB cable.",
@@ -24,7 +24,7 @@
"status": "light purple"
},
"step": {
"start": "Now follow these steps:",
"start": "Enter update mode (see video):",
"disconnectIo": "Disconnect any device from port A on the hub.",
"holdButton": "Press and hold the {button} on the hub.",
"connectUsb": "Connect the USB cable.",
@@ -32,5 +32,11 @@
"waitAppConnect": "The app will automatically connect and start restoring the firmware.",
"releaseButton": "Release the {button}.",
"keepHolding": "Keep holding the {button}."
},
"connect": {
"start": "Activate the connection:",
"clickConnectAndFlash": "Click the {flashButton} button below.",
"selectDevice": "In the pop-up dialog, select {deviceName} and click {connectButton}.",
"connectButton": "Connect"
}
}
@@ -400,15 +400,11 @@ const BootloaderModePanel: React.VoidFunctionComponent<BootloaderModePanelProps>
const i18n = useI18n();
return (
<div className={dialogBody}>
<BootloaderInstructions hubType={hubType} />
<p>
{i18n.translate('bootloaderPanel.instruction2', {
flashFirmware: (
<strong>{i18n.translate('flashFirmwareButton.label')}</strong>
),
})}
</p>
<div className={classNames(Classes.DIALOG_BODY, Classes.RUNNING_TEXT)}>
<BootloaderInstructions
hubType={hubType}
flashButtonText={i18n.translate('flashFirmwareButton.label')}
/>
</div>
);
};
@@ -53,8 +53,7 @@
}
},
"bootloaderPanel": {
"title": "Enter update mode",
"instruction2": "Then click the {flashFirmware} button below to connect to the hub and flash the firmware."
"title": "Install"
},
"backButton": {
"label": "Back"
@@ -63,6 +62,6 @@
"label": "Next"
},
"flashFirmwareButton": {
"label": "Connect and flash"
"label": "Install"
}
}
@@ -52,7 +52,7 @@ describe('flashing', () => {
await user.click(dialog.getByRole('radio', { name: hubName }));
await user.click(dialog.getByRole('button', { name: 'Next' }));
await user.click(dialog.getByRole('button', { name: 'Flash' }));
await user.click(dialog.getByRole('button', { name: 'Restore' }));
expect(dispatch).toHaveBeenCalledWith(firmwareRestoreOfficialDfu(hub));
},
@@ -11,8 +11,12 @@ import {
import classNames from 'classnames';
import React from 'react';
import { useDispatch } from 'react-redux';
import { legoRegisteredTrademark } from '../../app/constants';
import { hubHasUSB } from '../../components/hubPicker';
import {
legoEducationSpikeRegisteredTrademark,
legoMindstormsRegisteredTrademark,
legoRegisteredTrademark,
} from '../../app/constants';
import { Hub, hubHasUSB } from '../../components/hubPicker';
import { HubPicker } from '../../components/hubPicker/HubPicker';
import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks';
import { useSelector } from '../../reducers';
@@ -47,18 +51,25 @@ const RestoreFirmwarePanel: React.VoidFunctionComponent = () => {
return (
<div className={classNames(Classes.DIALOG_BODY, Classes.RUNNING_TEXT)}>
<BootloaderInstructions hubType={hubType} recovery />
<BootloaderInstructions
hubType={hubType}
recovery
flashButtonText={i18n.translate('restoreFirmwarePanel.flashButton')}
/>
{hubHasUSB(hubType) ? (
<>
<p>
{i18n.translate('restoreFirmwarePanel.instruction2.dfu', {
flashFirmware: (
<strong>
{i18n.translate('restoreFirmwarePanel.flashButton')}
</strong>
),
lego: legoRegisteredTrademark,
})}
{i18n.translate('restoreFirmwarePanel.instruction2.updateApp', {
app:
hubType === Hub.Inventor
? legoMindstormsRegisteredTrademark
: legoEducationSpikeRegisteredTrademark,
})}{' '}
{hubType !== Hub.Inventor
? i18n.translate(
'restoreFirmwarePanel.instruction2.updateAppVersion',
)
: ''}
</p>
<div className="pb-spacer" />
<Button
@@ -14,8 +14,9 @@
"ble": {
"message": "When the progress bar is done, the hub will reboot and the app will connect. The official firmware has been restored."
},
"dfu": "Then click the {flashFirmware} button below to connect to the hub and flash the firmware. After flashing is complete, connect the hub to one of the official {lego} apps and use it to update to the latest official firmware."
"updateApp": "Once the recovery is complete, use the {app} app to get the latest official firmware.",
"updateAppVersion": "You can use version 2 (legacy) or version 3 to do so."
},
"flashButton": "Flash"
"flashButton": "Restore"
}
}