Browse Source

Merge pull request #84 from LN-Zap/fix/channels

Fix UI/UX updates
renovate/lint-staged-8.x
JimmyMow 7 years ago
committed by GitHub
parent
commit
63a5efe561
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 133
      app/components/Channels/NetworkChannels.js
  2. 114
      app/components/Channels/NetworkChannels.scss
  3. 4
      app/components/LndSyncing/LndSyncing.js
  4. 1
      app/components/Nav/Nav.js
  5. 86
      app/components/Peers/Peers.js
  6. 59
      app/components/Peers/Peers.scss
  7. 13
      app/components/Wallet/ReceiveModal.js
  8. 11
      app/components/Wallet/ReceiveModal.scss
  9. 7
      app/components/Wallet/Wallet.js
  10. 63
      app/containers/Root.js
  11. BIN
      app/icons/1024x1024.png
  12. BIN
      app/icons/128x128.png
  13. BIN
      app/icons/16x16.png
  14. BIN
      app/icons/24x24.png
  15. BIN
      app/icons/256x256.png
  16. BIN
      app/icons/32x32.png
  17. BIN
      app/icons/48x48.png
  18. BIN
      app/icons/512x512.png
  19. BIN
      app/icons/64x64.png
  20. BIN
      app/icons/96x96.png
  21. 1
      app/icons/globe.svg
  22. 14
      app/lnd/methods/index.js
  23. 2
      app/lnd/methods/paymentsController.js
  24. 42
      app/lnd/utils/index.js
  25. 2
      app/main.dev.js
  26. 4
      app/reducers/address.js
  27. 26
      app/reducers/channels.js
  28. 8
      app/reducers/info.js
  29. 6
      app/reducers/lnd.js
  30. 43
      app/reducers/network.js
  31. 18
      app/routes/activity/components/Activity.js
  32. 6
      app/routes/activity/components/Activity.scss
  33. 9
      app/routes/activity/containers/ActivityContainer.js
  34. 22
      app/routes/app/components/App.js
  35. 175
      app/routes/channels/components/Channels.js
  36. 50
      app/routes/channels/components/Channels.scss
  37. 75
      app/routes/peers/components/Peers.js
  38. 55
      app/routes/peers/components/Peers.scss
  39. 1
      app/variables.scss
  40. 8
      package.json
  41. BIN
      resources/bin/darwin/lnd
  42. BIN
      resources/bin/linux/lnd
  43. 51
      resources/zap_2.svg
  44. 95
      yarn.lock

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;
}
}
}

4
app/components/LndSyncing/LndSyncing.js

@ -21,7 +21,7 @@ class LndSyncing extends Component {
}, },
{ {
title: 'Onion Routing', title: 'Onion Routing',
description: 'Onion routing is a technique for anonymous communication over a computer network. In an onion network, messages are encapsulated in layers of encryption, analogous to layers of an onion.' // eslint-diabale-line description: 'Onion routing is a technique for anonymous communication over a computer network. In an onion network, messages are encapsulated in layers of encryption, analogous to layers of an onion.' // eslint-disable-line
} }
], ],
currentFact: 0 currentFact: 0
@ -41,7 +41,7 @@ class LndSyncing extends Component {
<div className={styles.container}> <div className={styles.container}>
<h3>zap</h3> <h3>zap</h3>
<div className={styles.loading}> <div className={styles.loading}>
{!fetchingBlockHeight && <h4>{syncPercentage}%</h4>} {!fetchingBlockHeight && <h4>{syncPercentage > 0 && `${syncPercentage}%`}</h4>}
<div className={styles.spinner} /> <div className={styles.spinner} />
<h1>syncing your lightning node to the blockchain</h1> <h1>syncing your lightning node to the blockchain</h1>
</div> </div>

1
app/components/Nav/Nav.js

@ -6,7 +6,6 @@ import Isvg from 'react-inlinesvg'
import walletIcon from 'icons/wallet.svg' import walletIcon from 'icons/wallet.svg'
import peersIcon from 'icons/peers.svg' import peersIcon from 'icons/peers.svg'
import channelsIcon from 'icons/channels.svg' import channelsIcon from 'icons/channels.svg'
import settingsIcon from 'icons/settings.svg'
import styles from './Nav.scss' import styles from './Nav.scss'

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;
}
}

13
app/components/Wallet/ReceiveModal.js

@ -6,7 +6,7 @@ import QRCode from 'qrcode.react'
import { showNotification } from 'notifications' import { showNotification } from 'notifications'
import styles from './ReceiveModal.scss' import styles from './ReceiveModal.scss'
const ReceiveModal = ({ isOpen, hideActivityModal, pubkey, address }) => { const ReceiveModal = ({ isOpen, hideActivityModal, pubkey, address, newAddress }) => {
const customStyles = { const customStyles = {
overlay: { overlay: {
cursor: 'pointer' cursor: 'pointer'
@ -16,7 +16,7 @@ const ReceiveModal = ({ isOpen, hideActivityModal, pubkey, address }) => {
left: '20%', left: '20%',
right: '0', right: '0',
bottom: 'auto', bottom: 'auto',
width: '40%', width: '60%',
margin: '50px auto' margin: '50px auto'
} }
} }
@ -43,8 +43,12 @@ const ReceiveModal = ({ isOpen, hideActivityModal, pubkey, address }) => {
</section> </section>
<section> <section>
<h4>Deposit Address (<span onClick={() => copyOnClick(address)}>Copy</span>)</h4> <div className={styles.addressHeader}>
<h4>Deposit Address (<span onClick={() => copyOnClick(address)}>Copy</span>)</h4>
<span className={styles.newAddress} onClick={() => newAddress('p2pkh')}>New Address</span>
</div>
<p>{address}</p> <p>{address}</p>
<div className={styles.qrcode}> <div className={styles.qrcode}>
<QRCode value={address} /> <QRCode value={address} />
</div> </div>
@ -58,7 +62,8 @@ ReceiveModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
hideActivityModal: PropTypes.func.isRequired, hideActivityModal: PropTypes.func.isRequired,
pubkey: PropTypes.string.isRequired, pubkey: PropTypes.string.isRequired,
address: PropTypes.string.isRequired address: PropTypes.string.isRequired,
newAddress: PropTypes.func.isRequired
} }
export default ReceiveModal export default ReceiveModal

11
app/components/Wallet/ReceiveModal.scss

@ -36,3 +36,14 @@
} }
} }
} }
.addressHeader {
display: flex;
flex-direction: row;
justify-content: space-between;
.newAddress {
text-decoration: underline;
font-size: 12px;
}
}

7
app/components/Wallet/Wallet.js

