Browse Source

fix(peers): peers makeover

renovate/lint-staged-8.x
Jack Mallers 7 years ago
parent
commit
b492fc6bd7
  1. 133
      app/components/Channels/NetworkChannels.js
  2. 114
      app/components/Channels/NetworkChannels.scss
  3. 86
      app/components/Peers/Peers.js
  4. 59
      app/components/Peers/Peers.scss
  5. 2
      app/components/Wallet/ReceiveModal.js
  6. 2
      app/main.dev.js
  7. 6
      app/routes/activity/components/Activity.js
  8. 4
      app/routes/activity/containers/ActivityContainer.js
  9. 6
      app/routes/app/components/App.js
  10. 102
      app/routes/channels/components/Channels.js
  11. 2
      app/routes/channels/components/Channels.scss
  12. 4
      app/routes/network/components/Network.js
  13. 68
      app/routes/peers/components/Peers.js
  14. 55
      app/routes/peers/components/Peers.scss

133
app/components/Channels/NetworkChannels.js

@ -1,133 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ForceGraph, ForceGraphNode, ForceGraphLink } from 'react-vis-force'
import { FaCircle } from 'react-icons/lib/fa'
import styles from './NetworkChannels.scss'
class NetworkChannels extends Component {
constructor(props) {
super(props)
this.state = {
ready: false
}
}
componentWillMount() {
setTimeout(() => {
this.setState({ ready: true })
}, 1000)
this.props.setCurrentChannel(this.props.channels[0])
}
render() {
const { ready } = this.state
const {
channels,
network: { nodes, edges, selectedChannel, networkLoading },
identity_pubkey,
setCurrentChannel
} = this.props
if (!ready || networkLoading) {
return (
<div className={styles.networkLoading}>
<h1>loading network graph</h1>
</div>
)
}
return (
<div className={styles.networkchannels}>
<div className={styles.network}>
<ForceGraph
simulationOptions={
{
width: 1000,
height: 1000,
strength: {
charge: -750
}
}
}
labelAttr='label'
opacityFactor={1}
zoomOptions={{ minScale: 0.1, maxScale: 5 }}
zoom
animate
highlightDependencies
>
{
nodes.map(node => (
<ForceGraphNode
r={15}
label={node.pub_key}
key={node.pub_key}
node={{ id: node.pub_key }}
className={`${styles.node} ${identity_pubkey === node.pub_key && styles.active}`}
fill={identity_pubkey === node.pub_key ? 'green' : 'silver'}
/>
))
}
{
edges.map(edge => (
<ForceGraphLink
className={`${styles.line} ${selectedChannel.chan_id === edge.channel_id && styles.active}`}
key={edge.channel_id}
link={{ source: edge.node1_pub, target: edge.node2_pub }}
/>
))
}
</ForceGraph>
</div>
<div className={styles.channels}>
<ul>
{
channels.map((channel, index) => (
<li
key={index}
className={`${styles.channel} ${channel.chan_id === selectedChannel.chan_id && styles.active}`}
onClick={() => setCurrentChannel(channel)}
>
<header>
{
channel.active ?
<span className={styles.active}>
<FaCircle />
<i>active</i>
</span>
:
<span className={styles.notactive}>
<FaCircle />
<i>not active</i>
</span>
}
</header>
<div className={styles.content}>
<div>
<h4>Remote Pubkey</h4>
<h2>{`${channel.remote_pubkey.substring(30, channel.remote_pubkey.length)}...`}</h2>
</div>
<div>
<h4>Channel Point</h4>
<h2>{`${channel.channel_point.substring(30, channel.channel_point.length)}...`}</h2>
</div>
</div>
</li>
))
}
</ul>
</div>
</div>
)
}
}
NetworkChannels.propTypes = {
channels: PropTypes.array.isRequired,
network: PropTypes.object.isRequired,
identity_pubkey: PropTypes.string.isRequired,
setCurrentChannel: PropTypes.func.isRequired
}
export default NetworkChannels

114
app/components/Channels/NetworkChannels.scss

