mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-15 02:54:07 +00:00
components/hubPicker: back state with localStorage
This saves the selected hub in localStorage. This will "remember" the state between app launches and also synchronize the state between various users of the component. Both of these should mean fewer clicks required by the user.
This commit is contained in:
@@ -3,21 +3,16 @@
|
||||
|
||||
import { Radio, RadioGroup } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { useHubPickerSelectedHub } from './hooks';
|
||||
import { Hub } from '.';
|
||||
|
||||
type HubPickerProps = {
|
||||
hubType: Hub;
|
||||
onChange: (hubType: Hub) => void;
|
||||
};
|
||||
export const HubPicker: React.VoidFunctionComponent = () => {
|
||||
const [selectedHub, setSelectedHub] = useHubPickerSelectedHub();
|
||||
|
||||
export const HubPicker: React.VoidFunctionComponent<HubPickerProps> = ({
|
||||
hubType,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<RadioGroup
|
||||
selectedValue={hubType}
|
||||
onChange={(e) => onChange(e.currentTarget.value as Hub)}
|
||||
selectedValue={selectedHub}
|
||||
onChange={(e) => setSelectedHub(e.currentTarget.value as Hub)}
|
||||
>
|
||||
<Radio value={Hub.Move}>BOOST Move Hub</Radio>
|
||||
<Radio value={Hub.City}>City Hub</Radio>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { useLocalStorage } from 'usehooks-ts';
|
||||
import { Hub } from '.';
|
||||
|
||||
/**
|
||||
* Hook for {@link HubPicker} state backed by local storage.
|
||||
* @returns Tuple of the current state and setter (like useState()).
|
||||
*/
|
||||
export function useHubPickerSelectedHub() {
|
||||
return useLocalStorage('hubPicker.selectedHub', Hub.Move);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ describe('accept', () => {
|
||||
|
||||
await user.click(button);
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
newFileWizardDidAccept('test', '.py', Hub.Technic),
|
||||
newFileWizardDidAccept('test', '.py', Hub.Move),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('accept', () => {
|
||||
);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
newFileWizardDidAccept('test', '.py', Hub.Technic),
|
||||
newFileWizardDidAccept('test', '.py', Hub.Move),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Button, Classes, Dialog, FormGroup } from '@blueprintjs/core';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { useId } from 'react-aria';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Hub } from '../../components/hubPicker';
|
||||
import { HubPicker } from '../../components/hubPicker/HubPicker';
|
||||
import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks';
|
||||
import { useFileStorageMetadata } from '../../fileStorage/hooks';
|
||||
import {
|
||||
FileNameValidationResult,
|
||||
@@ -18,9 +18,6 @@ import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup';
|
||||
import { newFileWizardDidAccept, newFileWizardDidCancel } from './actions';
|
||||
import { useI18n } from './i18n';
|
||||
|
||||
// This should be set to the most commonly used hub.
|
||||
const defaultHub = Hub.Technic;
|
||||
|
||||
const NewFileWizard: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
const dispatch = useDispatch();
|
||||
@@ -33,8 +30,8 @@ const NewFileWizard: React.VoidFunctionComponent = () => {
|
||||
pythonFileExtension,
|
||||
files.map((f) => f.path),
|
||||
);
|
||||
const [hubType, setHubType] = useState(defaultHub);
|
||||
|
||||
const [hubType] = useHubPickerSelectedHub();
|
||||
const fileNameInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleSubmit = useCallback<React.FormEventHandler>(
|
||||
@@ -70,7 +67,7 @@ const NewFileWizard: React.VoidFunctionComponent = () => {
|
||||
onChange={setFileName}
|
||||
/>
|
||||
<FormGroup label={i18n.translate('smartHub.label')}>
|
||||
<HubPicker hubType={hubType} onChange={setHubType} />
|
||||
<HubPicker />
|
||||
</FormGroup>
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
hubHasUSB,
|
||||
} from '../../components/hubPicker';
|
||||
import { HubPicker } from '../../components/hubPicker/HubPicker';
|
||||
import { useHubPickerSelectedHub } from '../../components/hubPicker/hooks';
|
||||
import { FileMetadata } from '../../fileStorage';
|
||||
import { useFileStorageMetadata } from '../../fileStorage/hooks';
|
||||
import { useSelector } from '../../reducers';
|
||||
@@ -57,21 +58,13 @@ const dialogBody = classNames(
|
||||
'pb-firmware-installPybricksDialog-body',
|
||||
);
|
||||
|
||||
type SelectHubPanelProps = {
|
||||
hubType: Hub;
|
||||
onChange: (hubType: Hub) => void;
|
||||
};
|
||||
|
||||
const SelectHubPanel: React.VoidFunctionComponent<SelectHubPanelProps> = ({
|
||||
hubType,
|
||||
onChange,
|
||||
}) => {
|
||||
const SelectHubPanel: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
|
||||
return (
|
||||
<div className={dialogBody}>
|
||||
<p>{i18n.translate('selectHubPanel.message')}</p>
|
||||
<HubPicker hubType={hubType} onChange={onChange} />
|
||||
<HubPicker />
|
||||
<Popover2
|
||||
popoverClassName={Classes2.POPOVER2_CONTENT_SIZING}
|
||||
placement="right-end"
|
||||
@@ -436,16 +429,14 @@ const BootloaderModePanel: React.VoidFunctionComponent<BootloaderModePanelProps>
|
||||
);
|
||||
};
|
||||
|
||||
const defaultHubType = Hub.Technic;
|
||||
|
||||
export const InstallPybricksDialog: React.VoidFunctionComponent = () => {
|
||||
const { isOpen } = useSelector((s) => s.firmware.installPybricksDialog);
|
||||
const dispatch = useDispatch();
|
||||
const [hubType, setHubType] = useState(defaultHubType);
|
||||
const [hubName, setHubName] = useState('');
|
||||
const [includeProgram, setIncludeProgram] = useState(false);
|
||||
const [selectedIncludeFile, setSelectedIncludeFile] = useState<FileMetadata>();
|
||||
const [licenseAccepted, setLicenseAccepted] = useState(false);
|
||||
const [hubType] = useHubPickerSelectedHub();
|
||||
const { data } = useFirmware(hubType);
|
||||
const i18n = useI18n();
|
||||
|
||||
@@ -470,7 +461,7 @@ export const InstallPybricksDialog: React.VoidFunctionComponent = () => {
|
||||
<DialogStep
|
||||
id="hub"
|
||||
title={i18n.translate('selectHubPanel.title')}
|
||||
panel={<SelectHubPanel hubType={hubType} onChange={setHubType} />}
|
||||
panel={<SelectHubPanel />}
|
||||
nextButtonProps={{ text: i18n.translate('nextButton.label') }}
|
||||
/>
|
||||
<DialogStep
|
||||
|
||||
Reference in New Issue
Block a user