Browse Source

refactor(general): remove contactModal components

renovate/lint-staged-8.x
Tom Kirkpatrick 6 years ago
parent
commit
54ed782ff5
No known key found for this signature in database GPG Key ID: 72203A8EC5967EA8
  1. 6
      app/components/App/App.js
  2. 139
      app/components/Contacts/ContactModal/ContactModal.js
  3. 151
      app/components/Contacts/ContactModal/ContactModal.scss
  4. 3
      app/components/Contacts/ContactModal/index.js
  5. 275
      app/components/Contacts/ContactsForm/ContactsForm.js
  6. 253
      app/components/Contacts/ContactsForm/ContactsForm.scss
  7. 3
      app/components/Contacts/ContactsForm/index.js
  8. 14
      app/containers/App.js
  9. 28
      app/reducers/channels.js
  10. 20
      test/unit/reducers/__snapshots__/channels.spec.js.snap

6
app/components/App/App.js

@ -9,8 +9,6 @@ import ChannelForm from 'components/Contacts/ChannelForm'
import Network from 'components/Contacts/Network'
import AddChannel from 'components/Contacts/AddChannel'
import ContactModal from 'components/Contacts/ContactModal'
import ReceiveModal from 'components/Wallet/ReceiveModal'
import ActivityModal from 'components/Activity/ActivityModal'
@ -52,7 +50,6 @@ class App extends Component {
error: { error },
clearError,
contactModalProps,
contactsFormProps,
networkTabProps,
receiveModalProps,
@ -71,8 +68,6 @@ class App extends Component {
<div className={styles.titleBar} />
<GlobalError error={error} clearError={clearError} />
<ContactModal {...contactModalProps} />
<ChannelForm {...channelFormProps} />
<ReceiveModal {...receiveModalProps} />
@ -98,7 +93,6 @@ App.propTypes = {
closeForm: PropTypes.func.isRequired,
error: PropTypes.object.isRequired,
currentTicker: PropTypes.object,
contactModalProps: PropTypes.object,
contactsFormProps: PropTypes.object,
networkTabProps: PropTypes.object,
activityModalProps: PropTypes.object,

139
app/components/Contacts/ContactModal/ContactModal.js

@ -1,139 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import FaCircle from 'react-icons/lib/fa/circle'
import MdClose from 'react-icons/lib/md/close'
import { btc } from 'lib/utils'
import styles from './ContactModal.scss'
const ContactModal = ({
isOpen,
channel,
closeContactModal,
channelNodes,
closeChannel,
closingChannelIds
}) => {
if (!channel) {
return <span />
}
const customStyles = {
overlay: {
cursor: 'pointer',
overflowY: 'auto'
},
content: {
top: 'auto',
left: '0',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
borderRadius: 'none',
padding: '0'
}
}
const removeClicked = () => {
closeChannel({
channel_point: channel.channel_point,
chan_id: channel.chan_id,
force: !channel.active
})
}
// the remote node for the channel
const node = channelNodes.find(node => node.pub_key === channel.remote_pubkey)
return (
<ReactModal
isOpen={isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp
shouldCloseOnOverlayClick
onRequestClose={closeContactModal}
parentSelector={() => document.body}
style={customStyles}
>
{channel && (
<div className={styles.container}>
<header className={styles.header}>
<div className={`${styles.status} ${channel.active ? styles.online : undefined}`}>
<FaCircle style={{ verticalAlign: 'top' }} />
<span>{channel.active ? 'Online' : 'Offline'}</span>
</div>
<div className={styles.closeContainer}>
<span onClick={closeContactModal}>
<MdClose />
</span>
</div>
</header>
<section className={styles.title}>
{node && <h1>{node.alias}</h1>}
<h2>{channel.remote_pubkey}</h2>
</section>
<section className={styles.stats}>
<div className={styles.pay}>
<h4>Can Pay</h4>
<div className={styles.meter}>
<div
className={styles.amount}
style={{ width: `${(channel.local_balance / channel.capacity) * 100}%` }}
/>
</div>
<span>{btc.satoshisToBtc(channel.local_balance)} BTC</span>
</div>
<div className={styles.pay}>
<h4>Can Receive</h4>
<div className={styles.meter}>
<div
className={styles.amount}
style={{ width: `${(channel.remote_balance / channel.capacity) * 100}%` }}
/>
</div>
<span>{btc.satoshisToBtc(channel.remote_balance)} BTC</span>
</div>
<div className={styles.sent}>
<h4>Total Bitcoin Sent</h4>
<p>{btc.satoshisToBtc(channel.total_satoshis_sent)} BTC</p>
</div>
<div className={styles.received}>
<h4>Total Bitcoin Received</h4>
<p>{btc.satoshisToBtc(channel.total_satoshis_received)} BTC</p>
</div>
</section>
<footer>
{closingChannelIds.includes(channel.chan_id) ? (
<span className={styles.inactive}>
<div className={styles.loading}>
<div className={styles.spinner} />
</div>
</span>
) : (
<div onClick={removeClicked}>Remove</div>
)}
</footer>
</div>
)}
</ReactModal>
)
}
ContactModal.propTypes = {
channel: PropTypes.object,
isOpen: PropTypes.bool.isRequired,
closeContactModal: PropTypes.func.isRequired,
channelNodes: PropTypes.array.isRequired,
closeChannel: PropTypes.func.isRequired,
closingChannelIds: PropTypes.array.isRequired
}
export default ContactModal

151
app/components/Contacts/ContactModal/ContactModal.scss

@ -1,151 +0,0 @@
@import 'styles/variables.scss';
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
background: $lightgrey;
padding: 20px;
.status {
font-size: 12px;
color: $darkestgrey;
&.online {
color: $green;
}
span {
margin-left: 5px;
}
}
.closeContainer {
background: $lightgrey;
line-height: 12px;
span {
color: $darkestgrey;
cursor: pointer;
}
}
}
.container section {
margin-bottom: 30px;
padding: 0 20px;
.pay,
.receive,
.sent,
.received {
margin: 40px 0;
}
}
.container .title {
margin: 0;
padding: 30px 20px;
background: $lightgrey;
h1 {
color: $secondary;
font-weight: bold;
font-size: 16px;
letter-spacing: 1.1px;
margin-bottom: 10px;
}
h2 {
font-size: 12px;
color: $darkestgrey;
font-weight: 100;
}
}
.stats {
h4 {
color: $secondary;
font-weight: bold;
font-size: 12px;
}
span {
font-size: 14px;
}
p {
margin-top: 10px;
color: $darkestgrey;
}
.meter,
.amount {
height: 10px;
border-radius: 10px;
}
.meter {
background: $darkgrey;
width: 100%;
margin: 10px 0;
}
.amount {
background: $darkestgrey;
}
}
.container footer {
padding: 20px;
text-align: center;
div {
color: $red;
font-size: 18px;
&:hover {
color: lighten($red, 10%);
}
}
}
@-webkit-keyframes animation-rotate {
100% {
-webkit-transform: rotate(360deg);
}
}
@-moz-keyframes animation-rotate {
100% {
-moz-transform: rotate(360deg);
}
}
@-o-keyframes animation-rotate {
100% {
-o-transform: rotate(360deg);
}
}
@keyframes animation-rotate {
100% {
transform: rotate(360deg);
}
}
.spinner {
border: 1px solid rgba(0, 0, 0, 0.1);
border-left-color: rgba(0, 0, 0, 0.4);
-webkit-border-radius: 999px;
-moz-border-radius: 999px;
border-radius: 999px;
margin: 0 auto;
height: 20px;
width: 20px;
-webkit-animation: animation-rotate 1000ms linear infinite;
-moz-animation: animation-rotate 1000ms linear infinite;
-o-animation: animation-rotate 1000ms linear infinite;
animation: animation-rotate 1000ms linear infinite;
}

3
app/components/Contacts/ContactModal/index.js

@ -1,3 +0,0 @@
import ContactModal from './ContactModal'
export default ContactModal

275
app/components/Contacts/ContactsForm/ContactsForm.js

@ -1,275 +0,0 @@
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

253
app/components/Contacts/ContactsForm/ContactsForm.scss

@ -1,253 +0,0 @@
@import 'styles/variables.scss';
.modal {
position: absolute;
width: 50%;
margin: 50px auto;
top: auto;
left: 0;
right: 0;
bottom: auto;
background: $white;
outline: none;
z-index: -2;
border: 1px solid $darkgrey;
header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 15px;
border-bottom: 1px solid $darkgrey;
h1,
svg {
font-size: 22px;
}
svg {
cursor: pointer;
}
}
}
.form {
padding: 30px 15px;
.search {
.searchInput {
width: calc(100% - 30px);
padding: 10px 15px;
outline: 0;
border: 0;
background: $lightgrey;
color: $darkestgrey;
border-radius: 5px;
font-size: 16px;
}
}
.networkResults {
overflow-y: auto;
height: 250px;
margin-top: 30px;
padding: 20px 0;
li {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 10px 0;
h2 {
font-size: 16px;
font-weight: bold;
letter-spacing: 1.3px;
span {
display: inline-block;
vertical-align: middle;
&:nth-child(1) {
font-size: 14px;
font-weight: bold;
letter-spacing: 1.3px;
}
&:nth-child(2) {
color: $darkestgrey;
font-size: 12px;
line-height: 14px;
}
}
}
.connect {
cursor: pointer;
color: $darkestgrey;
transition: all 0.25s;
font-size: 12px;
&:hover {
color: $main;
}
}
.inactive {
font-size: 12px;
display: inline-block;
vertical-align: top;
&.online {
color: $green;
}
&.offline {
color: $darkestgrey;
}
&.pending {
color: $orange;
}
&.private {
color: darken($darkestgrey, 50%);
}
}
}
}
}
.manualForm {
position: relative;
background: $lightgrey;
color: $darkestgrey;
padding: 30px 15px;
h2 {
font-size: 16px;
margin-bottom: 10px;
}
input {
border: 0;
outline: 0;
background: transparent;
color: $darkestgrey;
border-bottom: 1px solid $darkestgrey;
padding: 10px 5px;
width: 80%;
}
.submit {
display: inline-block;
vertical-align: middle;
width: 15%;
margin-left: 2.5%;
font-size: 12px;
&:hover {
cursor: pointer;
color: $main;
}
}
.manualFormSpinner {
position: absolute;
right: 0;
top: 40%;
padding: 0 10px;
}
}
.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;
font-size: 14px;
span {
&.amount {
&:hover {
input {
border: 1px solid $darkgrey;
cursor: text;
}
}
input {
border: 1px solid transparent;
padding: 0;
outline: 0;
font-weight: bold;
font-size: 14px;
line-height: 14px;
transition: all 0.25s;
&.isEditing {
width: 100%;
border-bottom: 1px solid $darkgrey;
}
}
}
&:nth-child(2) {
margin-left: 2px;
}
}
.caption svg {
font-size: 10px;
color: $darkestgrey;
}
}
@-webkit-keyframes animation-rotate {
100% {
-webkit-transform: rotate(360deg);
}
}
@-moz-keyframes animation-rotate {
100% {
-moz-transform: rotate(360deg);
}
}
@-o-keyframes animation-rotate {
100% {
-o-transform: rotate(360deg);
}
}
@keyframes animation-rotate {
100% {
transform: rotate(360deg);
}
}
.spinner {
border: 1px solid rgba(0, 0, 0, 0.1);
border-left-color: rgba(0, 0, 0, 0.4);
-webkit-border-radius: 999px;
-moz-border-radius: 999px;
border-radius: 999px;
margin: 0 auto;
height: 20px;
width: 20px;
-webkit-animation: animation-rotate 1000ms linear infinite;
-moz-animation: animation-rotate 1000ms linear infinite;
-o-animation: animation-rotate 1000ms linear infinite;
animation: animation-rotate 1000ms linear infinite;
}

3
app/components/Contacts/ContactsForm/index.js

@ -1,3 +0,0 @@
import ContactsForm from './ContactsForm'
export default ContactsForm

14
app/containers/App.js

@ -45,7 +45,6 @@ import {
toggleFilterPulldown,
changeFilter,
updateChannelSearchQuery,
closeContactModal,
setSelectedChannel
} from 'reducers/channels'
@ -115,7 +114,6 @@ const mapDispatchToProps = {
toggleFilterPulldown,
changeFilter,
updateChannelSearchQuery,
closeContactModal,
setSelectedChannel,
openContactsForm,
@ -361,16 +359,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
pendingOpenChannelPubkeys: stateProps.pendingOpenChannelPubkeys
}
const contactModalProps = {
closeContactModal: dispatchProps.closeContactModal,
closeChannel: dispatchProps.closeChannel,
isOpen: stateProps.channels.contactModal.isOpen,
channel: stateProps.channels.contactModal.channel,
channelNodes: stateProps.channelNodes,
closingChannelIds: stateProps.channels.closingChannelIds
}
const activityModalProps = {
itemType: stateProps.activity.modal.itemType,
itemId: stateProps.activity.modal.itemId,
@ -478,8 +466,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
networkTabProps,
// props for the contacts form
contactsFormProps,
// props for the contact modal
contactModalProps,
// props for the receive modal
receiveModalProps,
// props for the activity modals

28
app/reducers/channels.js

@ -37,9 +37,6 @@ export const REMOVE_LOADING_PUBKEY = 'REMOVE_LOADING_PUBKEY'
export const ADD_ClOSING_CHAN_ID = 'ADD_ClOSING_CHAN_ID'
export const REMOVE_ClOSING_CHAN_ID = 'REMOVE_ClOSING_CHAN_ID'
export const OPEN_CONTACT_MODAL = 'OPEN_CONTACT_MODAL'
export const CLOSE_CONTACT_MODAL = 'CLOSE_CONTACT_MODAL'
export const SET_SELECTED_CHANNEL = 'SET_SELECTED_CHANNEL'
export const GET_SUGGESTED_NODES = 'GET_SUGGESTED_NODES'
@ -134,19 +131,6 @@ export function removeClosingChanId(chanId) {
}
}
export function openContactModal(channel) {
return {
type: OPEN_CONTACT_MODAL,
channel
}
}
export function closeContactModal() {
return {
type: CLOSE_CONTACT_MODAL
}
}
export function setSelectedChannel(selectedChannel) {
return {
type: SET_SELECTED_CHANNEL,
@ -263,7 +247,6 @@ export const closeChannelSuccessful = () => dispatch => {
export const pushclosechannelupdated = (event, { chan_id }) => dispatch => {
dispatch(fetchChannels())
dispatch(removeClosingChanId(chan_id))
dispatch(closeContactModal())
}
// Receive IPC event for closing channel end
@ -393,12 +376,6 @@ const ACTION_HANDLERS = {
closingChannelIds: state.closingChannelIds.filter(closingChanId => closingChanId !== chanId)
}),
[OPEN_CONTACT_MODAL]: (state, { channel }) => ({
...state,
contactModal: { isOpen: true, channel }
}),
[CLOSE_CONTACT_MODAL]: state => ({ ...state, contactModal: { isOpen: false, channel: null } }),
[SET_SELECTED_CHANNEL]: (state, { selectedChannel }) => ({ ...state, selectedChannel }),
[GET_SUGGESTED_NODES]: state => ({ ...state, suggestedNodesLoading: true }),
@ -629,11 +606,6 @@ const initialState = {
loadingChannelPubkeys: [],
closingChannelIds: [],
contactModal: {
isOpen: false,
channel: null
},
selectedChannel: null,
// nodes stored at zap.jackmallers.com/suggested-peers manages by JimmyMow

20
test/unit/reducers/__snapshots__/channels.spec.js.snap

@ -13,10 +13,6 @@ Object {
"channelsLoading": true,
"closingChannel": false,
"closingChannelIds": Array [],
"contactModal": Object {
"channel": null,
"isOpen": false,
},
"filter": Object {
"key": "ALL_CHANNELS",
"name": "All",
@ -77,10 +73,6 @@ Object {
"channelsLoading": false,
"closingChannel": false,
"closingChannelIds": Array [],
"contactModal": Object {
"channel": null,
"isOpen": false,
},
"filter": Object {
"key": "ALL_CHANNELS",
"name": "All",
@ -144,10 +136,6 @@ Object {
"channelsLoading": false,
"closingChannel": false,
"closingChannelIds": Array [],
"contactModal": Object {
"channel": null,
"isOpen": false,
},
"filter": Object {
"key": "ALL_CHANNELS",
"name": "All",
@ -205,10 +193,6 @@ Object {
"channelsLoading": false,
"closingChannel": false,
"closingChannelIds": Array [],
"contactModal": Object {
"channel": null,
"isOpen": false,
},
"filter": Object {
"key": "ALL_CHANNELS",
"name": "All",
@ -269,10 +253,6 @@ Object {
"channelsLoading": false,
"closingChannel": false,
"closingChannelIds": Array [],
"contactModal": Object {
"channel": null,
"isOpen": false,
},
"filter": Object {
"key": "ALL_CHANNELS",
"name": "All",

Loading…
Cancel
Save