diff --git a/src/explorer/FileNameFormGroup.tsx b/src/explorer/FileNameFormGroup.tsx index c2154b8b..30f9032d 100644 --- a/src/explorer/FileNameFormGroup.tsx +++ b/src/explorer/FileNameFormGroup.tsx @@ -3,15 +3,14 @@ import { Classes, FormGroup, InputGroup, Intent, Tag } from '@blueprintjs/core'; import { useI18n } from '@shopify/react-i18n'; -import React, { useMemo } from 'react'; -import { FileNameValidationResult, validateFileName } from '../pybricksMicropython/lib'; -import { useSelector } from '../reducers'; +import React from 'react'; +import { FileNameValidationResult } from '../pybricksMicropython/lib'; import { NewFileWizardStringId } from './i18n'; import en from './i18n.en.json'; type FileNameHelpTextProps = { /** The result of the file name validation. */ - validation: Exclude; + validation: FileNameValidationResult; }; /** @@ -81,12 +80,12 @@ type FileNameFormGroupProps = { readonly fileName: string; /** The file extension (including leading ".") */ readonly fileExtension: string; + /** The result of the file name validation. */ + readonly validationResult: FileNameValidationResult; /** Ref to get handle to input (e.g to be able to call focus()) */ readonly inputRef?: React.RefObject; /** Called when the user changes the text in the input box. */ readonly onChange: (newName: string) => void; - /** Called when `fileName` is validated. */ - readonly onValidation: (result: FileNameValidationResult) => void; }; /** @@ -95,30 +94,22 @@ type FileNameFormGroupProps = { const FileNameFormGroup: React.VoidFunctionComponent = ({ fileName, fileExtension, + validationResult, inputRef, onChange, - onValidation, }) => { const [i18n] = useI18n({ id: 'explorer', translations: { en }, fallback: en }); - const fileNames = useSelector((s) => s.fileStorage.fileNames); - const [fileNameValidation, fileNameIntent] = useMemo(() => { - const result = validateFileName(fileName, fileExtension, fileNames); - - // can't call callback now because it would break react, so defer it - setTimeout(() => onValidation(result), 0); - - return [ - result, - result === FileNameValidationResult.IsOk ? Intent.NONE : Intent.DANGER, - ]; - }, [fileName, fileExtension, fileNames]); + const fileNameIntent = + validationResult === FileNameValidationResult.IsOk + ? Intent.NONE + : Intent.DANGER; return ( } + subLabel={} > = ({ const dispatch = useDispatch(); const [fileName, setFileName] = useState(''); - const [fileNameValidation, setFileNameValidation] = useState( - FileNameValidationResult.Unknown, + const fileNames = useSelector((s) => s.fileStorage.fileNames); + const fileNameValidation = validateFileName( + fileName, + pythonFileExtension, + fileNames, ); const [hubType, setHubType] = useState(defaultHub); @@ -59,9 +64,9 @@ const NewFileWizard: React.VoidFunctionComponent = ({ = ({ const [baseName, extension] = oldName.split(/(\.\w+)$/); const [newName, setNewName] = useState(baseName); - const [result, setResult] = useState(FileNameValidationResult.Unknown); + const fileNames = useSelector((s) => s.fileStorage.fileNames); + const result = validateFileName(newName, extension, fileNames); const inputRef = useRef(null); @@ -61,9 +63,9 @@ const RenameFileDialog: React.VoidFunctionComponent = ({
diff --git a/src/pybricksMicropython/lib.ts b/src/pybricksMicropython/lib.ts index 8e51d8bb..47a85fbb 100644 --- a/src/pybricksMicropython/lib.ts +++ b/src/pybricksMicropython/lib.ts @@ -12,8 +12,6 @@ export const pythonFileMimeType = 'text/x-python'; /** File name validation results. */ export enum FileNameValidationResult { - /** The result is not yet known. */ - Unknown, /** The file name is acceptable. */ IsOk, /** The file name is an empty string. */ @@ -42,7 +40,7 @@ export function validateFileName( fileName: string, extension: string, existingFiles: ReadonlyArray, -): Exclude { +): FileNameValidationResult { if (existingFiles.includes(`${fileName}${extension}`)) { return FileNameValidationResult.AlreadyExists; }