diff --git a/app/components/Contacts/ClosingContact.js b/app/components/Contacts/ClosingContact.js deleted file mode 100644 index 9da04523..00000000 --- a/app/components/Contacts/ClosingContact.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FaCircle } from 'react-icons/lib/fa' -import { btc, blockExplorer } from 'utils' -import styles from './Contact.scss' - -const ClosingContact = ({ channel }) => ( -
  • -
    -

    - - - Removing - blockExplorer.showChannelClosing(channel)}> - (Details) - - -

    -

    {channel.channel.remote_node_pub}

    -
    -
    -
    -

    Can Pay

    -

    {btc.satoshisToBtc(channel.channel.local_balance)}BTC

    -
    -
    -

    Can Receive

    -

    {btc.satoshisToBtc(channel.channel.remote_balance)}BTC

    -
    -
    -
  • -) - -ClosingContact.propTypes = { - channel: PropTypes.object.isRequired -} - -export default ClosingContact diff --git a/app/components/Contacts/Contact.scss b/app/components/Contacts/Contact.scss deleted file mode 100644 index 1027f3f3..00000000 --- a/app/components/Contacts/Contact.scss +++ /dev/null @@ -1,156 +0,0 @@ -@import '../../variables.scss'; - -.friend { - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 30px 60px 60px 60px; - cursor: pointer; - transition: all 0.25s; - - &.loading { - .info { - opacity: 0.2; - } - } - - &:hover { - background: $lightgrey; - } - - .limits { - display: flex; - flex-direction: row; - justify-content: space-between; - - div { - margin: 0 10px; - - h4 { - font-size: 12px; - margin-bottom: 20px; - } - } - } - - .info { - p { - margin-bottom: 20px; - - &.online { - color: $green; - - svg { - color: $green; - } - } - - &.pending { - color: $orange; - - svg { - color: $orange; - } - - i { - margin-left: 5px; - color: $darkestgrey; - cursor: pointer; - - &:hover { - text-decoration: underline; - } - } - } - - &.closing { - color: $red; - - svg { - color: $red; - } - - i { - margin-left: 5px; - color: $darkestgrey; - cursor: pointer; - - &:hover { - text-decoration: underline; - } - } - } - - svg, span { - display: inline-block; - vertical-align: top; - } - - svg { - margin-right: 5px; - width: 12px; - height: 12px; - color: $darkestgrey; - } - - span { - font-size: 12px; - } - } - - h2 { - color: $black; - font-size: 14px; - font-weight: bold; - letter-spacing: 1.3px; - - span { - color: $darkestgrey; - margin-left: 5px; - } - } - } -} - -@-webkit-keyframes animation-rotate { - 100% { - -webkit-transform: rotate(360deg); - } -} - -@-moz-keyframes animation-rotate { - 100% { - -moz-transform: rotate(360deg); - } -} - -@-o-keyframes animation-rotate { - 100% { - -o-transform: rotate(360deg); - } -} - -@keyframes animation-rotate { - 100% { - transform: rotate(360deg); - } -} - -.spinner { - border: 1px solid rgba(0, 0, 0, 0.1); - border-left-color: rgba(0, 0, 0, 0.4); - -webkit-border-radius: 999px; - -moz-border-radius: 999px; - border-radius: 999px; -} - -.spinner { - margin: 0 auto; - height: 50px; - width: 50px; - -webkit-animation: animation-rotate 1000ms linear infinite; - -moz-animation: animation-rotate 1000ms linear infinite; - -o-animation: animation-rotate 1000ms linear infinite; - animation: animation-rotate 1000ms linear infinite; -} - diff --git a/app/components/Contacts/ContactModal.js b/app/components/Contacts/ContactModal.js index 58a760ab..77f41fc3 100644 --- a/app/components/Contacts/ContactModal.js +++ b/app/components/Contacts/ContactModal.js @@ -26,7 +26,7 @@ const ContactModal = ({ }, content: { top: 'auto', - left: '20%', + left: '0', right: '0', bottom: 'auto', width: '40%', diff --git a/app/components/Contacts/ContactsForm.scss b/app/components/Contacts/ContactsForm.scss index 19123176..977195f9 100644 --- a/app/components/Contacts/ContactsForm.scss +++ b/app/components/Contacts/ContactsForm.scss @@ -6,7 +6,7 @@ margin: 50px auto; position: absolute; top: auto; - left: 20%; + left: 0; right: 0; bottom: auto; background: $white; diff --git a/app/components/Contacts/LoadingContact.js b/app/components/Contacts/LoadingContact.js deleted file mode 100644 index 19770345..00000000 --- a/app/components/Contacts/LoadingContact.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FaCircle } from 'react-icons/lib/fa' -import styles from './Contact.scss' - -const LoadingContact = ({ pubkey, isClosing }) => ( -
  • -
    -

    - - - { - isClosing ? - 'Closing' - : - 'Loading' - } - -

    -

    {pubkey}

    -
    -
    -
    -
    -
    -
    -
  • -) - -LoadingContact.propTypes = { - pubkey: PropTypes.string.isRequired, - isClosing: PropTypes.bool.isRequired -} - -export default LoadingContact diff --git a/app/components/Contacts/Network.js b/app/components/Contacts/Network.js new file mode 100644 index 00000000..27d9cd24 --- /dev/null +++ b/app/components/Contacts/Network.js @@ -0,0 +1,186 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import find from 'lodash/find' +import Isvg from 'react-inlinesvg' +import { FaAngleDown, FaCircle, FaRepeat } from 'react-icons/lib/fa' +import { btc } from 'utils' +import plus from 'icons/plus.svg' +import search from 'icons/search.svg' +import styles from './Network.scss' + +class Network extends Component { + constructor(props) { + super(props) + + this.state = { + refreshing: false + } + } + + render() { + const { + channels: { + searchQuery, + filterPulldown, + filter + // loadingChannelPubkeys, + // closingChannelIds + }, + currentChannels, + balance, + currentTicker, + + nodes, + + fetchChannels, + openContactsForm, + + nonActiveFilters, + toggleFilterPulldown, + changeFilter, + + updateChannelSearchQuery, + + openContactModal + } = 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 channels + 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) + } + + const displayNodeName = (channel) => { + const node = find(nodes, n => channel.remote_pubkey === n.pub_key) + + if (node && node.alias.length) { return node.alias } + + return channel.remote_pubkey ? channel.remote_pubkey.substring(0, 10) : channel.remote_node_pub.substring(0, 10) + } + + const channelStatus = (channel) => { + if (Object.prototype.hasOwnProperty.call(channel, 'confirmation_height')) { return 'pending' } + if (Object.prototype.hasOwnProperty.call(channel, 'closing_txid')) { return 'closing' } + if (!channel.active) { return 'offline' } + + return 'online' + } + + const usdAmount = btc.satoshisToUsd(balance.channelBalance, currentTicker.price_usd) + + return ( +
    +
    +
    +

    My Network

    + + {btc.satoshisToBtc(balance.channelBalance)}BTC ≈ ${usdAmount ? usdAmount.toLocaleString() : ''} + +
    +
    + +
    +
    + +
    +
    +
    +

    + {filter.name} +

    +
      + { + nonActiveFilters.map(f => ( +
    • changeFilter(f)}> + {f.name} +
    • + )) + } +
    +
    + +
    + { this.repeat = ref }}> + { + this.state.refreshing ? + + : + 'Refresh' + } + +
    +
    + + +
    + + +
    + ) + } +} + +Network.propTypes = { + currentChannels: PropTypes.array.isRequired, + nodes: PropTypes.array.isRequired, + nonActiveFilters: PropTypes.array.isRequired, + + channels: PropTypes.object.isRequired, + balance: PropTypes.object.isRequired, + currentTicker: PropTypes.object.isRequired, + + fetchChannels: PropTypes.func.isRequired, + openContactsForm: PropTypes.func.isRequired, + toggleFilterPulldown: PropTypes.func.isRequired, + changeFilter: PropTypes.func.isRequired, + updateChannelSearchQuery: PropTypes.func.isRequired, + openContactModal: PropTypes.func.isRequired +} + +export default Network diff --git a/app/components/Contacts/Network.scss b/app/components/Contacts/Network.scss new file mode 100644 index 00000000..ffa83e63 --- /dev/null +++ b/app/components/Contacts/Network.scss @@ -0,0 +1,179 @@ +@import '../../variables.scss'; + +.network { + position: relative; + width: 20%; + display: inline-block; + vertical-align: top; + height: 100vh; + background: #31343f; +} + +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + background: #2D303B; + padding: 10px 20px; + color: $white; + + h2 { + font-size: 14px; + font-weight: bold; + letter-spacing: 1.2px; + margin-bottom: 5px; + } + + .channelAmount { + font-size: 10px; + opacity: 0.5; + } + + .addChannel { + cursor: pointer; + transition: all 0.25s; + + &:hover { + color: $darkestgrey; + } + } +} + +.channels { + padding: 20px; + + .listHeader { + position: relative; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: baseline; + + h2, h2 span { + color: $white; + cursor: pointer; + transition: color 0.25s; + + &:hover { + color: $darkestgrey; + } + } + + h2, .filters li { + font-size: 12px; + } + + .filters { + display: none; + + &.active { + display: block; + position: absolute; + bottom: -100px; + z-index: 10; + + li { + margin: 10px 0; + cursor: pointer; + color: $white; + + &:hover { + color: $darkestgrey; + } + } + } + } + + span { + color: $white; + opacity: 1; + font-size: 10px; + cursor: pointer; + transition: all 0.25s; + + &:hover { + opacity: 0.5; + } + } + } + + ul { + margin-top: 20px; + } + + .fade { + opacity: 0.1; + } + + .channel { + display: flex; + flex-direction: row; + justify-content: space-between; + color: $white; + padding: 10px 0; + margin: 10px 0; + cursor: pointer; + + span:nth-child(1) { + font-size: 12px; + } + + .online { + color: $green; + } + + .pending { + color: $orange; + } + + .offline { + color: $darkestgrey; + } + + svg { + width: 5px; + height: 5px; + } + } +} + +.search { + position: absolute; + bottom: 20px; + width: calc(100% - 40px); + padding: 10px 20px; + border-top: 1px solid $darkestgrey; + + .input { + display: inline-block; + vertical-align: top; + height: 100%; + } + + .label { + width: 5%; + line-height: 50px; + font-size: 25px; + text-align: center; + cursor: pointer; + color: $white; + opacity: 0.5; + + svg { + width: 14px; + height:14px; + } + } + + .text { + width: calc(95% - 20px); + background: transparent; + outline: 0; + padding: 0 10px; + border: 0; + border-radius: 0; + height: 50px; + font-size: 12px; + color: $white; + } +} diff --git a/app/components/Contacts/OfflineContact.js b/app/components/Contacts/OfflineContact.js deleted file mode 100644 index 2b4621b6..00000000 --- a/app/components/Contacts/OfflineContact.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FaCircle } from 'react-icons/lib/fa' -import { btc } from 'utils' -import styles from './Contact.scss' - -const OfflineContact = ({ channel, openContactModal }) => ( -
  • openContactModal(channel)}> -
    -

    - - Offline -

    -

    {channel.remote_pubkey}

    -
    -
    -
    -

    Can Pay

    -

    {btc.satoshisToBtc(channel.local_balance)}BTC

    -
    -
    -

    Can Receive

    -

    {btc.satoshisToBtc(channel.remote_balance)}BTC

    -
    -
    -
  • -) - -OfflineContact.propTypes = { - channel: PropTypes.object.isRequired, - openContactModal: PropTypes.func.isRequired -} - -export default OfflineContact diff --git a/app/components/Contacts/OnlineContact.js b/app/components/Contacts/OnlineContact.js deleted file mode 100644 index 5c98f5f5..00000000 --- a/app/components/Contacts/OnlineContact.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FaCircle } from 'react-icons/lib/fa' -import { btc } from 'utils' -import styles from './Contact.scss' - -const OnlineContact = ({ channel, openContactModal }) => ( -
  • openContactModal(channel)}> -
    -

    - - Online -

    -

    {channel.remote_pubkey}

    -
    -
    -
    -

    Can Pay

    -

    {btc.satoshisToBtc(channel.local_balance)}BTC

    -
    -
    -

    Can Receive

    -

    {btc.satoshisToBtc(channel.remote_balance)}BTC

    -
    -
    -
  • -) - -OnlineContact.propTypes = { - channel: PropTypes.object.isRequired, - openContactModal: PropTypes.func.isRequired -} - -export default OnlineContact diff --git a/app/components/Contacts/OnlineContact.scss b/app/components/Contacts/OnlineContact.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/app/components/Contacts/PendingContact.js b/app/components/Contacts/PendingContact.js deleted file mode 100644 index d56bfaec..00000000 --- a/app/components/Contacts/PendingContact.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { FaCircle } from 'react-icons/lib/fa' -import { btc, blockExplorer } from 'utils' -import styles from './Contact.scss' - -const PendingContact = ({ channel }) => ( -
  • -
    -

    - - - Pending - blockExplorer.showChannelPoint(channel)}> - (Details) - - -

    -

    {channel.channel.remote_node_pub}

    -
    -
    -
    -

    Can Pay

    -

    {btc.satoshisToBtc(channel.channel.local_balance)}BTC

    -
    -
    -

    Can Receive

    -

    {btc.satoshisToBtc(channel.channel.remote_balance)}BTC

    -
    -
    -
  • -) - -PendingContact.propTypes = { - channel: PropTypes.object.isRequired -} - -export default PendingContact diff --git a/app/components/LoadingBolt/LoadingBolt.scss b/app/components/LoadingBolt/LoadingBolt.scss index 11f2ceb6..87102286 100644 --- a/app/components/LoadingBolt/LoadingBolt.scss +++ b/app/components/LoadingBolt/LoadingBolt.scss @@ -7,7 +7,7 @@ left: 0; width: 100%; height: 100%; - background: $white; + background: $spaceblue; text-align: center; } diff --git a/app/components/Nav/Nav.js b/app/components/Nav/Nav.js deleted file mode 100644 index 38584bfd..00000000 --- a/app/components/Nav/Nav.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { NavLink } from 'react-router-dom' -import Isvg from 'react-inlinesvg' - -import walletIcon from 'icons/wallet_2.svg' -import peersIcon from 'icons/contacts.svg' -import networkIcon from 'icons/network.svg' -import helpIcon from 'icons/help_2.svg' - -import styles from './Nav.scss' - -const Nav = ({ openPayForm, openRequestForm }) => ( - -) - -Nav.propTypes = { - openPayForm: PropTypes.func.isRequired, - openRequestForm: PropTypes.func.isRequired -} - -export default Nav diff --git a/app/components/Nav/Nav.scss b/app/components/Nav/Nav.scss deleted file mode 100644 index 88fa5ef5..00000000 --- a/app/components/Nav/Nav.scss +++ /dev/null @@ -1,188 +0,0 @@ -@import '../../variables.scss'; - -.nav { - display: inline-block; - vertical-align: top; - width: 15%; - font-size: 24px; - background: $secondary; - height: 100vh; - position: relative; - min-width: 15%; - color: $white; -} - -.header { - padding: 20px; - - h1 { - color: $main; - font-size: 30px; - font-weight: 300; - text-align: center; - float: left; - -webkit-font-smoothing: antialiased; - margin-right: 5px; - } - - span { - font-family: "Open Sans", "Helvetica Neue", Helvetica; - color: #bbb; - font-size: 11px; - letter-spacing: 2px; - text-transform: uppercase; - text-align: right; - line-height: 100%; - display: inline-block; - vertical-align: middle; - } -} - -.info { - padding: 25px 10px 10px 10px; - - .link { - display: inline-block; - vertical-align: top; - list-style-type: none; - width: 50%; - cursor: pointer; - } -} - - -.currency { - margin: 0 1px; - - &.active { - color: $main; - } - - span { - display: inline-block; - vertical-align: middle; - - svg[data-icon='ltc'] { - width: 24px; - height: 28px; - - g { - transform: scale(1.75) translate(-5px, -5px); - } - } - } -} - -.logo { - text-align: center; - margin-top: 20px; - margin-bottom: 35%; - - svg { - width: 100px; - height: 100px; - } -} - -.balance { - text-align: right; - color: $main; - - p { - margin: 2px 0; - - &:first-child { - font-size: 14px; - } - - &:nth-child(2) { - font-size: 12px; - } - - span { - display: inline-block; - vertical-align: top; - - svg[data-icon='ltc'] { - width: 10px; - height: 10px; - - g { - transform: scale(1.75) translate(-5px, -5px); - } - } - } - } -} - -.links { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-top: 50%; - - .link { - position: relative; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - width: 100%; - color: $darkestgrey; - opacity: 0.5; - cursor: pointer; - text-decoration: none; - transition: all 0.25s; - - li { - margin: 12.5px 0; - min-width: 150px; - } - - &.active { - color: $main; - opacity: 1.0; - - svg g { - stroke: $main; - } - } - - svg { - width: 28px; - height: 28px; - vertical-align: middle; - } - - span { - margin-left: 15px; - line-height: 22px; - font-size: 18px; - font-weight: 500; - letter-spacing: .2px; - } - } -} - -.buttons { - width: 75%; - font-size: 18px; - position: absolute; - bottom: 10px; - right: 12.5%; - - .button { - margin-bottom: 20px; - font-weight: bold; - cursor: pointer; - text-transform: uppercase; - letter-spacing: .2px; - font-size: 0.75em; - } -} - - -.content { - width: 80%; -} diff --git a/app/components/Nav/index.js b/app/components/Nav/index.js deleted file mode 100644 index 768d2c60..00000000 --- a/app/components/Nav/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Nav from './Nav' - -export default Nav diff --git a/app/components/Network/CanvasNetworkGraph.js b/app/components/Network/CanvasNetworkGraph.js deleted file mode 100644 index 597c6473..00000000 --- a/app/components/Network/CanvasNetworkGraph.js +++ /dev/null @@ -1,274 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import * as d3Force from 'd3-force' -import * as d3Selection from 'd3-selection' -import * as d3Zoom from 'd3-zoom' - -import styles from './CanvasNetworkGraph.scss' - -const d3 = Object.assign({}, d3Force, d3Selection, d3Zoom) - -function generateSimulationData(nodes, edges) { - const resNodes = nodes.map(node => Object.assign(node, { id: node.pub_key })) - const resEdges = edges.map(node => Object.assign(node, { source: node.node1_pub, target: node.node2_pub })) - - return { - nodes: resNodes, - links: resEdges - } -} - -class CanvasNetworkGraph extends Component { - constructor(props) { - super(props) - - this.state = { - simulationData: { - nodes: [], - links: [] - }, - - svgLoaded: false - } - - this.startSimulation = this.startSimulation.bind(this) - this.zoomActions = this.zoomActions.bind(this) - this.ticked = this.ticked.bind(this) - this.restart = this.restart.bind(this) - } - - componentDidMount() { - // wait for the svg to be in the DOM before we start the simulation - const svgInterval = setInterval(() => { - if (document.getElementById('mapContainer')) { - d3.select('#mapContainer') - .append('svg') - .attr('id', 'map') - .attr('width', '100%') - .attr('height', '100%') - - this.startSimulation() - - clearInterval(svgInterval) - } - }, 1000) - } - - componentWillReceiveProps(nextProps) { - const { network } = nextProps - const { simulationData: { nodes, links } } = this.state - - const simulationDataEmpty = !nodes.length && !links.length - const networkDataLoaded = network.nodes.length || network.edges.length - const prevNetwork = this.props.network - - if ( - // update the simulationData only if - // the simulationData is empty and we have network data - (simulationDataEmpty && networkDataLoaded) || - // the nodes or edges have changed - (prevNetwork.nodes.length !== network.nodes.length || prevNetwork.edges.length !== network.edges.length)) { - this.setState({ - simulationData: generateSimulationData(network.nodes, network.edges) - }) - } - } - - componentDidUpdate(prevProps) { - const { - selectedPeerPubkeys, - selectedChannelIds, - currentRouteChanIds - } = this.props - - if (prevProps.selectedPeerPubkeys.length !== selectedPeerPubkeys.length) { - this.updateSelectedPeers() - } - - if (prevProps.selectedChannelIds.length !== selectedChannelIds.length) { - this.updateSelectedChannels() - } - - if (prevProps.currentRouteChanIds.length !== currentRouteChanIds.length) { - this.renderSelectedRoute() - } - } - - componentWillUnmount() { - d3.select('#map') - .remove() - } - - updateSelectedPeers() { - const { selectedPeerPubkeys } = this.props - - // remove active class - d3.selectAll('.active-peer').classed('active-peer', false) - - // add active class to all selected peers - selectedPeerPubkeys.forEach((pubkey) => { - d3.select(`#node-${pubkey}`).classed('active-peer', true) - }) - } - - updateSelectedChannels() { - const { selectedChannelIds } = this.props - - // remove active class - d3.selectAll('.active-channel').classed('active-channel', false) - - // add active class to all selected peers - selectedChannelIds.forEach((chanid) => { - d3.select(`#link-${chanid}`).classed('active-channel', true) - }) - } - - startSimulation() { - const { simulationData: { nodes, links } } = this.state - - // grab the svg el along with the attributes - const svg = d3.select('#map') - const svgBox = svg.node().getBBox() - - this.g = svg.append('g').attr('transform', `translate(${svgBox.width / 2},${svgBox.height / 2})`) - this.link = this.g.append('g').attr('stroke', 'white').attr('stroke-width', 1.5).selectAll('.link') - this.node = this.g.append('g').attr('stroke', 'silver').attr('stroke-width', 1.5).selectAll('.node') - - this.simulation = d3.forceSimulation(nodes) - .force('charge', d3.forceManyBody().strength(-750)) - .force('link', d3.forceLink(links).id(d => d.pub_key).distance(500)) - .force('collide', d3.forceCollide(300)) - .on('tick', this.ticked) - .on('end', () => { - this.setState({ svgLoaded: true }) - }) - // zoom - const zoom_handler = d3.zoom().on('zoom', this.zoomActions) - zoom_handler(svg) - - this.restart() - } - - zoomActions() { - this.g.attr('transform', d3Selection.event.transform) - } - - ticked() { - this.node.attr('cx', d => d.x) - .attr('cy', d => d.y) - - this.link.attr('x1', d => d.source.x) - .attr('y1', d => d.source.y) - .attr('x2', d => d.target.x) - .attr('y2', d => d.target.y) - } - - restart() { - const { identity_pubkey } = this.props - const { simulationData: { nodes, links } } = this.state - - // Apply the general update pattern to the nodes. - this.node = this.node.data(nodes, d => d.pub_key) - this.node.exit().remove() - this.node = this.node.enter() - .append('circle') - .attr('stroke', () => 'silver') - .attr('fill', d => (d.pub_key === identity_pubkey ? '#FFF' : '#353535')) - .attr('r', () => 100) - .attr('id', d => `node-${d.pub_key}`) - .attr('class', 'network-node') - .merge(this.node) - - // Apply the general update pattern to the links. - this.link = this.link.data(links, d => `${d.source.id}-${d.target.id}`) - this.link.exit().remove() - this.link = - this.link.enter() - .append('line') - .attr('id', d => `link-${d.channel_id}`) - .attr('class', 'network-link') - .merge(this.link) - - // Update and restart the simulation. - this.simulation.nodes(nodes) - this.simulation.force('link').links(links) - this.simulation.restart() - } - - renderSelectedRoute() { - const { currentRouteChanIds } = this.props - - // remove all route animations before rendering new ones - d3.selectAll('.animated-route-circle').remove() - - currentRouteChanIds.forEach((chanId) => { - const link = document.getElementById(`link-${chanId}`) - - if (!link) { return } - const x1 = link.x1.baseVal.value - const x2 = link.x2.baseVal.value - const y1 = link.y1.baseVal.value - const y2 = link.y2.baseVal.value - - // create the circle that represent btc traveling through a channel - this.g - .append('circle') - .attr('id', `circle-${chanId}`) - .attr('class', 'animated-route-circle') - .attr('r', 50) - .attr('cx', x1) - .attr('cy', y1) - .attr('fill', '#FFDC53') - - // we want the animation to repeat back and forth, this function executes that visually - const repeat = () => { - d3.select(`#circle-${chanId}`) - .transition() - .attr('cx', x2) - .attr('cy', y2) - .duration(1000) - .transition() - .duration(1000) - .attr('cx', x1) - .attr('cy', y1) - .on('end', repeat) - } - - // call repeat to animate the circle - repeat() - }) - } - - render() { - const { svgLoaded } = this.state - - return ( -
    - { - !svgLoaded && -
    -
    -
    -
    -
    -
    -
    loading
    -
    -
    - } -
    - ) - } -} - -CanvasNetworkGraph.propTypes = { - identity_pubkey: PropTypes.string.isRequired, - - network: PropTypes.object.isRequired, - - selectedPeerPubkeys: PropTypes.array.isRequired, - selectedChannelIds: PropTypes.array.isRequired, - currentRouteChanIds: PropTypes.array.isRequired -} - -export default CanvasNetworkGraph diff --git a/app/components/Network/CanvasNetworkGraph.scss b/app/components/Network/CanvasNetworkGraph.scss deleted file mode 100644 index 7a9d1da9..00000000 --- a/app/components/Network/CanvasNetworkGraph.scss +++ /dev/null @@ -1,156 +0,0 @@ -@import '../../variables.scss'; - -@keyframes fadein { - 0% { background: $white; } - 50% { background: lighten($secondary, 50%); } - 100% { background: $secondary; animation-fill-mode:forwards; } -} - -.mapContainer { - position: relative; - display: inline-block; - width: 70%; - height: 100%; -} - -.loadingContainer { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: $secondary; -} - -.loadingWrap { - position: relative; - top: calc(50% - 150px); - width: 150px; - margin: 0 auto; -} - -.loader { - position: absolute; - top: 0; - z-index: 10; - width: 50px; - height: 50px; - border: 15px solid; - border-radius: 50%; - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); - animation: loadEr 3s infinite; -} - -@keyframes loadEr { - 0% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); - - } - 10.4% { - border-top-color: rgba(44,44,44,0.5); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); - } - 20.8% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); - } -31.2% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0.5); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); -} -41.6% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); - transform: rotate(40deg); -} -52% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0.5); - border-left-color: rgba(33,33,33,0); -} -62.4% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0); -} -72.8% { - border-top-color: rgba(44,44,44,0); - border-right-color: rgba(55,55,55,0); - border-bottom-color: rgba(66,66,66,0); - border-left-color: rgba(33,33,33,0.5); -} -} - -.loaderbefore { - width: 50px; - height:50px; - border: 15px solid #ddd; - border-radius: 50%; - position: absolute; - top: 0; - z-index: 9; -} - -.circular { - position: absolute; - top: -15px; - left: -15px; - width: 70px; - height: 70px; - border: 20px solid; - border-radius: 50%; - border-top-color: #333; - border-left-color: #fff; - border-bottom-color: #333; - border-right-color: #fff; - opacity: 0.2; - animation: poof 5s infinite; -} -@keyframes poof { - 0% {transform: scale(1,1) rotate(0deg); opacity: 0.2;} - 50% {transform: scale(4,4) rotate(360deg); opacity: 0;} -} -.another { - opacity: 0.1; - transform: rotate(90deg); - animation: poofity 5s infinite; - animation-delay: 1s; -} -@keyframes poofity { - 0% {transform: scale(1,1) rotate(90deg); opacity: 0.1;} - 50% {transform: scale(4,4) rotate(-360deg); opacity: 0;} -} - -.text { - position: absolute; - top: 95px; - left: 8px; - font-family: Arial; - text-transform: uppercase; - color: #888; - animation: opaa 10s infinite; -} -@keyframes opaa { - 0% {opacity: 1;} -10% {opacity: 0.5} -15% {opacity: 1;} -30% {opacity: 1;} -65% {opacity: 0.3;} -90% {opacity: 0.8;} -} \ No newline at end of file diff --git a/app/components/Network/ChannelsList.js b/app/components/Network/ChannelsList.js deleted file mode 100644 index 2cf78594..00000000 --- a/app/components/Network/ChannelsList.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { btc, blockExplorer } from 'utils' -import styles from './ChannelsList.scss' - -const ChannelsList = ({ channels, updateSelectedChannels, selectedChannelIds }) => ( -
      - { - channels.map(channel => ( -
    • updateSelectedChannels(channel)}> - - -
      -

      Capacity: {btc.satoshisToBtc(channel.capacity)}

      - blockExplorer.showChannelPoint({ channel })}>Channel Point -
      - -
      -

      Remote Pubkey:

      -

      {channel.remote_pubkey.substring(0, Math.min(30, channel.remote_pubkey.length))}...

      -
      - -
      -
      -

      Sent:

      -

      {btc.satoshisToBtc(channel.total_satoshis_sent)} BTC

      -
      -
      -

      Received:

      -

      {btc.satoshisToBtc(channel.total_satoshis_received)} BTC

      -
      -
      -
    • - )) - } -
    -) - -ChannelsList.propTypes = { - channels: PropTypes.array.isRequired, - updateSelectedChannels: PropTypes.func.isRequired, - selectedChannelIds: PropTypes.array.isRequired -} - -export default ChannelsList diff --git a/app/components/Network/ChannelsList.scss b/app/components/Network/ChannelsList.scss deleted file mode 100644 index fd177e5f..00000000 --- a/app/components/Network/ChannelsList.scss +++ /dev/null @@ -1,78 +0,0 @@ -@import '../../variables.scss'; - -.channels { - color: $white; - margin-top: 50px; -} - -.channel { - position: relative; - margin: 20px 0; - padding: 10px 40px; - cursor: pointer; - transition: all 0.25s; - - &:hover { - background: darken(#353535, 10%); - - .dot { - background: #88D4A2; - opacity: 0.5; - } - } - - .dot { - position: absolute; - top: calc(15% - 10px); - left: 5%; - width: 10px; - height: 10px; - border: 1px solid #979797; - border-radius: 50%; - - &.active { - background: #88D4A2; - } - } - - header { - margin-bottom: 10px; - display: flex; - flex-direction: row; - justify-content: space-between; - - h1 { - margin-bottom: 10px; - } - - span { - font-size: 10px; - text-decoration: underline; - transition: all 0.25s; - - &:hover { - color: #88D4A2; - } - } - } - - - section { - margin: 10px 0; - - h4 { - font-weight: bold; - text-transform: uppercase; - font-size: 10px; - margin-bottom: 5px; - } - } - - .funds { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-top: 20px; - } -} diff --git a/app/components/Network/PeersList.js b/app/components/Network/PeersList.js deleted file mode 100644 index a7364c69..00000000 --- a/app/components/Network/PeersList.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import styles from './PeersList.scss' - -const PeersList = ({ peers, updateSelectedPeers, selectedPeerPubkeys }) => ( -
      - { - peers.map(peer => ( -
    • updateSelectedPeers(peer)}> - -

      {peer.address}

      -

      {peer.pub_key}

      -
    • - )) - } -
    -) - -PeersList.propTypes = { - peers: PropTypes.array.isRequired, - updateSelectedPeers: PropTypes.func.isRequired, - selectedPeerPubkeys: PropTypes.array.isRequired -} - -export default PeersList diff --git a/app/components/Network/PeersList.scss b/app/components/Network/PeersList.scss deleted file mode 100644 index e2ee2861..00000000 --- a/app/components/Network/PeersList.scss +++ /dev/null @@ -1,46 +0,0 @@ -@import '../../variables.scss'; - -.peers { - color: $white; - margin-top: 50px; -} - -.peer { - position: relative; - margin: 20px 0; - padding: 10px 40px; - cursor: pointer; - transition: all 0.25s; - - &:hover { - background: darken(#353535, 10%); - - .dot { - background: #5589F3; - opacity: 0.5; - } - } - - .dot { - position: absolute; - top: calc(50% - 10px); - left: 5%; - width: 10px; - height: 10px; - border: 1px solid #979797; - border-radius: 50%; - - &.active { - background: #5589F3; - } - } - - h1 { - font-size: 16px; - margin-bottom: 10px; - } - - h4 { - font-size: 8px; - } -} \ No newline at end of file diff --git a/app/components/Network/TransactionForm.js b/app/components/Network/TransactionForm.js deleted file mode 100644 index 5c226057..00000000 --- a/app/components/Network/TransactionForm.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { btc } from 'utils' -import styles from './TransactionForm.scss' - -const TransactionForm = ({ - updatePayReq, pay_req, loadingRoutes, payReqRoutes, setCurrentRoute, currentRoute -}) => ( -
    -
    - updatePayReq(event.target.value)} - /> -
    - - { - loadingRoutes && -
    -
    -

    calculating all routes...

    -
    - } - -
      - { - payReqRoutes.map((route, index) => ( -
    • setCurrentRoute(route)}> -
      -

      Route #{index + 1}

      - Hops: {route.hops.length} -
      - -
      -
      -

      Amount

      - {btc.satoshisToBtc(route.total_amt)} BTC -
      - -
      -

      Fees

      - {btc.satoshisToBtc(route.total_fees)} BTC -
      -
      -
    • - )) - } -
    -
    -) - -TransactionForm.propTypes = { - updatePayReq: PropTypes.func.isRequired, - pay_req: PropTypes.string.isRequired, - loadingRoutes: PropTypes.bool.isRequired, - payReqRoutes: PropTypes.array.isRequired, - setCurrentRoute: PropTypes.func.isRequired, - currentRoute: PropTypes.object.isRequired -} - -export default TransactionForm diff --git a/app/components/Network/TransactionForm.scss b/app/components/Network/TransactionForm.scss deleted file mode 100644 index f4c35cf7..00000000 --- a/app/components/Network/TransactionForm.scss +++ /dev/null @@ -1,125 +0,0 @@ -@import '../../variables.scss'; - -@-webkit-keyframes animation-rotate { - 100% { - -webkit-transform: rotate(360deg); - } -} - -@-moz-keyframes animation-rotate { - 100% { - -moz-transform: rotate(360deg); - } -} - -@-o-keyframes animation-rotate { - 100% { - -o-transform: rotate(360deg); - } -} - -@keyframes animation-rotate { - 100% { - transform: rotate(360deg); - } -} - -.spinner { - border: 1px solid rgba(255, 220, 83, 0.1); - border-left-color: rgba(255, 220, 83, 0.4); - -webkit-border-radius: 999px; - -moz-border-radius: 999px; - border-radius: 999px; -} - -.spinner { - margin: 0 auto; - height: 100px; - width: 100px; - -webkit-animation: animation-rotate 1000ms linear infinite; - -moz-animation: animation-rotate 1000ms linear infinite; - -o-animation: animation-rotate 1000ms linear infinite; - animation: animation-rotate 1000ms linear infinite; -} - -.loading { - margin-top: 40px; - - h1 { - text-align: center; - margin-top: 25px; - } -} - -.transactionForm { - color: $white; - margin-top: 50px; - - .form { - padding: 0 20px; - } - - .transactionInput { - outline: 0; - border: 0; - border-bottom: 1px solid $secondary; - color: $secondary; - background: transparent; - padding: 5px; - width: 100%; - font-size: 14px; - color: $white; - } -} - -.routes { - margin-top: 40px; -} - -.route { - margin: 20px 0; - padding: 20px; - cursor: pointer; - - &:hover { - background: darken(#353535, 10%); - } - - &.active { - background: darken(#353535, 10%); - } - - header { - display: flex; - flex-direction: row; - justify-content: space-between; - margin-bottom: 20px; - - h1 { - font-size: 16px; - font-weight: bold; - } - - span { - font-weight: bold; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 1.2px; - } - } - - .data { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - - section { - h4 { - font-size: 12px; - font-weight: bold; - margin-bottom: 5px; - } - } - } -} \ No newline at end of file diff --git a/app/components/Wallet/ReceiveModal.js b/app/components/Wallet/ReceiveModal.js index 02498160..8a02f0db 100644 --- a/app/components/Wallet/ReceiveModal.js +++ b/app/components/Wallet/ReceiveModal.js @@ -17,7 +17,7 @@ const ReceiveModal = ({ }, content: { top: 'auto', - left: '20%', + left: '0', right: '0', bottom: 'auto', width: '40%', diff --git a/app/components/Wallet/Wallet.js b/app/components/Wallet/Wallet.js index 08ec36a1..dcd0021c 100644 --- a/app/components/Wallet/Wallet.js +++ b/app/components/Wallet/Wallet.js @@ -1,9 +1,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { FaQrcode } from 'react-icons/lib/fa' +import { FaAngleDown } from 'react-icons/lib/fa' import Isvg from 'react-inlinesvg' import { btc } from 'utils' -import skinnyBitcoinIcon from 'icons/skinny_bitcoin.svg' +import bitcoinIcon from 'icons/bitcoin.svg' +import zapLogo from 'icons/zap_logo.svg' +import qrCode from 'icons/qrcode.svg' import ReceiveModal from './ReceiveModal' import styles from './Wallet.scss' @@ -23,10 +25,14 @@ class Wallet extends Component { balance, address, info, - newAddress + newAddress, + currentTicker, + openPayForm, + openRequestForm } = this.props const { modalOpen, qrCodeType } = this.state + const usdAmount = parseFloat(btc.satoshisToUsd(balance.walletBalance, currentTicker.price_usd)) const changeQrCode = () => { const qrCodeNum = this.state.qrCodeType === 1 ? 2 : 1 @@ -51,22 +57,39 @@ class Wallet extends Component { ) }
    +
    +
    + +
    + +
    +
    + {info.data.alias} + +
    +
    +
    +
    - +
    -

    {btc.satoshisToBtc(parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance))} BTC

    - {btc.satoshisToBtc(balance.walletBalance)} available - {btc.satoshisToBtc(balance.channelBalance)} in channels +

    + + {btc.satoshisToBtc(parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance))}BTC + + this.setState({ modalOpen: true })}> + + +

    + ≈ ${usdAmount ? usdAmount.toLocaleString() : ''}
    -
    this.setState({ modalOpen: true })}> - - Address -
    +
    Pay
    +
    Request
    @@ -79,7 +102,10 @@ Wallet.propTypes = { balance: PropTypes.object.isRequired, address: PropTypes.string.isRequired, info: PropTypes.object.isRequired, - newAddress: PropTypes.func.isRequired + newAddress: PropTypes.func.isRequired, + currentTicker: PropTypes.object.isRequired, + openPayForm: PropTypes.func.isRequired, + openRequestForm: PropTypes.func.isRequired } export default Wallet diff --git a/app/components/Wallet/Wallet.scss b/app/components/Wallet/Wallet.scss index 80ff9dd4..db643089 100644 --- a/app/components/Wallet/Wallet.scss +++ b/app/components/Wallet/Wallet.scss @@ -1,10 +1,31 @@ @import '../../variables.scss'; .wallet { - cursor: pointer; - background: $lightgrey; + background: $bluegrey; + color: $white; transition: background 0.25s; height: 150px; + padding: 20px 40px; +} + +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + + .logo span svg { + width: 64px; + height: 24px; + } + + .user { + cursor: pointer; + transition: all 0.25s; + + &:hover { + opacity: 0.5; + } + } } .left, .right { @@ -14,7 +35,7 @@ height: 150px; .leftContent, .rightContent { - padding: 25px; + padding: 25px 0; } } @@ -23,8 +44,8 @@ flex-direction: row; .bitcoinLogo svg { - width: 100px; - height: 100px; + width: 32px; + height: 32px; } .details { @@ -33,14 +54,37 @@ justify-content: center; h1 { - font-size: 24px; - font-weight: bold; - margin-bottom: 10px; - letter-spacing: 1.5px; + display: flex; + flex-direction: row; + + span:nth-child(1) { + font-size: 24px; + line-height: 32px; + font-weight: 500; + margin-left: 10px; + margin-bottom: 5px; + letter-spacing: 1.5px; + } + + span:nth-child(2) svg { + color: $white; + width: 20px; + height: 32px; + opacity: 1; + margin-left: 5px; + cursor: pointer; + transition: all 0.25s; + + &:hover { + opacity: 0.5; + } + } } - span { - margin: 2.5px 0; + .usdValue { + font-size: 12px; + margin-left: 10px; + font-style: italic; } } @@ -56,42 +100,24 @@ justify-content: flex-end; align-items: right; - .addressButton { - box-shadow: none; + .pay, .request { + font-size: 16px; + font-weight: bold; + color: $white; + background: $spaceblue; + padding: 15px; + width: 100px; + text-align: center; + border-radius: 5px; + cursor: pointer; transition: all 0.25s; - padding-top: 12px; - padding-bottom: 10px; - font-size: 14px; - - &:hover { - background: darken($main, 10%); - } - - span { - display: inline-block; - vertical-align: top; - - &:nth-child(1) svg { - width: 14px; - height: 14px; - margin-right: 5px; - } - } - } - - div { - padding: 7px 20px; - background: $main; - transition: background 0.25s; - color: $black; &:hover { - background: darken($main, 10%); + opacity: 0.5; } - svg { - font-size: 35px; - margin-right: 10px; + &:nth-child(1) { + margin-right: 20px; } } } diff --git a/app/icons/bitcoin.svg b/app/icons/bitcoin.svg new file mode 100644 index 00000000..63b9304f --- /dev/null +++ b/app/icons/bitcoin.svg @@ -0,0 +1,16 @@ + + + + Bitcoin + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/app/icons/check_circle.svg b/app/icons/check_circle.svg new file mode 100644 index 00000000..d95153c8 --- /dev/null +++ b/app/icons/check_circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/clock.svg b/app/icons/clock.svg new file mode 100644 index 00000000..833c0608 --- /dev/null +++ b/app/icons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/qrcode.svg b/app/icons/qrcode.svg new file mode 100644 index 00000000..fe674bc6 --- /dev/null +++ b/app/icons/qrcode.svg @@ -0,0 +1,27 @@ + + + + Group 23 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/icons/search.svg b/app/icons/search.svg new file mode 100644 index 00000000..c69110d6 --- /dev/null +++ b/app/icons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/zap_logo.svg b/app/icons/zap_logo.svg new file mode 100644 index 00000000..2e0032a4 --- /dev/null +++ b/app/icons/zap_logo.svg @@ -0,0 +1,16 @@ + + + + Group 5 + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/app/main.dev.js b/app/main.dev.js index d8f8621b..662fe1a0 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -146,7 +146,8 @@ const startLnd = () => { '--neutrino.connect=127.0.0.1:18333', '--autopilot.active', '--debuglevel=debug', - '--noencryptwallet' + '--noencryptwallet', + '--alias=jimmymow' ] ) .on('error', error => console.log(`lnd error: ${error}`)) diff --git a/app/reducers/activity.js b/app/reducers/activity.js index e9b672ae..e08ff65e 100644 --- a/app/reducers/activity.js +++ b/app/reducers/activity.js @@ -7,11 +7,11 @@ const initialState = { filterPulldown: false, filter: { key: 'ALL_ACTIVITY', name: 'All Activity' }, filters: [ - { key: 'ALL_ACTIVITY', name: 'All Activity' }, - { key: 'LN_ACTIVITY', name: 'LN Activity' }, - { key: 'PAYMENT_ACTIVITY', name: 'LN Payments' }, - { key: 'INVOICE_ACTIVITY', name: 'LN Invoices' }, - { key: 'TRANSACTION_ACTIVITY', name: 'On-chain Activity' } + { key: 'ALL_ACTIVITY', name: 'All' }, + { key: 'SENT_ACTIVITY', name: 'Sent' }, + { key: 'REQUESTED_ACTIVITY', name: 'Requested' }, + { key: 'PENDING_ACTIVITY', name: 'Pending' }, + { key: 'FUNDED_ACTIVITY', name: 'Funding Transactions' } ], modal: { modalType: null, @@ -91,6 +91,7 @@ const searchSelector = state => state.activity.searchText const paymentsSelector = state => state.payment.payments const invoicesSelector = state => state.invoice.invoices const transactionsSelector = state => state.transaction.transactions +const channelsSelector = state => state.channels.channels const allActivity = createSelector( searchSelector, @@ -117,33 +118,46 @@ const allActivity = createSelector( } ) -const lnActivity = createSelector( - paymentsSelector, +const invoiceActivity = createSelector( invoicesSelector, - (payments, invoices) => [...payments, ...invoices].sort((a, b) => b.creation_date - a.creation_date) + invoices => invoices ) -const paymentActivity = createSelector( +const sentActivity = createSelector( + transactionsSelector, paymentsSelector, - payments => payments + (transactions, payments) => { + const sentTransactions = transactions.filter(transaction => transaction.amount < 0) + return [...sentTransactions, ...payments].sort((a, b) => { + const aTimestamp = Object.prototype.hasOwnProperty.call(a, 'time_stamp') ? a.time_stamp : a.creation_date + const bTimestamp = Object.prototype.hasOwnProperty.call(b, 'time_stamp') ? b.time_stamp : b.creation_date + + return bTimestamp - aTimestamp + }) + } ) -const invoiceActivity = createSelector( +const pendingActivity = createSelector( invoicesSelector, - invoices => invoices + invoices => invoices.filter(invoice => !invoice.settled) ) -const transactionActivity = createSelector( +const fundedActivity = createSelector( transactionsSelector, - transactions => transactions + channelsSelector, + (transactions, channels) => { + const fundingTxIds = channels.map(channel => channel.channel_point.split(':')[0]) + + return transactions.filter(transaction => fundingTxIds.includes(transaction.tx_hash)) + } ) const FILTERS = { ALL_ACTIVITY: allActivity, - LN_ACTIVITY: lnActivity, - PAYMENT_ACTIVITY: paymentActivity, - INVOICE_ACTIVITY: invoiceActivity, - TRANSACTION_ACTIVITY: transactionActivity + SENT_ACTIVITY: sentActivity, + REQUESTED_ACTIVITY: invoiceActivity, + PENDING_ACTIVITY: pendingActivity, + FUNDED_ACTIVITY: fundedActivity } activitySelectors.currentActivity = createSelector( diff --git a/app/reducers/channels.js b/app/reducers/channels.js index f38c4ae3..6c255519 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -512,13 +512,13 @@ const initialState = { viewType: 0, filterPulldown: false, - filter: { key: 'ALL_CHANNELS', name: 'All Contacts' }, + filter: { key: 'ALL_CHANNELS', name: 'All' }, filters: [ - { key: 'ALL_CHANNELS', name: 'All Contacts' }, - { key: 'ACTIVE_CHANNELS', name: 'Online Contacts' }, - { key: 'NON_ACTIVE_CHANNELS', name: 'Offline Contacts' }, - { key: 'OPEN_PENDING_CHANNELS', name: 'Pending Contacts' }, - { key: 'CLOSING_PENDING_CHANNELS', name: 'Closing Contacts' } + { key: 'ALL_CHANNELS', name: 'All' }, + { key: 'ACTIVE_CHANNELS', name: 'Online' }, + { key: 'NON_ACTIVE_CHANNELS', name: 'Offline' }, + { key: 'OPEN_PENDING_CHANNELS', name: 'Pending' }, + { key: 'CLOSING_PENDING_CHANNELS', name: 'Closing' } ], loadingChannelPubkeys: [], diff --git a/app/routes.js b/app/routes.js index bf141588..7433c130 100644 --- a/app/routes.js +++ b/app/routes.js @@ -3,16 +3,10 @@ import React from 'react' import { Switch, Route } from 'react-router' import App from './routes/app' import Activity from './routes/activity' -import Contacts from './routes/contacts' -import Network from './routes/network' -import Help from './routes/help' const routes = () => ( - - - diff --git a/app/routes/activity/components/Activity.js b/app/routes/activity/components/Activity.js index 60c83d5d..726721b8 100644 --- a/app/routes/activity/components/Activity.js +++ b/app/routes/activity/components/Activity.js @@ -1,7 +1,5 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { MdSearch } from 'react-icons/lib/md' -import { FaAngleDown } from 'react-icons/lib/fa' import Wallet from 'components/Wallet' import LoadingBolt from 'components/LoadingBolt' @@ -47,20 +45,19 @@ class Activity extends Component { render() { const { ticker, - updateSearchText, invoice: { invoiceLoading }, address: { address }, balance, info, payment: { paymentLoading }, currentTicker, - activity: { modal, filter, filterPulldown, searchText }, + activity: { modal, filters, filter, filterPulldown }, hideActivityModal, changeFilter, - toggleFilterPulldown, currentActivity, - nonActiveFilters, - newAddress + newAddress, + openPayForm, + openRequestForm } = this.props if (invoiceLoading || paymentLoading) { return } @@ -77,33 +74,26 @@ class Activity extends Component { currentTicker={currentTicker} /> - - -
    - - updateSearchText(event.target.value)} - className={`${styles.text} ${styles.input}`} - placeholder='Search by hash...' - type='text' - id='invoiceSearch' - /> -
    +
    -

    - {filter.name} -

    -
      +
        { - nonActiveFilters.map(f => ( -
      • changeFilter(f)}> - {f.name} + filters.map(f => ( +
      • changeFilter(f)}> + {f.name} + +
      • )) } @@ -132,7 +122,6 @@ Activity.propTypes = { fetchBalance: PropTypes.func.isRequired, ticker: PropTypes.object.isRequired, - updateSearchText: PropTypes.func.isRequired, invoice: PropTypes.object.isRequired, payment: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired, @@ -141,11 +130,11 @@ Activity.propTypes = { hideActivityModal: PropTypes.func.isRequired, changeFilter: PropTypes.func.isRequired, newAddress: PropTypes.func.isRequired, - toggleFilterPulldown: PropTypes.func.isRequired, + openPayForm: PropTypes.func.isRequired, + openRequestForm: PropTypes.func.isRequired, activity: PropTypes.object.isRequired, currentActivity: PropTypes.array.isRequired, - nonActiveFilters: PropTypes.array.isRequired, address: PropTypes.object.isRequired, balance: PropTypes.object.isRequired, info: PropTypes.object.isRequired diff --git a/app/routes/activity/components/Activity.scss b/app/routes/activity/components/Activity.scss index 38e9caff..04579a6b 100644 --- a/app/routes/activity/components/Activity.scss +++ b/app/routes/activity/components/Activity.scss @@ -32,91 +32,67 @@ .activities { background: $white; + height: 100%; .header { - padding: 60px 0 20px 0; + background: $spaceblue; + color: $white; margin: 0 auto; + padding: 0 40px; + border-bottom: 1px solid $spaceborder; - section { - position: relative; - margin-left: auto; - margin-right: auto; - padding-left: 100px; - padding-right: 100px; - max-width: 964px; - - h2 { - color: $bluegrey; - } - } - h2, h2 span { - color: $bluegrey; - cursor: pointer; - transition: color 0.25s; + .filters { + display: flex; + flex-direction: row; + + li { + position: relative; + margin: 0 15px; + opacity: 0.5; + font-size: 14px; + cursor: pointer; + padding: 20px 0; - &:hover { - color: lighten($bluegrey, 10%); + &.activeFilter { + opacity: 1; + } + + &:nth-child(1) { + margin-left: 0; + } } - } - - 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; + + .activeBorder { + width: 100%; + height: 1px; + background: white; position: absolute; - bottom: -100px; - z-index: 10; - - li { - margin: 5px 0; - cursor: pointer; - - &:hover { - color: $main; - } - } - } - } + bottom: -1px; + } + } } } .activityContainer { - background: $white; + background: $spaceblue; transition: opacity 0.25s; padding-bottom: 50px; + min-height: 100vh; + height: 100vh; + overflow-y: scroll; + padding-top: 20px; &.pulldown { opacity: 0.15; } } -.activityList { - width: 75%; - margin: 0 auto; - background: $white; -} - .activity { - padding: 0 100px; + padding: 0 40px; &:hover { - background-color: #f0f0f0; - transition-delay: 0s; - outline: $grey solid 1px; + } .left, .center, .right { diff --git a/app/routes/activity/components/components/Activity.scss b/app/routes/activity/components/components/Activity.scss index 993f309b..2eca71a2 100644 --- a/app/routes/activity/components/components/Activity.scss +++ b/app/routes/activity/components/components/Activity.scss @@ -4,15 +4,12 @@ display: flex; flex-direction: row; cursor: pointer; - max-width: 960px; - margin: 0 auto; height: 76px; align-items: center; - border-bottom: 1px solid $grey; font-size: 14px; transition: background-color .1s linear; transition-delay: .1s; - color: $darkestgrey; + color: $white; position: relative; } @@ -37,22 +34,20 @@ justify-content: center; align-items: center; padding-right: 50px; + min-width: 90px; - time { - text-transform: uppercase; + section { + margin: 2.5px 0; + } - &:nth-child(1) { - display: flex; - align-items: center; - height: 38px; - font-size: 18px; - margin-bottom: 5px; - } + svg { + width: 12.5px; + height: 12.5px; + } - &:nth-child(2) { - font-size: 12px; - } - } + time { + font-size: 12px; + } } .data { @@ -61,6 +56,10 @@ flex: 6; justify-content: space-evenly; + &:nth-child(2) { + font-size: 10px; + } + .title { margin-bottom: 5px; } @@ -70,23 +69,24 @@ } h3, span { - font-size: 18px; + font-size: 14px; font-weight: bold; + letter-spacing: 1.2px; } .icon { display: inline-block; flex: none; position: relative; - width: 36px; - height: 36px; - border: 1px solid $darkestgrey; + width: 20px; + height: 20px; + background: #31343f; border-radius: 50%; - margin-right: 15px; + margin-right: 5px; svg { - color: $main; - font-size: 16px; + color: $white; + font-size: 10px; vertical-align: middle; display: flex; top: 0; @@ -116,16 +116,33 @@ flex-direction: column; flex: 1; text-align: right; - font-size: 16px; - - &.positive span:nth-child(1) { - font-weight: bold; - color: $main; - } + font-size: 12px; + color: $white; span { + &:nth-child(1) { + margin-bottom: 5px; + } + &:nth-child(2) { - font-size: 12px; + font-size: 10px; + opacity: 0.5; + } + + .plus, .minus { + margin-right: 2px; + } + + .plus { + color: $green; + } + + .minus { + color: $red; } } } + +.unpaid svg { + opacity: 0.5; +} diff --git a/app/routes/activity/components/components/Invoice/Invoice.js b/app/routes/activity/components/components/Invoice/Invoice.js index 78f040c4..b5e64634 100644 --- a/app/routes/activity/components/components/Invoice/Invoice.js +++ b/app/routes/activity/components/components/Invoice/Invoice.js @@ -2,31 +2,31 @@ import React from 'react' import PropTypes from 'prop-types' import Moment from 'react-moment' import 'moment-timezone' -import { FaBolt, FaClockO } from 'react-icons/lib/fa' +import Isvg from 'react-inlinesvg' +import { FaBolt } from 'react-icons/lib/fa' import { btc } from 'utils' +import checkmarkIcon from 'icons/check_circle.svg' +import clockIcon from 'icons/clock.svg' import styles from '../Activity.scss' const Invoice = ({ invoice, ticker, currentTicker, showActivityModal }) => (
        showActivityModal('INVOICE', { invoice })}> - { - !invoice.settled ? -
        - - - -
        - : - null - }
        - - {invoice.creation_date * 1000} - - - {invoice.creation_date * 1000} - +
        + { + invoice.settled ? + + : + + + + } +
        +
        + {invoice.creation_date * 1000} {invoice.creation_date * 1000} +
        @@ -46,7 +46,7 @@ const Invoice = ({
        - + + + { ticker.currency === 'usd' ? btc.satoshisToUsd(invoice.value, currentTicker.price_usd) diff --git a/app/routes/activity/components/components/Modal/Modal.js b/app/routes/activity/components/components/Modal/Modal.js index 4e040aef..349dde3a 100644 --- a/app/routes/activity/components/components/Modal/Modal.js +++ b/app/routes/activity/components/components/Modal/Modal.js @@ -24,7 +24,7 @@ const Modal = ({ }, content: { top: 'auto', - left: '20%', + left: '0', right: '0', bottom: 'auto', width: '40%', diff --git a/app/routes/activity/components/components/Payment/Payment.js b/app/routes/activity/components/components/Payment/Payment.js index ecf5ceed..2b97e060 100644 --- a/app/routes/activity/components/components/Payment/Payment.js +++ b/app/routes/activity/components/components/Payment/Payment.js @@ -2,8 +2,10 @@ import React from 'react' import PropTypes from 'prop-types' import Moment from 'react-moment' import 'moment-timezone' +import Isvg from 'react-inlinesvg' import { FaBolt } from 'react-icons/lib/fa' import { btc } from 'utils' +import checkmarkIcon from 'icons/check_circle.svg' import styles from '../Activity.scss' const Payment = ({ @@ -11,12 +13,12 @@ const Payment = ({ }) => (
        showActivityModal('PAYMENT', { payment })}>
        - - {payment.creation_date * 1000} - - - {payment.creation_date * 1000} - +
        + +
        +
        + {payment.creation_date * 1000} {payment.creation_date * 1000} +
        @@ -36,7 +38,7 @@ const Payment = ({
        - - + - { ticker.currency === 'usd' ? btc.satoshisToUsd(payment.value, currentTicker.price_usd) diff --git a/app/routes/activity/components/components/Transaction/Transaction.js b/app/routes/activity/components/components/Transaction/Transaction.js index 36fd7fff..1660946f 100644 --- a/app/routes/activity/components/components/Transaction/Transaction.js +++ b/app/routes/activity/components/components/Transaction/Transaction.js @@ -2,8 +2,10 @@ import React from 'react' import PropTypes from 'prop-types' import Moment from 'react-moment' import 'moment-timezone' +import Isvg from 'react-inlinesvg' import { FaChain } from 'react-icons/lib/fa' import { btc } from 'utils' +import checkmarkIcon from 'icons/check_circle.svg' import styles from '../Activity.scss' const Transaction = ({ @@ -11,12 +13,12 @@ const Transaction = ({ }) => (
        showActivityModal('TRANSACTION', { transaction })}>
        - - {transaction.time_stamp * 1000} - - - {transaction.time_stamp * 1000} - +
        + +
        +
        + {transaction.time_stamp * 1000} {transaction.time_stamp * 1000} +
        @@ -36,7 +38,7 @@ const Transaction = ({
        0 ? styles.positive : styles.negative}`}> - { transaction.amount > 0 ? '+' : '' } + 0 ? styles.plus : styles.minus}>{ transaction.amount > 0 ? '+' : '-' } { ticker.currency === 'usd' ? btc.satoshisToUsd(transaction.amount, currentTicker.price_usd) diff --git a/app/routes/activity/containers/ActivityContainer.js b/app/routes/activity/containers/ActivityContainer.js index 1caa3a50..5437ca83 100644 --- a/app/routes/activity/containers/ActivityContainer.js +++ b/app/routes/activity/containers/ActivityContainer.js @@ -21,6 +21,7 @@ import { updateSearchText } from 'reducers/activity' import { newAddress } from 'reducers/address' +import { setFormType } from 'reducers/form' import Activity from '../components/Activity' @@ -36,7 +37,8 @@ const mapDispatchToProps = { toggleFilterPulldown, newAddress, fetchBalance, - updateSearchText + updateSearchText, + setFormType } const mapStateToProps = state => ({ @@ -62,4 +64,15 @@ const mapStateToProps = state => ({ nonActiveFilters: activitySelectors.nonActiveFilters(state) }) -export default connect(mapStateToProps, mapDispatchToProps)(Activity) +const mergeProps = (stateProps, dispatchProps, ownProps) => ({ + ...stateProps, + ...dispatchProps, + ...ownProps, + + // action to open the pay form + openPayForm: () => dispatchProps.setFormType('PAY_FORM'), + // action to open the request form + openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM') +}) + +export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Activity) diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 67333e36..401eb89f 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -1,19 +1,34 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' + import GlobalError from 'components/GlobalError' import LoadingBolt from 'components/LoadingBolt' + import Form from 'components/Form' import ModalRoot from 'components/ModalRoot' -import Nav from 'components/Nav' + +import Network from 'components/Contacts/Network' +import ContactModal from 'components/Contacts/ContactModal' +import ContactsForm from 'components/Contacts/ContactsForm' + import styles from './App.scss' class App extends Component { componentWillMount() { - const { fetchTicker, fetchInfo, newAddress } = this.props + const { fetchTicker, fetchInfo, newAddress, fetchChannels, fetchBalance, fetchDescribeNetwork } = this.props + // fetch price ticker fetchTicker() + // fetch node info fetchInfo() + // fetch new address for wallet newAddress('np2wkh') + // fetch nodes channels + fetchChannels() + // fetch nodes balance + fetchBalance() + // fetch LN network from nides POV + fetchDescribeNetwork() } render() { @@ -24,14 +39,16 @@ class App extends Component { currentTicker, form, - openPayForm, - openRequestForm, formProps, closeForm, error: { error }, clearError, + contactModalProps, + contactsFormProps, + networkTabProps, + children } = this.props @@ -49,16 +66,16 @@ class App extends Component { currency={ticker.currency} /> -
        + + -
        ) } @@ -71,17 +88,19 @@ App.propTypes = { formProps: PropTypes.object.isRequired, closeForm: PropTypes.func.isRequired, error: PropTypes.object.isRequired, + currentTicker: PropTypes.object, + contactModalProps: PropTypes.object, + contactsFormProps: PropTypes.object, + networkTabProps: PropTypes.object, newAddress: PropTypes.func.isRequired, fetchInfo: PropTypes.func.isRequired, hideModal: PropTypes.func.isRequired, fetchTicker: PropTypes.func.isRequired, - openPayForm: PropTypes.func.isRequired, - openRequestForm: PropTypes.func.isRequired, clearError: PropTypes.func.isRequired, - - currentTicker: PropTypes.object, - + fetchChannels: PropTypes.func.isRequired, + fetchBalance: PropTypes.func.isRequired, + fetchDescribeNetwork: PropTypes.func.isRequired, children: PropTypes.object.isRequired } diff --git a/app/routes/app/components/App.scss b/app/routes/app/components/App.scss index c5018759..0f0a89cd 100644 --- a/app/routes/app/components/App.scss +++ b/app/routes/app/components/App.scss @@ -2,8 +2,7 @@ .content { position: relative; - width: 85%; - height: 100vh; + width: 80%; display: inline-block; vertical-align: top; overflow-y: scroll; diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js index e670af8b..b1e4ab65 100644 --- a/app/routes/app/containers/AppContainer.js +++ b/app/routes/app/containers/AppContainer.js @@ -1,6 +1,8 @@ import { withRouter } from 'react-router' import { connect } from 'react-redux' + import { fetchTicker, setCurrency, tickerSelectors } from 'reducers/ticker' + import { newAddress } from 'reducers/address' import { fetchInfo } from 'reducers/info' @@ -8,23 +10,54 @@ import { fetchInfo } from 'reducers/info' import { showModal, hideModal } from 'reducers/modal' import { setFormType } from 'reducers/form' + import { setPayAmount, setPayInput, updatePayErrors, payFormSelectors } from 'reducers/payform' + import { setRequestAmount, setRequestMemo } from 'reducers/requestform' import { sendCoins } from 'reducers/transaction' + import { payInvoice } from 'reducers/payment' + import { createInvoice, fetchInvoice } from 'reducers/invoice' import { fetchBlockHeight, lndSelectors } from 'reducers/lnd' -import { clearError } from 'reducers/error' +import { + fetchChannels, + openChannel, + closeChannel, + channelsSelectors, + currentChannels, + toggleFilterPulldown, + changeFilter, + updateChannelSearchQuery, + openContactModal, + closeContactModal +} from 'reducers/channels' + +import { + openContactsForm, + closeContactsForm, + updateContactFormSearchQuery, + updateManualFormSearchQuery, + updateContactCapacity, + contactFormSelectors, + updateManualFormErrors +} from 'reducers/contactsform' + +import { fetchBalance } from 'reducers/balance' + +import { fetchDescribeNetwork } from 'reducers/network' +import { clearError } from 'reducers/error' import App from '../components/App' const mapDispatchToProps = { fetchTicker, setCurrency, + newAddress, fetchInfo, @@ -48,7 +81,28 @@ const mapDispatchToProps = { fetchInvoice, fetchBlockHeight, - clearError + clearError, + + fetchBalance, + + fetchChannels, + openChannel, + closeChannel, + toggleFilterPulldown, + changeFilter, + updateChannelSearchQuery, + openContactModal, + closeContactModal, + + openContactsForm, + closeContactsForm, + updateContactFormSearchQuery, + updateManualFormSearchQuery, + updateContactCapacity, + contactFormSelectors, + updateManualFormErrors, + + fetchDescribeNetwork } const mapStateToProps = state => ({ @@ -59,6 +113,10 @@ const mapStateToProps = state => ({ info: state.info, payment: state.payment, transaction: state.transaction, + peers: state.peers, + channels: state.channels, + contactsform: state.contactsform, + balance: state.balance, form: state.form, payform: state.payform, @@ -69,6 +127,8 @@ const mapStateToProps = state => ({ error: state.error, + network: state.network, + currentTicker: tickerSelectors.currentTicker(state), isOnchain: payFormSelectors.isOnchain(state), isLn: payFormSelectors.isLn(state), @@ -76,7 +136,18 @@ const mapStateToProps = state => ({ inputCaption: payFormSelectors.inputCaption(state), showPayLoadingScreen: payFormSelectors.showPayLoadingScreen(state), payFormIsValid: payFormSelectors.payFormIsValid(state), - syncPercentage: lndSelectors.syncPercentage(state) + syncPercentage: lndSelectors.syncPercentage(state), + + filteredNetworkNodes: contactFormSelectors.filteredNetworkNodes(state), + showManualForm: contactFormSelectors.showManualForm(state), + manualFormIsValid: contactFormSelectors.manualFormIsValid(state), + + currentChannels: currentChannels(state), + activeChannelPubkeys: channelsSelectors.activeChannelPubkeys(state), + nonActiveChannelPubkeys: channelsSelectors.nonActiveChannelPubkeys(state), + pendingOpenChannelPubkeys: channelsSelectors.pendingOpenChannelPubkeys(state), + nonActiveFilters: channelsSelectors.nonActiveFilters(state), + channelNodes: channelsSelectors.channelNodes(state) }) const mergeProps = (stateProps, dispatchProps, ownProps) => { @@ -172,20 +243,70 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { return {} } + const networkTabProps = { + currentChannels: stateProps.currentChannels, + channels: stateProps.channels, + balance: stateProps.balance, + currentTicker: stateProps.currentTicker, + contactsform: stateProps.contactsform, + nodes: stateProps.network.nodes, + nonActiveFilters: stateProps.nonActiveFilters, + + fetchChannels: dispatchProps.fetchChannels, + openContactsForm: dispatchProps.openContactsForm, + contactFormSelectors: dispatchProps.contactFormSelectors, + updateManualFormError: dispatchProps.updateManualFormErrors, + toggleFilterPulldown: dispatchProps.toggleFilterPulldown, + changeFilter: dispatchProps.changeFilter, + updateChannelSearchQuery: dispatchProps.updateChannelSearchQuery, + openContactModal: dispatchProps.openContactModal + } + + const contactsFormProps = { + closeContactsForm: dispatchProps.closeContactsForm, + updateContactFormSearchQuery: dispatchProps.updateContactFormSearchQuery, + updateManualFormSearchQuery: dispatchProps.updateManualFormSearchQuery, + updateContactCapacity: dispatchProps.updateContactCapacity, + openChannel: dispatchProps.openChannel, + updateManualFormErrors: dispatchProps.updateManualFormErrors, + + contactsform: stateProps.contactsform, + filteredNetworkNodes: stateProps.filteredNetworkNodes, + loadingChannelPubkeys: stateProps.channels.loadingChannelPubkeys, + showManualForm: stateProps.showManualForm, + manualFormIsValid: stateProps.manualFormIsValid, + activeChannelPubkeys: stateProps.activeChannelPubkeys, + nonActiveChannelPubkeys: stateProps.nonActiveChannelPubkeys, + pendingOpenChannelPubkeys: stateProps.pendingOpenChannelPubkeys + } + + const contactModalProps = { + closeContactModal: dispatchProps.closeContactModal, + closeChannel: dispatchProps.closeChannel, + + isOpen: stateProps.channels.contactModal.isOpen, + channel: stateProps.channels.contactModal.channel, + channelNodes: stateProps.channelNodes, + closingChannelIds: stateProps.channels.closingChannelIds + } + return { ...stateProps, ...dispatchProps, ...ownProps, + // props for the network sidebar + networkTabProps, + // props for the contacts form + contactsFormProps, + // props for the contact modal + contactModalProps, // Props to pass to the pay form formProps: formProps(stateProps.form.formType), - // action to open the pay form - openPayForm: () => dispatchProps.setFormType('PAY_FORM'), - // action to open the request form - openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM'), // action to close form closeForm: () => dispatchProps.setFormType(null) + } } diff --git a/app/routes/contacts/components/Contacts.js b/app/routes/contacts/components/Contacts.js deleted file mode 100644 index d2057fa3..00000000 --- a/app/routes/contacts/components/Contacts.js +++ /dev/null @@ -1,196 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' - -import Isvg from 'react-inlinesvg' -import { MdSearch } from 'react-icons/lib/md' -import { FaAngleDown, FaRepeat } from 'react-icons/lib/fa' - -import ContactModal from 'components/Contacts/ContactModal' -import ContactsForm from 'components/Contacts/ContactsForm' -import OnlineContact from 'components/Contacts/OnlineContact' -import PendingContact from 'components/Contacts/PendingContact' -import ClosingContact from 'components/Contacts/ClosingContact' -import OfflineContact from 'components/Contacts/OfflineContact' -import LoadingContact from 'components/Contacts/LoadingContact' - -import plus from 'icons/plus.svg' - -import styles from './Contacts.scss' - -class Contacts extends Component { - constructor(props) { - super(props) - - this.state = { - refreshing: false - } - } - - componentWillMount() { - const { fetchChannels, fetchPeers, fetchDescribeNetwork } = this.props - - fetchChannels() - fetchPeers() - fetchDescribeNetwork() - } - - render() { - const { - channels: { - searchQuery, - filterPulldown, - filter, - loadingChannelPubkeys, - closingChannelIds - }, - currentChannels, - activeChannels, - fetchChannels, - updateChannelSearchQuery, - - toggleFilterPulldown, - changeFilter, - nonActiveFilters, - - openContactsForm, - openContactModal, - - contactModalProps, - contactsFormProps - } = this.props - - console.log('currentChannels: ', currentChannels) - 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 channels - 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 ( -
        - - - -
        -
        -
        -

        Contacts ({activeChannels.length} online)

        -
        -
        -
        -
        - - Add -
        -
        -
        - -
        - - updateChannelSearchQuery(event.target.value)} - className={`${styles.text} ${styles.input}`} - placeholder='Search your contacts list...' - type='text' - id='channelSearch' - /> -
        - -
        -
        -

        - {filter.name} -

        -
          - { - nonActiveFilters.map(f => ( -
        • changeFilter(f)}> - {f.name} -
        • - )) - } -
        -
        -
        - { this.repeat = ref }}> - { - this.state.refreshing ? - - : - 'Refresh' - } - -
        -
        - -
          - { - loadingChannelPubkeys.map(pubkey => ) - } - - { - currentChannels.length > 0 && currentChannels.map((channel, index) => { - if (closingChannelIds.includes(channel.chan_id)) { - return - } else if (Object.prototype.hasOwnProperty.call(channel, 'confirmation_height')) { - return - } else if (Object.prototype.hasOwnProperty.call(channel, 'closing_txid')) { - return - } else if (!channel.active) { - return - } - return - }) - } -
        -
        - ) - } -} - -Contacts.propTypes = { - fetchPeers: PropTypes.func.isRequired, - fetchDescribeNetwork: PropTypes.func.isRequired, - - channels: PropTypes.object.isRequired, - currentChannels: PropTypes.array.isRequired, - activeChannels: PropTypes.array.isRequired, - fetchChannels: PropTypes.func.isRequired, - updateChannelSearchQuery: PropTypes.func.isRequired, - - toggleFilterPulldown: PropTypes.func.isRequired, - changeFilter: PropTypes.func.isRequired, - nonActiveFilters: PropTypes.array.isRequired, - - openContactsForm: PropTypes.func.isRequired, - openContactModal: PropTypes.func.isRequired, - - contactModalProps: PropTypes.object.isRequired, - contactsFormProps: PropTypes.object.isRequired -} - -export default Contacts diff --git a/app/routes/contacts/components/Contacts.scss b/app/routes/contacts/components/Contacts.scss deleted file mode 100644 index 5440ef3e..00000000 --- a/app/routes/contacts/components/Contacts.scss +++ /dev/null @@ -1,172 +0,0 @@ -@import '../../../variables.scss'; - -.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; - - span { - display: inline-block; - vertical-align: middle; - font-size: 16px; - } - } - } - } - - .newFriendContainer { - padding: 20px 40px; - - .newFriendButton { - box-shadow: none; - transition: all 0.25s; - padding-top: 12px; - padding-bottom: 10px; - font-size: 14px; - - &:hover { - background: darken($main, 10%); - } - - span { - display: inline-block; - vertical-align: top; - - &:nth-child(1) svg { - width: 14px; - height: 14px; - margin-right: 5px; - } - } - } - } -} - -.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; - } -} - -.filtersContainer { - position: relative; - display: flex; - flex-direction: row; - justify-content: space-between; - margin-top: 20px; - 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 { - cursor: pointer; - color: $darkestgrey; - transition: all 0.25s; - - &:hover { - color: $main; - } - - svg { - font-size: 12px; - color: $darkestgrey; - - &:hover { - color: $darkestgrey; - } - } - } - } -} - -.friends { - padding: 10px 0 60px 0; - opacity: 1; - transition: all 0.25s; - - &.fade { - opacity: 0.05; - } -} diff --git a/app/routes/contacts/containers/ContactsContainer.js b/app/routes/contacts/containers/ContactsContainer.js deleted file mode 100644 index 017dac01..00000000 --- a/app/routes/contacts/containers/ContactsContainer.js +++ /dev/null @@ -1,115 +0,0 @@ -import { withRouter } from 'react-router' -import { connect } from 'react-redux' - -import { - fetchChannels, - openChannel, - closeChannel, - - updateChannelSearchQuery, - toggleFilterPulldown, - changeFilter, - openContactModal, - closeContactModal, - currentChannels, - - channelsSelectors -} from 'reducers/channels' - -import { fetchPeers } from 'reducers/peers' - -import { fetchDescribeNetwork } from 'reducers/network' - -import { - openContactsForm, - closeContactsForm, - updateContactFormSearchQuery, - updateManualFormSearchQuery, - updateContactCapacity, - contactFormSelectors, - updateManualFormErrors -} from 'reducers/contactsform' - -import Contacts from '../components/Contacts' - -const mapDispatchToProps = { - openContactsForm, - closeContactsForm, - openContactModal, - closeContactModal, - updateContactFormSearchQuery, - updateManualFormSearchQuery, - updateContactCapacity, - openChannel, - closeChannel, - updateChannelSearchQuery, - toggleFilterPulldown, - changeFilter, - updateManualFormErrors, - fetchChannels, - fetchPeers, - fetchDescribeNetwork -} - -const mapStateToProps = state => ({ - channels: state.channels, - peers: state.peers, - network: state.network, - contactsform: state.contactsform, - - currentChannels: currentChannels(state), - activeChannels: channelsSelectors.activeChannels(state), - activeChannelPubkeys: channelsSelectors.activeChannelPubkeys(state), - nonActiveChannels: channelsSelectors.nonActiveChannels(state), - nonActiveChannelPubkeys: channelsSelectors.nonActiveChannelPubkeys(state), - pendingOpenChannels: channelsSelectors.pendingOpenChannels(state), - pendingOpenChannelPubkeys: channelsSelectors.pendingOpenChannelPubkeys(state), - closingPendingChannels: channelsSelectors.closingPendingChannels(state), - nonActiveFilters: channelsSelectors.nonActiveFilters(state), - channelNodes: channelsSelectors.channelNodes(state), - - filteredNetworkNodes: contactFormSelectors.filteredNetworkNodes(state), - showManualForm: contactFormSelectors.showManualForm(state), - manualFormIsValid: contactFormSelectors.manualFormIsValid(state) -}) - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - const contactModalProps = { - closeContactModal: dispatchProps.closeContactModal, - closeChannel: dispatchProps.closeChannel, - - isOpen: stateProps.channels.contactModal.isOpen, - channel: stateProps.channels.contactModal.channel, - channelNodes: stateProps.channelNodes, - closingChannelIds: stateProps.channels.closingChannelIds, - manualFormIsValid: stateProps.manualFormIsValid - } - - const contactsFormProps = { - closeContactsForm: dispatchProps.closeContactsForm, - updateContactFormSearchQuery: dispatchProps.updateContactFormSearchQuery, - updateManualFormSearchQuery: dispatchProps.updateManualFormSearchQuery, - updateContactCapacity: dispatchProps.updateContactCapacity, - openChannel: dispatchProps.openChannel, - contactsform: stateProps.contactsform, - filteredNetworkNodes: stateProps.filteredNetworkNodes, - loadingChannelPubkeys: stateProps.channels.loadingChannelPubkeys, - showManualForm: stateProps.showManualForm, - manualFormIsValid: stateProps.manualFormIsValid, - activeChannelPubkeys: stateProps.activeChannelPubkeys, - nonActiveChannelPubkeys: stateProps.nonActiveChannelPubkeys, - pendingOpenChannelPubkeys: stateProps.pendingOpenChannelPubkeys, - updateManualFormErrors: dispatchProps.updateManualFormErrors - } - - return { - ...stateProps, - ...dispatchProps, - ...ownProps, - - contactModalProps, - contactsFormProps - } -} - -export default withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(Contacts)) diff --git a/app/routes/contacts/index.js b/app/routes/contacts/index.js deleted file mode 100644 index 20acba65..00000000 --- a/app/routes/contacts/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ContactsContainer from './containers/ContactsContainer' - -export default ContactsContainer diff --git a/app/routes/help/components/Help.js b/app/routes/help/components/Help.js deleted file mode 100644 index 0eb5f8d6..00000000 --- a/app/routes/help/components/Help.js +++ /dev/null @@ -1,88 +0,0 @@ -import { shell } from 'electron' -import React, { Component } from 'react' - -import { MdSearch } from 'react-icons/lib/md' - -import styles from './Help.scss' - -class Help extends Component { - constructor(props) { - super(props) - - this.state = { - videos: [ - { - id: '8kZq6eec49A', - title: 'Syncing and Depositing - Zap Lightning Network Wallet Tutorial (Video 1)' - }, - { - id: 'xSiTH63fOQM', - title: 'Adding a contact - Zap Lightning Network Wallet Tutorial (Video 2)' - }, - { - id: 'c0SLmywYDHU', - title: 'Making a Lightning Network payment - Zap Lightning Network Wallet Tutorial (Video 3)' - }, - { - id: 'Xrx2TiiF90Q', - title: 'Receive Lightning Network payment - Zap Lightning Network Wallet Tutorial (Video 4)' - }, - { - id: 'YfxukBHnwUM', - title: 'Network Map - Zap Lightning Network Wallet Tutorial (Video 5)' - }, - { - id: 'NORklrrYzOg', - title: 'Using an explorer to add Zap contacts - Zap Lightning Network Wallet Tutorial (Video 6)' - } - ], - searchQuery: '' - } - } - - render() { - const { videos, searchQuery } = this.state - const filteredVideos = videos.filter(video => video.title.includes(searchQuery)) - - return ( -
        -
        -

        Video tutorials

        -
        - -
        - - this.setState({ searchQuery: event.target.value })} - className={`${styles.text} ${styles.input}`} - placeholder='Search the video library...' - type='text' - id='helpSearch' - /> -
        - -
          - { - filteredVideos.map((video, index) => ( -
        • -