diff --git a/CHANGELOG.md b/CHANGELOG.md index 05d02675..5e6a8708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ## [Unreleased] +### Added +- Added videos to firmware flash dialogs ([support#728]). + ### Changed - Moved documentation show/hide button from settings to editor area ([support#778]). @@ -14,6 +17,7 @@ - Fixed slow firmware flash on Android ([support#438]). [support#438]: https://github.com/pybricks/support/issues/438 +[support#728]: https://github.com/pybricks/support/issues/728 [support#778]: https://github.com/pybricks/support/issues/778 [support#807]: https://github.com/pybricks/support/issues/807 [support#823]: https://github.com/pybricks/support/issues/823 diff --git a/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx index e8f76314..87db2f4f 100644 --- a/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx +++ b/src/firmware/bootloaderInstructions/BootloaderInstructions.tsx @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import './bootloaderInstructions.scss'; import { Callout, Intent } from '@blueprintjs/core'; -import React, { useMemo } from 'react'; +import classNames from 'classnames'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { pybricksUsbDfuWindowsDriverInstallUrl, pybricksUsbLinuxUdevRulesUrl, @@ -10,12 +12,42 @@ import { import ExternalLinkIcon from '../../components/ExternalLinkIcon'; import { Hub, hubHasBluetoothButton, hubHasUSB } from '../../components/hubPicker'; import { isLinux, isWindows } from '../../utils/os'; +import cityHubMp4 from './assets/bootloader-cityhub-540.mp4'; +import cityHubVtt from './assets/bootloader-cityhub-metadata.vtt'; +import essentialHubMp4 from './assets/bootloader-essentialhub-540.mp4'; +import essentialHubVtt from './assets/bootloader-essentialhub-metadata.vtt'; +import inventorHubMp4 from './assets/bootloader-inventorhub-540.mp4'; +import inventorHubVtt from './assets/bootloader-inventorhub-metadata.vtt'; +import moveHubMp4 from './assets/bootloader-movehub-540.mp4'; +import moveHubVtt from './assets/bootloader-movehub-metadata.vtt'; +import primeHubMp4 from './assets/bootloader-primehub-540.mp4'; +import primeHubVtt from './assets/bootloader-primehub-metadata.vtt'; +import technicHubMp4 from './assets/bootloader-technichub-540.mp4'; +import technicHubVtt from './assets/bootloader-technichub-metadata.vtt'; import { useI18n } from './i18n'; type BootloaderInstructionsProps = { hubType: Hub; }; +const videoFileMap: ReadonlyMap = new Map([ + [Hub.City, cityHubMp4], + [Hub.Essential, essentialHubMp4], + [Hub.Inventor, inventorHubMp4], + [Hub.Move, moveHubMp4], + [Hub.Prime, primeHubMp4], + [Hub.Technic, technicHubMp4], +]); + +const metadataFileMap: ReadonlyMap = new Map([ + [Hub.City, cityHubVtt], + [Hub.Essential, essentialHubVtt], + [Hub.Inventor, inventorHubVtt], + [Hub.Move, moveHubVtt], + [Hub.Prime, primeHubVtt], + [Hub.Technic, technicHubVtt], +]); + /** * Provides customized instructions on how to enter bootloader mode based * on the hub type. @@ -41,6 +73,36 @@ const BootloaderInstructions: React.VoidFunctionComponent< }; }, [i18n, hubType]); + const metadataTrackRef = useRef(null); + const [activeStep, setActiveStep] = useState(''); + + useEffect(() => { + const element = metadataTrackRef.current; + + // istanbul ignore if: should not happen + if (element === null) { + return; + } + + // istanbul ignore else: jsdom doesn't support video + if (process.env.NODE_ENV === 'test') { + return; + } + + const handleCueChange = (e: Event) => { + const track = e.target as TextTrack; + setActiveStep(track.activeCues?.[0]?.id ?? ''); + }; + + element.track.addEventListener('cuechange', handleCueChange); + element.track.mode = 'hidden'; + + return () => { + element.track.removeEventListener('cuechange', handleCueChange); + element.track.mode = 'disabled'; + }; + }, [setActiveStep]); + return ( <> {hubHasUSB(hubType) && isLinux() && ( @@ -69,20 +131,68 @@ const BootloaderInstructions: React.VoidFunctionComponent< )} -

{i18n.translate('instruction')}

+ + + +
+ +