@ -21,7 +21,8 @@ class Wallet extends Component {
const { const {
balance, balance,
address, address,
info info,
newAddress
} = this.props } = this.props
const { modalOpen } = this.state const { modalOpen } = this.state
@ -35,6 +36,7 @@ class Wallet extends Component {
hideActivityModal={() => this.setState({ modalOpen: false })} hideActivityModal={() => this.setState({ modalOpen: false })}
pubkey={info.data.identity_pubkey} pubkey={info.data.identity_pubkey}
address={address} address={address}
newAddress={newAddress}
/>) />)
} }
<div className={styles.content}> <div className={styles.content}>
@ -65,7 +67,8 @@ class Wallet extends Component {
Wallet.propTypes = { Wallet.propTypes = {
balance: PropTypes.object.isRequired, balance: PropTypes.object.isRequired,
address: PropTypes.string.isRequired, address: PropTypes.string.isRequired,
info: PropTypes.object.isRequired info: PropTypes.object.isRequired,
newAddress: PropTypes.func.isRequired
} }
export default Wallet export default Wallet

63
app/containers/Root.js

@ -2,6 +2,7 @@
import React from 'react' import React from 'react'
import { Provider, connect } from 'react-redux' import { Provider, connect } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux' import { ConnectedRouter } from 'react-router-redux'
import PropTypes from 'prop-types'
import { fetchBlockHeight, lndSelectors } from 'reducers/lnd' import { fetchBlockHeight, lndSelectors } from 'reducers/lnd'
import LoadingBolt from 'components/LoadingBolt' import LoadingBolt from 'components/LoadingBolt'
import LndSyncing from 'components/LndSyncing' import LndSyncing from 'components/LndSyncing'
@ -22,40 +23,42 @@ type RootType = {
history: {} history: {}
}; };
class Root extends React.Component { const Root = ({
render() { store,
const { history,
store, lnd,
history, fetchBlockHeight,
lnd, syncPercentage
fetchBlockHeight, }) => {
syncPercentage // If we are syncing show the syncing screen
} = this.props if (lnd.syncing) {
console.log('lnd: ', lnd)
console.log('lnd: ', lnd)
if (lnd.syncing) {
return (
<LndSyncing
fetchBlockHeight={fetchBlockHeight}
fetchingBlockHeight={lnd.fetchingBlockHeight}
syncPercentage={syncPercentage}
/>
)
}
if (!lnd.grpcStarted) { return <LoadingBolt /> }
return ( return (
<Provider store={store}> <LndSyncing
<ConnectedRouter history={history}> fetchBlockHeight={fetchBlockHeight}
<Routes /> fetchingBlockHeight={lnd.fetchingBlockHeight}
</ConnectedRouter> syncPercentage={syncPercentage}
</Provider> />
) )
} }
// Don't launch the app without gRPC connection
if (!lnd.grpcStarted) { return <LoadingBolt /> }
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>
)
} }
Root.propTypes = {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
lnd: PropTypes.object.isRequired,
fetchBlockHeight: PropTypes.func.isRequired,
syncPercentage: PropTypes.number.isRequired
}
export default connect(mapStateToProps, mapDispatchToProps)(Root) export default connect(mapStateToProps, mapDispatchToProps)(Root)

BIN
app/icons/1024x1024.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

BIN
app/icons/128x128.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
app/icons/16x16.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 B

BIN
app/icons/24x24.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
app/icons/256x256.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

BIN
app/icons/32x32.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
app/icons/48x48.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
app/icons/512x512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

BIN
app/icons/64x64.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

BIN
app/icons/96x96.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

1
app/icons/globe.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>

After

Width:  |  Height:  |  Size: 409 B

14
app/lnd/methods/index.js

@ -24,7 +24,6 @@ import * as networkController from './networkController'
export default function (lnd, event, msg, data) { export default function (lnd, event, msg, data) {
console.log('msg: ', msg)
switch (msg) { switch (msg) {
case 'info': case 'info':
networkController.getInfo(lnd) networkController.getInfo(lnd)
@ -32,10 +31,7 @@ export default function (lnd, event, msg, data) {
event.sender.send('receiveInfo', infoData) event.sender.send('receiveInfo', infoData)
event.sender.send('receiveCryptocurrency', infoData.chains[0]) event.sender.send('receiveCryptocurrency', infoData.chains[0])
}) })
.catch(error => { .catch(() => event.sender.send('infoFailed'))
console.log('error: ', error)
event.sender.send('infoFailed')
})
break break
case 'describeNetwork': case 'describeNetwork':
networkController.describeGraph(lnd) networkController.describeGraph(lnd)
@ -129,11 +125,11 @@ export default function (lnd, event, msg, data) {
// Payment looks like { payment_preimage: Buffer, payment_route: Object } // Payment looks like { payment_preimage: Buffer, payment_route: Object }
// { paymentRequest } = data // { paymentRequest } = data
paymentsController.sendPaymentSync(lnd, data) paymentsController.sendPaymentSync(lnd, data)
.then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))) .then(({ payment_route }) => {
.catch((error) => { console.log('payinvoice success: ', payment_route)
console.log('payinvoice error: ', error) event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))
event.sender.send('paymentFailed', { error: error.toString() })
}) })
.catch(({ error }) => event.sender.send('paymentFailed', { error: error.toString() }))
break break
case 'sendCoins': case 'sendCoins':
// Transaction looks like { txid: String } // Transaction looks like { txid: String }

2
app/lnd/methods/paymentsController.js

@ -9,6 +9,8 @@ export function sendPaymentSync(lnd, { paymentRequest }) {
lnd.sendPaymentSync({ payment_request: paymentRequest }, (err, data) => { lnd.sendPaymentSync({ payment_request: paymentRequest }, (err, data) => {
if (err) { reject(err) } if (err) { reject(err) }
if (!data.payment_route) { reject({ error: data.payment_error }) }
resolve(data) resolve(data)
}) })
}) })

42
app/lnd/utils/index.js

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

2
app/main.dev.js

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

4
app/reducers/address.js

@ -28,9 +28,7 @@ export const newAddress = type => async (dispatch) => {
} }
// Receive IPC event for info // Receive IPC event for info
export const receiveAddress = (event, address) => dispatch => { export const receiveAddress = (event, address) => dispatch => dispatch({ type: RECEIVE_ADDRESS, address })
dispatch({ type: RECEIVE_ADDRESS, address })
}
// ------------------------------------ // ------------------------------------
// Action Handlers // Action Handlers

26
app/reducers/channels.js

