From 042b0fd0cb3db8f1df1ea249f4d994e4b991a27b Mon Sep 17 00:00:00 2001 From: RadoslavDimchev Date: Thu, 7 Sep 2023 15:14:02 +0300 Subject: [PATCH] feat: add live autocomplete validation --- .../AutocompleteInput.tsx | 36 +++++++++++++++---- .../ConfirmRecoveryPhrase.tsx | 32 ++++++++++++++++- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/AutocompleteInput.tsx b/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/AutocompleteInput.tsx index 39b61f1c..ad475748 100644 --- a/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/AutocompleteInput.tsx +++ b/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/AutocompleteInput.tsx @@ -12,17 +12,20 @@ import styles from './AutocompleteInput.module.css' type AutocompleteInputProps = { index: number + isValid: boolean + changeIsValid: (isValid: boolean, index: number) => void } -const AutocompleteInput = ({ index }: AutocompleteInputProps) => { +const AutocompleteInput = ({ index, isValid, changeIsValid }: AutocompleteInputProps) => { const [suggestions, setSuggestions] = useState([]) const [isFocused, setIsFocused] = useState(false) const word = useSelector((state: RootState) => state.keyGeneration.words[index]) const dispatch = useDispatch() useEffect(() => { - setSuggestions(wordlist.filter(w => w.startsWith(word.toLowerCase()))) - }, [word]) + const newSuggestions = getNewSuggestions(word) + setSuggestions(newSuggestions) + }, []) const handleInputChange = (e: React.ChangeEvent) => { if (!isFocused) { @@ -35,6 +38,11 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => { if (mnemonicLength === 1) { dispatch(setWord({ index, word: value })) + + const newSuggestions = getNewSuggestions(value) + setSuggestions(newSuggestions) + + changeIsValid(isWordValidTyping(value, newSuggestions), index) } else if (mnemonicLength === 24) { dispatch(setMnemonic(mnemonic)) dispatch(setIsCopyPastedPhrase(true)) @@ -51,6 +59,8 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => { setIsFocused(false) dispatch(setWord({ index, word: suggestion })) + + changeIsValid(isWordValidBlur(suggestion), index) } const handleInputFocus = () => { @@ -59,6 +69,19 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => { const handleInputBlur = () => { setIsFocused(false) + changeIsValid(isWordValidBlur(word), index) + } + + const getNewSuggestions = (value: string) => { + return wordlist.filter(w => w.startsWith(value.toLowerCase())) + } + + const isWordValidTyping = (value: string, newSuggestions: string[]) => { + return wordlist.includes(value) || newSuggestions.length > 0 || value === '' + } + + const isWordValidBlur = (value: string) => { + return wordlist.includes(value) } return ( @@ -71,7 +94,7 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => { onChange={handleInputChange} onFocus={handleInputFocus} onBlur={handleInputBlur} - style={inputStyle(index, isFocused)} + style={inputStyle(index, isFocused, isValid)} />
@@ -92,16 +115,17 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => { export default AutocompleteInput -const inputStyle = (index: number, isFocused: boolean) => { +const inputStyle = (index: number, isFocused: boolean, isValid: boolean) => { const style = { outline: 'none', padding: `12px 16px 12px ${index + 1 < 10 ? '35px' : '45px'}`, + border: isValid ? 'none' : '2px solid #E53E3E', } if (isFocused) { return { ...style, - border: '2px solid #4360DF', + border: isValid ? '2px solid #4360DF' : '2px solid #E53E3E', backgroundColor: '#f1f2f4', } } else { diff --git a/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/ConfirmRecoveryPhrase.tsx b/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/ConfirmRecoveryPhrase.tsx index 20521db4..2f2903a6 100644 --- a/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/ConfirmRecoveryPhrase.tsx +++ b/src/pages/ValidatorOnboarding/KeyGeneration/ConfirmRecoveryPhrase/ConfirmRecoveryPhrase.tsx @@ -3,8 +3,33 @@ import { Text } from '@status-im/components' import AutocompleteInput from './AutocompleteInput' import KeyGenerationTitle from '../KeyGenerationTitle' +import { useState, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { setIsRightPhrase } from '../../../../redux/ValidatorOnboarding/KeyGeneration/slice' +import { RootState } from '../../../../redux/store' const ConfirmRecoveryPhrase = () => { + const [validWords, setValidWords] = useState([]) + const { words } = useSelector((state: RootState) => state.keyGeneration) + const dispatch = useDispatch() + + useEffect(() => { + const newValidWords = Array.from({ length: 24 }).map(() => true) + setValidWords(newValidWords) + }, []) + + useEffect(() => { + const isValidWords = validWords.every(isValid => isValid) + const isFilledWords = words.every(word => word !== '') + dispatch(setIsRightPhrase(isValidWords && isFilledWords)) + }, [validWords]) + + const changeIsValid = (isValid: boolean, index: number) => { + const newValidWords = [...validWords] + newValidWords[index] = isValid + setValidWords(newValidWords) + } + return ( @@ -19,7 +44,12 @@ const ConfirmRecoveryPhrase = () => { }} > {Array.from({ length: 24 }).map((_, index) => ( - + ))}