diff --git a/app/components/Contacts/ContactsForm.js b/app/components/Contacts/ContactsForm.js
index 5a0955a2..51a8d597 100644
--- a/app/components/Contacts/ContactsForm.js
+++ b/app/components/Contacts/ContactsForm.js
@@ -10,29 +10,30 @@ class ContactsForm extends React.Component {
super(props)
this.state = {
- editing: false,
- manualFormInput: ''
+ editing: false
}
}
render() {
const {
contactsform,
+ contactsform: { showErrors },
closeContactsForm,
updateContactFormSearchQuery,
+ updateManualFormSearchQuery,
updateContactCapacity,
openChannel,
-
+ updateManualFormErrors,
activeChannelPubkeys,
nonActiveChannelPubkeys,
pendingOpenChannelPubkeys,
filteredNetworkNodes,
loadingChannelPubkeys,
- showManualForm
+ showManualForm,
+ manualFormIsValid
} = this.props
- const { editing, manualFormInput } = this.state
-
+ const { editing } = this.state
const renderRightSide = (node) => {
if (loadingChannelPubkeys.includes(node.pub_key)) {
@@ -94,15 +95,21 @@ class ContactsForm extends React.Component {
this.setState({ editing: true })
}
+
const manualFormSubmit = () => {
- if (!manualFormInput.length) { return }
- if (!manualFormInput.includes('@')) { return }
+ if (!manualFormIsValid.isValid) {
+ updateManualFormErrors(manualFormIsValid.errors)
+ updateManualFormSearchQuery('')
+ return
+ }
+ // clear any existing errors
- const [pubkey, host] = manualFormInput && manualFormInput.split('@')
+ updateManualFormErrors({ manualInput: null })
+ const [pubkey, host] = contactsform.manualSearchQuery && contactsform.manualSearchQuery.split('@')
openChannel({ pubkey, host, local_amt: contactsform.contactCapacity })
- this.setState({ manualFormInput: '' })
+ updateManualFormSearchQuery('')
}
return (
@@ -170,8 +177,8 @@ class ContactsForm extends React.Component {
this.setState({ manualFormInput: event.target.value })}
+ value={contactsform.manualSearchQuery}
+ onChange={event => updateManualFormSearchQuery(event.target.value)}
/>
Submit
@@ -184,6 +191,12 @@ class ContactsForm extends React.Component {
}
+
+
+ {showErrors.manualInput &&
+ {manualFormIsValid && manualFormIsValid.errors.manualInput}
+ }
+
}
@@ -220,14 +233,18 @@ class ContactsForm extends React.Component {
}
}
-
ContactsForm.propTypes = {
contactsform: PropTypes.object.isRequired,
closeContactsForm: PropTypes.func.isRequired,
updateContactFormSearchQuery: PropTypes.func.isRequired,
+ updateManualFormSearchQuery: PropTypes.func.isRequired,
+ manualFormIsValid: PropTypes.shape({
+ errors: PropTypes.object,
+ isValid: PropTypes.bool
+ }).isRequired,
updateContactCapacity: PropTypes.func.isRequired,
+ updateManualFormErrors: PropTypes.func.isRequired,
openChannel: PropTypes.func.isRequired,
-
activeChannelPubkeys: PropTypes.array.isRequired,
nonActiveChannelPubkeys: PropTypes.array.isRequired,
pendingOpenChannelPubkeys: PropTypes.array.isRequired,
diff --git a/app/components/Contacts/ContactsForm.scss b/app/components/Contacts/ContactsForm.scss
index 6b33b320..3c6b7a45 100644
--- a/app/components/Contacts/ContactsForm.scss
+++ b/app/components/Contacts/ContactsForm.scss
@@ -161,6 +161,18 @@
}
}
+.errorMessage {
+ margin: 10px 0;
+ min-height: 20px;
+ color: $red;
+ opacity: 0;
+ transition: all 0.25s ease;
+
+ &.active {
+ opacity: 1;
+ }
+}
+
.footer {
padding: 10px 15px;
border-top: 1px solid $darkgrey;
@@ -243,5 +255,4 @@
-moz-animation: animation-rotate 1000ms linear infinite;
-o-animation: animation-rotate 1000ms linear infinite;
animation: animation-rotate 1000ms linear infinite;
-}
-
+}
\ No newline at end of file
diff --git a/app/reducers/contactsform.js b/app/reducers/contactsform.js
index 40e2a777..5e5dd17b 100644
--- a/app/reducers/contactsform.js
+++ b/app/reducers/contactsform.js
@@ -1,12 +1,16 @@
import { createSelector } from 'reselect'
-
import filter from 'lodash/filter'
+import isEmpty from 'lodash/isEmpty'
// Initial State
const initialState = {
isOpen: false,
searchQuery: '',
- contactCapacity: 0.1
+ manualSearchQuery: '',
+ contactCapacity: 0.1,
+ showErrors: {
+ manualInput: false
+ }
}
// Constants
@@ -18,6 +22,10 @@ export const UPDATE_CONTACT_FORM_SEARCH_QUERY = 'UPDATE_CONTACT_FORM_SEARCH_QUER
export const UPDATE_CONTACT_CAPACITY = 'UPDATE_CONTACT_CAPACITY'
+export const UPDATE_MANUAL_FORM_ERRORS = 'UPDATE_MANUAL_FORM_ERRORS'
+
+export const UPDATE_MANUAL_FORM_SEARCH_QUERY = 'UPDATE_MANUAL_FORM_SEARCH_QUERY'
+
// ------------------------------------
// Actions
// ------------------------------------
@@ -40,6 +48,13 @@ export function updateContactFormSearchQuery(searchQuery) {
}
}
+export function updateManualFormSearchQuery(manualSearchQuery) {
+ return {
+ type: UPDATE_MANUAL_FORM_SEARCH_QUERY,
+ manualSearchQuery
+ }
+}
+
export function updateContactCapacity(contactCapacity) {
return {
type: UPDATE_CONTACT_CAPACITY,
@@ -47,6 +62,13 @@ export function updateContactCapacity(contactCapacity) {
}
}
+export function updateManualFormErrors(errorsObject) {
+ return {
+ type: UPDATE_MANUAL_FORM_ERRORS,
+ errorsObject
+ }
+}
+
// ------------------------------------
// Action Handlers
// ------------------------------------
@@ -56,7 +78,13 @@ const ACTION_HANDLERS = {
[UPDATE_CONTACT_FORM_SEARCH_QUERY]: (state, { searchQuery }) => ({ ...state, searchQuery }),
- [UPDATE_CONTACT_CAPACITY]: (state, { contactCapacity }) => ({ ...state, contactCapacity })
+ [UPDATE_MANUAL_FORM_SEARCH_QUERY]: (state, { searchQuery }) => ({ ...state, searchQuery }),
+
+ [UPDATE_CONTACT_CAPACITY]: (state, { contactCapacity }) => ({ ...state, contactCapacity }),
+
+ [UPDATE_MANUAL_FORM_ERRORS]: (state, { errorsObject }) => ({ ...state, showErrors: Object.assign(state.showErrors, errorsObject) }),
+
+ [UPDATE_MANUAL_FORM_SEARCH_QUERY]: (state, { manualSearchQuery }) => ({ ...state, manualSearchQuery })
}
// ------------------------------------
@@ -65,6 +93,7 @@ const ACTION_HANDLERS = {
const contactFormSelectors = {}
const networkNodesSelector = state => state.network.nodes
const searchQuerySelector = state => state.contactsform.searchQuery
+const manualSearchQuerySelector = state => state.contactsform.manualSearchQuery
contactFormSelectors.filteredNetworkNodes = createSelector(
@@ -97,6 +126,21 @@ contactFormSelectors.showManualForm = createSelector(
}
)
+contactFormSelectors.manualFormIsValid = createSelector(
+ manualSearchQuerySelector,
+ (input) => {
+ const errors = {}
+ if (!input.length || !input.includes('@')) {
+ errors.manualInput = 'Invalid format'
+ }
+ return {
+ errors,
+ isValid: isEmpty(errors)
+ }
+ }
+)
+
+
export { contactFormSelectors }
// ------------------------------------
@@ -106,4 +150,4 @@ export default function contactFormReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
-}
+}
\ No newline at end of file
diff --git a/app/routes/contacts/containers/ContactsContainer.js b/app/routes/contacts/containers/ContactsContainer.js
index b0afb19a..040d389a 100644
--- a/app/routes/contacts/containers/ContactsContainer.js
+++ b/app/routes/contacts/containers/ContactsContainer.js
@@ -24,8 +24,10 @@ import {
openContactsForm,
closeContactsForm,
updateContactFormSearchQuery,
+ updateManualFormSearchQuery,
updateContactCapacity,
- contactFormSelectors
+ contactFormSelectors,
+ updateManualFormErrors
} from 'reducers/contactsform'
import Contacts from '../components/Contacts'
@@ -36,13 +38,14 @@ const mapDispatchToProps = {
openContactModal,
closeContactModal,
updateContactFormSearchQuery,
+ updateManualFormSearchQuery,
updateContactCapacity,
openChannel,
closeChannel,
updateChannelSearchQuery,
toggleFilterPulldown,
changeFilter,
-
+ updateManualFormErrors,
fetchChannels,
fetchPeers,
fetchDescribeNetwork
@@ -66,7 +69,8 @@ const mapStateToProps = state => ({
channelNodes: channelsSelectors.channelNodes(state),
filteredNetworkNodes: contactFormSelectors.filteredNetworkNodes(state),
- showManualForm: contactFormSelectors.showManualForm(state)
+ showManualForm: contactFormSelectors.showManualForm(state),
+ manualFormIsValid: contactFormSelectors.manualFormIsValid(state)
})
const mergeProps = (stateProps, dispatchProps, ownProps) => {
@@ -77,23 +81,25 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
isOpen: stateProps.channels.contactModal.isOpen,
channel: stateProps.channels.contactModal.channel,
channelNodes: stateProps.channelNodes,
- closingChannelIds: stateProps.channels.closingChannelIds
+ closingChannelIds: stateProps.channels.closingChannelIds,
+ manualFormIsValid: stateProps.manualFormIsValid
}
const contactsFormProps = {
closeContactsForm: dispatchProps.closeContactsForm,
updateContactFormSearchQuery: dispatchProps.updateContactFormSearchQuery,
+ updateManualFormSearchQuery: dispatchProps.updateManualFormSearchQuery,
updateContactCapacity: dispatchProps.updateContactCapacity,
openChannel: dispatchProps.openChannel,
-
contactsform: stateProps.contactsform,
filteredNetworkNodes: stateProps.filteredNetworkNodes,
loadingChannelPubkeys: stateProps.channels.loadingChannelPubkeys,
showManualForm: stateProps.showManualForm,
-
+ manualFormIsValid: stateProps.manualFormIsValid,
activeChannelPubkeys: stateProps.activeChannelPubkeys,
nonActiveChannelPubkeys: stateProps.nonActiveChannelPubkeys,
- pendingOpenChannelPubkeys: stateProps.pendingOpenChannelPubkeys
+ pendingOpenChannelPubkeys: stateProps.pendingOpenChannelPubkeys,
+ updateManualFormErrors: dispatchProps.updateManualFormErrors
}
return {
@@ -106,4 +112,4 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
}
}
-export default withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(Contacts))
+export default withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(Contacts))
\ No newline at end of file