@ -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 |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 409 B |
@ -1,42 +0,0 @@ |
|||
import zbase32 from 'zbase32' |
|||
|
|||
function convertBigEndianBufferToLong(longBuffer) { |
|||
let longValue = 0 |
|||
const byteArray = Buffer.from(longBuffer).swap64() |
|||
|
|||
for (let i = byteArray.length - 1; i >= 0; i -= 1) { |
|||
longValue = (longValue * 256) + byteArray[i] |
|||
} |
|||
|
|||
return longValue |
|||
} |
|||
|
|||
export function decodeInvoice(payreq) { |
|||
const payreqBase32 = zbase32.decode(payreq) |
|||
|
|||
const bufferHexRotated = Buffer.from(payreqBase32).toString('hex') |
|||
const bufferHex = bufferHexRotated.substr(bufferHexRotated.length - 1, bufferHexRotated.length) |
|||
+ bufferHexRotated.substr(0, bufferHexRotated.length - 1) |
|||
const buffer = Buffer.from(bufferHex, 'hex') |
|||
|
|||
const pubkeyBuffer = buffer.slice(0, 33); |
|||
const pubkey = pubkeyBuffer.toString('hex'); |
|||
|
|||
const paymentHashBuffer = buffer.slice(33, 65) |
|||
const paymentHashHex = paymentHashBuffer.toString('hex') |
|||
|
|||
const valueBuffer = buffer.slice(65, 73) |
|||
|
|||
const amount = convertBigEndianBufferToLong(valueBuffer) |
|||
|
|||
return { |
|||
payreq, |
|||
pubkey, |
|||
amount, |
|||
r_hash: paymentHashHex |
|||
} |
|||
} |
|||
|
|||
export default { |
|||
decodeInvoice |
|||
} |
Before Width: | Height: | Size: 2.3 KiB |