@ -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 |