Skip to content

Commit

Permalink
feat(neuron-ui): add dynamic validation on the network editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY committed Aug 4, 2019
1 parent 9ff70c9 commit c634d5b
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 18 deletions.
33 changes: 25 additions & 8 deletions packages/neuron-ui/src/components/NetworkEditor/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,37 +77,54 @@ export const useInitialize = (
}, [dispatch, id, initialize, networks])
}

export const useInputs = (editor: EditorType) => {
export const useInputs = (editor: EditorType, usedNetworkNames: string[], t: any) => {
return useMemo(
() => [
{
...editor.remote,
label: i18n.t('settings.network.edit-network.rpc-url'),
tooltip: TooltipText.URL,
placeholder: PlaceHolder.URL,
onGetErrorMessage: (url: string) => {
if (!url) {
return t('messages.url-required')
}
if (!/^https?:\/\//.test(url)) {
return t('messages.rpc-url-should-have-protocol')
}
if (/\s/.test(url)) {
return t('messages.rpc-url-should-have-no-whitespaces')
}
return ''
},
},
{
...editor.name,
label: i18n.t('settings.network.edit-network.name'),
tooltip: TooltipText.Name,
placeholder: PlaceHolder.Name,
onGetErrorMessage: (name: string) => {
if (!name) {
return t('messages.name-required')
}
if (usedNetworkNames.includes(name)) {
return t('messages.network-name-used')
}
return ''
},
},
],
[editor.remote, editor.name]
[editor.remote, editor.name, usedNetworkNames, t]
)
}

export const useIsInputsValid = (editor: EditorType, cachedNetwork: State.Network | undefined) => {
const invalidParams = useMemo(() => !editor.name.value || !editor.remote.value, [
editor.name.value,
editor.remote.value,
])

const [errors, setErrors] = useState([!cachedNetwork && !editor.name.value, !cachedNetwork && !editor.remote.value])
const notModified = useMemo(
() => cachedNetwork && (cachedNetwork.name === editor.name.value && cachedNetwork.remote === editor.remote.value),
[cachedNetwork, editor.name.value, editor.remote.value]
)
return { invalidParams, notModified }
return { errors, setErrors, notModified }
}

export const useHandleSubmit = (
Expand Down
30 changes: 22 additions & 8 deletions packages/neuron-ui/src/components/NetworkEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef } from 'react'
import React, { useMemo, useRef } from 'react'
import { RouteComponentProps } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { Stack, PrimaryButton, DefaultButton, TextField } from 'office-ui-fabric-react'
Expand All @@ -17,28 +17,42 @@ const NetworkEditor = ({
}: React.PropsWithoutRef<StateWithDispatch & RouteComponentProps<{ id: string }>>) => {
const editor = useNetworkEditor()
const [t] = useTranslation()
const inputs = useInputs(editor)
const cachedNetworks = useRef(networks)
const cachedNetwork = cachedNetworks.current.find(network => network.id === id)
const usedNetworkNames = useMemo(
() => networks.map(n => n.name).filter(name => name !== ((cachedNetwork && cachedNetwork.name) || '')),
[networks]
)
const inputs = useInputs(editor, usedNetworkNames, t)
const goBack = useGoBack(history)
useInitialize(id, networks, editor.initialize, dispatch)

const cachedNetworks = useRef(networks)
const cachedNetwork = cachedNetworks.current.find(network => network.id === id)
const { invalidParams, notModified } = useIsInputsValid(editor, cachedNetwork)
const { errors, setErrors, notModified } = useIsInputsValid(editor, cachedNetwork)
const handleSubmit = useHandleSubmit(id, editor.name.value, editor.remote.value, networks, history, dispatch)

return (
<Stack tokens={{ childrenGap: 15 }}>
<h1>{t('settings.network.edit-network.title')}</h1>
<Stack tokens={{ childrenGap: 15 }}>
{inputs.map(inputProps => (
{inputs.map((inputProps, idx) => (
<Stack.Item key={inputProps.label}>
<TextField {...inputProps} key={inputProps.label} required />
<TextField
{...inputProps}
key={inputProps.label}
required
validateOnLoad={false}
onNotifyValidationResult={(msg: any) => {
const errs = [...errors]
errs.splice(idx, 1, msg !== '')
setErrors(errs)
}}
/>
</Stack.Item>
))}
</Stack>
<Stack horizontal horizontalAlign="end" tokens={{ childrenGap: 20 }}>
<DefaultButton onClick={goBack} text={t('common.cancel')} />
<PrimaryButton disabled={invalidParams || notModified} onClick={handleSubmit} text={t('common.save')} />
<PrimaryButton disabled={errors.includes(true) || notModified} onClick={handleSubmit} text={t('common.save')} />
</Stack>
</Stack>
)
Expand Down
4 changes: 3 additions & 1 deletion packages/neuron-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@
"addr-copied": "Address has been copied to the clipboard",
"qrcode-copied": "QR Code has been copied to the clipboard",
"lock-arg-copied": "Lock Arg has been copied to the clipboard",
"transaction-not-found": "The transaction is not found"
"transaction-not-found": "The transaction is not found",
"rpc-url-should-have-protocol": "The RPC URL should start with http(s)://",
"rpc-url-should-have-no-whitespaces": "The RPC URL should have no whitespaces"
},
"sync": {
"syncing": "Syncing",
Expand Down
4 changes: 3 additions & 1 deletion packages/neuron-ui/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@
"addr-copied": "地址已复制到剪贴板",
"qrcode-copied": "二维码已复制到剪贴板",
"lock-arg-copied": "Lock Arg 已复制到剪贴板",
"transaction-not-found": "未找到交易"
"transaction-not-found": "未找到交易",
"network-address-should-have-protocol": "RPC 地址应以 http(s)//: 开始",
"network-address-should-have-no-whitespaces": "RPC 地址不能包含空格"
},
"sync": {
"syncing": "同步中",
Expand Down

0 comments on commit c634d5b

Please sign in to comment.