+ {i18n.translate('instruction', { + startPoweredOff: hubHasUSB(hubType) + ? i18n.translate('startPoweredOff.usb') + : i18n.translate('startPoweredOff.default'), + })} +

    - {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. } + {hubType === Hub.City && ( +
  7. + {i18n.translate('step.disconnectIo')} +
  8. + )} -
  9. {i18n.translate('step.holdButton', { button })}
  10. +
  11. + {i18n.translate('step.holdButton', { button })} +
  12. - {hubHasUSB(hubType) &&
  13. {i18n.translate('step.connectUsb')}
  14. } + {/* not strictly necessary, but order is swapped in the video, + so we match it here. */} + {hubType !== Hub.Essential && hubHasUSB(hubType) && ( +
  15. + {i18n.translate('step.connectUsb')} +
  16. + )} -
  17. +
  18. {i18n.translate('step.waitForLight', { button, light, @@ -90,15 +200,38 @@ const BootloaderInstructions: React.VoidFunctionComponent< })}
  19. -
  20. - {i18n.translate( - /* hubs with USB will keep the power on, but other hubs won't */ - hubHasUSB(hubType) ? 'step.releaseButton' : 'step.keepHolding', - { + {hubType === Hub.Essential && hubHasUSB(hubType) && ( +
  21. + {i18n.translate('step.connectUsb')} +
  22. + )} + + {/* hubs with USB will keep the power on, but other hubs won't */} + {hubHasUSB(hubType) ? ( +
  23. + {i18n.translate('step.releaseButton', { button, - }, - )} -
  24. + })} + + ) : ( +
  25. + {i18n.translate('step.keepHolding', { + button, + })} +
  26. + )}
); diff --git a/src/firmware/bootloaderInstructions/assets/LICENSE b/src/firmware/bootloaderInstructions/assets/LICENSE new file mode 100644 index 00000000..0a579422 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/LICENSE @@ -0,0 +1,4 @@ +Files in this folder are licensed under the Creative Commons Attribution- +NonCommercial-ShareAlike 4.0 International License. To view a copy of this +license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a +letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-540.mp4 new file mode 100644 index 00000000..4b6c1ad4 Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-metadata.vtt new file mode 100644 index 00000000..c1a4aafb --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-cityhub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +disconnect-io +00:00.100 --> 00:08.000 + +hold-button +00:08.000 --> 00:14.000 + +wait-for-light +00:14.000 --> 00:21.000 + +keep-holding +00:21.000 --> 00:25.000 diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-540.mp4 new file mode 100644 index 00000000..2d7e3268 Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-metadata.vtt new file mode 100644 index 00000000..e5da2105 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-essentialhub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +hold-button +00:00.100 --> 00:05.000 + +wait-for-light +00:05.000 --> 00:09.000 + +connect-usb +00:09.000 --> 00:15.000 + +release-button +00:15.000 --> 00:21.000 diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-540.mp4 new file mode 100644 index 00000000..2d38b91d Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-metadata.vtt new file mode 100644 index 00000000..fb83cf66 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-inventorhub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +hold-button +00:00.100 --> 00:06.000 + +connect-usb +00:06.000 --> 00:13.000 + +wait-for-light +00:13.000 --> 00:20.000 + +release-button +00:20.000 --> 00:24.000 diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-movehub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-movehub-540.mp4 new file mode 100644 index 00000000..88b820d5 Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-movehub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-movehub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-movehub-metadata.vtt new file mode 100644 index 00000000..5e98eb44 --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-movehub-metadata.vtt @@ -0,0 +1,10 @@ +WEBVTT + +hold-button +00:00.100 --> 00:05.000 + +wait-for-light +00:05.000 --> 00:15.000 + +keep-holding +00:15.000 --> 00:20.000 diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-primehub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-primehub-540.mp4 new file mode 100644 index 00000000..c73be46e Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-primehub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-primehub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-primehub-metadata.vtt new file mode 100644 index 00000000..36c6594f --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-primehub-metadata.vtt @@ -0,0 +1,13 @@ +WEBVTT + +hold-button +00:00.100 --> 00:07.000 + +connect-usb +00:07.000 --> 00:13.000 + +wait-for-light +00:13.000 --> 00:19.000 + +release-button +00:19.000 --> 00:24.000 diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-technichub-540.mp4 b/src/firmware/bootloaderInstructions/assets/bootloader-technichub-540.mp4 new file mode 100644 index 00000000..a6e9ee6e Binary files /dev/null and b/src/firmware/bootloaderInstructions/assets/bootloader-technichub-540.mp4 differ diff --git a/src/firmware/bootloaderInstructions/assets/bootloader-technichub-metadata.vtt b/src/firmware/bootloaderInstructions/assets/bootloader-technichub-metadata.vtt new file mode 100644 index 00000000..ab9c147d --- /dev/null +++ b/src/firmware/bootloaderInstructions/assets/bootloader-technichub-metadata.vtt @@ -0,0 +1,10 @@ +WEBVTT + +hold-button +00:00.100 --> 00:07.000 + +wait-for-light +00:07.000 --> 00:16.000 + +keep-holding +00:16.000 --> 00:20.000 diff --git a/src/firmware/bootloaderInstructions/bootloaderInstructions.scss b/src/firmware/bootloaderInstructions/bootloaderInstructions.scss new file mode 100644 index 00000000..540b4dfb --- /dev/null +++ b/src/firmware/bootloaderInstructions/bootloaderInstructions.scss @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 The Pybricks Authors + +.pb-active-step { + font-weight: bolder; + font-size: larger; +} + +.pb-bootloader-video { + width: 90%; + align-self: center; +} diff --git a/src/firmware/bootloaderInstructions/translations/en.json b/src/firmware/bootloaderInstructions/translations/en.json index b4cf9f52..f9cc8a58 100644 --- a/src/firmware/bootloaderInstructions/translations/en.json +++ b/src/firmware/bootloaderInstructions/translations/en.json @@ -4,7 +4,11 @@ "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:", + "instruction": "To flash the firmware, the hub must be placed in bootloader mode. {startPoweredOff}, then follow the instructions below:", + "startPoweredOff": { + "default": "Start with the hub powered off", + "usb": "Start with the hub powered off and the USB cable disconnected" + }, "button": { "bluetooth": "Bluetooth button", "power": "power button" @@ -18,8 +22,6 @@ "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.", diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index da28af6a..0cb866d8 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -83,6 +83,15 @@ declare module '*.zip' { export default src; } +declare module '*.mp4' { + const src: string; + export default src; +} + +declare module '*.vtt' { + const src: string; + export default src; +} // https://webpack.js.org/api/hot-module-replacement/ interface NodeModule { hot?: { dispose: (callback: (data) => void) => void };