Browse Source

features(channels, sockets, sha256): add multichannel support with websockets + add sha256 library for invoices

renovate/lint-staged-8.x
Jack Mallers 7 years ago
parent
commit
e7420e75eb
  1. 27
      app/reducers/channels.js
  2. 46
      app/routes/activity/components/Activity.js
  3. 16
      app/routes/app/components/App.js
  4. 26
      app/routes/app/components/components/Socket.js
  5. 4
      app/routes/app/containers/AppContainer.js
  6. 7
      app/routes/wallet/components/components/Channels/Channels.js
  7. 1
      app/routes/wallet/components/components/Channels/components/Channel/Channel.js
  8. 12
      app/routes/wallet/components/components/Channels/components/Channel/Channel.scss
  9. 14
      app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js
  10. 56
      app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/ClosedPendingChannel.js
  11. 95
      app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/ClosedPendingChannel.scss
  12. 51
      app/routes/wallet/components/components/Channels/components/OpenPendingChannel/OpenPendingChannel.js
  13. 91
      app/routes/wallet/components/components/Channels/components/OpenPendingChannel/OpenPendingChannel.scss
  14. 2
      app/routes/wallet/components/components/Peers/Peers.js
  15. 12
      app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js
  16. 19
      package-lock.json
  17. 1
      package.json

27
app/reducers/channels.js

