From 39538163500599b3b9a22f842f5c7bcab3a216f6 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 27 Jul 2022 15:30:39 -0500 Subject: [PATCH] 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. --- src/components/hubPicker/HubPicker.tsx | 15 +++++---------- src/components/hubPicker/hooks.ts | 13 +++++++++++++ .../newFileWizard/NewFileWizard.test.tsx | 4 ++-- src/explorer/newFileWizard/NewFileWizard.tsx | 9 +++------ .../InstallPybricksDialog.tsx | 19 +++++-------------- 5 files changed, 28 insertions(+), 32 deletions(-) create mode 100644 src/components/hubPicker/hooks.ts diff --git a/src/components/hubPicker/HubPicker.tsx b/src/components/hubPicker/HubPicker.tsx index 13889395..b44aaa77 100644 --- a/src/components/hubPicker/HubPicker.tsx +++ b/src/components/hubPicker/HubPicker.tsx @@ -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 = ({ - hubType, - onChange, -}) => { return ( onChange(e.currentTarget.value as Hub)} + selectedValue={selectedHub} + onChange={(e) => setSelectedHub(e.currentTarget.value as Hub)} > BOOST Move Hub City Hub diff --git a/src/components/hubPicker/hooks.ts b/src/components/hubPicker/hooks.ts new file mode 100644 index 00000000..a2da107c --- /dev/null +++ b/src/components/hubPicker/hooks.ts @@ -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); +} diff --git a/src/explorer/newFileWizard/NewFileWizard.test.tsx b/src/explorer/newFileWizard/NewFileWizard.test.tsx index 5b1ba4ac..909a8b1b 100644 --- a/src/explorer/newFileWizard/NewFileWizard.test.tsx +++ b/src/explorer/newFileWizard/NewFileWizard.test.tsx @@ -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), ); }); }); diff --git a/src/explorer/newFileWizard/NewFileWizard.tsx b/src/explorer/newFileWizard/NewFileWizard.tsx index 09d54a82..ec4dc635 100644 --- a/src/explorer/newFileWizard/NewFileWizard.tsx +++ b/src/explorer/newFileWizard/NewFileWizard.tsx @@ -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(null); const handleSubmit = useCallback( @@ -70,7 +67,7 @@ const NewFileWizard: React.VoidFunctionComponent = () => { onChange={setFileName} /> - +
diff --git a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx index ec29e1ec..d707c7e0 100644 --- a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx +++ b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx @@ -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 = ({ - hubType, - onChange, -}) => { +const SelectHubPanel: React.VoidFunctionComponent = () => { const i18n = useI18n(); return (

{i18n.translate('selectHubPanel.message')}

- + ); }; -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(); const [licenseAccepted, setLicenseAccepted] = useState(false); + const [hubType] = useHubPickerSelectedHub(); const { data } = useFirmware(hubType); const i18n = useI18n(); @@ -470,7 +461,7 @@ export const InstallPybricksDialog: React.VoidFunctionComponent = () => { } + panel={} nextButtonProps={{ text: i18n.translate('nextButton.label') }} />