You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

275 lines
8.5 KiB

import React from 'react'
import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import MdClose from 'react-icons/lib/md/close'
import FaCircle from 'react-icons/lib/fa/circle'
import FaQuestionCircle from 'react-icons/lib/fa/question-circle'
import styles from './ContactsForm.scss'
class ContactsForm extends React.Component {
constructor(props) {
super(props)
this.state = {
editing: false
}
}
render() {
const {
contactsform,
contactsform: { showErrors },
closeContactsForm,
updateContactFormSearchQuery,
updateManualFormSearchQuery,
updateContactCapacity,
openChannel,
updateManualFormErrors,
activeChannelPubkeys,
nonActiveChannelPubkeys,
pendingOpenChannelPubkeys,
filteredNetworkNodes,
loadingChannelPubkeys,
showManualForm,
manualFormIsValid
} = this.props
const { editing } = this.state
const renderRightSide = node => {
if (loadingChannelPubkeys.includes(node.pub_key)) {
return (
<span className={styles.inactive}>
<div className={styles.loading}>
<div className={styles.spinner} />
</div>
</span>
)
}
if (activeChannelPubkeys.includes(node.pub_key)) {
return (
<span className={`${styles.online} ${styles.inactive}`}>
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Online</span>
</span>
)
}
if (nonActiveChannelPubkeys.includes(node.pub_key)) {
return (
<span className={`${styles.offline} ${styles.inactive}`}>
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Offline</span>
</span>
)
}
if (pendingOpenChannelPubkeys.includes(node.pub_key)) {
return (
<span className={`${styles.pending} ${styles.inactive}`}>
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Pending</span>
</span>
)
}
if (!node.addresses.length) {
return <span className={`${styles.private} ${styles.inactive}`}>Private</span>
}
return (
<span
className={`${styles.connect} hint--left`}
data-hint={`Connect with ${contactsform.contactCapacity} BTC`}
onClick={() =>
openChannel({
pubkey: node.pub_key,
host: node.addresses[0].addr,
local_amt: contactsform.contactCapacity
})
}
>
Connect
</span>
)
}
const inputClicked = () => {
if (editing) {
return
}
this.setState({ editing: true })
}
const manualFormSubmit = () => {
if (!manualFormIsValid.isValid) {
updateManualFormErrors(manualFormIsValid.errors)
updateManualFormSearchQuery('')
return
}
// clear any existing errors
updateManualFormErrors({ manualInput: null })
const [pubkey, host] =
contactsform.manualSearchQuery && contactsform.manualSearchQuery.split('@')
openChannel({ pubkey, host, local_amt: contactsform.contactCapacity })
updateManualFormSearchQuery('')
}
const searchUpdated = search => {
updateContactFormSearchQuery(search)
if (search.includes('@') && search.split('@')[0].length === 66) {
updateManualFormSearchQuery(search)
}
}
return (
<div>
<ReactModal
isOpen={contactsform.isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp
shouldCloseOnOverlayClick
onRequestClose={() => closeContactsForm}
parentSelector={() => document.body}
className={styles.modal}
>
<header>
<div>
<h1>Add Contact</h1>
</div>
<div onClick={closeContactsForm} className={styles.modalClose}>
<MdClose />
</div>
</header>
<div className={styles.form}>
<div className={styles.search}>
<input
type="text"
placeholder="Find contact by alias or pubkey"
className={styles.searchInput}
value={contactsform.searchQuery}
onChange={event => searchUpdated(event.target.value)}
/>
</div>
<ul className={styles.networkResults}>
{filteredNetworkNodes.map(node => (
<li key={node.pub_key}>
<section>
{node.alias.length > 0 ? (
<h2>
<span>{node.alias.trim()}</span>
<span>
({node.pub_key.substr(0, 10)}...{node.pub_key.substr(
node.pub_key.length - 10
)})
</span>
</h2>
) : (
<h2>
<span>{node.pub_key}</span>
</h2>
)}
</section>
<section>{renderRightSide(node)}</section>
</li>
))}
</ul>
</div>
{showManualForm && (
<div className={styles.manualForm}>
<h2>
Hm, looks like we cant see that contact from here. Want to try and manually
connect?
</h2>
<section>
<input
type="text"
placeholder="pubkey@host"
value={contactsform.manualSearchQuery}
onChange={event => updateManualFormSearchQuery(event.target.value)}
/>
<div className={styles.submit} onClick={manualFormSubmit}>
Submit
</div>
{loadingChannelPubkeys.length > 0 && (
<div className={styles.manualFormSpinner}>
<div className={styles.loading}>
<div className={styles.spinner} />
</div>
</div>
)}
</section>
<section
className={`${styles.errorMessage} ${
showErrors.manualInput ? styles.active : undefined
}`}
>
{showErrors.manualInput && (
<span>{manualFormIsValid && manualFormIsValid.errors.manualInput}</span>
)}
</section>
</div>
)}
<footer className={styles.footer}>
<div>
<span>Use</span>
<span className={styles.amount}>
<input
type="text"
value={contactsform.contactCapacity}
onChange={event => updateContactCapacity(event.target.value)}
onClick={inputClicked}
onKeyPress={event => event.charCode === 13 && this.setState({ editing: false })}
readOnly={!editing}
style={{
width: `${editing ? 20 : contactsform.contactCapacity.toString().length + 1}%`
}}
/>
</span>
<span className={styles.caption}>
BTC per contact
<i
data-hint="You aren't spending anything, just moving money onto the Lightning Network"
className="hint--top"
>
<FaQuestionCircle style={{ verticalAlign: 'top' }} />
</i>
</span>
</div>
</footer>
</ReactModal>
</div>
)
}
}
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,
filteredNetworkNodes: PropTypes.array.isRequired,
loadingChannelPubkeys: PropTypes.array.isRequired,
showManualForm: PropTypes.bool.isRequired
}
export default ContactsForm