@ -1,4 +1,5 @@
import { createSelector } from 'reselect'
import { usd, btc } from '../utils'
import { callApi, callApis } from '../api'
// ------------------------------------
// Constants
@ -65,19 +66,33 @@ export function openingChannel() {
}
}
export function openingSuccessful() {
return {
type: OPENING_SUCCESSFUL
}
}
export function openingFailure() {
return {
type: OPENING_FAILURE
}
}
export const fetchChannels = () => async (dispatch) => {
dispatch(getChannels())
const channels = await callApis(['channels', 'pending_channels'])
dispatch(receiveChannels(channels))
}
export const openChannel = () => async (dispatch) => {
const payload = {}
console.log('payload: ', payload)
return
export const openChannel = ({ pubkey, localamt, pushamt }) => async (dispatch) => {
const payload = { pubkey, localamt, pushamt }
dispatch(openingChannel())
// const channel = await callApi('addchannel', 'post', payload)
// dispatch(openingSuccessful(channel))
const channel = await callApi('addchannel', 'post', payload)
console.log('channel: ', channel)
channel.data ? dispatch(openingSuccessful()) : dispatch(openingFailure())
return channel
}
// ------------------------------------

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

@ -67,32 +67,26 @@ class Activity extends Component {
</span>
</header>
<div className={styles.activityContainer}>
<CSSTransitionGroup
transitionName='activity'
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{
tab === 1 ?
<Payments
key={1}
payment={payment}
payments={payments}
ticker={ticker}
setPayment={setPayment}
paymentModalOpen={paymentModalOpen}
/>
:
<Invoices
key={2}
invoice={invoice}
invoices={invoices}
ticker={ticker}
setInvoice={setInvoice}
invoiceModalOpen={invoiceModalOpen}
/>
}
</CSSTransitionGroup>
{
tab === 1 ?
<Payments
key={1}
payment={payment}
payments={payments}
ticker={ticker}
setPayment={setPayment}
paymentModalOpen={paymentModalOpen}
/>
:
<Invoices
key={2}
invoice={invoice}
invoices={invoices}
ticker={ticker}
setInvoice={setInvoice}
invoiceModalOpen={invoiceModalOpen}
/>
}
</div>
</div>
</div>

16
app/routes/app/components/App.js

@ -1,10 +1,15 @@
// @flow
import React, { Component } from 'react'
import Websocket from 'react-websocket'
import Form from './components/Form'
import Nav from './components/Nav.js'
import Socket from './components/Socket.js'
import styles from './App.scss'
export const CHANNEL_DATA = 'CHANNEL_DATA'
export const CHANNEL_END = 'CHANNEL_END'
export const CHANNEL_ERROR = 'CHANNEL_ERROR'
export const CHANNEL_STATUS = 'CHANNEL_STATUS'
class App extends Component {
componentWillMount() {
const { fetchTicker, fetchBalance } = this.props
@ -28,9 +33,10 @@ class App extends Component {
setForm,
createInvoice,
payInvoice,
fetchChannels,
children
} = this.props
return (
<div>
<Form
@ -57,11 +63,7 @@ class App extends Component {
{children}
</div>
<Websocket
debug
url='ws://localhost:3000/'
onMessage={(data) => console.log('data: ', data)}
/>
<Socket fetchChannels={fetchChannels} />
</div>
)
}

26
app/routes/app/components/components/Socket.js

@ -0,0 +1,26 @@
// @flow
import React, { Component } from 'react'
import Websocket from 'react-websocket'
class Socket extends Component {
render() {
const onMessage = ({ event, data }) => {
console.log('data: ', data)
this.props.fetchChannels()
// switch(data.event) {
// case CHANNEL_DATA:
// console.log('channel data')
// if (data.update === 'chan_pending') {
// let zapNotification = new Notification({
// })
// }
// }
}
return (
<Websocket debug url='ws://localhost:3000/' onMessage={onMessage} />
)
}
}
export default Socket

4
app/routes/app/containers/AppContainer.js

@ -6,6 +6,7 @@ import { fetchInfo } from '../../../reducers/info'
import { setForm } from '../../../reducers/form'
import { createInvoice } from '../../../reducers/invoice'
import { payInvoice } from '../../../reducers/payment'
import { fetchChannels } from '../../../reducers/channels'
import { setAmount, setMessage, setPubkey, setPaymentRequest } from '../../../reducers/form'
const mapDispatchToProps = {
@ -19,7 +20,8 @@ const mapDispatchToProps = {
setPaymentRequest,
setForm,
createInvoice,
payInvoice
payInvoice,
fetchChannels
}
const mapStateToProps = (state) => ({

7
app/routes/wallet/components/components/Channels/Channels.js

@ -24,8 +24,7 @@ class Channels extends Component {
allChannels,
openChannel
} = this.props
console.log('allChannels: ', allChannels)
console.log('openChannel: ', openChannel)
return (
<div className={styles.channels}>
<ChannelModal isOpen={channelModalOpen} resetChannel={setChannel} channel={modalChannel} />
@ -46,11 +45,11 @@ class Channels extends Component {
allChannels.map((channel, index) => {
if (channel.hasOwnProperty('blocks_till_open')) {
return (
<OpenPendingChannel key={index} />
<OpenPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else if (channel.hasOwnProperty('closing_txid')) {
return (
<ClosedPendingChannel key={index} />
<ClosedPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else {
return (

1
app/routes/wallet/components/components/Channels/components/Channel/Channel.js

@ -8,6 +8,7 @@ class Channel extends Component {
const { ticker, channel, setChannel } = this.props
return (
<li className={styles.channel} onClick={() => setChannel(channel)}>
<h1 className={styles.status}>Status: Open</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>

12
app/routes/wallet/components/components/Channels/components/Channel/Channel.scss

@ -19,9 +19,21 @@
border: none;
}
.status {
color: $main;
position: absolute;
top: 0;
left: 10px;
padding: 10px;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
}
.left, .right {
padding: 0 10px;
margin-bottom: 5;
margin-top: 25px;
section {
margin-bottom: 20px;

14
app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js

@ -2,10 +2,22 @@
import React, { Component } from 'react'
import ReactModal from 'react-modal'
import { FaUser, FaBitcoin, FaDollar } from 'react-icons/lib/fa'
import { usd, btc } from '../../../../../../../utils'
import styles from './ChannelForm.scss'
class ChannelForm extends Component {
render() {
const submitClicked = () => {
const { form: { node_key, local_amt, push_amt }, openChannel, ticker } = this.props
console.log('ticker: ', ticker)
const localamt = ticker.currency === 'btc' ? btc.btcToSatoshis(local_amt) : btc.btcToSatoshis(usd.usdToBtc(local_amt, ticker.btcTicker.price_usd))
const pushamt = ticker.currency === 'btc' ? btc.btcToSatoshis(push_amt) : btc.btcToSatoshis(usd.usdToBtc(push_amt, ticker.btcTicker.price_usd))
openChannel({ pubkey: node_key, localamt, pushamt }).then(channel => {
if (channel.data) { setForm({ isOpen: false }) }
})
}
const customStyles = {
overlay: {
cursor: 'pointer',
@ -99,7 +111,7 @@ class ChannelForm extends Component {
</ul>
<div className={styles.buttonGroup}>
<div className={styles.button}>
<div className={styles.button} onClick={submitClicked}>
Submit
</div>
</div>

56
app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/ClosedPendingChannel.js

@ -1,15 +1,63 @@
// @flow
import { shell } from 'electron'
import React, { Component } from 'react'
import { btc } from '../../../../../../../utils'
import styles from './ClosedPendingChannel.scss'
class ClosedPendingChannel extends Component {
render() {
const { ticker, channel, setChannel } = this.props
const { ticker, channel: { channel, closing_txid }, setChannel } = this.props
return (
<li className={styles.channel}>
ClosedPendingChannel
</li>
<li className={styles.channel} onClick={() => shell.openExternal(`https://testnet.smartbit.com.au/tx/${closing_txid}`)}>
<h1 className={styles.closing}>Status: Closing</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_node_pub}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
}
}

95
app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/ClosedPendingChannel.scss

@ -0,0 +1,95 @@
@import '../../../../../../../variables.scss';
.channel {
position: relative;
background: $white;
padding: 10px;
display: flex;
flex-direction: row;
justify-content: space-between;
border-top: 1px solid $grey;
cursor: pointer;
transition: all 0.25s;
opacity: 0.5;
&:hover {
opacity: 0.35;
}
&:first-child {
border: none;
}
.closing {
color: $red;
position: absolute;
top: 0;
left: 10px;
padding: 10px;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
}
.left, .right {
padding: 0 10px;
margin-bottom: 5;
margin-top: 25px;
section {
margin-bottom: 20px;
span {
text-transform: uppercase;
letter-spacing: 1.6px;
color: $black;
font-size: 10px;
font-weight: bold;
}
h2 {
font-size: 30px;
padding: 5px 0;
color: $main;
}
h4 {
margin-top: 5px;
}
}
}
.left {
flex: 7;
border-right: 1px solid $grey;
}
.right {
flex: 3;
.capacity {
text-align: center;
border-bottom: 1px solid $grey;
margin-bottom: 10px;
}
.balances {
display: flex;
justify-content: space-between;
section {
flex: 5;
text-align: center;
h4 {
color: $main;
font-size: 16px;
}
&:first-child {
border-right: 1px solid $grey;
}
}
}
}
}

51
app/routes/wallet/components/components/Channels/components/OpenPendingChannel/OpenPendingChannel.js

@ -5,10 +5,57 @@ import styles from './OpenPendingChannel.scss'
class OpenPendingChannel extends Component {
render() {
const { ticker, channel, setChannel } = this.props
const { ticker, channel: { channel } } = this.props
return (
<li className={styles.channel}>
OpenPendingChannel
<h1 className={styles.pending}>Status: Pending</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_node_pub}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
}

91
app/routes/wallet/components/components/Channels/components/OpenPendingChannel/OpenPendingChannel.scss

@ -0,0 +1,91 @@
@import '../../../../../../../variables.scss';
.channel {
position: relative;
background: $white;
padding: 10px;
display: flex;
flex-direction: row;
justify-content: space-between;
border-top: 1px solid $grey;
cursor: pointer;
transition: all 0.25s;
opacity: 0.5;
.pending {
color: $green;
position: absolute;
top: 0;
left: 10px;
padding: 10px;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
}
&:first-child {
border: none;
}
.left, .right {
padding: 0 10px;
margin-bottom: 5;
margin-top: 25px;
section {
margin-bottom: 20px;
span {
text-transform: uppercase;
letter-spacing: 1.6px;
color: $black;
font-size: 10px;
font-weight: bold;
}
h2 {
font-size: 30px;
padding: 5px 0;
color: $main;
}
h4 {
margin-top: 5px;
}
}
}
.left {
flex: 7;
border-right: 1px solid $grey;
}
.right {
flex: 3;
.capacity {
text-align: center;
border-bottom: 1px solid $grey;
margin-bottom: 10px;
}
.balances {
display: flex;
justify-content: space-between;
section {
flex: 5;
text-align: center;
h4 {
color: $main;
font-size: 16px;
}
&:first-child {
border-right: 1px solid $grey;
}
}
}
}
}

2
app/routes/wallet/components/components/Peers/Peers.js

@ -37,7 +37,7 @@ class Peers extends Component {
</div>
<ul>
{
!peersLoading && peers.length ?
!peersLoading ?
peers.map(peer => <Peer key={peer.peer_id} peer={peer} setPeer={setPeer} />)
:
'Loading...'

12
app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.js

@ -4,17 +4,10 @@ import ReactModal from 'react-modal'
import styles from './PeerForm.scss'
class PeerForm extends Component {
constructor(props, context) {
super(props, context)
this.state = {
pubkey: '02ef6248210e27b0f0df4d11d876e63f56e04bcb0054d0d8b6ba6a1a3e90dc56e1',
host: 'lnd-testnet-2.mably.com'
}
}
render() {
const submit = () => {
const { pubkey, host } = this.state
const { form: { pubkey, host } } = this.props
this.props.connect({ pubkey, host }).then(success => {
if (success.data) { setForm({ isOpen: false }) }
})
@ -37,7 +30,6 @@ class PeerForm extends Component {
}
const { form, setForm, connect } = this.props
const { pubkey, host } = this.state
return (
<div>
<ReactModal

19
package-lock.json

@ -3274,12 +3274,22 @@
"integrity": "sha1-w+VpiMU8ZRJ/tG1AMqOpACRv3JQ=",
"dev": true
},
"convert-hex": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/convert-hex/-/convert-hex-0.1.0.tgz",
"integrity": "sha1-CMBFaJIsJ3drii6BqV05M2LqC2U="
},
"convert-source-map": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
"integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=",
"dev": true
},
"convert-string": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/convert-string/-/convert-string-0.1.0.tgz",
"integrity": "sha1-ec5BqbsNA7z3LNxqjzxW+7xkQQo="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
@ -13427,6 +13437,15 @@
"inherits": "2.0.3"
}
},
"sha256": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/sha256/-/sha256-0.2.0.tgz",
"integrity": "sha1-c6C0GNqrcDW/+G6EkeNjQS/CqwU=",
"requires": {
"convert-hex": "0.1.0",
"convert-string": "0.1.0"
}
},
"shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",

1
package.json

@ -208,6 +208,7 @@
"redux-thunk": "^2.2.0",
"reselect": "^3.0.1",
"satoshi-bitcoin": "^1.0.4",
"sha256": "^0.2.0",
"source-map-support": "^0.4.15"
},
"devEngines": {

Loading…
Cancel
Save