mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
firmware/dfuWindowsDriverInstallDialog: move instructions in app
This replaces the link for Windows DFU USB driver installation instructions with a new in-app dialog containing the instructions. Fixes: https://github.com/pybricks/support/issues/858
This commit is contained in:
committed by
David Lechner
parent
601b111ec4
commit
5d3c74b777
@@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
import { useLocalStorage, useTernaryDarkMode } from 'usehooks-ts';
|
||||
import Activities from '../activities/Activities';
|
||||
import DfuWindowsDriverInstallDialog from '../firmware/dfuWindowsDriverInstallDialog/DfuWindowsDriverInstallDialog';
|
||||
import { InstallPybricksDialog } from '../firmware/installPybricksDialog/InstallPybricksDialog';
|
||||
import RestoreOfficialDialog from '../firmware/restoreOfficialDialog/RestoreOfficialDialog';
|
||||
import { useSettingIsShowDocsEnabled } from '../settings/hooks';
|
||||
@@ -248,6 +249,7 @@ const App: React.VFC = () => {
|
||||
</div>
|
||||
<StatusBar />
|
||||
<Tour />
|
||||
<DfuWindowsDriverInstallDialog />
|
||||
<InstallPybricksDialog />
|
||||
<RestoreOfficialDialog />
|
||||
<SponsorDialog />
|
||||
|
||||
@@ -45,9 +45,6 @@ export const pybricksBluetoothTroubleshootingUrl =
|
||||
export const pybricksUsbDfuTroubleshootingUrl =
|
||||
'https://github.com/pybricks/support/discussions/688';
|
||||
|
||||
export const pybricksUsbDfuWindowsDriverInstallUrl =
|
||||
'https://github.com/pybricks/support/discussions/688#discussioncomment-3201466';
|
||||
|
||||
export const pybricksUsbLinuxUdevRulesUrl =
|
||||
'https://github.com/pybricks/support/discussions/688#discussioncomment-3239099';
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@ import './bootloaderInstructions.scss';
|
||||
import { Callout, Intent } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import {
|
||||
legoRegisteredTrademark,
|
||||
pybricksUsbDfuWindowsDriverInstallUrl,
|
||||
pybricksUsbLinuxUdevRulesUrl,
|
||||
} from '../../app/constants';
|
||||
import ExternalLinkIcon from '../../components/ExternalLinkIcon';
|
||||
import { Hub, hubHasBluetoothButton, hubHasUSB } from '../../components/hubPicker';
|
||||
import { isLinux, isWindows } from '../../utils/os';
|
||||
import { firmwareDfuWindowsDriverInstallDialogDialogShow } from '../dfuWindowsDriverInstallDialog/actions';
|
||||
import cityHubMp4 from './assets/bootloader-cityhub-540.mp4';
|
||||
import cityHubVtt from './assets/bootloader-cityhub-metadata.vtt';
|
||||
import essentialHubMp4 from './assets/bootloader-essentialhub-540.mp4';
|
||||
@@ -100,6 +101,7 @@ const recoveryMetadataFileMap: ReadonlyMap<Hub, string> = new Map([
|
||||
const BootloaderInstructions: React.VoidFunctionComponent<
|
||||
BootloaderInstructionsProps
|
||||
> = ({ hubType, recovery, flashButtonText }) => {
|
||||
const dispatch = useDispatch();
|
||||
const i18n = useI18n();
|
||||
|
||||
const { button, light, lightPattern } = useMemo(() => {
|
||||
@@ -150,33 +152,6 @@ const BootloaderInstructions: React.VoidFunctionComponent<
|
||||
|
||||
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>
|
||||
)}
|
||||
|
||||
<video
|
||||
controls
|
||||
controlsList="nodownload nofullscreen"
|
||||
@@ -364,6 +339,46 @@ const BootloaderInstructions: React.VoidFunctionComponent<
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{hubHasUSB(hubType) && isLinux() && (
|
||||
<Callout intent={Intent.WARNING} icon="warning-sign">
|
||||
{i18n.translate('warning.linux.message', {
|
||||
learnMore: (
|
||||
<>
|
||||
<a
|
||||
href={pybricksUsbLinuxUdevRulesUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{i18n.translate('warning.linux.learnMore')}
|
||||
</a>
|
||||
<ExternalLinkIcon />
|
||||
</>
|
||||
),
|
||||
})}
|
||||
</Callout>
|
||||
)}
|
||||
|
||||
{hubHasUSB(hubType) && isWindows() && (
|
||||
<Callout intent={Intent.WARNING} icon="warning-sign">
|
||||
{i18n.translate('warning.windows.message', {
|
||||
instructions: (
|
||||
<a
|
||||
onMouseDown={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
dispatch(
|
||||
firmwareDfuWindowsDriverInstallDialogDialogShow(),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{i18n.translate('warning.windows.instructions')}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</Callout>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"warning": {
|
||||
"linux": "If you have never used Pybricks with USB on Linux, you will need to configure udev rules before you can install the 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."
|
||||
"linux": {
|
||||
"message": "If you have never used Pybricks with USB on Linux, you will need to configure udev rules before you can install the firmware. {learnMore}",
|
||||
"learnMore": "Learn more."
|
||||
},
|
||||
"windows": {
|
||||
"message": "If you have never used Pybricks with USB on Windows, you may need to manually install a USB driver before you can connect to the device. {instructions}",
|
||||
"instructions": "Click for instructions."
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"bluetooth": "Bluetooth button",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { cleanup } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { testRender } from '../../../test';
|
||||
import DfuWindowsDriverInstallDialog from './DfuWindowsDriverInstallDialog';
|
||||
import { firmwareDfuWindowsDriverInstallDialogDialogHide } from './actions';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
jest.resetAllMocks();
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
|
||||
it('should dispatch action when the close button is pressed', async () => {
|
||||
const [user, dialog, dispatch] = testRender(<DfuWindowsDriverInstallDialog />, {
|
||||
firmware: { dfuWindowsDriverInstallDialog: { isOpen: true } },
|
||||
});
|
||||
|
||||
await user.click(dialog.getByRole('button', { name: 'Close' }));
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
firmwareDfuWindowsDriverInstallDialogDialogHide(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should navigate when next button is pressed and dispatch action when the done button is pressed', async () => {
|
||||
const [user, dialog, dispatch] = testRender(<DfuWindowsDriverInstallDialog />, {
|
||||
firmware: { dfuWindowsDriverInstallDialog: { isOpen: true } },
|
||||
});
|
||||
|
||||
for (let i = 1; i < 9; i++) {
|
||||
await user.click(dialog.getByRole('button', { name: 'Next' }));
|
||||
}
|
||||
|
||||
await user.click(dialog.getByRole('button', { name: 'Done' }));
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
firmwareDfuWindowsDriverInstallDialogDialogHide(),
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,407 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import './dfuWindowsDriverInstallDialog.scss';
|
||||
import {
|
||||
Card,
|
||||
Classes,
|
||||
DialogStep,
|
||||
Elevation,
|
||||
MultistepDialog,
|
||||
} from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Hub } from '../../components/hubPicker';
|
||||
import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks';
|
||||
import { useSelector } from '../../reducers';
|
||||
import { firmwareDfuWindowsDriverInstallDialogDialogHide } from './actions';
|
||||
import { useI18n } from './i18n';
|
||||
|
||||
const dfuWindows1OpenDeviceManagerUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_1_open_device_manager.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows2LargeHubOpenPropertiesUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_2_large_hub_open_properties.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows2SmallHubOpenPropertiesUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_2_small_hub_open_properties.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows3LargeHubPropertiesUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_3_large_hub_properties.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows3SmallHubPropertiesUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_3_small_hub_properties.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows4LargeHubUpdateDriverUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_4_large_hub_update_driver.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows4SmallHubUpdateDriverUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_4_small_hub_update_driver.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows5LargeHubUpdateDriverPickUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_5_large_hub_update_driver_pick.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows5SmallHubUpdateDriverPickUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_5_small_hub_update_driver_pick.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows6LargeHubUpdateDriverSelectTypeUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_6_large_hub_update_driver_select_type.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows6SmallHubUpdateDriverSelectTypeUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_6_small_hub_update_driver_select_type.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows7LargeHubUpdateDriverSelectDriverUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_7_large_hub_update_driver_select_driver.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows7SmallHubUpdateDriverSelectDriverUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_7_small_hub_update_driver_select_driver.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows8UpdateDriverWarningUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_8_update_driver_warning.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows9LargeHubUpdateDriverDoneUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_9_large_hub_update_driver_done.png',
|
||||
import.meta.url,
|
||||
);
|
||||
const dfuWindows9SmallHubUpdateDriverDoneUrl = new URL(
|
||||
'@pybricks/images/dfu_windows_9_small_hub_update_driver_done.png',
|
||||
import.meta.url,
|
||||
);
|
||||
|
||||
// These names are hard-coded in the hub bootloader, so they don't get translated
|
||||
const dfuSmallHubUsbName = 'LEGO Technic Small Hub in DFU Mode';
|
||||
const dfuLargeHubUsbName = 'LEGO Technic Large Hub in DFU Mode';
|
||||
|
||||
type ScreenshotProps = {
|
||||
url: URL;
|
||||
};
|
||||
|
||||
const Screenshot: React.VoidFunctionComponent<ScreenshotProps> = ({ url }) => {
|
||||
return (
|
||||
<Card elevation={Elevation.FOUR}>
|
||||
<img src={url.toString()} alt="Screenshot" width={673} height={523} />
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
type HubTypeProps = {
|
||||
hub: Hub;
|
||||
};
|
||||
|
||||
const Step1: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot url={dfuWindows1OpenDeviceManagerUrl} />
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.1.message', {
|
||||
startMenu: <em>{i18n.translate('step.1.startMenu')}</em>,
|
||||
deviceManager: (
|
||||
<em>{i18n.translate('step.1.deviceManager')}</em>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step2: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows2SmallHubOpenPropertiesUrl
|
||||
: dfuWindows2LargeHubOpenPropertiesUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.2.message', {
|
||||
dfuUsbHubName: (
|
||||
<em>
|
||||
{hub === Hub.Essential
|
||||
? dfuSmallHubUsbName
|
||||
: dfuLargeHubUsbName}
|
||||
</em>
|
||||
),
|
||||
otherDevices: <em>{i18n.translate('step.2.otherDevices')}</em>,
|
||||
properties: <em>{i18n.translate('step.2.properties')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step3: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows3SmallHubPropertiesUrl
|
||||
: dfuWindows3LargeHubPropertiesUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.3.message', {
|
||||
properties: (
|
||||
<em>
|
||||
{i18n.translate('step.3.properties', {
|
||||
dfuUsbHubName: (
|
||||
<em>
|
||||
{hub === Hub.Essential
|
||||
? dfuSmallHubUsbName
|
||||
: dfuLargeHubUsbName}
|
||||
</em>
|
||||
),
|
||||
})}
|
||||
</em>
|
||||
),
|
||||
updateDriver: <em>{i18n.translate('step.3.updateDriver')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step4: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows4SmallHubUpdateDriverUrl
|
||||
: dfuWindows4LargeHubUpdateDriverUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.4.message', {
|
||||
updateDrivers: (
|
||||
<em>{i18n.translate('step.4.updateDrivers')}</em>
|
||||
),
|
||||
browse: <em>{i18n.translate('step.4.browse')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step5: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows5SmallHubUpdateDriverPickUrl
|
||||
: dfuWindows5LargeHubUpdateDriverPickUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.5.message', {
|
||||
letMePick: <em>{i18n.translate('step.5.letMePick')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step6: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows6SmallHubUpdateDriverSelectTypeUrl
|
||||
: dfuWindows6LargeHubUpdateDriverSelectTypeUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.6.message', {
|
||||
usbDevices: <em>{i18n.translate('step.6.usbDevices')}</em>,
|
||||
next: <em>{i18n.translate('step.6.next')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step7: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows7SmallHubUpdateDriverSelectDriverUrl
|
||||
: dfuWindows7LargeHubUpdateDriverSelectDriverUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.7.message', {
|
||||
manufacturer: <em>{i18n.translate('step.7.manufacturer')}</em>,
|
||||
winUsbDevice: <em>{i18n.translate('step.7.winUsbDevice')}</em>,
|
||||
model: <em>{i18n.translate('step.7.model')}</em>,
|
||||
next: <em>{i18n.translate('step.7.next')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step8: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot url={dfuWindows8UpdateDriverWarningUrl} />
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.8.message', {
|
||||
yes: <em>{i18n.translate('step.8.yes')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Step9: React.VoidFunctionComponent<HubTypeProps> = ({ hub }) => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Screenshot
|
||||
url={
|
||||
hub === Hub.Essential
|
||||
? dfuWindows9SmallHubUpdateDriverDoneUrl
|
||||
: dfuWindows9LargeHubUpdateDriverDoneUrl
|
||||
}
|
||||
/>
|
||||
<div className="pb-spacer" />
|
||||
<div className={Classes.RUNNING_TEXT}>
|
||||
<p>
|
||||
{i18n.translate('step.9.message', {
|
||||
close: <em>{i18n.translate('step.9.close')}</em>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DfuWindowsDriverInstallDialog: React.VoidFunctionComponent = () => {
|
||||
const [hub] = useHubPickerSelectedHub();
|
||||
const { isOpen } = useSelector((s) => s.firmware.dfuWindowsDriverInstallDialog);
|
||||
const dispatch = useDispatch();
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<MultistepDialog
|
||||
title={i18n.translate('title')}
|
||||
className="pb-dfu-windows-driver-install-dialog"
|
||||
isOpen={isOpen}
|
||||
onClose={() => dispatch(firmwareDfuWindowsDriverInstallDialogDialogHide())}
|
||||
finalButtonProps={{
|
||||
text: i18n.translate('doneButton.label'),
|
||||
onClick: () =>
|
||||
dispatch(firmwareDfuWindowsDriverInstallDialogDialogHide()),
|
||||
}}
|
||||
>
|
||||
<DialogStep
|
||||
id="1"
|
||||
panel={<Step1 />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="2"
|
||||
panel={<Step2 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="3"
|
||||
panel={<Step3 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="4"
|
||||
panel={<Step4 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="5"
|
||||
panel={<Step5 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="6"
|
||||
panel={<Step6 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="7"
|
||||
panel={<Step7 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="8"
|
||||
panel={<Step8 />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
id="9"
|
||||
panel={<Step9 hub={hub} />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
</MultistepDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default DfuWindowsDriverInstallDialog;
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { createAction } from '../../actions';
|
||||
|
||||
/** Actions that request the DFU Windows USB driver install dialog to be shown. */
|
||||
export const firmwareDfuWindowsDriverInstallDialogDialogShow = createAction(() => ({
|
||||
type: 'firmware.dfuWindowsDriverInstallDialog.action.show',
|
||||
}));
|
||||
|
||||
/** Actions that request the DFU Windows USB driver install dialog to be hidden. */
|
||||
export const firmwareDfuWindowsDriverInstallDialogDialogHide = createAction(() => ({
|
||||
type: 'firmware.dfuWindowsDriverInstallDialog.action.hide',
|
||||
}));
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
@use '@blueprintjs/core/lib/scss/variables' as bp;
|
||||
|
||||
.pb-dfu-windows-driver-install-dialog {
|
||||
& .#{bp.$ns}-multistep-dialog-panels {
|
||||
min-height: bp.$pt-grid-size * 75;
|
||||
|
||||
& .#{bp.$ns}-multistep-dialog-left-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .#{bp.$ns}-multistep-dialog-right-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& .#{bp.$ns}-card {
|
||||
margin-block: bp.$pt-grid-size * 1.5;
|
||||
margin-inline: auto;
|
||||
padding: unset;
|
||||
border-radius: unset;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
|
||||
& img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,23 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { Reducer, combineReducers } from '@reduxjs/toolkit';
|
||||
import {
|
||||
firmwareDfuWindowsDriverInstallDialogDialogHide,
|
||||
firmwareDfuWindowsDriverInstallDialogDialogShow,
|
||||
} from './actions';
|
||||
|
||||
/** Controls the flash Pybricks firmware dialog open state. */
|
||||
const isOpen: Reducer<boolean> = (state = false, action) => {
|
||||
if (firmwareDfuWindowsDriverInstallDialogDialogShow.matches(action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (firmwareDfuWindowsDriverInstallDialogDialogHide.matches(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default combineReducers({ isOpen });
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"title": "Windows DFU USB driver installation instructions",
|
||||
"nextButton": {
|
||||
"label": "Next"
|
||||
},
|
||||
"doneButton": {
|
||||
"label": "Done"
|
||||
},
|
||||
"step": {
|
||||
"1": {
|
||||
"message": "Right-click the Windows {startMenu} to open the context menu, then click {deviceManager} to open the Windows Device Manager.",
|
||||
"startMenu": "Start Menu",
|
||||
"deviceManager": "Device Manager"
|
||||
},
|
||||
"2": {
|
||||
"message": "In the Device Manager window, look for {dfuUsbHubName} under {otherDevices}. If it is there, right-click it and select {properties}. If it is not there, then the driver is already installed and you do not need to continue.",
|
||||
"otherDevices": "Other Devices",
|
||||
"properties": "Properties"
|
||||
},
|
||||
"3": {
|
||||
"message": "In the {properties} dialog, click the {updateDriver} button.",
|
||||
"properties": "{dfuUsbHubName} Properties",
|
||||
"updateDriver": "Update Driver..."
|
||||
},
|
||||
"4": {
|
||||
"message": "In the {updateDrivers} dialog, click the {browse} button.",
|
||||
"updateDrivers": "Update Drivers",
|
||||
"browse": "Browse my computer for drivers"
|
||||
},
|
||||
"5": {
|
||||
"message": "Then click the {letMePick} button.",
|
||||
"letMePick": "Let me pick from a list of available drivers on my computer"
|
||||
},
|
||||
"6": {
|
||||
"message": "In the list of device types, select {usbDevices}, then click the {next} button.",
|
||||
"usbDevices": "Universal Serial Bus devices",
|
||||
"next": "Next"
|
||||
},
|
||||
"7": {
|
||||
"message": "In the {manufacturer} list, select {winUsbDevice}. Then in the {model} list, also select {winUsbDevice}. Then click the {next} button.",
|
||||
"manufacturer": "Manufacturer",
|
||||
"winUsbDevice": "WinUsb Device",
|
||||
"model": "Model",
|
||||
"next": "Next"
|
||||
},
|
||||
"8": {
|
||||
"message": "A warning dialog will open stating that the driver is not recommended. Click the {yes} button to use the driver anyway. The the driver is compatible.",
|
||||
"yes": "Yes"
|
||||
},
|
||||
"9": {
|
||||
"message": "The driver installation is complete. Click the {close} button.",
|
||||
"close": "Close"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@ type State = ReturnType<typeof reducers>;
|
||||
test('initial state', () => {
|
||||
expect(reducers(undefined, {} as AnyAction)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"dfuWindowsDriverInstallDialog": {
|
||||
"isOpen": false,
|
||||
},
|
||||
"flashing": false,
|
||||
"installPybricksDialog": {
|
||||
"isOpen": false,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import { Reducer, combineReducers } from 'redux';
|
||||
import { didFailToFinish, didFinish, didProgress, didStart } from './actions';
|
||||
import dfuWindowsDriverInstallDialog from './dfuWindowsDriverInstallDialog/reducers';
|
||||
import installPybricksDialog from './installPybricksDialog/reducers';
|
||||
import restoreOfficialDialog from './restoreOfficialDialog/reducers';
|
||||
|
||||
@@ -31,6 +32,7 @@ const progress: Reducer<number | null> = (state = null, action) => {
|
||||
};
|
||||
|
||||
export default combineReducers({
|
||||
dfuWindowsDriverInstallDialog,
|
||||
installPybricksDialog,
|
||||
restoreOfficialDialog,
|
||||
flashing,
|
||||
|
||||
Reference in New Issue
Block a user