Browse Source

Merge pull request #82 from LN-Zap/feature/style-peers

Feature/style peers
renovate/lint-staged-8.x
JimmyMow 7 years ago
committed by GitHub
parent
commit
8a755aa8e4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/components/Nav/Nav.js
  2. 8
      app/components/Peers/Peer.scss
  3. 22
      app/components/Peers/PeerForm.js
  4. 16
      app/components/Peers/PeerForm.scss
  5. 1
      app/components/Peers/Peers.js
  6. 1
      app/icons/user.svg
  7. 32
      app/main.dev.js
  8. 2
      app/reducers/address.js
  9. 5
      app/reducers/ipc.js
  10. 15
      app/reducers/lnd.js
  11. 22
      app/reducers/peers.js
  12. 2
      app/routes.js
  13. 113
      app/routes/peers/components/Peers.js
  14. 96
      app/routes/peers/components/Peers.scss
  15. 52
      app/routes/peers/containers/PeersContainer.js
  16. 3
      app/routes/peers/index.js

2
app/components/Nav/Nav.js

@ -24,7 +24,7 @@ const Nav = ({ openPayForm, openRequestForm }) => (
<span>Wallet</span> <span>Wallet</span>
</li> </li>
</NavLink> </NavLink>
<NavLink exact to='/wallet' activeClassName={styles.active} className={styles.link}> <NavLink exact to='/peers' activeClassName={styles.active} className={styles.link}>
<span className={styles.activeBorder} /> <span className={styles.activeBorder} />
<li> <li>
<Isvg styles={{ verticalAlign: 'middle' }} src={peersIcon} /> <Isvg styles={{ verticalAlign: 'middle' }} src={peersIcon} />

8
app/components/Peers/Peer.scss

@ -2,14 +2,18 @@
.peer { .peer {
position: relative; position: relative;
background: $white; margin: 5px 0;
padding: 10px; padding: 10px;
border-top: 1px solid $grey; border-top: 1px solid $white;
cursor: pointer; cursor: pointer;
transition: all 0.25s; transition: all 0.25s;
list-style: none;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
&:hover { &:hover {
opacity: 0.75; opacity: 0.75;
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
} }
&:first-child { &:first-child {

22
app/components/Peers/PeerForm.js

@ -9,22 +9,6 @@ const PeerForm = ({ form, setForm, connect }) => {
connect({ pubkey, host }) connect({ pubkey, host })
} }
const customStyles = {
overlay: {
cursor: 'pointer',
overflowY: 'auto'
},
content: {
top: 'auto',
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
padding: '40px'
}
}
return ( return (
<div> <div>
<ReactModal <ReactModal
@ -34,7 +18,7 @@ const PeerForm = ({ form, setForm, connect }) => {
shouldCloseOnOverlayClick shouldCloseOnOverlayClick
onRequestClose={() => setForm({ isOpen: false })} onRequestClose={() => setForm({ isOpen: false })}
parentSelector={() => document.body} parentSelector={() => document.body}
style={customStyles} className={styles.modal}
> >
<div className={styles.form}> <div className={styles.form}>
<h1 className={styles.title}>Connect to a peer</h1> <h1 className={styles.title}>Connect to a peer</h1>
@ -62,8 +46,8 @@ const PeerForm = ({ form, setForm, connect }) => {
/> />
</section> </section>
<div className={styles.buttonGroup}> <div className='buttonContainer' onClick={submit}>
<div className={styles.button} onClick={submit}> <div className='buttonPrimary'>
Submit Submit
</div> </div>
</div> </div>

16
app/components/Peers/PeerForm.scss

@ -1,5 +1,21 @@
@import '../../variables.scss'; @import '../../variables.scss';
.modal {
position: relative;
width: 40%;
margin: 50px auto;
padding: 40px;
position: absolute;
top: auto;
left: 20%;
right: 0;
bottom: auto;
background: $white;
outline: none;
z-index: -2;
border: 1px solid $darkgrey;
}
.title { .title {
text-align: center; text-align: center;
font-size: 24px; font-size: 24px;

1
app/components/Peers/Peers.js

@ -36,6 +36,7 @@ const Peers = ({
return ( return (
<div className={styles.peers}> <div className={styles.peers}>
<PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={modalPeer} disconnect={disconnect} /> <PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={modalPeer} disconnect={disconnect} />
<PeerForm form={peerForm} setForm={setPeerForm} connect={connect} /> <PeerForm form={peerForm} setForm={setPeerForm} connect={connect} />
<div className={styles.header}> <div className={styles.header}>
<h3>Peers</h3> <h3>Peers</h3>

1
app/icons/user.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-user"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>

After

Width:  |  Height:  |  Size: 313 B

32
app/main.dev.js

@ -26,6 +26,9 @@ let mainWindow = null
let didFinishLoad = false let didFinishLoad = false
let startedSync = false
let sentGrpcDisconnect = false
let certPath let certPath
let certInterval let certInterval
@ -108,6 +111,7 @@ app.on('ready', async () => {
const menuBuilder = new MenuBuilder(mainWindow); const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu() menuBuilder.buildMenu()
sendGrpcDisconnected()
// Check to see if and LND process is running // Check to see if and LND process is running
lookup({ command: 'lnd' }, (err, results) => { lookup({ command: 'lnd' }, (err, results) => {
// There was an error checking for the LND process // There was an error checking for the LND process
@ -153,7 +157,6 @@ app.on('open-url', function (event, url) {
}) })
// Starts the LND node // Starts the LND node
// export const startLnd = (plat, certPath, mainWindow, startGrpc, sendLndSynced) => {
export const startLnd = () => { export const startLnd = () => {
const lndPath = path.join(__dirname, '..', 'resources', 'bin', plat, plat === 'win32' ? 'lnd.exe' : 'lnd') const lndPath = path.join(__dirname, '..', 'resources', 'bin', plat, plat === 'win32' ? 'lnd.exe' : 'lnd')
@ -220,7 +223,7 @@ const startGrpc = () => {
lndMethods(event, msg, data) lndMethods(event, msg, data)
}) })
sendGrpcStarted() sendGrpcConnected()
}) })
} }
@ -230,6 +233,8 @@ const sendLndSyncing = () => {
if (didFinishLoad) { if (didFinishLoad) {
clearInterval(sendLndSyncingInterval) clearInterval(sendLndSyncingInterval)
console.log('SENDING SYNCING')
startedSync = true
mainWindow.webContents.send('lndSyncing') mainWindow.webContents.send('lndSyncing')
} }
}, 1000) }, 1000)
@ -238,21 +243,34 @@ const sendLndSyncing = () => {
// Send the front end event letting them know LND is synced to the blockchain // Send the front end event letting them know LND is synced to the blockchain
const sendLndSynced = () => { const sendLndSynced = () => {
let sendLndSyncedInterval = setInterval(() => { let sendLndSyncedInterval = setInterval(() => {
if (didFinishLoad) { if (didFinishLoad && startedSync) {
clearInterval(sendLndSyncedInterval) clearInterval(sendLndSyncedInterval)
console.log('SENDING SYNCED')
mainWindow.webContents.send('lndSynced') mainWindow.webContents.send('lndSynced')
} }
}, 1000) }, 1000)
} }
// Send the front end event letting them know the gRPC connection has started // Send the front end event letting them know the gRPC connection has started
const sendGrpcStarted = () => { const sendGrpcDisconnected = () => {
let sendGrpcStartedInterval = setInterval(() => { let sendGrpcDisonnectedInterval = setInterval(() => {
if (didFinishLoad) { if (didFinishLoad) {
clearInterval(sendGrpcStartedInterval) clearInterval(sendGrpcDisonnectedInterval)
sentGrpcDisconnect = true
mainWindow.webContents.send('grpcDisconnected')
}
}, 1000)
}
// Send the front end event letting them know the gRPC connection has started
const sendGrpcConnected = () => {
let sendGrpcConnectedInterval = setInterval(() => {
if (didFinishLoad && sentGrpcDisconnect) {
clearInterval(sendGrpcConnectedInterval)
mainWindow.webContents.send('grpcStarted') mainWindow.webContents.send('grpcConnected')
} }
}, 1000) }, 1000)
} }

2
app/reducers/address.js

@ -24,13 +24,11 @@ export function getAddress() {
// Send IPC event for getinfo // Send IPC event for getinfo
export const newAddress = type => async (dispatch) => { export const newAddress = type => async (dispatch) => {
dispatch(getAddress()) dispatch(getAddress())
console.log('getting new address type: ', addressTypes[type])
ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } }) ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } })
} }
// Receive IPC event for info // Receive IPC event for info
export const receiveAddress = (event, address) => dispatch => { export const receiveAddress = (event, address) => dispatch => {
console.log('address: ', address)
dispatch({ type: RECEIVE_ADDRESS, address }) dispatch({ type: RECEIVE_ADDRESS, address })
} }

5
app/reducers/ipc.js

@ -1,5 +1,5 @@
import createIpc from 'redux-electron-ipc' import createIpc from 'redux-electron-ipc'
import { lndSyncing, lndSynced, lndStdout, grpcStarted } from './lnd' import { lndSyncing, lndSynced, lndStdout, grpcDisconnected, grpcConnected } from './lnd'
import { receiveInfo } from './info' import { receiveInfo } from './info'
import { receiveAddress } from './address' import { receiveAddress } from './address'
import { receiveCryptocurrency } from './ticker' import { receiveCryptocurrency } from './ticker'
@ -40,7 +40,8 @@ const ipc = createIpc({
lndSyncing, lndSyncing,
lndSynced, lndSynced,
lndStdout, lndStdout,
grpcStarted, grpcDisconnected,
grpcConnected,
receiveInfo, receiveInfo,

15
app/reducers/lnd.js

@ -14,7 +14,8 @@ export const RECEIVE_LINE = 'RECEIVE_LINE'
export const GET_BLOCK_HEIGHT = 'GET_BLOCK_HEIGHT' export const GET_BLOCK_HEIGHT = 'GET_BLOCK_HEIGHT'
export const RECEIVE_BLOCK_HEIGHT = 'RECEIVE_BLOCK_HEIGHT' export const RECEIVE_BLOCK_HEIGHT = 'RECEIVE_BLOCK_HEIGHT'
export const GRPC_STARTED = 'GRPC_STARTED' export const GRPC_DISCONNECTED = 'GRPC_DISCONNECTED'
export const GRPC_CONNECTED = 'GRPC_CONNECTED'
// ------------------------------------ // ------------------------------------
// Actions // Actions
@ -33,10 +34,9 @@ export const lndSynced = () => (dispatch) => {
dispatch({ type: STOP_SYNCING }) dispatch({ type: STOP_SYNCING })
} }
export const grpcStarted = () => (dispatch) => { export const grpcDisconnected = () => (dispatch) => dispatch({ type: GRPC_DISCONNECTED })
console.log('hello????')
dispatch({ type: GRPC_STARTED }) 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 => {
@ -89,7 +89,8 @@ const ACTION_HANDLERS = {
[GET_BLOCK_HEIGHT]: state => ({ ...state, fetchingBlockHeight: true }), [GET_BLOCK_HEIGHT]: state => ({ ...state, fetchingBlockHeight: true }),
[RECEIVE_BLOCK_HEIGHT]: (state, { blockHeight }) => ({ ...state, blockHeight, fetchingBlockHeight: false }), [RECEIVE_BLOCK_HEIGHT]: (state, { blockHeight }) => ({ ...state, blockHeight, fetchingBlockHeight: false }),
[GRPC_STARTED]: state => ({ ...state, grpcStarted: true }) [GRPC_DISCONNECTED]: state => ({ ...state, grpcStarted: false }),
[GRPC_CONNECTED]: state => ({ ...state, grpcStarted: true })
} }
// ------------------------------------ // ------------------------------------
@ -97,7 +98,7 @@ const ACTION_HANDLERS = {
// ------------------------------------ // ------------------------------------
const initialState = { const initialState = {
syncing: false, syncing: false,
grpcStarted: false, grpcStarted: true,
fetchingBlockHeight: false, fetchingBlockHeight: false,
lines: [], lines: [],
blockHeight: 0, blockHeight: 0,

22
app/reducers/peers.js

@ -16,6 +16,8 @@ export const SET_PEER_FORM = 'SET_PEER_FORM'
export const SET_PEER = 'SET_PEER' export const SET_PEER = 'SET_PEER'
export const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY'
export const GET_PEERS = 'GET_PEERS' export const GET_PEERS = 'GET_PEERS'
export const RECEIVE_PEERS = 'RECEIVE_PEERS' export const RECEIVE_PEERS = 'RECEIVE_PEERS'
@ -60,6 +62,13 @@ export function getPeers() {
} }
} }
export function updateSearchQuery(searchQuery) {
return {
type: UPDATE_SEARCH_QUERY,
searchQuery
}
}
// Send IPC event for peers // Send IPC event for peers
export const fetchPeers = () => async (dispatch) => { export const fetchPeers = () => async (dispatch) => {
dispatch(getPeers()) dispatch(getPeers())
@ -114,17 +123,27 @@ const ACTION_HANDLERS = {
[SET_PEER]: (state, { peer }) => ({ ...state, peer }), [SET_PEER]: (state, { peer }) => ({ ...state, peer }),
[GET_PEERS]: state => ({ ...state, peersLoading: true }), [GET_PEERS]: state => ({ ...state, peersLoading: true }),
[RECEIVE_PEERS]: (state, { peers }) => ({ ...state, peersLoading: false, peers }) [RECEIVE_PEERS]: (state, { peers }) => ({ ...state, peersLoading: false, peers }),
[UPDATE_SEARCH_QUERY]: (state, { searchQuery }) => ({ ...state, searchQuery })
} }
const peersSelectors = {} const peersSelectors = {}
const peerSelector = state => state.peers.peer const peerSelector = state => state.peers.peer
const peersSelector = state => state.peers.peers
const peersSearchQuerySelector = state => state.peers.searchQuery
peersSelectors.peerModalOpen = createSelector( peersSelectors.peerModalOpen = createSelector(
peerSelector, peerSelector,
peer => (!!peer) peer => (!!peer)
) )
peersSelectors.filteredPeers = createSelector(
peersSelector,
peersSearchQuerySelector,
(peers, query) => peers.filter(peer => peer.pub_key.includes(query) || peer.address.includes(query))
)
export { peersSelectors } export { peersSelectors }
// ------------------------------------ // ------------------------------------
@ -139,6 +158,7 @@ const initialState = {
pubkey: '', pubkey: '',
host: '' host: ''
}, },
searchQuery: '',
connecting: false, connecting: false,
disconnecting: false disconnecting: false
} }

2
app/routes.js

@ -4,12 +4,14 @@ import { Switch, Route } from 'react-router'
import App from './routes/app' import App from './routes/app'
import Activity from './routes/activity' import Activity from './routes/activity'
import Wallet from './routes/wallet' import Wallet from './routes/wallet'
import Peers from './routes/peers'
import Channels from './routes/channels' import Channels from './routes/channels'
export default () => ( export default () => (
<App> <App>
<Switch> <Switch>
<Route path='/wallet' component={Wallet} /> <Route path='/wallet' component={Wallet} />
<Route path='/peers' component={Peers} />
<Route path='/channels' component={Channels} /> <Route path='/channels' component={Channels} />
<Route path='/' component={Activity} /> <Route path='/' component={Activity} />
</Switch> </Switch>

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

@ -0,0 +1,113 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Isvg from 'react-inlinesvg'
import userIcon from 'icons/user.svg'
import { FaUser, FaRepeat } from 'react-icons/lib/fa'
import { MdSearch } from 'react-icons/lib/md'
import PeerForm from 'components/Peers/PeerForm'
import PeerModal from 'components/Peers/PeerModal'
import Peer from 'components/Peers/Peer'
import styles from './Peers.scss'
class Peers extends Component {
componentWillMount() {
this.props.fetchPeers()
}
render() {
const {
fetchPeers,
peerFormProps,
setPeerForm,
setPeer,
updateSearchQuery,
disconnectRequest,
peerModalOpen,
filteredPeers,
peers: { peer, searchQuery },
info: { data: { identity_pubkey } }
} = this.props
const refreshClicked = event => {
// store event in icon so we dont get an error when react clears it
const icon = event.currentTarget
// fetch peers
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>
<PeerForm {...peerFormProps} />
<PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={peer} disconnect={disconnectRequest} />
<header className={styles.header}>
<div className={styles.titleContainer}>
<div className={styles.left}>
<div className={styles.identityPubkey}>
<section className={styles.userIcon}>
<Isvg src={userIcon} />
</section>
<section>
<h4>Your node public key</h4>
<h2>{identity_pubkey}</h2>
</section>
</div>
</div>
</div>
<div className={styles.addPeerContainer}>
<div className={`buttonPrimary ${styles.newPeerButton}`} onClick={() => setPeerForm({ isOpen: true })}>
Add new peer
</div>
</div>
</header>
<div className={styles.search}>
<label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'>
<MdSearch />
</label>
<input
value={searchQuery}
onChange={event => updateSearchQuery(event.target.value)}
className={`${styles.text} ${styles.input}`}
placeholder='Search peers by their node public key or IP address'
type='text'
id='peersSearch'
/>
</div>
<div className={styles.refreshContainer}>
<span className={`${styles.refresh} hint--top`} data-hint='Refresh your peers list'>
<FaRepeat
onClick={refreshClicked}
/>
</span>
</div>
<div className={styles.peers}>
{
filteredPeers.map(filteredPeer => <Peer key={filteredPeer.peer_id} peer={filteredPeer} setPeer={setPeer} />)
}
</div>
</div>
)
}
}
Peers.propTypes = {
}
export default Peers

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

@ -0,0 +1,96 @@
@import '../../../variables.scss';
.search {
height: 75px;
padding: 2px 25px;
border-top: 1px solid $darkgrey;
border-bottom: 1px solid $darkgrey;
background: $white;
.input {
display: inline-block;
vertical-align: top;
height: 100%;
}
.label {
width: 5%;
line-height: 70px;
font-size: 25px;
text-align: center;
cursor: pointer;
}
.text {
width: 95%;
outline: 0;
padding: 0;
border: 0;
border-radius: 0;
height: 68px;
font-size: 18px;
}
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
.titleContainer {
padding: 40px;
.left {
padding: 10px 0;
}
.left, span {
display: inline-block;
}
.identityPubkey {
font-size: 30px;
margin-right: 10px;
display: flex;
flex-direction: row;
.userIcon {
margin-right: 10px;
}
section h4 {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 1.2px;
}
section h2 {
font-size: 14px;
margin-top: 5px;
}
}
}
.addPeerContainer {
padding: 40px;
.newPeerButton {
font-size: 14px;
}
}
}
.refreshContainer {
display: flex;
flex-direction: row;
padding: 20px 40px 0px 40px;
.refresh {
cursor: pointer;
color: $bluegrey;
}
}
.peers {
padding: 40px;
}

52
app/routes/peers/containers/PeersContainer.js

@ -0,0 +1,52 @@
import { withRouter } from 'react-router'
import { connect } from 'react-redux'
import {
fetchPeers,
setPeer,
setPeerForm,
connectRequest,
disconnectRequest,
updateSearchQuery,
peersSelectors
} from 'reducers/peers'
import Peers from '../components/Peers'
const mapDispatchToProps = {
fetchPeers,
setPeer,
peersSelectors,
setPeerForm,
connectRequest,
disconnectRequest,
updateSearchQuery
}
const mapStateToProps = state => ({
peers: state.peers,
info: state.info,
peerModalOpen: peersSelectors.peerModalOpen(state),
filteredPeers: peersSelectors.filteredPeers(state)
})
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const peerFormProps = {
setForm: dispatchProps.setPeerForm,
connect: dispatchProps.connectRequest,
form: stateProps.peers.peerForm
}
return {
...stateProps,
...dispatchProps,
...ownProps,
peerFormProps
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(Peers))

3
app/routes/peers/index.js

@ -0,0 +1,3 @@
import PeersContainer from './containers/PeersContainer'
export default PeersContainer
Loading…
Cancel
Save