fix: component behavior for obfuscated fields

This commit is contained in:
José Oliveira 2021-02-12 16:02:54 +00:00 committed by Josh Harvey
parent fcfe16d5eb
commit 54a19b25c1
13 changed files with 233 additions and 42 deletions

View file

@ -4,11 +4,8 @@ import { TextInput } from '../base'
const SecretInput = memo(({ value, onFocus, onBlur, ...props }) => { const SecretInput = memo(({ value, onFocus, onBlur, ...props }) => {
const [focused, setFocused] = useState(false) const [focused, setFocused] = useState(false)
const isPasswordFilled = props.isPasswordFilled
const placeholder = '⚬ ⚬ ⚬ This field is set ⚬ ⚬ ⚬' const placeholder = '⚬ ⚬ ⚬ This field is set ⚬ ⚬ ⚬'
const previouslyFilled = !!value
const tempValue = previouslyFilled ? '' : value
const innerOnFocus = event => { const innerOnFocus = event => {
setFocused(true) setFocused(true)
onFocus && onFocus(event) onFocus && onFocus(event)
@ -26,9 +23,9 @@ const SecretInput = memo(({ value, onFocus, onBlur, ...props }) => {
onFocus={innerOnFocus} onFocus={innerOnFocus}
onBlur={innerOnBlur} onBlur={innerOnBlur}
value={value} value={value}
InputProps={{ value: !focused ? tempValue : value }} InputProps={{ value: value }}
InputLabelProps={{ shrink: previouslyFilled || focused }} InputLabelProps={{ shrink: isPasswordFilled || value || focused }}
placeholder={previouslyFilled ? placeholder : ''} placeholder={isPasswordFilled ? placeholder : ''}
/> />
) )
}) })

View file

@ -26,8 +26,9 @@ const TextInput = memo(
...props ...props
}) => { }) => {
const classes = useStyles({ textAlign, width, size }) const classes = useStyles({ textAlign, width, size })
const filled = !error && !R.isNil(value) && !R.isEmpty(value) const isPasswordFilled = props.isPasswordFilled
const isTextFilled = !error && !R.isNil(value) && !R.isEmpty(value)
const filled = isPasswordFilled || isTextFilled
const inputClasses = { const inputClasses = {
[classes.bold]: bold [classes.bold]: bold
} }

View file

@ -1,23 +1,24 @@
import React, { memo } from 'react' import React, { memo } from 'react'
import { SecretInput } from '../base' import { SecretInput } from '../base'
const SecretInputFormik = memo(({ ...props }) => { const SecretInputFormik = memo(({ ...props }) => {
const { name, onChange, onBlur, value } = props.field const { name, onChange, onBlur, value } = props.field
const { touched, errors } = props.form const { touched, errors } = props.form
const isPasswordFilled = props.isPasswordFilled
const error = !!(touched[name] && errors[name])
const error = !isPasswordFilled && !!(touched[name] && errors[name])
return (
<SecretInput return (
name={name} <SecretInput
onChange={onChange} name={name}
onBlur={onBlur} onChange={onChange}
value={value} onBlur={onBlur}
error={error} value={value}
{...props} error={error}
/> {...props}
) />
}) )
})
export default SecretInputFormik
export default SecretInputFormik

View file

@ -5,6 +5,7 @@ import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import Modal from 'src/components/Modal' import Modal from 'src/components/Modal'
import { SecretInput } from 'src/components/inputs/formik'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import SingleRowTable from 'src/components/single-row-table/SingleRowTable' import SingleRowTable from 'src/components/single-row-table/SingleRowTable'
import { formatLong } from 'src/utils/string' import { formatLong } from 'src/utils/string'
@ -56,6 +57,47 @@ const Services = () => {
}))(faceElements) }))(faceElements)
} }
const getElements = ({ code, elements }) => {
return R.compose(
R.map(elem => {
return elem.component === SecretInput
? R.assoc(
'inputProps',
{
isPasswordFilled:
!R.isNil(accounts[code]) &&
!R.isNil(R.path([elem.code], accounts[code]))
},
elem
)
: R.identity(elem)
})
)(elements)
}
const getAccounts = ({ elements, code }) => {
const account = accounts[code]
const passwordFields = R.compose(
R.reject(R.isNil),
R.map(({ component, code }) => (component === SecretInput ? code : null))
)(elements)
return R.compose(
R.mapObjIndexed((value, key, obj) =>
R.includes(key, passwordFields) ? '' : value
)
)(account)
}
const getValidationSchema = ({
validationSchema,
code,
hasSecret,
getValidationSchema
}) => {
if (!hasSecret) return validationSchema
return getValidationSchema(accounts[code])
}
return ( return (
<div className={classes.wrapper}> <div className={classes.wrapper}>
<TitleSection title="3rd Party Services" /> <TitleSection title="3rd Party Services" />
@ -83,9 +125,9 @@ const Services = () => {
variables: { accounts: { [editingSchema.code]: it } } variables: { accounts: { [editingSchema.code]: it } }
}) })
} }
elements={editingSchema.elements} elements={getElements(editingSchema)}
validationSchema={editingSchema.validationSchema} validationSchema={getValidationSchema(editingSchema)}
value={accounts[editingSchema.code]} value={getAccounts(editingSchema)}
/> />
</Modal> </Modal>
)} )}