@ -1,10 +1,10 @@
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { btc } from 'utils' import { btc } from 'utils'
import { showNotification } from 'notifications'
import { fetchDescribeNetwork } from './network' import { fetchDescribeNetwork } from './network'
import { closeChannelForm } from './channelform' import { closeChannelForm, resetChannelForm } from './channelform'
import { setError } from './error' import { setError } from './error'
import { showNotification } from 'notifications'
// ------------------------------------ // ------------------------------------
// Constants // Constants
// ------------------------------------ // ------------------------------------
@ -113,28 +113,34 @@ export const openChannel = ({ pubkey, local_amt, push_amt }) => (dispatch) => {
// TODO: Decide how to handle streamed updates for channels // TODO: Decide how to handle streamed updates for channels
// Receive IPC event for openChannel // Receive IPC event for openChannel
export const channelSuccessful = () => (dispatch) => { export const channelSuccessful = () => (dispatch) => {
console.log('CHANNEL channelSuccessful')
dispatch(fetchChannels()) dispatch(fetchChannels())
dispatch(closeChannelForm()) dispatch(closeChannelForm())
dispatch(resetChannelForm())
} }
// Receive IPC event for updated channel // Receive IPC event for updated channel
export const pushchannelupdated = () => (dispatch) => { export const pushchannelupdated = (event, data) => (dispatch) => {
console.log('PUSH CHANNEL UPDATED: ', data)
dispatch(fetchChannels()) dispatch(fetchChannels())
} }
// Receive IPC event for channel end // Receive IPC event for channel end
export const pushchannelend = event => (dispatch) => { // eslint-disable-line export const pushchannelend = event => (dispatch) => { // eslint-disable-line
console.log('PUSH CHANNEL END: ')
dispatch(fetchChannels()) dispatch(fetchChannels())
} }
// Receive IPC event for channel error // Receive IPC event for channel error
export const pushchannelerror = (event, { error }) => (dispatch) => { export const pushchannelerror = (event, { error }) => (dispatch) => {
console.log('PUSH CHANNEL ERROR: ', error)
dispatch(openingFailure()) dispatch(openingFailure())
dispatch(setError(error)) dispatch(setError(error))
} }
// Receive IPC event for channel status // Receive IPC event for channel status
export const pushchannelstatus = event => (dispatch) => { // eslint-disable-line export const pushchannelstatus = (event, data) => (dispatch) => { // eslint-disable-line
console.log('PUSH CHANNEL STATUS: ', data)
dispatch(fetchChannels()) dispatch(fetchChannels())
} }
@ -194,18 +200,18 @@ export const channelGraphData = (event, data) => (dispatch, getState) => {
dispatch(fetchDescribeNetwork()) dispatch(fetchDescribeNetwork())
// loop through the channel updates // loop through the channel updates
for(let i = 0; i < channel_updates.length; i++) { for (let i = 0; i < channel_updates.length; i++) {
let channel_update = channel_updates[i] const channel_update = channel_updates[i]
let { advertising_node, connecting_node } = channel_update const { advertising_node, connecting_node } = channel_update
// if our node is involved in this update we wanna show a notification // if our node is involved in this update we wanna show a notification
if(info.data.identity_pubkey === advertising_node || info.data.identity_pubkey === connecting_node) { if (info.data.identity_pubkey === advertising_node || info.data.identity_pubkey === connecting_node) {
// this channel has to do with the user, lets fetch a new channel list for them // this channel has to do with the user, lets fetch a new channel list for them
// TODO: full fetch is probably not necessary // TODO: full fetch is probably not necessary
dispatch(fetchChannels()) dispatch(fetchChannels())
// Construct the notification // Construct the notification
let otherParty = info.data.identity_pubkey === advertising_node ? connecting_node : advertising_node const otherParty = info.data.identity_pubkey === advertising_node ? connecting_node : advertising_node
let notifBody = `No new friends, just new channels. Your channel with ${otherParty}` // eslint-disable-line let notifBody = `No new friends, just new channels. Your channel with ${otherParty}` // eslint-disable-line
const notifTitle = 'New channel detected' const notifTitle = 'New channel detected'
@ -217,7 +223,7 @@ export const channelGraphData = (event, data) => (dispatch, getState) => {
} }
// IPC event for channel graph status // IPC event for channel graph status
export const channelGraphStatus = (event, data) => (dispatch) => { export const channelGraphStatus = (event, data) => () => {
console.log('channelGraphStatus: ', data) console.log('channelGraphStatus: ', data)
} }

8
app/reducers/info.js

@ -19,23 +19,19 @@ export function getInfo() {
// Send IPC event for getinfo // Send IPC event for getinfo
export const fetchInfo = () => async (dispatch) => { export const fetchInfo = () => async (dispatch) => {
console.log('fetching info')
dispatch(getInfo()) dispatch(getInfo())
ipcRenderer.send('lnd', { msg: 'info' }) ipcRenderer.send('lnd', { msg: 'info' })
} }
// Receive IPC event for info // Receive IPC event for info
export const receiveInfo = (event, data) => dispatch => { export const receiveInfo = (event, data) => (dispatch) => {
console.log('receiving info and fetching other stuff')
dispatch(fetchBalance()) dispatch(fetchBalance())
dispatch(newAddress('p2pkh')) dispatch(newAddress('p2pkh'))
dispatch({ type: RECEIVE_INFO, data }) dispatch({ type: RECEIVE_INFO, data })
} }
// IPC info fetch failed // IPC info fetch failed
export const infoFailed = (event, data) => dispatch => { // export const infoFailed = (event, data) => dispatch => {}
console.log('INFO FAILED data: ', data)
}
// ------------------------------------ // ------------------------------------
// Action Handlers // Action Handlers

6
app/reducers/lnd.js

@ -41,12 +41,12 @@ export const lndSynced = () => (dispatch) => {
showNotification(notifTitle, notifBody) showNotification(notifTitle, notifBody)
} }
export const grpcDisconnected = () => (dispatch) => dispatch({ type: GRPC_DISCONNECTED }) export const grpcDisconnected = () => dispatch => dispatch({ type: GRPC_DISCONNECTED })
export const grpcConnected = () => (dispatch) => dispatch({ type: GRPC_CONNECTED }) export const grpcConnected = () => dispatch => dispatch({ type: GRPC_CONNECTED })
// Receive IPC event for LND streaming a line // Receive IPC event for LND streaming a line
export const lndStdout = (event, line) => dispatch => { export const lndStdout = (event, line) => (dispatch) => {
let height let height
let trimmed let trimmed

43
app/reducers/network.js

@ -14,6 +14,12 @@ export const SET_CURRENT_ROUTE = 'SET_CURRENT_ROUTE'
export const SET_CURRENT_CHANNEL = 'SET_CURRENT_CHANNEL' export const SET_CURRENT_CHANNEL = 'SET_CURRENT_CHANNEL'
export const SET_CURRENT_TAB = 'SET_CURRENT_TAB'
export const SET_CURRENT_PEER = 'SET_CURRENT_PEER'
export const UPDATE_PAY_REQ = 'UPDATE_PAY_REQ'
// ------------------------------------ // ------------------------------------
// Actions // Actions
// ------------------------------------ // ------------------------------------
@ -44,6 +50,27 @@ export function setCurrentChannel(selectedChannel) {
} }
} }
export function setCurrentTab(currentTab) {
return {
type: SET_CURRENT_TAB,
currentTab
}
}
export function setCurrentPeer(currentPeer) {
return {
type: SET_CURRENT_PEER,
currentPeer
}
}
export function updatePayReq(pay_req) {
return {
type: UPDATE_PAY_REQ,
pay_req
}
}
// Send IPC event for describeNetwork // Send IPC event for describeNetwork
export const fetchDescribeNetwork = () => (dispatch) => { export const fetchDescribeNetwork = () => (dispatch) => {
dispatch(getDescribeNetwork()) dispatch(getDescribeNetwork())
@ -83,7 +110,13 @@ const ACTION_HANDLERS = {
} }
), ),
[SET_CURRENT_CHANNEL]: (state, { selectedChannel }) => ({ ...state, selectedChannel }) [SET_CURRENT_CHANNEL]: (state, { selectedChannel }) => ({ ...state, selectedChannel }),
[SET_CURRENT_TAB]: (state, { currentTab }) => ({ ...state, currentTab }),
[SET_CURRENT_PEER]: (state, { currentPeer }) => ({ ...state, currentPeer }),
[UPDATE_PAY_REQ]: (state, { pay_req }) => ({ ...state, pay_req })
} }
// ------------------------------------ // ------------------------------------
@ -115,7 +148,13 @@ const initialState = {
routes: [], routes: [],
currentRoute: {} currentRoute: {}
}, },
selectedChannel: {} selectedChannel: {},
currentTab: 1,
currentPeer: {},
pay_req: ''
} }

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

