feat: add live autocomplete validation

This commit is contained in:
RadoslavDimchev 2023-09-07 15:14:02 +03:00
parent 1c52097350
commit 042b0fd0cb
2 changed files with 61 additions and 7 deletions

View File

@ -12,17 +12,20 @@ import styles from './AutocompleteInput.module.css'
type AutocompleteInputProps = { type AutocompleteInputProps = {
index: number index: number
isValid: boolean
changeIsValid: (isValid: boolean, index: number) => void
} }
const AutocompleteInput = ({ index }: AutocompleteInputProps) => { const AutocompleteInput = ({ index, isValid, changeIsValid }: AutocompleteInputProps) => {
const [suggestions, setSuggestions] = useState<string[]>([]) const [suggestions, setSuggestions] = useState<string[]>([])
const [isFocused, setIsFocused] = useState(false) const [isFocused, setIsFocused] = useState(false)
const word = useSelector((state: RootState) => state.keyGeneration.words[index]) const word = useSelector((state: RootState) => state.keyGeneration.words[index])
const dispatch = useDispatch() const dispatch = useDispatch()
useEffect(() => { useEffect(() => {
setSuggestions(wordlist.filter(w => w.startsWith(word.toLowerCase()))) const newSuggestions = getNewSuggestions(word)
}, [word]) setSuggestions(newSuggestions)
}, [])
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!isFocused) { if (!isFocused) {
@ -35,6 +38,11 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => {
if (mnemonicLength === 1) { if (mnemonicLength === 1) {
dispatch(setWord({ index, word: value })) dispatch(setWord({ index, word: value }))
const newSuggestions = getNewSuggestions(value)
setSuggestions(newSuggestions)
changeIsValid(isWordValidTyping(value, newSuggestions), index)
} else if (mnemonicLength === 24) { } else if (mnemonicLength === 24) {
dispatch(setMnemonic(mnemonic)) dispatch(setMnemonic(mnemonic))
dispatch(setIsCopyPastedPhrase(true)) dispatch(setIsCopyPastedPhrase(true))
@ -51,6 +59,8 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => {
setIsFocused(false) setIsFocused(false)
dispatch(setWord({ index, word: suggestion })) dispatch(setWord({ index, word: suggestion }))
changeIsValid(isWordValidBlur(suggestion), index)
} }
const handleInputFocus = () => { const handleInputFocus = () => {
@ -59,6 +69,19 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => {
const handleInputBlur = () => { const handleInputBlur = () => {
setIsFocused(false) 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 ( return (
@ -71,7 +94,7 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => {
onChange={handleInputChange} onChange={handleInputChange}
onFocus={handleInputFocus} onFocus={handleInputFocus}
onBlur={handleInputBlur} onBlur={handleInputBlur}
style={inputStyle(index, isFocused)} style={inputStyle(index, isFocused, isValid)}
/> />
</div> </div>
<div className={isFocused ? styles['suggestion-list'] : ''}> <div className={isFocused ? styles['suggestion-list'] : ''}>
@ -92,16 +115,17 @@ const AutocompleteInput = ({ index }: AutocompleteInputProps) => {
export default AutocompleteInput export default AutocompleteInput
const inputStyle = (index: number, isFocused: boolean) => { const inputStyle = (index: number, isFocused: boolean, isValid: boolean) => {
const style = { const style = {
outline: 'none', outline: 'none',
padding: `12px 16px 12px ${index + 1 < 10 ? '35px' : '45px'}`, padding: `12px 16px 12px ${index + 1 < 10 ? '35px' : '45px'}`,
border: isValid ? 'none' : '2px solid #E53E3E',
} }
if (isFocused) { if (isFocused) {
return { return {
...style, ...style,
border: '2px solid #4360DF', border: isValid ? '2px solid #4360DF' : '2px solid #E53E3E',
backgroundColor: '#f1f2f4', backgroundColor: '#f1f2f4',
} }
} else { } else {

View File

@ -3,8 +3,33 @@ import { Text } from '@status-im/components'
import AutocompleteInput from './AutocompleteInput' import AutocompleteInput from './AutocompleteInput'
import KeyGenerationTitle from '../KeyGenerationTitle' 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 ConfirmRecoveryPhrase = () => {
const [validWords, setValidWords] = useState<boolean[]>([])
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 ( return (
<YStack space={'$4'} style={{ width: '100%', marginTop: '20px' }}> <YStack space={'$4'} style={{ width: '100%', marginTop: '20px' }}>
<KeyGenerationTitle /> <KeyGenerationTitle />
@ -19,7 +44,12 @@ const ConfirmRecoveryPhrase = () => {
}} }}
> >
{Array.from({ length: 24 }).map((_, index) => ( {Array.from({ length: 24 }).map((_, index) => (
<AutocompleteInput key={index} index={index} /> <AutocompleteInput
key={index}
index={index}
isValid={validWords[index]}
changeIsValid={changeIsValid}
/>
))} ))}
</Stack> </Stack>
</YStack> </YStack>