Add sort and search to AddRecipient accounts list (#9257)

* sort and search accounts in AddRecipient component

* Update AddRecipient unit test

Co-authored-by: Erik Marks <rekmarks@protonmail.com>
feature/default_network_editable
Patryk Łucka 4 years ago committed by GitHub
parent ee205b893f
commit 128efc5b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      ui/app/pages/send/send-content/add-recipient/add-recipient.component.js
  2. 10
      ui/app/pages/send/send-content/add-recipient/add-recipient.container.js
  3. 18
      ui/app/pages/send/send-content/add-recipient/ens-input.component.js
  4. 11
      ui/app/pages/send/send-content/add-recipient/tests/add-recipient-container.test.js
  5. 31
      ui/app/pages/send/send.component.js
  6. 6
      ui/app/pages/send/tests/send-component.test.js

@ -22,6 +22,7 @@ export default class AddRecipient extends Component {
addressBookEntryName: PropTypes.string, addressBookEntryName: PropTypes.string,
contacts: PropTypes.array, contacts: PropTypes.array,
nonContacts: PropTypes.array, nonContacts: PropTypes.array,
setInternalSearch: PropTypes.func,
} }
constructor(props) { constructor(props) {
@ -140,15 +141,28 @@ export default class AddRecipient extends Component {
} }
renderTransfer() { renderTransfer() {
const { ownedAccounts } = this.props let { ownedAccounts } = this.props
const { query, setInternalSearch } = this.props
const { t } = this.context const { t } = this.context
const { isShowingTransfer } = this.state
if (isShowingTransfer && query) {
ownedAccounts = ownedAccounts.filter(
(item) =>
item.name.toLowerCase().indexOf(query.toLowerCase()) > -1 ||
item.address.toLowerCase().indexOf(query.toLowerCase()) > -1,
)
}
return ( return (
<div className="send__select-recipient-wrapper__list"> <div className="send__select-recipient-wrapper__list">
<Button <Button
type="link" type="link"
className="send__select-recipient-wrapper__list__link" className="send__select-recipient-wrapper__list__link"
onClick={() => this.setState({ isShowingTransfer: false })} onClick={() => {
setInternalSearch(false)
this.setState({ isShowingTransfer: false })
}}
> >
<div className="send__select-recipient-wrapper__list__back-caret" /> <div className="send__select-recipient-wrapper__list__back-caret" />
{t('backToAll')} {t('backToAll')}
@ -164,7 +178,12 @@ export default class AddRecipient extends Component {
renderMain() { renderMain() {
const { t } = this.context const { t } = this.context
const { query, ownedAccounts = [], addressBook } = this.props const {
query,
ownedAccounts = [],
addressBook,
setInternalSearch,
} = this.props
return ( return (
<div className="send__select-recipient-wrapper__list"> <div className="send__select-recipient-wrapper__list">
@ -178,7 +197,10 @@ export default class AddRecipient extends Component {
<Button <Button
type="link" type="link"
className="send__select-recipient-wrapper__list__link" className="send__select-recipient-wrapper__list__link"
onClick={() => this.setState({ isShowingTransfer: true })} onClick={() => {
setInternalSearch(true)
this.setState({ isShowingTransfer: true })
}}
> >
{t('transferBetweenAccounts')} {t('transferBetweenAccounts')}
</Button> </Button>

@ -23,14 +23,18 @@ function mapStateToProps(state) {
const addressBook = getAddressBook(state) const addressBook = getAddressBook(state)
const ownedAccounts = accountsWithSendEtherInfoSelector(state).sort((a, b) =>
a.name.localeCompare(b.name),
)
return { return {
ownedAccounts: accountsWithSendEtherInfoSelector(state),
addressBook, addressBook,
ensResolution,
addressBookEntryName, addressBookEntryName,
ensResolutionError: getSendEnsResolutionError(state),
contacts: addressBook.filter(({ name }) => Boolean(name)), contacts: addressBook.filter(({ name }) => Boolean(name)),
ensResolution,
ensResolutionError: getSendEnsResolutionError(state),
nonContacts: addressBook.filter(({ name }) => !name), nonContacts: addressBook.filter(({ name }) => !name),
ownedAccounts,
} }
} }

@ -38,6 +38,7 @@ export default class EnsInput extends Component {
onValidAddressTyped: PropTypes.func, onValidAddressTyped: PropTypes.func,
contact: PropTypes.object, contact: PropTypes.object,
value: PropTypes.string, value: PropTypes.string,
internalSearch: PropTypes.bool,
} }
state = { state = {
@ -47,11 +48,11 @@ export default class EnsInput extends Component {
} }
componentDidMount() { componentDidMount() {
const { network } = this.props const { network, internalSearch } = this.props
const networkHasEnsSupport = getNetworkEnsSupport(network) const networkHasEnsSupport = getNetworkEnsSupport(network)
this.setState({ ensResolution: ZERO_ADDRESS }) this.setState({ ensResolution: ZERO_ADDRESS })
if (networkHasEnsSupport) { if (networkHasEnsSupport && !internalSearch) {
const provider = global.ethereumProvider const provider = global.ethereumProvider
this.ens = new ENS({ provider, network }) this.ens = new ENS({ provider, network })
this.checkName = debounce(this.lookupEnsName, 200) this.checkName = debounce(this.lookupEnsName, 200)
@ -60,7 +61,7 @@ export default class EnsInput extends Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { input } = this.state const { input } = this.state
const { network, value } = this.props const { network, value, internalSearch } = this.props
let newValue let newValue
@ -81,6 +82,9 @@ export default class EnsInput extends Component {
if (newValue !== undefined) { if (newValue !== undefined) {
this.onChange({ target: { value: newValue } }) this.onChange({ target: { value: newValue } })
} }
if (!internalSearch && prevProps.internalSearch) {
this.resetInput()
}
} }
resetInput = () => { resetInput = () => {
@ -143,12 +147,15 @@ export default class EnsInput extends Component {
updateEnsResolution, updateEnsResolution,
updateEnsResolutionError, updateEnsResolutionError,
onValidAddressTyped, onValidAddressTyped,
internalSearch,
} = this.props } = this.props
const input = e.target.value const input = e.target.value
const networkHasEnsSupport = getNetworkEnsSupport(network) const networkHasEnsSupport = getNetworkEnsSupport(network)
this.setState({ input }, () => onChange(input)) this.setState({ input }, () => onChange(input))
if (internalSearch) {
return null
}
// Empty ENS state if input is empty // Empty ENS state if input is empty
// maybe scan ENS // maybe scan ENS
@ -161,7 +168,7 @@ export default class EnsInput extends Component {
updateEnsResolutionError( updateEnsResolutionError(
networkHasEnsSupport ? '' : 'Network does not support ENS', networkHasEnsSupport ? '' : 'Network does not support ENS',
) )
return return null
} }
if (isValidDomainName(input)) { if (isValidDomainName(input)) {
@ -172,6 +179,7 @@ export default class EnsInput extends Component {
updateEnsResolution('') updateEnsResolution('')
updateEnsResolutionError('') updateEnsResolutionError('')
} }
return null
} }
render() { render() {

@ -22,8 +22,10 @@ proxyquire('../add-recipient.container.js', {
getSendEnsResolutionError: (s) => `mockSendEnsResolutionError:${s}`, getSendEnsResolutionError: (s) => `mockSendEnsResolutionError:${s}`,
getAddressBook: (s) => [{ name: `mockAddressBook:${s}` }], getAddressBook: (s) => [{ name: `mockAddressBook:${s}` }],
getAddressBookEntry: (s) => `mockAddressBookEntry:${s}`, getAddressBookEntry: (s) => `mockAddressBookEntry:${s}`,
accountsWithSendEtherInfoSelector: (s) => accountsWithSendEtherInfoSelector: (s) => [
`mockAccountsWithSendEtherInfoSelector:${s}`, { name: `account2:${s}` },
{ name: `account1:${s}` },
],
}, },
'../../../../store/actions': actionSpies, '../../../../store/actions': actionSpies,
}) })
@ -36,7 +38,10 @@ describe('add-recipient container', function () {
contacts: [{ name: 'mockAddressBook:mockState' }], contacts: [{ name: 'mockAddressBook:mockState' }],
ensResolution: 'mockSendEnsResolution:mockState', ensResolution: 'mockSendEnsResolution:mockState',
ensResolutionError: 'mockSendEnsResolutionError:mockState', ensResolutionError: 'mockSendEnsResolutionError:mockState',
ownedAccounts: 'mockAccountsWithSendEtherInfoSelector:mockState', ownedAccounts: [
{ name: `account1:mockState` },
{ name: `account2:mockState` },
],
addressBookEntryName: undefined, addressBookEntryName: undefined,
nonContacts: [], nonContacts: [],
}) })

@ -63,6 +63,7 @@ export default class SendTransactionScreen extends Component {
query: '', query: '',
toError: null, toError: null,
toWarning: null, toWarning: null,
internalSearch: false,
} }
constructor(props) { constructor(props) {
@ -204,21 +205,30 @@ export default class SendTransactionScreen extends Component {
} }
onRecipientInputChange = (query) => { onRecipientInputChange = (query) => {
if (query) { const { internalSearch } = this.state
this.dValidate(query)
} else { if (!internalSearch) {
this.validate(query) if (query) {
this.dValidate(query)
} else {
this.dValidate.cancel()
this.validate(query)
}
} }
this.setState({ this.setState({ query })
query, }
})
setInternalSearch(internalSearch) {
this.setState({ query: '', internalSearch })
} }
validate(query) { validate(query) {
const { hasHexData, tokens, sendToken, network } = this.props const { hasHexData, tokens, sendToken, network } = this.props
if (!query) { const { internalSearch } = this.state
if (!query || internalSearch) {
this.setState({ toError: '', toWarning: '' }) this.setState({ toError: '', toWarning: '' })
return return
} }
@ -293,6 +303,7 @@ export default class SendTransactionScreen extends Component {
} }
renderInput() { renderInput() {
const { internalSearch } = this.state
return ( return (
<EnsInput <EnsInput
className="send__to-row" className="send__to-row"
@ -314,6 +325,7 @@ export default class SendTransactionScreen extends Component {
onReset={() => this.props.updateSendTo('', '')} onReset={() => this.props.updateSendTo('', '')}
updateEnsResolution={this.props.updateSendEnsResolution} updateEnsResolution={this.props.updateSendEnsResolution}
updateEnsResolutionError={this.props.updateSendEnsResolutionError} updateEnsResolutionError={this.props.updateSendEnsResolutionError}
internalSearch={internalSearch}
/> />
) )
} }
@ -327,6 +339,9 @@ export default class SendTransactionScreen extends Component {
} }
query={this.state.query} query={this.state.query}
toError={toError} toError={toError}
setInternalSearch={(internalSearch) =>
this.setInternalSearch(internalSearch)
}
/> />
) )
} }

@ -435,6 +435,7 @@ describe('Send Component', function () {
) )
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '0x80F061544cC398520615B5d3e7A3BedD70cd4510', query: '0x80F061544cC398520615B5d3e7A3BedD70cd4510',
toError: null, toError: null,
toWarning: null, toWarning: null,
@ -449,6 +450,7 @@ describe('Send Component', function () {
clock.tick(1001) clock.tick(1001)
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510', query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510',
toError: 'invalidAddressRecipient', toError: 'invalidAddressRecipient',
toWarning: null, toWarning: null,
@ -464,6 +466,7 @@ describe('Send Component', function () {
clock.tick(1001) clock.tick(1001)
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510', query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510',
toError: 'invalidAddressRecipientNotEthNetwork', toError: 'invalidAddressRecipientNotEthNetwork',
toWarning: null, toWarning: null,
@ -479,6 +482,7 @@ describe('Send Component', function () {
clock.tick(1001) clock.tick(1001)
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510', query: '0x80F061544cC398520615B5d3e7a3BedD70cd4510',
toError: 'invalidAddressRecipientNotEthNetwork', toError: 'invalidAddressRecipientNotEthNetwork',
toWarning: null, toWarning: null,
@ -486,6 +490,7 @@ describe('Send Component', function () {
instance.onRecipientInputChange('') instance.onRecipientInputChange('')
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '', query: '',
toError: '', toError: '',
toWarning: '', toWarning: '',
@ -501,6 +506,7 @@ describe('Send Component', function () {
clock.tick(1001) clock.tick(1001)
assert.deepEqual(instance.state, { assert.deepEqual(instance.state, {
internalSearch: false,
query: '0x13cb85823f78Cff38f0B0E90D3e975b8CB3AAd64', query: '0x13cb85823f78Cff38f0B0E90D3e975b8CB3AAd64',
toError: null, toError: null,
toWarning: 'knownAddressRecipient', toWarning: 'knownAddressRecipient',

Loading…
Cancel
Save