@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import { MdSearch } from 'react-icons/lib/md' import { MdSearch } from 'react-icons/lib/md'
import { FaAngleDown } from 'react-icons/lib/fa' import { FaAngleDown } from 'react-icons/lib/fa'
import Wallet from 'components/Wallet'
import Invoice from './components/Invoice' import Invoice from './components/Invoice'
import Payment from './components/Payment' import Payment from './components/Payment'
import Transaction from './components/Transaction' import Transaction from './components/Transaction'
@ -44,6 +45,9 @@ class Activity extends Component {
ticker, ticker,
searchInvoices, searchInvoices,
invoice: { invoicesSearchText, invoiceLoading }, invoice: { invoicesSearchText, invoiceLoading },
address: { address },
balance,
info,
payment: { paymentLoading }, payment: { paymentLoading },
currentTicker, currentTicker,
activity: { modal, filter, filterPulldown }, activity: { modal, filter, filterPulldown },
@ -51,7 +55,8 @@ class Activity extends Component {
changeFilter, changeFilter,
toggleFilterPulldown, toggleFilterPulldown,
currentActivity, currentActivity,
nonActiveFilters nonActiveFilters,
newAddress
} = this.props } = this.props
if (invoiceLoading || paymentLoading) { return <div>Loading...</div> } if (invoiceLoading || paymentLoading) { return <div>Loading...</div> }
@ -66,6 +71,8 @@ class Activity extends Component {
currentTicker={currentTicker} currentTicker={currentTicker}
/> />
<Wallet balance={balance} address={address} info={info} newAddress={newAddress} />
<div className={styles.search}> <div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='invoiceSearch'> <label className={`${styles.label} ${styles.input}`} htmlFor='invoiceSearch'>
<MdSearch /> <MdSearch />
@ -116,18 +123,25 @@ Activity.propTypes = {
fetchPayments: PropTypes.func.isRequired, fetchPayments: PropTypes.func.isRequired,
fetchInvoices: PropTypes.func.isRequired, fetchInvoices: PropTypes.func.isRequired,
fetchTransactions: PropTypes.func.isRequired, fetchTransactions: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired, ticker: PropTypes.object.isRequired,
searchInvoices: PropTypes.func.isRequired, searchInvoices: PropTypes.func.isRequired,
invoice: PropTypes.object.isRequired, invoice: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired, payment: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,
showActivityModal: PropTypes.func.isRequired, showActivityModal: PropTypes.func.isRequired,
hideActivityModal: PropTypes.func.isRequired, hideActivityModal: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired, changeFilter: PropTypes.func.isRequired,
newAddress: PropTypes.func.isRequired,
toggleFilterPulldown: PropTypes.func.isRequired, toggleFilterPulldown: PropTypes.func.isRequired,
activity: PropTypes.object.isRequired, activity: PropTypes.object.isRequired,
currentActivity: PropTypes.array.isRequired, currentActivity: PropTypes.array.isRequired,
nonActiveFilters: PropTypes.array.isRequired nonActiveFilters: PropTypes.array.isRequired,
address: PropTypes.object.isRequired,
balance: PropTypes.object.isRequired,
info: PropTypes.object.isRequired
} }
export default Activity export default Activity

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

@ -1,7 +1,7 @@
@import '../../../variables.scss'; @import '../../../variables.scss';
.search { .search {
height: 75px; height: 55px;
padding: 2px; padding: 2px;
border-bottom: 1px solid $darkgrey; border-bottom: 1px solid $darkgrey;
@ -13,7 +13,7 @@
.label { .label {
width: 5%; width: 5%;
line-height: 70px; line-height: 50px;
font-size: 25px; font-size: 25px;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
@ -25,7 +25,7 @@
padding: 0; padding: 0;
border: 0; border: 0;
border-radius: 0; border-radius: 0;
height: 68px; height: 50px;
font-size: 18px; font-size: 18px;
} }
} }

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

