diff --git a/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx new file mode 100644 index 00000000..373df559 --- /dev/null +++ b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx @@ -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() && ( + + {i18n.translate('warning.linux')}{' '} + + {i18n.translate('warning.learnMore')} + + + + )} + {hubHasUSB(hubType) && isWindows() && ( + + {i18n.translate('warning.windows')}{' '} + + {i18n.translate('warning.learnMore')} + + + + )} +

{i18n.translate('instruction')}

+
    + {hubHasUSB(hubType) &&
  1. {i18n.translate('step.disconnectUsb')}
  2. } + +
  3. {i18n.translate('step.powerOff')}
  4. + + {/* City hub has power issues and requires disconnecting motors/sensors */} + {hubType === Hub.City &&
  5. {i18n.translate('step.disconnectIo')}
  6. } + +
  7. {i18n.translate('step.holdButton', { button })}
  8. + + {hubHasUSB(hubType) &&
  9. {i18n.translate('step.connectUsb')}
  10. } + +
  11. + {i18n.translate('step.waitForLight', { + button, + light, + lightPattern, + })} +
  12. + +
  13. + {i18n.translate( + /* hubs with USB will keep the power on, but other hubs won't */ + hubHasUSB(hubType) ? 'step.releaseButton' : 'step.keepHolding', + { + button, + }, + )} +
  14. +
+ + ); +}; + +export default BootloaderInstructions; diff --git a/src/firmware/bootloaderInstructions/i18n.ts b/src/firmware/bootloaderInstructions/i18n.ts new file mode 100644 index 00000000..eb8dc486 --- /dev/null +++ b/src/firmware/bootloaderInstructions/i18n.ts @@ -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 { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} diff --git a/src/firmware/bootloaderInstructions/translations/en.json b/src/firmware/bootloaderInstructions/translations/en.json new file mode 100644 index 00000000..b4cf9f52 --- /dev/null +++ b/src/firmware/bootloaderInstructions/translations/en.json @@ -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}." + } +} diff --git a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx index e9964f56..a301e4db 100644 --- a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx +++ b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx @@ -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 }) => { 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 (
- {hubHasUSB(hubType) && isLinux() && ( - - {i18n.translate('bootloaderPanel.warning.linux')}{' '} - - {i18n.translate('bootloaderPanel.warning.learnMore')} - - - - )} - {hubHasUSB(hubType) && isWindows() && ( - - {i18n.translate('bootloaderPanel.warning.windows')}{' '} - - {i18n.translate('bootloaderPanel.warning.learnMore')} - - - - )} - -
-

{i18n.translate('bootloaderPanel.instruction1')}

-
    - {hubHasUSB(hubType) && ( -
  1. {i18n.translate('bootloaderPanel.step.disconnectUsb')}
  2. - )} - -
  3. {i18n.translate('bootloaderPanel.step.powerOff')}
  4. - - {/* City hub has power issues and requires disconnecting motors/sensors */} - {hubType === Hub.City && ( -
  5. {i18n.translate('bootloaderPanel.step.disconnectIo')}
  6. - )} - -
  7. - {i18n.translate('bootloaderPanel.step.holdButton', { button })} -
  8. - - {hubHasUSB(hubType) && ( -
  9. {i18n.translate('bootloaderPanel.step.connectUsb')}
  10. - )} - -
  11. - {i18n.translate('bootloaderPanel.step.waitForLight', { - button, - light, - lightPattern, - })} -
  12. - -
  13. - {i18n.translate( - /* hubs with USB will keep the power on, but other hubs won't */ - hubHasUSB(hubType) - ? 'bootloaderPanel.step.releaseButton' - : 'bootloaderPanel.step.keepHolding', - { - button, - }, - )} -
  14. -
-

- {i18n.translate('bootloaderPanel.instruction2', { - flashFirmware: ( - - {i18n.translate('flashFirmwareButton.label')} - - ), - })} -

-
+ +

+ {i18n.translate('bootloaderPanel.instruction2', { + flashFirmware: ( + {i18n.translate('flashFirmwareButton.label')} + ), + })} +

); }; diff --git a/src/firmware/installPybricksDialog/translations/en.json b/src/firmware/installPybricksDialog/translations/en.json index 3f254b24..771f9637 100644 --- a/src/firmware/installPybricksDialog/translations/en.json +++ b/src/firmware/installPybricksDialog/translations/en.json @@ -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": {