firmware/bootloaderInstructions: extract component

This extracts the instructions for placing a hub in bootloader mode to
a separate component so that it can be shared.
This commit is contained in:
David Lechner
2022-11-18 19:28:00 -06:00
committed by David Lechner
parent f167f168ee
commit 44169bd178
5 changed files with 161 additions and 141 deletions
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { Callout, Intent } from '@blueprintjs/core';
import React, { useMemo } from 'react';
import {
pybricksUsbDfuWindowsDriverInstallUrl,
pybricksUsbLinuxUdevRulesUrl,
} from '../../app/constants';
import { Hub, hubHasBluetoothButton, hubHasUSB } from '../../components/hubPicker';
import ExternalLinkIcon from '../../utils/ExternalLinkIcon';
import { isLinux, isWindows } from '../../utils/os';
import { useI18n } from './i18n';
type BootloaderInstructionsProps = {
hubType: Hub;
};
/**
* Provides customized instructions on how to enter bootloader mode based
* on the hub type.
*/
const BootloaderInstructions: React.VoidFunctionComponent<
BootloaderInstructionsProps
> = ({ hubType }) => {
const i18n = useI18n();
const { button, light, lightPattern } = useMemo(() => {
return {
button: i18n.translate(
hubHasBluetoothButton(hubType) ? 'button.bluetooth' : 'button.power',
),
light: i18n.translate(
hubHasBluetoothButton(hubType) ? 'light.bluetooth' : 'light.status',
),
lightPattern: i18n.translate(
hubHasBluetoothButton(hubType)
? 'lightPattern.bluetooth'
: 'lightPattern.status',
),
};
}, [i18n, hubType]);
return (
<>
{hubHasUSB(hubType) && isLinux() && (
<Callout intent={Intent.WARNING} icon="warning-sign">
{i18n.translate('warning.linux')}{' '}
<a
href={pybricksUsbLinuxUdevRulesUrl}
target="_blank"
rel="noreferrer"
>
{i18n.translate('warning.learnMore')}
</a>
<ExternalLinkIcon />
</Callout>
)}
{hubHasUSB(hubType) && isWindows() && (
<Callout intent={Intent.WARNING} icon="warning-sign">
{i18n.translate('warning.windows')}{' '}
<a
href={pybricksUsbDfuWindowsDriverInstallUrl}
target="_blank"
rel="noreferrer"
>
{i18n.translate('warning.learnMore')}
</a>
<ExternalLinkIcon />
</Callout>
)}
<p>{i18n.translate('instruction')}</p>
<ol>
{hubHasUSB(hubType) && <li>{i18n.translate('step.disconnectUsb')}</li>}
<li>{i18n.translate('step.powerOff')}</li>
{/* City hub has power issues and requires disconnecting motors/sensors */}
{hubType === Hub.City && <li>{i18n.translate('step.disconnectIo')}</li>}
<li>{i18n.translate('step.holdButton', { button })}</li>
{hubHasUSB(hubType) && <li>{i18n.translate('step.connectUsb')}</li>}
<li>
{i18n.translate('step.waitForLight', {
button,
light,
lightPattern,
})}
</li>
<li>
{i18n.translate(
/* hubs with USB will keep the power on, but other hubs won't */
hubHasUSB(hubType) ? 'step.releaseButton' : 'step.keepHolding',
{
button,
},
)}
</li>
</ol>
</>
);
};
export default BootloaderInstructions;
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { useI18n as useShopifyI18n } from '@shopify/react-i18n';
import type { TypedI18n } from '../../i18n';
import type translations from './translations/en.json';
export function useI18n(): TypedI18n<typeof translations> {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
@@ -0,0 +1,30 @@
{
"warning": {
"linux": "If you have never used Pybricks with USB on Linux, you will need to configure udev rules to allow permission before you can flash the hub firmware.",
"windows": " If you have never used Pybricks with USB on Windows, you may need to manually install a USB driver before you can flash the hub firmware.",
"learnMore": "Learn more."
},
"instruction": "To flash the firmware, the hub must be placed in bootloader mode. Follow the steps below to do this:",
"button": {
"bluetooth": "Bluetooth button",
"power": "power button"
},
"light": {
"bluetooth": "Bluetooth light",
"status": "hub status light"
},
"lightPattern": {
"bluetooth": "pink-green-blue-off",
"status": "light purple"
},
"step": {
"disconnectUsb": "Disconnect the USB cable from the hub.",
"powerOff": "Turn off the hub.",
"disconnectIo": "Disconnect all motors and sensors from the I/O ports on the hub.",
"holdButton": "Press and hold the {button} on the hub.",
"connectUsb": "Connect the USB cable.",
"waitForLight": "Keep holding the {button} and wait for the {light} to start flashing {lightPattern}. This takes about 5 seconds.",
"releaseButton": "Release the {button}",
"keepHolding": "Keep holding the {button}."
}
}
@@ -4,7 +4,6 @@
import './installPybricksDialog.scss';
import {
Button,
Callout,
Checkbox,
Classes,
Collapse,
@@ -23,28 +22,18 @@ import { Classes as Classes2, Popover2 } from '@blueprintjs/popover2';
import { FirmwareMetadata, HubType } from '@pybricks/firmware';
import { fileOpen } from 'browser-fs-access';
import classNames from 'classnames';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { useDispatch } from 'react-redux';
import { useLocalStorage } from 'usehooks-ts';
import { alertsShowAlert } from '../../alerts/actions';
import {
pybricksUsbDfuWindowsDriverInstallUrl,
pybricksUsbLinuxUdevRulesUrl,
} from '../../app/constants';
import HelpButton from '../../components/HelpButton';
import {
Hub,
hubBootloaderType,
hubHasBluetoothButton,
hubHasUSB,
} from '../../components/hubPicker';
import { Hub, hubBootloaderType } from '../../components/hubPicker';
import { HubPicker } from '../../components/hubPicker/HubPicker';
import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks';
import { useSelector } from '../../reducers';
import { ensureError } from '../../utils';
import ExternalLinkIcon from '../../utils/ExternalLinkIcon';
import { isLinux, isWindows } from '../../utils/os';
import BootloaderInstructions from '../bootloaderInstructions/BootloaderInstructions';
import {
firmwareInstallPybricksDialogAccept,
firmwareInstallPybricksDialogCancel,
@@ -55,6 +44,7 @@ import { validateHubName } from '.';
const dialogBody = classNames(
Classes.DIALOG_BODY,
Classes.RUNNING_TEXT,
'pb-firmware-installPybricksDialog-body',
);
@@ -409,107 +399,16 @@ const BootloaderModePanel: React.VoidFunctionComponent<BootloaderModePanelProps>
}) => {
const i18n = useI18n();
const { button, light, lightPattern } = useMemo(() => {
return {
button: i18n.translate(
hubHasBluetoothButton(hubType)
? 'bootloaderPanel.button.bluetooth'
: 'bootloaderPanel.button.power',
),
light: i18n.translate(
hubHasBluetoothButton(hubType)
? 'bootloaderPanel.light.bluetooth'
: 'bootloaderPanel.light.status',
),
lightPattern: i18n.translate(
hubHasBluetoothButton(hubType)
? 'bootloaderPanel.lightPattern.bluetooth'
: 'bootloaderPanel.lightPattern.status',
),
};
}, [i18n, hubType]);
return (
<div className={dialogBody}>
{hubHasUSB(hubType) && isLinux() && (
<Callout intent={Intent.WARNING} icon="warning-sign">
{i18n.translate('bootloaderPanel.warning.linux')}{' '}
<a
href={pybricksUsbLinuxUdevRulesUrl}
target="_blank"
rel="noreferrer"
>
{i18n.translate('bootloaderPanel.warning.learnMore')}
</a>
<ExternalLinkIcon />
</Callout>
)}
{hubHasUSB(hubType) && isWindows() && (
<Callout intent={Intent.WARNING} icon="warning-sign">
{i18n.translate('bootloaderPanel.warning.windows')}{' '}
<a
href={pybricksUsbDfuWindowsDriverInstallUrl}
target="_blank"
rel="noreferrer"
>
{i18n.translate('bootloaderPanel.warning.learnMore')}
</a>
<ExternalLinkIcon />
</Callout>
)}
<div className={Classes.RUNNING_TEXT}>
<p>{i18n.translate('bootloaderPanel.instruction1')}</p>
<ol>
{hubHasUSB(hubType) && (
<li>{i18n.translate('bootloaderPanel.step.disconnectUsb')}</li>
)}
<li>{i18n.translate('bootloaderPanel.step.powerOff')}</li>
{/* City hub has power issues and requires disconnecting motors/sensors */}
{hubType === Hub.City && (
<li>{i18n.translate('bootloaderPanel.step.disconnectIo')}</li>
)}
<li>
{i18n.translate('bootloaderPanel.step.holdButton', { button })}
</li>
{hubHasUSB(hubType) && (
<li>{i18n.translate('bootloaderPanel.step.connectUsb')}</li>
)}
<li>
{i18n.translate('bootloaderPanel.step.waitForLight', {
button,
light,
lightPattern,
})}
</li>
<li>
{i18n.translate(
/* hubs with USB will keep the power on, but other hubs won't */
hubHasUSB(hubType)
? 'bootloaderPanel.step.releaseButton'
: 'bootloaderPanel.step.keepHolding',
{
button,
},
)}
</li>
</ol>
<p>
{i18n.translate('bootloaderPanel.instruction2', {
flashFirmware: (
<strong>
{i18n.translate('flashFirmwareButton.label')}
</strong>
),
})}
</p>
</div>
<BootloaderInstructions hubType={hubType} />
<p>
{i18n.translate('bootloaderPanel.instruction2', {
flashFirmware: (
<strong>{i18n.translate('flashFirmwareButton.label')}</strong>
),
})}
</p>
</div>
);
};
@@ -54,34 +54,6 @@
},
"bootloaderPanel": {
"title": "Place hub in bootloader mode",
"warning": {
"linux": "If you have never used Pybricks with USB on Linux, you will need to configure udev rules to allow permission before you can flash the hub firmware.",
"windows": " If you have never used Pybricks with USB on Windows, you may need to manually install a USB driver before you can flash the hub firmware.",
"learnMore": "Learn more."
},
"instruction1": "To flash the firmware, the hub must be placed in bootloader mode. Follow the steps below to do this:",
"button": {
"bluetooth": "Bluetooth button",
"power": "power button"
},
"light": {
"bluetooth": "Bluetooth light",
"status": "hub status light"
},
"lightPattern": {
"bluetooth": "pink-green-blue-off",
"status": "light purple"
},
"step": {
"disconnectUsb": "Disconnect the USB cable from the hub.",
"powerOff": "Turn off the hub.",
"disconnectIo": "Disconnect all motors and sensors from the I/O ports on the hub.",
"holdButton": "Press and hold the {button} on the hub.",
"connectUsb": "Connect the USB cable.",
"waitForLight": "Keep holding the {button} and wait for the {light} to start flashing {lightPattern}. This takes about 5 seconds.",
"releaseButton": "Release the {button}",
"keepHolding": "Keep holding the {button}."
},
"instruction2": "Then click the {flashFirmware} button below to connect to the hub and flash the firmware."
},
"backButton": {