mirror of
https://github.com/status-im/nimbus-gui.git
synced 2025-02-23 16:48:13 +00:00
feat: add live autocomplete validation
This commit is contained in:
parent
1c52097350
commit
042b0fd0cb
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user