View file

@ -6,11 +6,14 @@ import {
Autocomplete Autocomplete
} from 'src/components/inputs/formik' } from 'src/components/inputs/formik'
import secretTest from './helper'
const isDefined = it => it && it.length const isDefined = it => it && it.length
export default { export default {
code: 'bitgo', code: 'bitgo',
name: 'BitGo', name: 'BitGo',
hasSecret: true,
title: 'BitGo (Wallet)', title: 'BitGo (Wallet)',
elements: [ elements: [
{ {
@ -127,5 +130,51 @@ export default {
environment: Yup.string() environment: Yup.string()
.matches(/(prod|test)/) .matches(/(prod|test)/)
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
token: Yup.string()
.max(100, 'Too long')
.required(),
BTCWalletId: Yup.string().max(100, 'Too long'),
BTCWalletPassphrase: Yup.string()
.max(100, 'Too long')
.when('BTCWalletId', {
is: isDefined,
then: Yup.string().test(secretTest(account?.BTCWalletPassphrase))
}),
LTCWalletId: Yup.string().max(100, 'Too long'),
LTCWalletPassphrase: Yup.string()
.max(100, 'Too long')
.when('LTCWalletId', {
is: isDefined,
then: Yup.string().test(secretTest(account?.LTCWalletPassphrase))
}),
ZECWalletId: Yup.string().max(100, 'Too long'),
ZECWalletPassphrase: Yup.string()
.max(100, 'Too long')
.when('ZECWalletId', {
is: isDefined,
then: Yup.string().test(secretTest(account?.ZECWalletPassphrase))
}),
BCHWalletId: Yup.string().max(100, 'Too long'),
BCHWalletPassphrase: Yup.string()
.max(100, 'Too long')
.when('BCHWalletId', {
is: isDefined,
then: Yup.string().test(secretTest(account?.BCHWalletPassphrase))
}),
DASHWalletId: Yup.string().max(100, 'Too long'),
DASHWalletPassphrase: Yup.string()
.max(100, 'Too long')
.when('DASHWalletId', {
is: isDefined,
then: Yup.string().test(secretTest(account?.DASHWalletPassphrase))
}),
environment: Yup.string()
.matches(/(prod|test)/)
.required()
}
return Yup.object().shape(schema)
}
} }

View file

@ -3,9 +3,12 @@ import * as Yup from 'yup'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput' import TextInputFormik from 'src/components/inputs/formik/TextInput'
import secretTest from './helper'
export default { export default {
code: 'bitstamp', code: 'bitstamp',
name: 'Bitstamp', name: 'Bitstamp',
hasSecret: true,
title: 'Bitstamp (Exchange)', title: 'Bitstamp (Exchange)',
elements: [ elements: [
{ {
@ -28,7 +31,6 @@ export default {
component: SecretInputFormik component: SecretInputFormik
} }
], ],
validationSchema: Yup.object().shape({ validationSchema: Yup.object().shape({
clientId: Yup.string() clientId: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
@ -39,5 +41,19 @@ export default {
secret: Yup.string() secret: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
clientId: Yup.string()
.max(100, 'Too long')
.required(),
key: Yup.string()
.max(100, 'Too long')
.required(),
secret: Yup.string()
.max(100, 'Too long')
.test(secretTest(account?.secret))
}
return Yup.object().shape(schema)
}
} }

View file

@ -5,6 +5,7 @@ import TextInputFormik from 'src/components/inputs/formik/TextInput'
export default { export default {
code: 'blockcypher', code: 'blockcypher',
name: 'Blockcypher', name: 'Blockcypher',
hasSecret: false,
title: 'Blockcypher (Payments)', title: 'Blockcypher (Payments)',
elements: [ elements: [
{ {

View file

@ -0,0 +1,12 @@
import * as R from 'ramda'
const secretTest = secret => ({
test(val) {
if (R.isNil(secret) && R.isNil(val)) {
return this.createError()
}
return true
}
})
export default secretTest

View file

@ -3,9 +3,12 @@ import * as Yup from 'yup'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput' import TextInputFormik from 'src/components/inputs/formik/TextInput'
import secretTest from './helper'
export default { export default {
code: 'infura', code: 'infura',
name: 'Infura', name: 'Infura',
hasSecret: true,
title: 'Infura (Wallet)', title: 'Infura (Wallet)',
elements: [ elements: [
{ {
@ -37,5 +40,19 @@ export default {
endpoint: Yup.string() endpoint: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
apiKey: Yup.string()
.max(100, 'Too long')
.required(),
apiSecret: Yup.string()
.max(100, 'Too long')
.test(secretTest(account?.apiSecret)),
endpoint: Yup.string()
.max(100, 'Too long')
.required()
}
return Yup.object().shape(schema)
}
} }

View file

@ -3,9 +3,12 @@ import * as Yup from 'yup'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput' import TextInputFormik from 'src/components/inputs/formik/TextInput'
import secretTest from './helper'
export default { export default {
code: 'itbit', code: 'itbit',
name: 'itBit', name: 'itBit',
hasSecret: true,
title: 'itBit (Exchange)', title: 'itBit (Exchange)',
elements: [ elements: [
{ {
@ -46,5 +49,22 @@ export default {
clientSecret: Yup.string() clientSecret: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
userId: Yup.string()
.max(100, 'Too long')
.required(),
walletId: Yup.string()
.max(100, 'Too long')
.required(),
clientKey: Yup.string()
.max(100, 'Too long')
.required(),
clientSecret: Yup.string()
.max(100, 'Too long')
.test(secretTest(account?.clientSecret))
}
return Yup.object().shape(schema)
}
} }

View file

@ -3,9 +3,12 @@ import * as Yup from 'yup'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput' import TextInputFormik from 'src/components/inputs/formik/TextInput'
import secretTest from './helper'
export default { export default {
code: 'kraken', code: 'kraken',
name: 'Kraken', name: 'Kraken',
hasSecret: true,
title: 'Kraken (Exchange)', title: 'Kraken (Exchange)',
elements: [ elements: [
{ {
@ -28,5 +31,16 @@ export default {
privateKey: Yup.string() privateKey: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
apiKey: Yup.string()
.max(100, 'Too long')
.required(),
privateKey: Yup.string()
.max(100, 'Too long')
.test(secretTest(account?.privateKey))
}
return Yup.object().shape(schema)
}
} }

View file

@ -5,6 +5,7 @@ import TextInputFormik from 'src/components/inputs/formik/TextInput'
export default { export default {
code: 'mailgun', code: 'mailgun',
name: 'Mailgun', name: 'Mailgun',
hasSecret: false,
title: 'Mailgun (Email)', title: 'Mailgun (Email)',
elements: [ elements: [
{ {

View file

@ -3,9 +3,12 @@ import * as Yup from 'yup'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput' import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput' import TextInputFormik from 'src/components/inputs/formik/TextInput'
import secretTest from './helper'
export default { export default {
code: 'twilio', code: 'twilio',
name: 'Twilio', name: 'Twilio',
hasSecret: true,
title: 'Twilio (SMS)', title: 'Twilio (SMS)',
elements: [ elements: [
{ {
@ -44,5 +47,22 @@ export default {
toNumber: Yup.string() toNumber: Yup.string()
.max(100, 'Too long') .max(100, 'Too long')
.required() .required()
}) }),
getValidationSchema: account => {
const schema = {
accountSid: Yup.string()
.max(100, 'Too long')
.required(),
authToken: Yup.string()
.max(100, 'Too long')
.test(secretTest(account?.authToken)),
fromNumber: Yup.string()
.max(100, 'Too long')
.required(),
toNumber: Yup.string()
.max(100, 'Too long')
.required()
}
return Yup.object().shape(schema)
}
} }