Jack Mallers
7 years ago
22 changed files with 281 additions and 800 deletions
@ -0,0 +1,106 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
|
|||
import ReactModal from 'react-modal' |
|||
import { FaClose, FaCircle } from 'react-icons/lib/fa' |
|||
|
|||
import { btc } from 'utils' |
|||
|
|||
import styles from './ContactModal.scss' |
|||
|
|||
const ContactModal = ({ isOpen, channel, closeContactModal }) => { |
|||
console.log('channel: ', channel) |
|||
const customStyles = { |
|||
overlay: { |
|||
cursor: 'pointer', |
|||
overflowY: 'auto' |
|||
}, |
|||
content: { |
|||
top: 'auto', |
|||
left: '20%', |
|||
right: '0', |
|||
bottom: 'auto', |
|||
width: '40%', |
|||
margin: '50px auto', |
|||
borderRadius: 'none', |
|||
padding: '0' |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<ReactModal |
|||
isOpen={isOpen} |
|||
contentLabel='No Overlay Click Modal' |
|||
ariaHideApp |
|||
shouldCloseOnOverlayClick |
|||
onRequestClose={closeContactModal} |
|||
parentSelector={() => document.body} |
|||
style={customStyles} |
|||
> |
|||
{ |
|||
channel && |
|||
<div className={styles.container}> |
|||
<header className={styles.header}> |
|||
<div className={`${styles.status} ${channel.active && styles.online}`}> |
|||
<FaCircle style={{ verticalAlign: 'top' }} /> |
|||
<span> |
|||
{ |
|||
channel.active ? |
|||
'Online' |
|||
: |
|||
'Offline' |
|||
} |
|||
</span> |
|||
</div> |
|||
<div className={styles.closeContainer}> |
|||
<span onClick={closeContactModal}> |
|||
<FaClose /> |
|||
</span> |
|||
</div> |
|||
</header> |
|||
|
|||
<section className={styles.title}> |
|||
<h2>{channel.remote_pubkey}</h2> |
|||
</section> |
|||
|
|||
<section className={styles.stats}> |
|||
<div className={styles.pay}> |
|||
<h4>Can Pay</h4> |
|||
<div className={styles.meter}> |
|||
<div className={styles.amount} style={{ width: `${(channel.local_balance / channel.capacity) * 100}%` }} /> |
|||
</div> |
|||
<span>{btc.satoshisToBtc(channel.local_balance)} BTC</span> |
|||
</div> |
|||
|
|||
<div className={styles.pay}> |
|||
<h4>Can Receive</h4> |
|||
<div className={styles.meter}> |
|||
<div className={styles.amount} style={{ width: `${(channel.remote_balance / channel.capacity) * 100}%` }} /> |
|||
</div> |
|||
<span>{btc.satoshisToBtc(channel.remote_balance)} BTC</span> |
|||
</div> |
|||
|
|||
<div className={styles.sent}> |
|||
<h4>Total Bitcoin Sent</h4> |
|||
<p>{btc.satoshisToBtc(channel.total_satoshis_sent)} BTC</p> |
|||
</div> |
|||
<div className={styles.received}> |
|||
<h4>Total Bitcoin Received</h4> |
|||
<p>{btc.satoshisToBtc(channel.total_satoshis_received)} BTC</p> |
|||
</div> |
|||
</section> |
|||
|
|||
<footer> |
|||
<div>Remove</div> |
|||
</footer> |
|||
</div> |
|||
} |
|||
</ReactModal> |
|||
) |
|||
} |
|||
|
|||
ContactModal.propTypes = { |
|||
|
|||
} |
|||
|
|||
export default ContactModal |
@ -0,0 +1,100 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: flex-end; |
|||
background: $lightgrey; |
|||
margin-bottom: 30px; |
|||
padding: 20px; |
|||
|
|||
.status { |
|||
font-size: 12px; |
|||
color: $darkestgrey; |
|||
|
|||
&.online { |
|||
color: $green; |
|||
} |
|||
|
|||
span { |
|||
margin-left: 5px; |
|||
} |
|||
} |
|||
|
|||
.closeContainer { |
|||
background: $lightgrey; |
|||
line-height: 12px; |
|||
|
|||
span { |
|||
color: $darkestgrey; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.container section { |
|||
margin-bottom: 30px; |
|||
padding: 0 20px; |
|||
|
|||
.pay, .receive, .sent, .received { |
|||
margin: 40px 0; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
|
|||
h2 { |
|||
color: $secondary; |
|||
font-weight: bold; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
} |
|||
|
|||
.stats { |
|||
|
|||
h4 { |
|||
color: $secondary; |
|||
font-weight: bold; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
span { |
|||
font-size: 14px; |
|||
} |
|||
|
|||
p { |
|||
margin-top: 10px; |
|||
color: $darkestgrey; |
|||
} |
|||
|
|||
.meter, .amount { |
|||
height: 10px; |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.meter { |
|||
background: $darkgrey; |
|||
width: 100%; |
|||
margin: 10px 0; |
|||
} |
|||
|
|||
.amount { |
|||
background: $darkestgrey; |
|||
} |
|||
} |
|||
|
|||
.container footer { |
|||
padding: 20px; |
|||
text-align: center; |
|||
|
|||
div { |
|||
color: $red; |
|||
font-size: 18px; |
|||
|
|||
&:hover { |
|||
color: lighten($red, 10%); |
|||
} |
|||
} |
|||
} |
@ -1,205 +0,0 @@ |
|||
import React, { Component } from 'react' |
|||
import PropTypes from 'prop-types' |
|||
|
|||
import { FaAngleDown, FaRepeat } from 'react-icons/lib/fa' |
|||
import { MdSearch } from 'react-icons/lib/md' |
|||
|
|||
import OpenPendingChannel from 'components/Channels/OpenPendingChannel' |
|||
import ClosedPendingChannel from 'components/Channels/ClosedPendingChannel' |
|||
import Channel from 'components/Channels/Channel' |
|||
import ChannelForm from 'components/ChannelForm' |
|||
|
|||
import styles from './Channels.scss' |
|||
|
|||
class Channels extends Component { |
|||
constructor(props) { |
|||
super(props) |
|||
|
|||
this.state = { |
|||
refreshing: false |
|||
} |
|||
} |
|||
|
|||
componentWillMount() { |
|||
const { fetchChannels, fetchPeers } = this.props |
|||
|
|||
fetchChannels() |
|||
fetchPeers() |
|||
} |
|||
|
|||
render() { |
|||
const { |
|||
fetchChannels, |
|||
closeChannel, |
|||
channels: { |
|||
searchQuery, |
|||
filterPulldown, |
|||
filter, |
|||
viewType |
|||
}, |
|||
|
|||
nonActiveFilters, |
|||
toggleFilterPulldown, |
|||
changeFilter, |
|||
|
|||
currentChannels, |
|||
updateChannelSearchQuery, |
|||
|
|||
openChannelForm, |
|||
|
|||
ticker, |
|||
currentTicker, |
|||
|
|||
channelFormProps |
|||
} = this.props |
|||
|
|||
const refreshClicked = () => { |
|||
// turn the spinner on
|
|||
this.setState({ refreshing: true }) |
|||
|
|||
// store event in icon so we dont get an error when react clears it
|
|||
const icon = this.repeat.childNodes |
|||
|
|||
// fetch peers
|
|||
fetchChannels() |
|||
|
|||
// wait for the svg to appear as child
|
|||
const svgTimeout = setTimeout(() => { |
|||
if (icon[0].tagName === 'svg') { |
|||
// spin icon for 1 sec
|
|||
icon[0].style.animation = 'spin 1000ms linear 1' |
|||
clearTimeout(svgTimeout) |
|||
} |
|||
}, 1) |
|||
|
|||
// clear animation after the second so we can reuse it
|
|||
const refreshTimeout = setTimeout(() => { |
|||
icon[0].style.animation = '' |
|||
this.setState({ refreshing: false }) |
|||
clearTimeout(refreshTimeout) |
|||
}, 1000) |
|||
} |
|||
|
|||
return ( |
|||
<div className={`${styles.container} ${viewType === 1 && styles.graphview}`}> |
|||
<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}> |
|||
<label className={`${styles.label} ${styles.input}`} htmlFor='channelSearch'> |
|||
<MdSearch /> |
|||
</label> |
|||
<input |
|||
value={searchQuery} |
|||
onChange={event => updateChannelSearchQuery(event.target.value)} |
|||
className={`${styles.text} ${styles.input}`} |
|||
placeholder='Search channels by funding transaction or remote public key' |
|||
type='text' |
|||
id='channelSearch' |
|||
/> |
|||
</div> |
|||
|
|||
<div className={styles.filtersContainer}> |
|||
<section> |
|||
<h2 onClick={toggleFilterPulldown} className={styles.filterTitle}> |
|||
{filter.name} <span className={filterPulldown && styles.pulldown}><FaAngleDown /></span> |
|||
</h2> |
|||
<ul className={`${styles.filters} ${filterPulldown && styles.active}`}> |
|||
{ |
|||
nonActiveFilters.map(f => ( |
|||
<li key={f.key} onClick={() => changeFilter(f)}> |
|||
{f.name} |
|||
</li> |
|||
)) |
|||
} |
|||
</ul> |
|||
</section> |
|||
<section className={styles.refreshContainer}> |
|||
<span className={styles.refresh} onClick={refreshClicked} ref={(ref) => { this.repeat = ref }}> |
|||
{ |
|||
this.state.refreshing ? |
|||
<FaRepeat /> |
|||
: |
|||
'Refresh' |
|||
} |
|||
</span> |
|||
</section> |
|||
</div> |
|||
|
|||
<div className={`${styles.channels} ${filterPulldown && styles.fade}`}> |
|||
<ul className={viewType === 1 && styles.cardsContainer}> |
|||
{ |
|||
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 ( |
|||
<Channel |
|||
key={index} |
|||
ticker={ticker} |
|||
channel={channel} |
|||
closeChannel={closeChannel} |
|||
currentTicker={currentTicker} |
|||
/> |
|||
) |
|||
}) |
|||
} |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
Channels.propTypes = { |
|||
fetchChannels: PropTypes.func.isRequired, |
|||
|
|||
channels: PropTypes.object.isRequired, |
|||
currentChannels: PropTypes.array.isRequired, |
|||
nonActiveFilters: PropTypes.array.isRequired, |
|||
|
|||
updateChannelSearchQuery: PropTypes.func.isRequired, |
|||
setCurrentChannel: PropTypes.func.isRequired, |
|||
openChannelForm: PropTypes.func.isRequired, |
|||
closeChannel: PropTypes.func.isRequired, |
|||
toggleFilterPulldown: PropTypes.func.isRequired, |
|||
changeFilter: PropTypes.func.isRequired, |
|||
fetchPeers: PropTypes.func.isRequired, |
|||
|
|||
ticker: PropTypes.object.isRequired, |
|||
currentTicker: PropTypes.object.isRequired, |
|||
|
|||
channelFormProps: PropTypes.object.isRequired |
|||
} |
|||
|
|||
export default Channels |
@ -1,178 +0,0 @@ |
|||
@import '../../../variables.scss'; |
|||
|
|||
.container.graphview { |
|||
background: $black; |
|||
} |
|||
|
|||
.search { |
|||
height: 55px; |
|||
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: 50px; |
|||
font-size: 20px; |
|||
text-align: center; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.text { |
|||
width: 95%; |
|||
outline: 0; |
|||
padding: 0; |
|||
border: 0; |
|||
border-radius: 0; |
|||
height: 50px; |
|||
font-size: 16px; |
|||
} |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
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 { |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
padding: 20px 40px; |
|||
|
|||
h2, h2 span { |
|||
color: $bluegrey; |
|||
cursor: pointer; |
|||
transition: color 0.25s; |
|||
|
|||
&:hover { |
|||
color: lighten($bluegrey, 10%); |
|||
} |
|||
} |
|||
|
|||
h2, .filters li { |
|||
text-transform: uppercase; |
|||
letter-spacing: 1.5px; |
|||
color: $darkestgrey; |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
h2 span.pulldown { |
|||
color: $main; |
|||
} |
|||
|
|||
.filters { |
|||
display: none; |
|||
|
|||
&.active { |
|||
display: block; |
|||
position: absolute; |
|||
bottom: -100px; |
|||
z-index: 10; |
|||
|
|||
li { |
|||
margin: 5px 0; |
|||
cursor: pointer; |
|||
|
|||
&:hover { |
|||
color: $main; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.refreshContainer { |
|||
text-align: right; |
|||
cursor: pointer; |
|||
|
|||
.refresh { |
|||
text-decoration: underline; |
|||
|
|||
svg { |
|||
font-size: 12px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
.layoutsContainer { |
|||
padding: 40px; |
|||
|
|||
span { |
|||
font-size: 30px; |
|||
color: $grey; |
|||
cursor: pointer; |
|||
transition: all 0.25s; |
|||
|
|||
&:nth-child(1) { |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
&:hover { |
|||
color: $darkestgrey; |
|||
} |
|||
|
|||
&.active { |
|||
color: $darkestgrey; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.createChannelContainer { |
|||
padding: 40px; |
|||
|
|||
.newChannelButton { |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
|
|||
.channels { |
|||
padding: 10px 40px 40px 40px; |
|||
transition: opacity 0.25s; |
|||
|
|||
&.fade { |
|||
opacity: 0.05; |
|||
} |
|||
|
|||
.cardsContainer { |
|||
display: flex; |
|||
justify-content: center; |
|||
flex-wrap: wrap; |
|||
box-sizing: border-box; |
|||
} |
|||
} |
@ -1,104 +0,0 @@ |
|||
import { withRouter } from 'react-router' |
|||
import { connect } from 'react-redux' |
|||
|
|||
import { |
|||
fetchChannels, |
|||
openChannel, |
|||
closeChannel, |
|||
updateChannelSearchQuery, |
|||
setViewType, |
|||
currentChannels, |
|||
|
|||
toggleFilterPulldown, |
|||
changeFilter, |
|||
|
|||
channelsSelectors |
|||
} from 'reducers/channels' |
|||
|
|||
import { |
|||
openChannelForm, |
|||
changeStep, |
|||
setNodeKey, |
|||
setLocalAmount, |
|||
setPushAmount, |
|||
closeChannelForm, |
|||
channelFormSelectors |
|||
} from 'reducers/channelform' |
|||
|
|||
import { fetchPeers } from 'reducers/peers' |
|||
|
|||
import { tickerSelectors } from 'reducers/ticker' |
|||
|
|||
import { fetchDescribeNetwork, setCurrentChannel } from '../../../reducers/network' |
|||
|
|||
import Channels from '../components/Channels' |
|||
|
|||
const mapDispatchToProps = { |
|||
fetchChannels, |
|||
openChannel, |
|||
closeChannel, |
|||
updateChannelSearchQuery, |
|||
setViewType, |
|||
toggleFilterPulldown, |
|||
changeFilter, |
|||
|
|||
openChannelForm, |
|||
closeChannelForm, |
|||
setNodeKey, |
|||
setLocalAmount, |
|||
setPushAmount, |
|||
changeStep, |
|||
|
|||
fetchPeers, |
|||
|
|||
fetchDescribeNetwork, |
|||
setCurrentChannel |
|||
} |
|||
|
|||
const mapStateToProps = state => ({ |
|||
channels: state.channels, |
|||
openChannels: state.channels.channels, |
|||
channelform: state.channelform, |
|||
peers: state.peers, |
|||
ticker: state.ticker, |
|||
network: state.network, |
|||
identity_pubkey: state.info.data.identity_pubkey, |
|||
|
|||
currentChannels: currentChannels(state), |
|||
activeChanIds: channelsSelectors.activeChanIds(state), |
|||
nonActiveFilters: channelsSelectors.nonActiveFilters(state), |
|||
activeChannels: channelsSelectors.activeChannels(state), |
|||
|
|||
currentTicker: tickerSelectors.currentTicker(state), |
|||
|
|||
channelFormHeader: channelFormSelectors.channelFormHeader(state), |
|||
channelFormProgress: channelFormSelectors.channelFormProgress(state), |
|||
stepTwoIsValid: channelFormSelectors.stepTwoIsValid(state) |
|||
}) |
|||
|
|||
const mergeProps = (stateProps, dispatchProps, ownProps) => { |
|||
const channelFormProps = { |
|||
openChannel: dispatchProps.openChannel, |
|||
closeChannelForm: dispatchProps.closeChannelForm, |
|||
changeStep: dispatchProps.changeStep, |
|||
setNodeKey: dispatchProps.setNodeKey, |
|||
setLocalAmount: dispatchProps.setLocalAmount, |
|||
setPushAmount: dispatchProps.setPushAmount, |
|||
|
|||
channelform: stateProps.channelform, |
|||
channelFormHeader: stateProps.channelFormHeader, |
|||
channelFormProgress: stateProps.channelFormProgress, |
|||
stepTwoIsValid: stateProps.stepTwoIsValid, |
|||
peers: stateProps.peers.peers |
|||
} |
|||
|
|||
return { |
|||
...stateProps, |
|||
...dispatchProps, |
|||
...ownProps, |
|||
|
|||
channelFormProps |
|||
} |
|||
} |
|||
|
|||
export default withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(Channels)) |
@ -1,3 +0,0 @@ |
|||
import ChannelsContainer from './containers/ChannelsContainer' |
|||
|
|||
export default ChannelsContainer |
@ -1,137 +0,0 @@ |
|||
import React, { Component } from 'react' |
|||
import PropTypes from 'prop-types' |
|||
|
|||
import { 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 { |
|||
constructor(props) { |
|||
super(props) |
|||
|
|||
this.state = { |
|||
refreshing: false |
|||
} |
|||
} |
|||
|
|||
componentWillMount() { |
|||
this.props.fetchPeers() |
|||
} |
|||
|
|||
render() { |
|||
const { |
|||
fetchPeers, |
|||
peerFormProps, |
|||
setPeerForm, |
|||
setPeer, |
|||
updateSearchQuery, |
|||
disconnectRequest, |
|||
|
|||
peerModalOpen, |
|||
filteredPeers, |
|||
peers: { peer, searchQuery } |
|||
} = this.props |
|||
|
|||
const refreshClicked = () => { |
|||
// turn the spinner on
|
|||
this.setState({ refreshing: true }) |
|||
|
|||
// store event in icon so we dont get an error when react clears it
|
|||
const icon = this.repeat.childNodes |
|||
|
|||
// fetch peers
|
|||
fetchPeers() |
|||
|
|||
// wait for the svg to appear as child
|
|||
const svgTimeout = setTimeout(() => { |
|||
if (icon[0].tagName === 'svg') { |
|||
// spin icon for 1 sec
|
|||
icon[0].style.animation = 'spin 1000ms linear 1' |
|||
clearTimeout(svgTimeout) |
|||
} |
|||
}, 1) |
|||
|
|||
// clear animation after the second so we can reuse it
|
|||
const refreshTimeout = setTimeout(() => { |
|||
icon[0].style.animation = '' |
|||
this.setState({ refreshing: false }) |
|||
clearTimeout(refreshTimeout) |
|||
}, 1000) |
|||
} |
|||
|
|||
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}> |
|||
<h1>Peers</h1> |
|||
</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} onClick={refreshClicked} ref={(ref) => { this.repeat = ref }}> |
|||
{ |
|||
this.state.refreshing ? |
|||
<FaRepeat /> |
|||
: |
|||
'Refresh' |
|||
} |
|||
</span> |
|||
</div> |
|||
|
|||
<div className={styles.peers}> |
|||
{ |
|||
filteredPeers.map(filteredPeer => <Peer key={filteredPeer.peer_id} peer={filteredPeer} setPeer={setPeer} />) |
|||
} |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
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.shape({ |
|||
peer: PropTypes.object, |
|||
searchQuery: PropTypes.string |
|||
}).isRequired |
|||
} |
|||
|
|||
export default Peers |
@ -1,81 +0,0 @@ |
|||
@import '../../../variables.scss'; |
|||
|
|||
.search { |
|||
height: 55px; |
|||
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: 50px; |
|||
font-size: 20px; |
|||
text-align: center; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.text { |
|||
width: 95%; |
|||
outline: 0; |
|||
padding: 0; |
|||
border: 0; |
|||
border-radius: 0; |
|||
height: 50px; |
|||
font-size: 16px; |
|||
} |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
background: $lightgrey; |
|||
|
|||
.titleContainer { |
|||
padding: 20px 40px; |
|||
|
|||
.left { |
|||
padding: 10px 0; |
|||
|
|||
h1 { |
|||
text-transform: uppercase; |
|||
font-size: 26px; |
|||
margin-right: 5px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.addPeerContainer { |
|||
padding: 20px 40px; |
|||
|
|||
.newPeerButton { |
|||
font-size: 14px; |
|||
margin-left: 10px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.refreshContainer { |
|||
padding: 20px 40px 0 40px; |
|||
text-align: right; |
|||
cursor: pointer; |
|||
|
|||
.refresh { |
|||
text-decoration: underline; |
|||
|
|||
svg { |
|||
font-size: 12px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.peers { |
|||
padding: 40px; |
|||
} |
@ -1,52 +0,0 @@ |
|||
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)) |
@ -1,3 +0,0 @@ |
|||
import PeersContainer from './containers/PeersContainer' |
|||
|
|||
export default PeersContainer |
Loading…
Reference in new issue