@ -1,114 +0,0 @@
@import '../../variables.scss';
@keyframes dash {
to {
stroke-dashoffset: 1000;
}
}
.networkLoading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: $black;
h1 {
font-size: 22px;
color: $green;
font-family: 'courier';
text-align: center;
margin-top: 25%;
}
}
.networkchannels {
position: relative;
}
.network, .channels {
display: inline-block;
vertical-align: top;
}
.network {
width: 70%;
}
.channels {
width: calc(30% - 2px);
border: 1px solid $darkestgrey;
border-radius: 5px;
}
.node {
r: 15;
fill: $darkestgrey;
.active {
r: 25;
fill: $green;
stroke: $green;
}
}
.line.active {
stroke-width: 10;
opacity: 1;
stroke: $green;
stroke-dasharray: 100;
animation: dash 2.5s infinite linear;
}
.channel {
padding: 10px;
background: rgba(0, 0, 0, 0.7);
cursor: pointer;
transition: all 0.25s;
border-bottom: 1px solid $white;
&.active, &:hover {
background: rgba(255, 255, 255, 0.4);
}
header {
margin-bottom: 10px;
.active {
color: $green;
}
.notactive {
color: $green;
}
span svg {
font-size: 10px;
}
i {
text-transform: uppercase;
font-size: 10px;
margin-left: 5px;
}
}
.content {
div {
margin-bottom: 5px;
}
h4 {
text-transform: uppercase;
margin-bottom: 5px;
font-size: 10px;
color: $main;
}
h2 {
font-size: 14px;
color: $white;
}
}
}

86
app/components/Peers/Peers.js

@ -1,86 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { TiPlus } from 'react-icons/lib/ti'
import { FaRepeat } from 'react-icons/lib/fa'
import PeerModal from './PeerModal'
import PeerForm from './PeerForm'
import Peer from './Peer'
import styles from './Peers.scss'
const Peers = ({
fetchPeers,
peersLoading,
peers,
setPeer,
modalPeer,
peerModalOpen,
peerForm,
setPeerForm,
connect,
disconnect
}) => {
const refreshClicked = (event) => {
// store event in icon so we dont get an error when react clears it
const icon = event.currentTarget
// fetch peers
fetchPeers()
// clear animation after the second so we can reuse it
setTimeout(() => { icon.style.animation = '' }, 1000)
// spin icon for 1 sec
icon.style.animation = 'spin 1000ms linear 1'
}
return (
<div className={styles.peers}>
<PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={modalPeer} disconnect={disconnect} />
<PeerForm form={peerForm} setForm={setPeerForm} connect={connect} />
<div className={styles.header}>
<h3>Peers</h3>
<span
className={`${styles.refresh} hint--top`}
data-hint='Refresh your peers list'
>
<FaRepeat
style={{ verticalAlign: 'baseline' }}
onClick={refreshClicked}
/>
</span>
<div
className={`${styles.connectPeer} hint--top`}
data-hint='Connect to a peer'
onClick={() => setPeerForm({ isOpen: true })}
>
<TiPlus />
</div>
</div>
<ul>
{
!peersLoading ?
peers.map(peer => <Peer key={peer.peer_id} peer={peer} setPeer={setPeer} />)
:
'Loading...'
}
</ul>
</div>
)
}
Peers.propTypes = {
fetchPeers: PropTypes.func.isRequired,
peersLoading: PropTypes.bool.isRequired,
peers: PropTypes.array.isRequired,
setPeer: PropTypes.func.isRequired,
modalPeer: PropTypes.object,
peerModalOpen: PropTypes.bool.isRequired,
peerForm: PropTypes.object.isRequired,
setPeerForm: PropTypes.func.isRequired,
connect: PropTypes.func.isRequired,
disconnect: PropTypes.func.isRequired
}
export default Peers

59
app/components/Peers/Peers.scss

@ -1,59 +0,0 @@
@import '../../variables.scss';
.peers {
width: 75%;
margin: 50px auto;
.header {
margin-bottom: 10px;
h3, .connectPeer {
display: inline-block;
}
h3 {
text-align: left;
}
.refresh {
cursor: pointer;
margin-left: 5px;
font-size: 12px;
vertical-align: top;
color: $darkestgrey;
line-height: 14px;
transition: color 0.25s;
&:hover {
color: $main;
}
}
.connectPeer {
float: right;
cursor: pointer;
svg {
padding: 3px;
border-radius: 50%;
border: 1px solid $main;
color: $main;
transition: all 0.25s;
&:hover {
border-color: darken($main, 10%);
color: darken($main, 10%);
}
}
}
}
h3 {
text-transform: uppercase;
color: $darkestgrey;
letter-spacing: 1.6px;
font-size: 14px;
font-weight: 400;
margin-bottom: 10px;
}
}