@ -19,6 +19,8 @@ import {
toggleFilterPulldown, toggleFilterPulldown,
activitySelectors activitySelectors
} from 'reducers/activity' } from 'reducers/activity'
import { newAddress } from 'reducers/address'
import Activity from '../components/Activity' import Activity from '../components/Activity'
const mapDispatchToProps = { const mapDispatchToProps = {
@ -31,12 +33,17 @@ const mapDispatchToProps = {
showActivityModal, showActivityModal,
hideActivityModal, hideActivityModal,
changeFilter, changeFilter,
toggleFilterPulldown toggleFilterPulldown,
newAddress
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
activity: state.activity, activity: state.activity,
balance: state.balance,
address: state.address,
info: state.info,
payment: state.payment, payment: state.payment,
invoice: state.invoice, invoice: state.invoice,

22
app/routes/app/components/App.js

@ -1,17 +1,15 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import LndSyncing from 'components/LndSyncing'
import GlobalError from 'components/GlobalError' import GlobalError from 'components/GlobalError'
import LoadingBolt from 'components/LoadingBolt' import LoadingBolt from 'components/LoadingBolt'
import Form from 'components/Form' import Form from 'components/Form'
import ModalRoot from 'components/ModalRoot' import ModalRoot from 'components/ModalRoot'
import Nav from 'components/Nav' import Nav from 'components/Nav'
import Wallet from 'components/Wallet'
import styles from './App.scss' import styles from './App.scss'
class App extends Component { class App extends Component {
componentWillMount() { componentWillMount() {
const { fetchTicker, fetchBalance, fetchInfo, newAddress, lnd: { syncing } } = this.props const { fetchTicker, fetchBalance, fetchInfo, newAddress } = this.props
fetchTicker() fetchTicker()
fetchBalance() fetchBalance()
@ -21,17 +19,11 @@ class App extends Component {
render() { render() {
const { const {
lnd,
syncPercentage,
fetchBlockHeight,
modal: { modalType, modalProps }, modal: { modalType, modalProps },
hideModal, hideModal,
ticker, ticker,
currentTicker, currentTicker,
address: { address },
balance, balance,
info,
form, form,
openPayForm, openPayForm,
@ -66,11 +58,6 @@ class App extends Component {
/> />
<div className={styles.content}> <div className={styles.content}>
<Wallet
balance={balance}
address={address}
info={info}
/>
{children} {children}
</div> </div>
</div> </div>
@ -79,16 +66,9 @@ class App extends Component {
} }
App.propTypes = { App.propTypes = {
lnd: PropTypes.object.isRequired,
syncPercentage: PropTypes.number.isRequired,
fetchBlockHeight: PropTypes.func.isRequired,
modal: PropTypes.object.isRequired, modal: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired, ticker: PropTypes.object.isRequired,
address: PropTypes.object.isRequired,
balance: PropTypes.object.isRequired, balance: PropTypes.object.isRequired,
info: PropTypes.object.isRequired,
form: PropTypes.object.isRequired, form: PropTypes.object.isRequired,
formProps: PropTypes.object.isRequired, formProps: PropTypes.object.isRequired,
closeForm: PropTypes.func.isRequired, closeForm: PropTypes.func.isRequired,

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

@ -1,24 +1,30 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { FaAlignJustify, FaGlobe, FaAngleDown, FaRepeat } from 'react-icons/lib/fa' import { FaAngleDown, FaRepeat } from 'react-icons/lib/fa'
import { MdSearch } from 'react-icons/lib/md' import { MdSearch } from 'react-icons/lib/md'
import OpenPendingChannel from 'components/Channels/OpenPendingChannel' import OpenPendingChannel from 'components/Channels/OpenPendingChannel'
import ClosedPendingChannel from 'components/Channels/ClosedPendingChannel' import ClosedPendingChannel from 'components/Channels/ClosedPendingChannel'
import Channel from 'components/Channels/Channel' import Channel from 'components/Channels/Channel'
import NetworkChannels from 'components/Channels/NetworkChannels'
import ChannelForm from 'components/ChannelForm' import ChannelForm from 'components/ChannelForm'
import styles from './Channels.scss' import styles from './Channels.scss'
class Channels extends Component { class Channels extends Component {
constructor(props) {
super(props)
this.state = {
refreshing: false
}
}
componentWillMount() { componentWillMount() {
const { fetchChannels, fetchPeers, fetchDescribeNetwork } = this.props const { fetchChannels, fetchPeers } = this.props
fetchChannels() fetchChannels()
fetchPeers() fetchPeers()
fetchDescribeNetwork()
} }
render() { render() {
@ -36,48 +42,61 @@ class Channels extends Component {
toggleFilterPulldown, toggleFilterPulldown,
changeFilter, changeFilter,
activeChannels,
currentChannels, currentChannels,
openChannels,
updateChannelSearchQuery, updateChannelSearchQuery,
setViewType,
openChannelForm, openChannelForm,
ticker, ticker,
currentTicker, currentTicker,
channelFormProps, channelFormProps
network,
identity_pubkey,
setCurrentChannel
} = this.props } = this.props
const refreshClicked = (event) => { const refreshClicked = () => {
// turn the spinner on
this.setState({ refreshing: true })
// store event in icon so we dont get an error when react clears it // store event in icon so we dont get an error when react clears it
const icon = event.currentTarget const icon = this.refs.repeat.childNodes
// fetch peers // fetch peers
fetchChannels() fetchChannels()
// clear animation after the second so we can reuse it // wait for the svg to appear as child
setTimeout(() => { icon.style.animation = '' }, 1000) const svgTimeout = setTimeout(() => {
if (icon[0].tagName === 'svg') {
// spin icon for 1 sec // spin icon for 1 sec
icon.style.animation = 'spin 1000ms linear 1' icon[0].style.animation = 'spin 1000ms linear 1'
} clearTimeout(svgTimeout)
}
}, 1)
const networkClicked = () => { // clear animation after the second so we can reuse it
if (!activeChannels.length) { return } const refreshTimeout = setTimeout(() => {
icon[0].style.animation = ''
setViewType(1) this.setState({ refreshing: false })
clearTimeout(refreshTimeout)
}, 1000)
} }
return ( return (
<div className={`${styles.container} ${viewType === 1 && styles.graphview}`}> <div className={`${styles.container} ${viewType === 1 && styles.graphview}`}>
<ChannelForm {...channelFormProps} /> <ChannelForm {...channelFormProps} />
<header className={styles.header}>
<div className={styles.titleContainer}>
<div className={styles.left}>
<h1>Channels</h1>
</div>
</div>
<div className={styles.createChannelContainer}>
<div className={`buttonPrimary ${styles.newChannelButton}`} onClick={openChannelForm}>
Create new channel
</div>
</div>
</header>
<div className={styles.search}> <div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'> <label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'>
<MdSearch /> <MdSearch />
@ -91,21 +110,6 @@ class Channels extends Component {
id='channelSearch' id='channelSearch'
/> />
</div> </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
</div>
</div>
</header>
<div className={styles.filtersContainer}> <div className={styles.filtersContainer}>
<section> <section>
@ -122,62 +126,55 @@ class Channels extends Component {
} }
</ul> </ul>
</section> </section>
<section className={`${styles.refreshContainer} hint--left`} data-hint='Refresh your channels list'> <section className={styles.refreshContainer}>
<FaRepeat <span className={styles.refresh} onClick={refreshClicked} ref='repeat'>
style={{ verticalAlign: 'baseline' }} {
onClick={refreshClicked} this.state.refreshing ?
/> <FaRepeat />
:
'Refresh'
}
</span>
</section> </section>
</div> </div>
<div className={`${styles.channels} ${filterPulldown && styles.fade}`}> <div className={`${styles.channels} ${filterPulldown && styles.fade}`}>
{ <ul className={viewType === 1 && styles.cardsContainer}>
viewType === 0 && {
<ul className={viewType === 1 && styles.cardsContainer}> currentChannels.map((channel, index) => {
{ if (Object.prototype.hasOwnProperty.call(channel, 'blocks_till_open')) {
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/'}
/>
)
}
return ( return (
<Channel <OpenPendingChannel
key={index} key={index}
channel={channel}
ticker={ticker} 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} channel={channel}
closeChannel={closeChannel} ticker={ticker}
currentTicker={currentTicker} currentTicker={currentTicker}
explorerLinkBase={'https://testnet.smartbit.com.au/'}
/> />
) )
}) }
} return (
</ul> <Channel
} key={index}
{ viewType === 1 && ticker={ticker}
<NetworkChannels channel={channel}
channels={openChannels} closeChannel={closeChannel}
network={network} currentTicker={currentTicker}
identity_pubkey={identity_pubkey} />
setCurrentChannel={setCurrentChannel} )
/> })
} }
</ul>
</div> </div>
</div> </div>
) )
@ -186,29 +183,23 @@ class Channels extends Component {
Channels.propTypes = { Channels.propTypes = {
fetchChannels: PropTypes.func.isRequired, fetchChannels: PropTypes.func.isRequired,
fetchPeers: PropTypes.func.isRequired,
channels: PropTypes.object.isRequired, channels: PropTypes.object.isRequired,
currentChannels: PropTypes.array.isRequired, currentChannels: PropTypes.array.isRequired,
openChannels: PropTypes.array.isRequired,
nonActiveFilters: PropTypes.array.isRequired, nonActiveFilters: PropTypes.array.isRequired,
updateChannelSearchQuery: PropTypes.func.isRequired, updateChannelSearchQuery: PropTypes.func.isRequired,
setViewType: PropTypes.func.isRequired,
setCurrentChannel: PropTypes.func.isRequired, setCurrentChannel: PropTypes.func.isRequired,
openChannelForm: PropTypes.func.isRequired, openChannelForm: PropTypes.func.isRequired,
closeChannel: PropTypes.func.isRequired, closeChannel: PropTypes.func.isRequired,
toggleFilterPulldown: PropTypes.func.isRequired, toggleFilterPulldown: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired, changeFilter: PropTypes.func.isRequired,
fetchPeers: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired, ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,
channelFormProps: PropTypes.object.isRequired, channelFormProps: PropTypes.object.isRequired
network: PropTypes.object.isRequired,
fetchDescribeNetwork: PropTypes.func.isRequired,
identity_pubkey: PropTypes.string.isRequired
} }
export default Channels export default Channels

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

@ -5,8 +5,9 @@
} }
.search { .search {
height: 75px; height: 55px;
padding: 2px 25px; padding: 2px 25px;
border-top: 1px solid $darkgrey;
border-bottom: 1px solid $darkgrey; border-bottom: 1px solid $darkgrey;
background: $white; background: $white;
@ -18,8 +19,8 @@
.label { .label {
width: 5%; width: 5%;
line-height: 70px; line-height: 50px;
font-size: 25px; font-size: 20px;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
} }
@ -30,8 +31,8 @@
padding: 0; padding: 0;
border: 0; border: 0;
border-radius: 0; border-radius: 0;
height: 68px; height: 50px;
font-size: 18px; font-size: 16px;
} }
} }
@ -39,6 +40,30 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
background: $lightgrey;
.titleContainer {
padding: 20px 40px;
.left {
padding: 10px 0;
h1 {
text-transform: uppercase;
font-size: 26px;
margin-right: 5px;
}
}
}
.createChannelContainer {
padding: 20px 40px;
.createChannelButton {
font-size: 14px;
margin-left: 10px;
}
}
} }
.filtersContainer { .filtersContainer {
@ -46,7 +71,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
padding: 0 40px; padding: 20px 40px;
h2, h2 span { h2, h2 span {
color: $bluegrey; color: $bluegrey;
@ -91,15 +116,20 @@
} }
.refreshContainer { .refreshContainer {
color: $bluegrey; text-align: right;
cursor: pointer;
&:hover { .refresh {
cursor: pointer; text-decoration: underline;
color: lighten($bluegrey, 10%);
svg {
font-size: 12px;
}
} }
} }
} }
.layoutsContainer { .layoutsContainer {
padding: 40px; padding: 40px;

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

@ -1,9 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Isvg from 'react-inlinesvg'
import userIcon from 'icons/user.svg' import { FaRepeat } from 'react-icons/lib/fa'
import { FaUser, FaRepeat } from 'react-icons/lib/fa'
import { MdSearch } from 'react-icons/lib/md' import { MdSearch } from 'react-icons/lib/md'
import PeerForm from 'components/Peers/PeerForm' import PeerForm from 'components/Peers/PeerForm'
@ -13,6 +11,14 @@ import Peer from 'components/Peers/Peer'
import styles from './Peers.scss' import styles from './Peers.scss'
class Peers extends Component { class Peers extends Component {
constructor(props) {
super(props)
this.state = {
refreshing: false
}
}
componentWillMount() { componentWillMount() {
this.props.fetchPeers() this.props.fetchPeers()
} }
@ -28,22 +34,34 @@ class Peers extends Component {
peerModalOpen, peerModalOpen,
filteredPeers, filteredPeers,
peers: { peer, searchQuery }, peers: { peer, searchQuery }
info: { data: { identity_pubkey } }
} = this.props } = this.props
const refreshClicked = event => { const refreshClicked = () => {
// turn the spinner on
this.setState({ refreshing: true })
// store event in icon so we dont get an error when react clears it // store event in icon so we dont get an error when react clears it
const icon = event.currentTarget const icon = this.refs.repeat.childNodes
// fetch peers // fetch peers
fetchPeers() fetchPeers()
// clear animation after the second so we can reuse it // wait for the svg to appear as child
setTimeout(() => { icon.style.animation = '' }, 1000) const 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 // clear animation after the second so we can reuse it
icon.style.animation = 'spin 1000ms linear 1' const refreshTimeout = setTimeout(() => {
icon[0].style.animation = ''
this.setState({ refreshing: false })
clearTimeout(refreshTimeout)
}, 1000)
} }
return ( return (
@ -55,15 +73,7 @@ class Peers extends Component {
<header className={styles.header}> <header className={styles.header}>
<div className={styles.titleContainer}> <div className={styles.titleContainer}>
<div className={styles.left}> <div className={styles.left}>
<div className={styles.identityPubkey}> <h1>Peers</h1>
<section className={styles.userIcon}>
<Isvg src={userIcon} />
</section>
<section>
<h4>Your node public key</h4>
<h2>{identity_pubkey}</h2>
</section>
</div>
</div> </div>
</div> </div>
<div className={styles.addPeerContainer}> <div className={styles.addPeerContainer}>
@ -74,7 +84,6 @@ class Peers extends Component {
</header> </header>
<div className={styles.search}> <div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'> <label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'>
<MdSearch /> <MdSearch />
</label> </label>
@ -89,10 +98,13 @@ class Peers extends Component {
</div> </div>
<div className={styles.refreshContainer}> <div className={styles.refreshContainer}>
<span className={`${styles.refresh} hint--top`} data-hint='Refresh your peers list'> <span className={styles.refresh} onClick={refreshClicked} ref='repeat'>
<FaRepeat {
onClick={refreshClicked} this.state.refreshing ?
/> <FaRepeat />
:
'Refresh'
}
</span> </span>
</div> </div>
@ -107,7 +119,18 @@ class Peers extends Component {
} }
Peers.propTypes = { 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,
peers: PropTypes.object.isRequired,
peer: PropTypes.object,
searchQuery: PropTypes.string
} }
export default Peers export default Peers

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

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

1
app/variables.scss

@ -11,6 +11,7 @@ $darkestgrey: #999999;
$bluegrey: #555459; $bluegrey: #555459;
$green: #0bb634; $green: #0bb634;
$terminalgreen: #00FF00;
$red: #ff0b00; $red: #ff0b00;
$blue: #007bb6; $blue: #007bb6;
$curve: cubic-bezier(0.650, 0.000, 0.450, 1.000); $curve: cubic-bezier(0.650, 0.000, 0.450, 1.000);

8
package.json

@ -202,7 +202,6 @@
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qrcode.react": "^0.7.1", "qrcode.react": "^0.7.1",
"react": "^15.6.1", "react": "^15.6.1",
"react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.6.1", "react-dom": "^15.6.1",
"react-hot-loader": "3.0.0-beta.6", "react-hot-loader": "3.0.0-beta.6",
"react-inlinesvg": "^0.6.2", "react-inlinesvg": "^0.6.2",
@ -212,18 +211,13 @@
"react-router": "^4.1.1", "react-router": "^4.1.1",
"react-router-dom": "^4.1.1", "react-router-dom": "^4.1.1",
"react-router-redux": "^5.0.0-alpha.6", "react-router-redux": "^5.0.0-alpha.6",
"react-svg": "^2.1.21",
"react-svg-morph": "^0.1.10",
"react-vis-force": "^0.3.1",
"react-websocket": "^1.1.7",
"redux": "^3.7.1", "redux": "^3.7.1",
"redux-electron-ipc": "^1.1.10", "redux-electron-ipc": "^1.1.10",
"redux-thunk": "^2.2.0", "redux-thunk": "^2.2.0",
"reselect": "^3.0.1", "reselect": "^3.0.1",
"satoshi-bitcoin": "^1.0.4", "satoshi-bitcoin": "^1.0.4",
"source-map-support": "^0.4.15", "source-map-support": "^0.4.15",
"xtend": "^4.0.1", "xtend": "^4.0.1"
"zbase32": "^0.0.2"
}, },
"devEngines": { "devEngines": {
"node": ">=7.x", "node": ">=7.x",

BIN
resources/bin/darwin/lnd

Binary file not shown.

BIN
resources/bin/linux/lnd

Binary file not shown.

51
resources/zap_2.svg

@ -1,51 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="128" height="128" viewBox="0 0 100 100">
<defs>
<g id="background">
<path fill="url(#background-gradient)" stroke="none" d=" M 90.75 23.05 Q 89.5 23.05 88.85 24.15 88.15 25.25 88.85 26.35 97.25 40.2 95.15 56.15 93.05 71.65 81.75 82.6 70.35 93.7 54.9 95.25 32.95 97.55 17.6 81.85 2.2 66.2 4.85 44.2 6.6 30.2 16.15 19.7 25.65 9.1 39.25 5.8 50.55 3 61.7 5.95 62.85 6.3 63.7 5.65 64.6 5 64.6 3.85 64.6 2.25 62.95 1.7 52.55 -1.1 41.6 0.65 25.55 3.3 14.4 14.85 3.1 26.25 0.65 42.4 -2.95 66.9 14 84.5 30.85 102.1 55.2 99.7 72.6 97.9 85.2 85.4 97.95 72.8 99.7 55.35 101.45 38.55 92.75 24.05 92.05 23.05 90.75 23.05 Z"/>
</g><mask id="mask">
<path fill="#FFFFFF" stroke="none" d=" M 82.2 82.15 Q 95.5 68.85 95.5 49.95 95.5 31.1 82.2 17.8 68.85 4.5 50 4.5 31.15 4.5 17.8 17.8 4.5 31.1 4.5 49.95 4.5 68.85 17.8 82.15 31.15 95.45 50 95.45 68.85 95.45 82.2 82.15 Z"/>
</mask>
<linearGradient id="background-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#1d1d1d" offset="0%"/>
<stop stop-color="#ebb864" offset="100%"/>
</linearGradient>
<g transform="scale(2.0833333333333335)" id="picture"><path d="M38 8c-.8 0-1.5.1-2.2.3C33.6 3.4 28.7 0 23 0 15.9 0 10.1 5.3 9.1 12.1 8.8 12 8.4 12 8 12c-4.4 0-8 3.6-8 8s3.6 8 8 8h30c5.5 0 10-4.5 10-10S43.5 8 38 8zm0 18H8c-3.3 0-6-2.7-6-6s2.7-6 6-6c.3 0 .5 0 .9.1l2 .3.3-2C11.9 6.5 17 2 23 2c4.7 0 9 2.8 10.9 7.1l.7 1.5 1.6-.4c.6-.1 1.2-.2 1.8-.2 4.4 0 8 3.6 8 8s-3.6 8-8 8zm-10.7 8l1.9-4H18.7L16 35.5c-.1.2 0 .5.3.5h4.5L16 47.8c0 .1 0 .2.1.2 0 0 .1 0 .2-.1l15.6-13.6c.2-.2.2-.3-.1-.3h-4.5zm-3.9 3.8l.3-.6.1-.1h.6l-1 .7z"/></g>
<linearGradient id="picture-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#1d1d1d" offset="0%"/>
<stop stop-color="#ebb864" offset="100%"/>
</linearGradient>
</defs>
<use xlink:href="#background" fill="url(#background-gradient)" />
<g mask="url(#mask)">
<g transform="
translate(
50 50
)
translate(0 0) scale(0.5)
translate(
-50 -50
)">
<use xlink:href="#picture" fill="url(#picture-gradient)" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

95
yarn.lock

@ -1837,10 +1837,6 @@ center-align@^0.1.1:
align-text "^0.1.3" align-text "^0.1.3"
lazy-cache "^1.0.3" lazy-cache "^1.0.3"
chain-function@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
chainsaw@~0.1.0: chainsaw@~0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
@ -2532,31 +2528,6 @@ currently-unhandled@^0.4.1:
dependencies: dependencies:
array-find-index "^1.0.1" array-find-index "^1.0.1"
d3-collection@1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
d3-dispatch@1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8"
d3-force@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3"
dependencies:
d3-collection "1"
d3-dispatch "1"
d3-quadtree "1"
d3-timer "1"
d3-quadtree@1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438"
d3-timer@1:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531"
d@1: d@1:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
@ -2778,10 +2749,6 @@ dom-converter@~0.1:
dependencies: dependencies:
utila "~0.3" utila "~0.3"
dom-helpers@^3.2.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
dom-serializer@0, dom-serializer@~0.1.0: dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@ -5603,7 +5570,7 @@ lodash.range@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash.range/-/lodash.range-3.2.0.tgz#f461e588f66683f7eadeade513e38a69a565a15d" resolved "https://registry.yarnpkg.com/lodash.range/-/lodash.range-3.2.0.tgz#f461e588f66683f7eadeade513e38a69a565a15d"
lodash.reduce@^4.4.0, lodash.reduce@^4.6.0: lodash.reduce@^4.4.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
@ -6973,7 +6940,7 @@ promise@^7.1.1:
dependencies: dependencies:
asap "~2.0.3" asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8: prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8:
version "15.5.10" version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies: dependencies:
@ -7096,12 +7063,6 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-addons-css-transition-group@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/react-addons-css-transition-group/-/react-addons-css-transition-group-15.6.0.tgz#69887cf6e4874d25cd66e22a699e29f0d648aba0"
dependencies:
react-transition-group "^1.2.0"
react-addons-test-utils@^15.6.0: react-addons-test-utils@^15.6.0:
version "15.6.0" version "15.6.0"
resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.6.0.tgz#062d36117fe8d18f3ba5e06eb33383b0b85ea5b9" resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.6.0.tgz#062d36117fe8d18f3ba5e06eb33383b0b85ea5b9"
@ -7182,10 +7143,6 @@ react-redux@^5.0.5:
loose-envify "^1.1.0" loose-envify "^1.1.0"
prop-types "^15.5.10" prop-types "^15.5.10"
react-render-to-json@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/react-render-to-json/-/react-render-to-json-0.0.4.tgz#0db588e2952cea05a66a3390554c46c3ea74d006"
react-router-dom@^4.1.1: react-router-dom@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.1.1.tgz#3021ade1f2c160af97cf94e25594c5f294583025" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.1.1.tgz#3021ade1f2c160af97cf94e25594c5f294583025"
@ -7215,19 +7172,6 @@ react-router@^4.1.1:
prop-types "^15.5.4" prop-types "^15.5.4"
warning "^3.0.0" warning "^3.0.0"
react-svg-morph@^0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/react-svg-morph/-/react-svg-morph-0.1.10.tgz#780cf6823a6ab640a1367e43b7c9509d68157bf3"
dependencies:
react-render-to-json "0.0.4"
svgpath "^2.1.0"
react-svg@^2.1.21:
version "2.1.21"
resolved "https://registry.yarnpkg.com/react-svg/-/react-svg-2.1.21.tgz#0caf34002649542967f04e197480d784768e914c"
dependencies:
svg-injector "^1.1.3"
react-test-renderer@^15.6.1: react-test-renderer@^15.6.1:
version "15.6.1" version "15.6.1"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.1.tgz#026f4a5bb5552661fd2cc4bbcd0d4bc8a35ebf7e" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.1.tgz#026f4a5bb5552661fd2cc4bbcd0d4bc8a35ebf7e"
@ -7246,29 +7190,6 @@ react-transform-hmr@^1.0.3:
global "^4.3.0" global "^4.3.0"
react-proxy "^1.1.7" react-proxy "^1.1.7"
react-transition-group@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.0.tgz#b51fc921b0c3835a7ef7c571c79fc82c73e9204f"
dependencies:
chain-function "^1.0.0"
dom-helpers "^3.2.0"
loose-envify "^1.3.1"
prop-types "^15.5.6"
warning "^3.0.0"
react-vis-force@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/react-vis-force/-/react-vis-force-0.3.1.tgz#c7bc96a4e872409f5d4c0fa93fe89c94554d47b7"
dependencies:
d3-force "^1.0.2"
global "^4.3.0"
lodash.reduce "^4.6.0"
prop-types "^15.5.10"
react-websocket@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/react-websocket/-/react-websocket-1.1.7.tgz#0a761f3de354d4731f55343456e03b1f6005b492"
react@^15.6.1: react@^15.6.1:
version "15.6.1" version "15.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
@ -8388,10 +8309,6 @@ supports-color@^4.2.1:
dependencies: dependencies:
has-flag "^2.0.0" has-flag "^2.0.0"
svg-injector@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/svg-injector/-/svg-injector-1.1.3.tgz#8fba18d7419e5f818e712c4f82d83ee357610e61"
svg-tags@^1.0.0: svg-tags@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
@ -8408,10 +8325,6 @@ svgo@^0.7.0:
sax "~1.2.1" sax "~1.2.1"
whet.extend "~0.9.9" whet.extend "~0.9.9"
svgpath@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/svgpath/-/svgpath-2.2.1.tgz#0834bb67c89a76472b2bd06cc101fa7b517b222c"
symbol-observable@^1.0.3: symbol-observable@^1.0.3:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
@ -9376,10 +9289,6 @@ yauzl@2.4.1:
dependencies: dependencies:
fd-slicer "~1.0.1" fd-slicer "~1.0.1"
zbase32@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/zbase32/-/zbase32-0.0.2.tgz#169c6f2130a6c27a84247017538b56826a54b283"
zip-stream@^1.1.0: zip-stream@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.1.1.tgz#5216b48bbb4d2651f64d5c6e6f09eb4a7399d557" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.1.1.tgz#5216b48bbb4d2651f64d5c6e6f09eb4a7399d557"

Loading…
Cancel
Save