mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-15 02:54:07 +00:00
bootloaderInstructions: add videos
This adds videos to show how to enter bootloader mode. Issue: https://github.com/pybricks/support/issues/728
This commit is contained in:
committed by
David Lechner
parent
be4a18bcdd
commit
0f871c6b2c
@@ -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<Hub, string> = new Map([
|
||||
[Hub.City, cityHubMp4],
|
||||
[Hub.Essential, essentialHubMp4],
|
||||
[Hub.Inventor, inventorHubMp4],
|
||||
[Hub.Move, moveHubMp4],
|
||||
[Hub.Prime, primeHubMp4],
|
||||
[Hub.Technic, technicHubMp4],
|
||||
]);
|
||||
|
||||
const metadataFileMap: ReadonlyMap<Hub, string> = 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<HTMLTrackElement>(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<
|
||||
<ExternalLinkIcon />
|
||||
</Callout>
|
||||
)}
|
||||
<p>{i18n.translate('instruction')}</p>
|
||||
|
||||
<video
|
||||
controls
|
||||
controlsList="nodownload nofullscreen"
|
||||
muted
|
||||
disablePictureInPicture
|
||||
className="pb-bootloader-video"
|
||||
>
|
||||
<source src={videoFileMap.get(hubType)} type="video/mp4" />
|
||||
<track
|
||||
kind="metadata"
|
||||
src={metadataFileMap.get(hubType)}
|
||||
ref={metadataTrackRef}
|
||||
/>
|
||||
</video>
|
||||
|
||||
<div className="pb-spacer" />
|
||||
|
||||
<p>
|
||||
{i18n.translate('instruction', {
|
||||
startPoweredOff: hubHasUSB(hubType)
|
||||
? i18n.translate('startPoweredOff.usb')
|
||||
: i18n.translate('startPoweredOff.default'),
|
||||
})}
|
||||
</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>}
|
||||
{hubType === Hub.City && (
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'disconnect-io' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.disconnectIo')}
|
||||
</li>
|
||||
)}
|
||||
|
||||
<li>{i18n.translate('step.holdButton', { button })}</li>
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'hold-button' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.holdButton', { button })}
|
||||
</li>
|
||||
|
||||
{hubHasUSB(hubType) && <li>{i18n.translate('step.connectUsb')}</li>}
|
||||
{/* not strictly necessary, but order is swapped in the video,
|
||||
so we match it here. */}
|
||||
{hubType !== Hub.Essential && hubHasUSB(hubType) && (
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'connect-usb' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.connectUsb')}
|
||||
</li>
|
||||
)}
|
||||
|
||||
<li>
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'wait-for-light' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.waitForLight', {
|
||||
button,
|
||||
light,
|
||||
@@ -90,15 +200,38 @@ const BootloaderInstructions: React.VoidFunctionComponent<
|
||||
})}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{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) && (
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'connect-usb' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.connectUsb')}
|
||||
</li>
|
||||
)}
|
||||
|
||||
{/* hubs with USB will keep the power on, but other hubs won't */}
|
||||
{hubHasUSB(hubType) ? (
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'release-button' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.releaseButton', {
|
||||
button,
|
||||
},
|
||||
)}
|
||||
</li>
|
||||
})}
|
||||
</li>
|
||||
) : (
|
||||
<li
|
||||
className={classNames(
|
||||
activeStep === 'keep-holding' && 'pb-active-step',
|
||||
)}
|
||||
>
|
||||
{i18n.translate('step.keepHolding', {
|
||||
button,
|
||||
})}
|
||||
</li>
|
||||
)}
|
||||
</ol>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.",
|
||||
|
||||
Vendored
+9
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user