2
app/components/Wallet/ReceiveModal.js

@ -16,7 +16,7 @@ const ReceiveModal = ({ isOpen, hideActivityModal, pubkey, address }) => {
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
width: '60%',
margin: '50px auto'
}
}

2
app/main.dev.js

@ -165,7 +165,7 @@ export const startLnd = () => {
'--bitcoin.active',
'--bitcoin.testnet',
'--neutrino.active',
'--neutrino.connect=165.227.7.29:18333',
'--neutrino.connect=faucet.lightning.community:18333',
'--autopilot.active',
'--debuglevel=debug',
'--no-macaroons',

6
app/routes/activity/components/Activity.js

@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import { MdSearch } from 'react-icons/lib/md'
import { FaAngleDown } from 'react-icons/lib/fa'
import Wallet from 'components/Wallet'
import Invoice from './components/Invoice'
import Payment from './components/Payment'
import Transaction from './components/Transaction'
@ -44,6 +45,9 @@ class Activity extends Component {
ticker,
searchInvoices,
invoice: { invoicesSearchText, invoiceLoading },
address: { address },
balance,
info,
payment: { paymentLoading },
currentTicker,
activity: { modal, filter, filterPulldown },
@ -66,6 +70,8 @@ class Activity extends Component {
currentTicker={currentTicker}
/>
<Wallet balance={balance} address={address} info={info} />
<div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='invoiceSearch'>
<MdSearch />

4
app/routes/activity/containers/ActivityContainer.js

@ -37,6 +37,10 @@ const mapDispatchToProps = {
const mapStateToProps = state => ({
activity: state.activity,
balance: state.balance,
address: state.address,
info: state.info,
payment: state.payment,
invoice: state.invoice,

6
app/routes/app/components/App.js

@ -6,7 +6,6 @@ import LoadingBolt from 'components/LoadingBolt'
import Form from 'components/Form'
import ModalRoot from 'components/ModalRoot'
import Nav from 'components/Nav'
import Wallet from 'components/Wallet'
import styles from './App.scss'
class App extends Component {
@ -66,11 +65,6 @@ class App extends Component {
/>
<div className={styles.content}>
<Wallet
balance={balance}
address={address}
info={info}
/>
{children}
</div>
</div>

102
app/routes/channels/components/Channels.js

@ -7,18 +7,16 @@ import { MdSearch } from 'react-icons/lib/md'
import OpenPendingChannel from 'components/Channels/OpenPendingChannel'
import ClosedPendingChannel from 'components/Channels/ClosedPendingChannel'
import Channel from 'components/Channels/Channel'
import NetworkChannels from 'components/Channels/NetworkChannels'
import ChannelForm from 'components/ChannelForm'
import styles from './Channels.scss'
class Channels extends Component {
componentWillMount() {
const { fetchChannels, fetchPeers, fetchDescribeNetwork } = this.props
const { fetchChannels, fetchPeers } = this.props
fetchChannels()
fetchPeers()
fetchDescribeNetwork()
}
render() {
@ -47,18 +45,14 @@ class Channels extends Component {
ticker,
currentTicker,
channelFormProps,
network,
identity_pubkey,
setCurrentChannel
channelFormProps
} = this.props
const refreshClicked = (event) => {
// store event in icon so we dont get an error when react clears it
const icon = event.currentTarget
// fetch peers
// fetch channels
fetchChannels()
// clear animation after the second so we can reuse it
@ -68,12 +62,6 @@ class Channels extends Component {
icon.style.animation = 'spin 1000ms linear 1'
}
const networkClicked = () => {
if (!activeChannels.length) { return }
setViewType(1)
}
return (
<div className={`${styles.container} ${viewType === 1 && styles.graphview}`}>
<ChannelForm {...channelFormProps} />
@ -92,14 +80,6 @@ class Channels extends Component {
/>
</div>
<header className={styles.header}>
<div className={styles.layoutsContainer}>
<span className={viewType === 0 && styles.active} onClick={() => setViewType(0)}>
<FaAlignJustify />
</span>
<span className={viewType === 1 && styles.active} onClick={networkClicked}>
<FaGlobe />
</span>
</div>
<div className={styles.createChannelContainer}>
<div className={`buttonPrimary ${styles.newChannelButton}`} onClick={openChannelForm}>
Create new channel
@ -131,53 +111,42 @@ class Channels extends Component {
</div>
<div className={`${styles.channels} ${filterPulldown && styles.fade}`}>
{
viewType === 0 &&
<ul className={viewType === 1 && styles.cardsContainer}>
{
currentChannels.map((channel, index) => {
if (Object.prototype.hasOwnProperty.call(channel, 'blocks_till_open')) {
return (
<OpenPendingChannel
key={index}
channel={channel}
ticker={ticker}
currentTicker={currentTicker}
explorerLinkBase={'https://testnet.smartbit.com.au/'}
/>
)
} else if (Object.prototype.hasOwnProperty.call(channel, 'closing_txid')) {
return (
<ClosedPendingChannel
key={index}
channel={channel}
ticker={ticker}
currentTicker={currentTicker}
explorerLinkBase={'https://testnet.smartbit.com.au/'}
/>
)
}
<ul className={viewType === 1 && styles.cardsContainer}>
{
currentChannels.map((channel, index) => {
if (Object.prototype.hasOwnProperty.call(channel, 'blocks_till_open')) {
return (
<Channel
<OpenPendingChannel
key={index}
channel={channel}
ticker={ticker}
currentTicker={currentTicker}
explorerLinkBase={'https://testnet.smartbit.com.au/'}
/>
)
} else if (Object.prototype.hasOwnProperty.call(channel, 'closing_txid')) {
return (
<ClosedPendingChannel
key={index}
channel={channel}
closeChannel={closeChannel}
ticker={ticker}
currentTicker={currentTicker}
explorerLinkBase={'https://testnet.smartbit.com.au/'}
/>
)
})
}
</ul>
}
{ viewType === 1 &&
<NetworkChannels
channels={openChannels}
network={network}
identity_pubkey={identity_pubkey}
setCurrentChannel={setCurrentChannel}
/>
}
}
return (
<Channel
key={index}
ticker={ticker}
channel={channel}
closeChannel={closeChannel}
currentTicker={currentTicker}
/>
)
})
}
</ul>
</div>
</div>
)
@ -186,7 +155,6 @@ class Channels extends Component {
Channels.propTypes = {
fetchChannels: PropTypes.func.isRequired,
fetchPeers: PropTypes.func.isRequired,
channels: PropTypes.object.isRequired,
currentChannels: PropTypes.array.isRequired,
@ -204,11 +172,7 @@ Channels.propTypes = {
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
channelFormProps: PropTypes.object.isRequired,
network: PropTypes.object.isRequired,
fetchDescribeNetwork: PropTypes.func.isRequired,
identity_pubkey: PropTypes.string.isRequired
channelFormProps: PropTypes.object.isRequired
}
export default Channels

2
app/routes/channels/components/Channels.scss

@ -38,7 +38,7 @@
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: flex-end;
}
.filtersContainer {

4
app/routes/network/components/Network.js

@ -115,9 +115,7 @@ class Network extends Component {
</ul>
<div className={styles.currentTab}>
{
renderTab()
}
{renderTab()}
</div>
</section>
</div>

68
app/routes/peers/components/Peers.js

@ -13,6 +13,14 @@ import Peer from 'components/Peers/Peer'
import styles from './Peers.scss'
class Peers extends Component {
constructor(props) {
super(props)
this.state = {
refreshing: false
}
}
componentWillMount() {
this.props.fetchPeers()
}
@ -28,22 +36,34 @@ class Peers extends Component {
peerModalOpen,
filteredPeers,
peers: { peer, searchQuery },
info: { data: { identity_pubkey } }
peers: { peer, searchQuery }
} = this.props
const refreshClicked = event => {
// turn the spinner on
this.setState({ refreshing: true })
// store event in icon so we dont get an error when react clears it
const icon = event.currentTarget
let icon = this.refs.repeat.childNodes
// fetch peers
fetchPeers()
// clear animation after the second so we can reuse it
setTimeout(() => { icon.style.animation = '' }, 1000)
// wait for the svg to appear as child
let svgTimeout = setTimeout(() => {
if (icon[0].tagName === 'svg') {
// spin icon for 1 sec
icon[0].style.animation = 'spin 1000ms linear 1'
clearTimeout(svgTimeout)
}
}, 1)
// spin icon for 1 sec
icon.style.animation = 'spin 1000ms linear 1'
// clear animation after the second so we can reuse it
let refreshTimeout = setTimeout(() => {
icon[0].style.animation = ''
this.setState({ refreshing: false })
clearTimeout(refreshTimeout)
}, 1000)
}
return (
@ -55,15 +75,7 @@ class Peers extends Component {
<header className={styles.header}>
<div className={styles.titleContainer}>
<div className={styles.left}>
<div className={styles.identityPubkey}>
<section className={styles.userIcon}>
<Isvg src={userIcon} />
</section>
<section>
<h4>Your node public key</h4>
<h2>{identity_pubkey}</h2>
</section>
</div>
<h1>Peers</h1>
</div>
</div>
<div className={styles.addPeerContainer}>
@ -74,7 +86,6 @@ class Peers extends Component {
</header>
<div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'>
<MdSearch />
</label>
@ -89,10 +100,13 @@ class Peers extends Component {
</div>
<div className={styles.refreshContainer}>
<span className={`${styles.refresh} hint--top`} data-hint='Refresh your peers list'>
<FaRepeat
onClick={refreshClicked}
/>
<span className={styles.refresh} onClick={refreshClicked} ref='repeat'>
{
this.state.refreshing ?
<FaRepeat />
:
'Refresh'
}
</span>
</div>
@ -107,7 +121,17 @@ class Peers extends Component {
}
Peers.propTypes = {
fetchPeers: PropTypes.func.isRequired,
peerFormProps: PropTypes.object.isRequired,
setPeerForm: PropTypes.func.isRequired,
setPeer: PropTypes.func.isRequired,
updateSearchQuery: PropTypes.func.isRequired,
disconnectRequest: PropTypes.func.isRequired,
peerModalOpen: PropTypes.bool.isRequired,
filteredPeers: PropTypes.array.isRequired,
peer: PropTypes.object,
searchQuery: PropTypes.string
}
export default Peers

55
app/routes/peers/components/Peers.scss

@ -1,7 +1,7 @@
@import '../../../variables.scss';
.search {
height: 75px;
height: 55px;
padding: 2px 25px;
border-top: 1px solid $darkgrey;
border-bottom: 1px solid $darkgrey;
@ -15,8 +15,8 @@
.label {
width: 5%;
line-height: 70px;
font-size: 25px;
line-height: 50px;
font-size: 20px;
text-align: center;
cursor: pointer;
}
@ -27,8 +27,8 @@
padding: 0;
border: 0;
border-radius: 0;
height: 68px;
font-size: 18px;
height: 50px;
font-size: 16px;
}
}
@ -36,58 +36,43 @@
display: flex;
flex-direction: row;
justify-content: space-between;
background: $lightgrey;
.titleContainer {
padding: 40px;
padding: 20px 40px;
.left {
padding: 10px 0;
}
.left, span {
display: inline-block;
}
.identityPubkey {
font-size: 30px;
margin-right: 10px;
display: flex;
flex-direction: row;
.userIcon {
margin-right: 10px;
}
section h4 {
font-size: 10px;
h1 {
text-transform: uppercase;
letter-spacing: 1.2px;
}
section h2 {
font-size: 14px;
margin-top: 5px;
font-size: 26px;
margin-right: 5px;
}
}
}
.addPeerContainer {
padding: 40px;
padding: 20px 40px;
.newPeerButton {
font-size: 14px;
margin-left: 10px;
}
}
}
.refreshContainer {
display: flex;
flex-direction: row;
padding: 20px 40px 0px 40px;
padding: 20px 40px 0 40px;
text-align: right;
cursor: pointer;
.refresh {
cursor: pointer;
color: $bluegrey;
text-decoration: underline;
svg {
font-size: 12px;
}
}
}

Loading…
Cancel
Save