Browse Source

fix(lint): start fixing linting error

renovate/lint-staged-8.x
Jack Mallers 7 years ago
parent
commit
abb1b02a36
  1. 27
      .eslintrc
  2. 9
      app/api/index.js
  3. 10
      app/containers/Root.js
  4. 10
      app/package.json
  5. 10
      app/reducers/activity.js
  6. 8
      app/reducers/balance.js
  7. 42
      app/reducers/channels.js
  8. 29
      app/reducers/form.js
  9. 4
      app/reducers/info.js
  10. 24
      app/reducers/payment.js
  11. 9
      app/reducers/ticker.js
  12. 44
      app/routes/activity/components/Activity.js
  13. 188
      app/routes/activity/components/components/Invoices.js
  14. 74
      app/routes/activity/components/components/Modal.js
  15. 190
      app/routes/activity/components/components/Payments.js
  16. 38
      app/routes/activity/containers/ActivityContainer.js
  17. 2
      app/routes/activity/index.js
  18. 219
      app/routes/app/components/components/Form/Form.js
  19. 2
      app/routes/app/components/components/Form/index.js
  20. 36
      app/routes/app/components/components/Socket.js
  21. 36
      app/routes/app/containers/AppContainer.js
  22. 2
      app/routes/app/index.js
  23. 43
      app/routes/wallet/components/Wallet.js
  24. 129
      app/routes/wallet/components/components/Channels/Channels.js
  25. 2
      app/routes/wallet/components/components/Channels/Channels.scss
  26. 120
      app/routes/wallet/components/components/Channels/components/Channel/Channel.js
  27. 2
      app/routes/wallet/components/components/Channels/components/Channel/index.js
  28. 221
      app/routes/wallet/components/components/Channels/components/ChannelForm/ChannelForm.js
  29. 2
      app/routes/wallet/components/components/Channels/components/ChannelForm/index.js
  30. 2
      app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.js
  31. 2
      app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.scss
  32. 2
      app/routes/wallet/components/components/Channels/components/ChannelModal/index.js
  33. 121
      app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/ClosedPendingChannel.js
  34. 2
      app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/index.js
  35. 118
      app/routes/wallet/components/components/Channels/components/OpenPendingChannel/OpenPendingChannel.js
  36. 2
      app/routes/wallet/components/components/Channels/components/OpenPendingChannel/index.js
  37. 2
      app/routes/wallet/components/components/Channels/index.js
  38. 87
      app/routes/wallet/components/components/Peers/Peers.js
  39. 2
      app/routes/wallet/components/components/Peers/Peers.scss
  40. 27
      app/routes/wallet/components/components/Peers/components/Peer/Peer.js
  41. 2
      app/routes/wallet/components/components/Peers/components/Peer/Peer.scss
  42. 2
      app/routes/wallet/components/components/Peers/components/Peer/index.js
  43. 2
      app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.scss
  44. 2
      app/routes/wallet/components/components/Peers/components/PeerForm/index.js
  45. 2
      app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.scss
  46. 2
      app/routes/wallet/components/components/Peers/components/PeerModal/index.js
  47. 2
      app/routes/wallet/components/components/Peers/index.js
  48. 68
      app/routes/wallet/containers/WalletContainer.js
  49. 2
      app/routes/wallet/index.js
  50. 2
      app/store/configureStore.dev.js
  51. 6
      app/utils/index.js
  52. 6
      app/utils/usd.js
  53. 2
      internals/scripts/CheckNodeEnv.js
  54. 396
      package-lock.json
  55. 11
      package.json
  56. 13
      test/actions/__snapshots__/counter.spec.js.snap
  57. 41
      test/actions/counter.spec.js
  58. 68
      test/components/Counter.spec.js
  59. 63
      test/components/__snapshots__/Counter.spec.js.snap
  60. 57
      test/containers/CounterPage.spec.js
  61. 9
      test/reducers/__snapshots__/counter.spec.js.snap
  62. 22
      test/reducers/counter.spec.js
  63. 10
      webpack.config.base.js
  64. 2
      webpack.config.main.prod.js

27
.eslintrc

@ -10,27 +10,12 @@
"node": true
},
"rules": {
"arrow-parens": ["off"],
"compat/compat": "error",
"consistent-return": "off",
"comma-dangle": "off",
"flowtype-errors/show-errors": "error",
"generator-star-spacing": "off",
"import/no-unresolved": "error",
"import/no-extraneous-dependencies": "off",
"no-console": "off",
"no-use-before-define": "off",
"no-multi-assign": "off",
"promise/param-names": "error",
"promise/always-return": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
"react/sort-comp": ["error", {
"order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"]
}],
"react/jsx-no-bind": "off",
"react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
"react/prefer-stateless-function": "off"
"comma-dangle": ["error", "never"],
"semi": 0,
"indent": 2,
"jsx-quotes": ["error", "prefer-single"],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"no-static-element-interactions": 0
},
"plugins": [
"flowtype",

9
app/api/index.js

@ -24,12 +24,11 @@ export function callApi(endpoint, method = 'get', data = null) {
}
return axios(payload)
.then(response => response.data)
.catch(error => error)
.then(response => response.data)
.catch(error => error)
}
export function callApis(endpoints) {
const BASE_URL = 'http://localhost:3000/api/'
return axios.all(endpoints.map(endpoint => callApi(endpoint)))
}
@ -39,6 +38,6 @@ export function requestTicker() {
method: 'get',
url: BASE_URL
})
.then(response => response.data)
.catch(error => error)
.then(response => response.data)
.catch(error => error)
}

10
app/containers/Root.js

@ -1,8 +1,8 @@
// @flow
import React from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import Routes from '../routes';
import React from 'react'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import Routes from '../routes'
type RootType = {
store: {},
@ -16,5 +16,5 @@ export default function Root({ store, history }: RootType) {
<Routes />
</ConnectedRouter>
</Provider>
);
)
}

10
app/package.json

@ -1,12 +1,12 @@
{
"name": "electron-react-boilerplate",
"productName": "electron-react-boilerplate",
"version": "1.0.0",
"name": "Zap Desktop",
"productName": "Zap Desktop",
"version": "0.0.1",
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
"main": "./main.prod.js",
"author": {
"name": "C. T. Lin",
"email": "chentsulin@gmail.com",
"name": "Jack Mallers",
"email": "jimmymowschess@gmail.com",
"url": "https://github.com/chentsulin"
},
"scripts": {

10
app/reducers/activity.js

@ -18,7 +18,7 @@ export function receiveActvity(data) {
return {
type: RECEIVE_ACTIVITY,
payments: data[0].data.payments.reverse(),
invoices: data[1].data.invoices.reverse()
invoices: data[1].data.invoices.reverse()
}
}
@ -32,8 +32,10 @@ export const fetchActivity = () => async (dispatch) => {
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[GET_ACTIVITY]: (state) => ({ ...state, activityLoading: true }),
[RECEIVE_ACTIVITY]: (state, { payments, invoices }) => ({ ...state, activityLoading: false, payments, invoices })
[GET_ACTIVITY]: state => ({ ...state, activityLoading: true }),
[RECEIVE_ACTIVITY]: (state, { payments, invoices }) => (
{ ...state, activityLoading: false, payments, invoices }
)
}
// ------------------------------------
@ -49,4 +51,4 @@ export default function activityReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

8
app/reducers/balance.js

@ -32,8 +32,10 @@ export const fetchBalance = () => async (dispatch) => {
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[GET_BALANCE]: (state) => ({ ...state, balanceLoading: true }),
[RECEIVE_BALANCE]: (state, { walletBalance, channelBalance }) => ({ ...state, balanceLoading: false, walletBalance, channelBalance })
[GET_BALANCE]: state => ({ ...state, balanceLoading: true }),
[RECEIVE_BALANCE]: (state, { walletBalance, channelBalance }) => (
{ ...state, balanceLoading: false, walletBalance, channelBalance }
)
}
// ------------------------------------
@ -49,4 +51,4 @@ export default function balanceReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

42
app/reducers/channels.js

@ -1,5 +1,4 @@
import { createSelector } from 'reselect'
import { usd, btc } from '../utils'
import { callApi, callApis } from '../api'
// ------------------------------------
// Constants
@ -47,19 +46,6 @@ export function receiveChannels(channels) {
}
}
export function getPendingChannels() {
return {
type: GET_PENDING_CHANNELS
}
}
export function receivePendingChannels({ pendingChannels }) {
return {
type: RECEIVE_PENDING_CHANNELS,
pendingChannels
}
}
export function openingChannel() {
return {
type: OPENING_CHANNEL
@ -88,9 +74,12 @@ export const openChannel = ({ pubkey, localamt, pushamt }) => async (dispatch) =
const payload = { pubkey, localamt, pushamt }
dispatch(openingChannel())
const channel = await callApi('addchannel', 'post', payload)
console.log('channel: ', channel)
channel.data ? dispatch(openingSuccessful()) : dispatch(openingFailure())
if (channel.data) {
dispatch(openingSuccessful())
} else {
dispatch(openingFailure())
}
return channel
}
@ -99,12 +88,16 @@ export const openChannel = ({ pubkey, localamt, pushamt }) => async (dispatch) =
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[SET_CHANNEL_FORM]: (state, { form }) => ({ ...state, channelForm: Object.assign({}, state.channelForm, form) }),
[SET_CHANNEL_FORM]: (state, { form }) => (
{ ...state, channelForm: Object.assign({}, state.channelForm, form) }
),
[SET_CHANNEL]: (state, { channel }) => ({ ...state, channel }),
[GET_CHANNELS]: (state) => ({ ...state, channelsLoading: true }),
[RECEIVE_CHANNELS]: (state, { channels, pendingChannels }) => ({ ...state, channelsLoading: false, channels, pendingChannels }),
[RECEIVE_CHANNELS]: (state, { channels, pendingChannels }) => (
{ ...state, channelsLoading: false, channels, pendingChannels }
),
[OPENING_CHANNEL]: (state) => ({ ...state, openingChannel: true }),
}
@ -140,18 +133,7 @@ const initialState = {
pendingChannels: {
total_limbo_balance: '',
pending_open_channels: [],
pending_closing_channels: [
{
"channel": {
"remote_node_pub": "02ef6248210e27b0f0df4d11d876e63f56e04bcb0054d0d8b6ba6a1a3e90dc56e1",
"channel_point": "5f6c522970e81069075c27be8799d0e2fb16dd4975cbd84c07b1a8bc9ece9918:0",
"capacity": "10000",
"local_balance": "312",
"remote_balance": "0"
},
"closing_txid": "4c0a25b0955e9efca46065a317a9560c9e3618356d4985e1a905eeb662e40bdb"
}
],
pending_closing_channels: [],
pending_force_closing_channels: []
},
channel: null,

29
app/reducers/form.js

@ -1,3 +1,13 @@
// Initial State
const initialState = {
modalOpen: false,
formType: 'pay',
amount: '0',
message: '',
pubkey: '',
payment_request: ''
}
// Constants
// ------------------------------------
export const SET_FORM = 'SET_FORM'
@ -39,10 +49,10 @@ export function setPubkey(pubkey) {
}
}
export function setPaymentRequest(payment_request) {
export function setPaymentRequest(paymentRequest) {
return {
type: SET_PAYMENT_REQUEST,
payment_request
paymentRequest
}
}
@ -60,24 +70,17 @@ const ACTION_HANDLERS = {
[SET_AMOUNT]: (state, { amount }) => ({ ...state, amount }),
[SET_MESSAGE]: (state, { message }) => ({ ...state, message }),
[SET_PUBKEY]: (state, { pubkey }) => ({ ...state, pubkey }),
[SET_PAYMENT_REQUEST]: (state, { payment_request }) => ({ ...state, payment_request }),
[SET_PAYMENT_REQUEST]: (state, { paymentRequest }) => (
{ ...state, payment_request: paymentRequest }
),
[RESET_FORM]: () => (initialState)
}
// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
modalOpen: false,
formType: 'pay',
amount: '0',
message: '',
pubkey: '',
payment_request: ''
}
export default function formReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

4
app/reducers/info.js

@ -31,7 +31,7 @@ export const fetchInfo = () => async (dispatch) => {
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[GET_INFO]: (state) => ({ ...state, infoLoading: true }),
[GET_INFO]: state => ({ ...state, infoLoading: true }),
[RECEIVE_INFO]: (state, { data }) => ({ ...state, infoLoading: false, data })
}
@ -47,4 +47,4 @@ export default function infoReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

24
app/reducers/payment.js

@ -1,6 +1,6 @@
import { createSelector } from 'reselect'
import { callApi } from '../api'
import { btc } from '../utils'
// ------------------------------------
// Constants
// ------------------------------------
@ -58,19 +58,19 @@ export function paymentFailed() {
export const fetchPayments = () => async (dispatch) => {
dispatch(getPayments())
const payments = await callApi('payments')
payments ?
if (payments) {
dispatch(receivePayments(payments.data))
:
} else {
dispatch(paymentFailed())
}
return payments
}
export const payInvoice = (payment_request) => async (dispatch) => {
console.log('payment_request: ', payment_request)
export const payInvoice = (paymentRequest) => async (dispatch) => {
dispatch(sendPayment())
const payment = await callApi('sendpayment', 'post', { payment_request })
console.log('payment: ', payment)
const payment = await callApi('sendpayment', 'post', { payment_request: paymentRequest })
payment ?
dispatch(fetchPayments())
@ -86,9 +86,11 @@ export const payInvoice = (payment_request) => async (dispatch) => {
// ------------------------------------
const ACTION_HANDLERS = {
[SET_PAYMENT]: (state, { payment }) => ({ ...state, payment }),
[GET_PAYMENTS]: (state) => ({ ...state, paymentLoading: true }),
[GET_PAYMENTS]: state => ({ ...state, paymentLoading: true }),
[RECEIVE_PAYMENTS]: (state, { payments }) => ({ ...state, paymentLoading: false, payments }),
[PAYMENT_SUCCESSFULL]: (state, { payment }) => ({ ...state, paymentLoading: false, payments: [payment, ...state.payments] })
[PAYMENT_SUCCESSFULL]: (state, { payment }) => (
{ ...state, paymentLoading: false, payments: [payment, ...state.payments] }
)
}
const paymentSelectors = {}
@ -96,7 +98,7 @@ const modalPaymentSelector = state => state.payment.payment
paymentSelectors.paymentModalOpen = createSelector(
modalPaymentSelector,
payment => payment ? true : false
payment => (payment ? true : false)
)
export { paymentSelectors }
@ -114,4 +116,4 @@ export default function paymentReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

9
app/reducers/ticker.js

@ -1,4 +1,3 @@
import { createSelector } from 'reselect'
import { requestTicker } from '../api'
// ------------------------------------
// Constants
@ -43,8 +42,10 @@ export const fetchTicker = () => async (dispatch) => {
// ------------------------------------
const ACTION_HANDLERS = {
[SET_CURRENCY]: (state, { currency }) => ({ ...state, currency }),
[GET_TICKER]: (state) => ({ ...state, tickerLoading: true }),
[RECIEVE_TICKER]: (state, { ticker }) => ({...state, tickerLoading: false, btcTicker: ticker[0] })
[GET_TICKER]: state => ({ ...state, tickerLoading: true }),
[RECIEVE_TICKER]: (state, { ticker }) => (
{ ...state, tickerLoading: false, btcTicker: ticker[0] }
)
}
// ------------------------------------
@ -61,4 +62,4 @@ export default function tickerReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
}

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

@ -1,5 +1,5 @@
// @flow
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import { MdSearch } from 'react-icons/lib/md'
import Payments from './components/Payments'
@ -7,19 +7,19 @@ import Invoices from './components/Invoices'
import styles from './Activity.scss'
class Activity extends Component {
constructor(props, context) {
super(props, context)
this.state = {
tab: 1
}
constructor(props, context) {
super(props, context)
this.state = {
tab: 1
}
}
componentWillMount() {
const { fetchPayments, fetchInvoices } = this.props
componentWillMount() {
const { fetchPayments, fetchInvoices } = this.props
fetchPayments()
fetchInvoices()
}
fetchPayments()
fetchInvoices()
}
render() {
const { tab } = this.state
@ -39,10 +39,10 @@ class Activity extends Component {
return (
<div>
<div className={styles.search}>
<label className={`${styles.label} ${styles.input}`}>
<MdSearch />
</label>
<input
<label className={`${styles.label} ${styles.input}`}>
<MdSearch />
</label>
<input
value={tab === 1 ? '' : invoicesSearchText}
onChange={event => tab === 1 ? null : searchInvoices(event.target.value)}
className={`${styles.text} ${styles.input}`}
@ -70,7 +70,6 @@ class Activity extends Component {
{
tab === 1 ?
<Payments
key={1}
payment={payment}
payments={payments}
ticker={ticker}
@ -79,7 +78,6 @@ class Activity extends Component {
/>
:
<Invoices
key={2}
invoice={invoice}
invoices={invoices}
ticker={ticker}
@ -94,4 +92,16 @@ class Activity extends Component {
}
}
Activity.propTypes = {
ticker: PropTypes.object.isRequired,
searchInvoices: PropTypes.func.isRequired,
invoices: PropTypes.array.isRequired,
invoice: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired,
setPayment: PropTypes.func.isRequired,
setInvoice: PropTypes.func.isRequired,
paymentModalOpen: PropTypes.bool.isRequired,
invoiceModalOpen: PropTypes.bool.isRequired
}
export default Activity

188
app/routes/activity/components/components/Invoices.js

@ -1,5 +1,5 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { FaBitcoin, FaDollar } from 'react-icons/lib/fa'
@ -9,105 +9,105 @@ import Modal from './Modal'
import { btc } from '../../../../utils'
import styles from './Invoices.scss'
class Invoices extends Component {
render() {
const {
invoice,
invoices,
ticker,
setInvoice,
invoiceModalOpen
} = this.props
return (
<div>
<Modal isOpen={invoiceModalOpen} resetObject={setInvoice}>
{
invoice ?
<div className={styles.invoiceModal}>
<h3>{invoice.memo}</h3>
<h1>
{
ticker.currency === 'btc' ?
<FaBitcoin style={{ verticalAlign: 'top' }} />
:
<FaDollar style={{ verticalAlign: 'top' }} />
}
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(invoice.value)
:
btc.satoshisToUsd(invoice.value, ticker.btcTicker.price_usd)
}
</span>
</h1>
<div className={styles.qrcode}>
<QRCode value={invoice.payment_request} size={200} />
<input
readOnly
className={styles.paymentRequest}
onClick={(event) => event.target.select()}
defaultValue={invoice.payment_request}
/>
</div>
<div className={styles.settled}>
{
invoice.settled ?
<p><MdCheck style={{ verticalAlign: 'top' }} /> Paid</p>
:
<p>Not Paid</p>
}
</div>
<p className={styles.date}>
Created on
<Moment format='MMM Do'>
{invoice.creation_date * 1000}
</Moment>
</p>
</div>
:
null
}
</Modal>
<ul className={styles.invoices}>
<li className={styles.invoiceTitles}>
const Invoices = ({
invoice,
invoices,
ticker,
setInvoice,
invoiceModalOpen
}) => (
<div>
<Modal isOpen={invoiceModalOpen} resetObject={setInvoice}>
{
invoice ?
<div className={styles.invoiceModal}>
<h3>{invoice.memo}</h3>
<h1>
{
ticker.currency === 'btc' ?
<FaBitcoin style={{ verticalAlign: 'top' }} />
:
<FaDollar style={{ verticalAlign: 'top' }} />
}
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(invoice.value)
:
btc.satoshisToUsd(invoice.value, ticker.btcTicker.price_usd)
}
</span>
</h1>
<div className={styles.qrcode}>
<QRCode value={invoice.payment_request} size={200} />
<input
readOnly
className={styles.paymentRequest}
onClick={(event) => event.target.select()}
defaultValue={invoice.payment_request}
/>
</div>
<div className={styles.settled}>
{
invoice.settled ?
<p><MdCheck style={{ verticalAlign: 'top' }} /> Paid</p>
:
<p>Not Paid</p>
}
</div>
<p className={styles.date}>
Created on
<Moment format='MMM Do'>{invoice.creation_date * 1000}</Moment>
</p>
</div>
:
null
}
</Modal>
<ul className={styles.invoices}>
<li className={styles.invoiceTitles}>
<div className={styles.left}>
<div>Payment Request</div>
</div>
<div className={styles.center}>
<div>Memo</div>
</div>
<div className={styles.right}>
<div>Amount</div>
</div>
</li>
{
invoices.map((invoice, index) =>
<li key={index} className={styles.invoice} onClick={() => setInvoice(invoice)}>
<div className={styles.left}>
<div>Payment Request</div>
<div className={styles.path}>{`${invoice.payment_request.substring(0, 75)}...`}</div>
</div>
<div className={styles.center}>
<div>Memo</div>
<div>{invoice.memo}</div>
</div>
<div className={styles.right}>
<div>Amount</div>
<div className={invoice.settled ? styles.settled : null}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(invoice.value)
:
btc.satoshisToUsd(invoice.value, ticker.btcTicker.price_usd)
}
</div>
</div>
</li>
{
invoices.map((invoice, index) =>
<li key={index} className={styles.invoice} onClick={() => setInvoice(invoice)}>
<div className={styles.left}>
<div className={styles.path}>{`${invoice.payment_request.substring(0, 75)}...`}</div>
</div>
<div className={styles.center}>
<div>{invoice.memo}</div>
</div>
<div className={styles.right}>
<div className={invoice.settled ? styles.settled : null}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(invoice.value)
:
btc.satoshisToUsd(invoice.value, ticker.btcTicker.price_usd)
}
</div>
</div>
</li>
)
}
</ul>
</div>
)
}
)
}
</ul>
</div>
)
Invoices.propTypes = {
invoice: PropTypes.object,
invoices: PropTypes.array.isRequired,
ticker: PropTypes.object.isRequired,
setInvoice: PropTypes.func.isRequired,
invoiceModalOpen: PropTypes.bool.isRequired
}
export default Invoices

74
app/routes/activity/components/components/Modal.js

@ -1,45 +1,43 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import Moment from 'react-moment'
import 'moment-timezone'
class Modal extends Component {
render() {
const customStyles = {
overlay: {
cursor: 'pointer'
},
content : {
top: 'auto',
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto'
}
const Modal = ({ isOpen, resetObject, children }) => {
const customStyles = {
overlay: {
cursor: 'pointer'
},
content : {
top: 'auto',
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto'
}
const {
isOpen,
resetObject,
children
} = this.props
return (
<ReactModal
isOpen={isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp={true}
shouldCloseOnOverlayClick={true}
onRequestClose={() => resetObject(null)}
parentSelector={() => document.body}
style={customStyles}
>
{children}
</ReactModal>
)
}
}
return (
<ReactModal
isOpen={isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp={true}
shouldCloseOnOverlayClick={true}
onRequestClose={() => resetObject(null)}
parentSelector={() => document.body}
style={customStyles}
>
{children}
</ReactModal>
)
}
Modal.propTypes = {
isOpen: PropTypes.bool.isRequired,
resetObject: PropTypes.func.isRequired,
children: PropTypes.object
}
export default Modal
export default Modal

190
app/routes/activity/components/components/Payments.js

@ -1,5 +1,5 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { FaBitcoin, FaDollar } from 'react-icons/lib/fa'
@ -7,107 +7,107 @@ import Modal from './Modal'
import { btc } from '../../../../utils'
import styles from './Payments.scss'
class Payments extends Component {
render() {
const {
payment,
payments,
ticker,
setPayment,
paymentModalOpen
} = this.props
console.log('payments: ', payments)
return (
<div>
<Modal isOpen={paymentModalOpen} resetObject={setPayment}>
{
payment ?
<div className={styles.paymentModal}>
<h3>{payment.payment_hash}</h3>
<h1>
{
ticker.currency === 'btc' ?
<FaBitcoin style={{ verticalAlign: 'top' }} />
:
<FaDollar style={{ verticalAlign: 'top' }} />
}
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.value)
:
btc.satoshisToUsd(payment.value, ticker.btcTicker.price_usd)
}
</span>
</h1>
<dl>
<dt>Fee</dt>
<dd>{payment.fee}</dd>
<dt>Date</dt>
<dd>
<Moment format='MMM Do'>
{payment.creation_date * 1000}
</Moment></dd>
</dl>
</div>
:
null
}
</Modal>
<ul className={styles.payments}>
<li className={styles.paymentTitles}>
const Payments = ({
payment,
payments,
ticker,
setPayment,
paymentModalOpen
}) => (
<div>
<Modal isOpen={paymentModalOpen} resetObject={setPayment}>
{
payment ?
<div className={styles.paymentModal}>
<h3>{payment.payment_hash}</h3>
<h1>
{
ticker.currency === 'btc' ?
<FaBitcoin style={{ verticalAlign: 'top' }} />
:
<FaDollar style={{ verticalAlign: 'top' }} />
}
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.value)
:
btc.satoshisToUsd(payment.value, ticker.btcTicker.price_usd)
}
</span>
</h1>
<dl>
<dt>Fee</dt>
<dd>{payment.fee}</dd>
<dt>Date</dt>
<dd>
<Moment format='MMM Do'>{payment.creation_date * 1000}</Moment>
</dd>
</dl>
</div>
:
null
}
</Modal>
<ul className={styles.payments}>
<li className={styles.paymentTitles}>
<div className={styles.left}>
<div>Public Key</div>
</div>
<div className={styles.center}>
<div>Date</div>
</div>
<div className={styles.center}>
<div>Fee</div>
</div>
<div className={styles.right}>
<div>Amount</div>
</div>
</li>
{
payments.map((payment, index) =>
<li key={index} className={styles.payment} onClick={() => setPayment(payment)}>
<div className={styles.left}>
<div>Public Key</div>
<div className={styles.path}>{payment.path[0]}</div>
</div>
<div className={styles.center}>
<div>Date</div>
<div className={styles.date}>
<Moment format="MMMM Do">{payment.creation_date * 1000}</Moment>
</div>
</div>
<div className={styles.center}>
<div>Fee</div>
<div className={styles.right}>
<span className={styles.fee}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.fee)
:
btc.satoshisToUsd(payment.fee, ticker.btcTicker.price_usd)
}
</span>
</div>
<div className={styles.right}>
<div>Amount</div>
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.value)
:
btc.satoshisToUsd(payment.value, ticker.btcTicker.price_usd)
}
</span>
</div>
</li>
{
payments.map((payment, index) =>
<li key={index} className={styles.payment} onClick={() => setPayment(payment)}>
<div className={styles.left}>
<div className={styles.path}>{payment.path[0]}</div>
</div>
<div className={styles.center}>
<div className={styles.date}>
<Moment format="MMMM Do">{payment.creation_date * 1000}</Moment>
</div>
</div>
<div className={styles.right}>
<span className={styles.fee}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.fee)
:
btc.satoshisToUsd(payment.fee, ticker.btcTicker.price_usd)
}
</span>
</div>
<div className={styles.right}>
<span className={styles.value}>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(payment.value)
:
btc.satoshisToUsd(payment.value, ticker.btcTicker.price_usd)
}
</span>
</div>
</li>
)
}
</ul>
</div>
)
}
)
}
</ul>
</div>
)
Payments.propTypes = {
payment: PropTypes.object,
payments: PropTypes.array.isRequired,
ticker: PropTypes.object.isRequired,
setPayment: PropTypes.func.isRequired,
paymentModalOpen: PropTypes.bool.isRequired
}
export default Payments

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

@ -1,37 +1,37 @@
import { connect } from 'react-redux'
import {
fetchInvoices,
searchInvoices,
setInvoice,
invoiceSelectors
fetchInvoices,
searchInvoices,
setInvoice,
invoiceSelectors
} from '../../../reducers/invoice'
import {
setPayment,
fetchPayments,
paymentSelectors
setPayment,
fetchPayments,
paymentSelectors
} from '../../../reducers/payment'
import Activity from '../components/Activity'
const mapDispatchToProps = {
setPayment,
setInvoice,
fetchPayments,
fetchInvoices,
searchInvoices
setPayment,
setInvoice,
fetchPayments,
fetchInvoices,
searchInvoices
}
const mapStateToProps = (state) => ({
activity: state.activity,
activity: state.activity,
payment: state.payment,
payment: state.payment,
invoice: state.invoice,
invoices: invoiceSelectors.invoices(state),
invoice: state.invoice,
invoices: invoiceSelectors.invoices(state),
ticker: state.ticker,
ticker: state.ticker,
paymentModalOpen: paymentSelectors.paymentModalOpen(state),
invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state)
paymentModalOpen: paymentSelectors.paymentModalOpen(state),
invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state)
})
export default connect(mapStateToProps, mapDispatchToProps)(Activity)

2
app/routes/activity/index.js

@ -1,3 +1,3 @@
import ActivityContainer from './containers/ActivityContainer'
export default ActivityContainer
export default ActivityContainer

219
app/routes/app/components/components/Form/Form.js

@ -1,122 +1,137 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { FaDollar, FaBitcoin } from 'react-icons/lib/fa'
import { MdArrowBack, MdClose } from 'react-icons/lib/md'
import { MdClose } from 'react-icons/lib/md'
import { btc } from '../../../../../utils'
import styles from './Form.scss'
class Form extends Component {
render() {
const {
form: { formType, amount, message, pubkey, payment_request },
setAmount,
setMessage,
setPubkey,
setPaymentRequest,
ticker: { currency, btcTicker },
isOpen,
close,
createInvoice,
payInvoice,
fetchInvoice,
formInvoice
} = this.props
const requestClicked = () => {
createInvoice(amount, message, currency, btcTicker.price_usd)
.then(success => {
const Form = ({
form: { formType, amount, message, payment_request },
setAmount,
setMessage,
setPaymentRequest,
ticker: { currency, btcTicker },
isOpen,
close,
createInvoice,
payInvoice,
fetchInvoice,
formInvoice
}) => {
const requestClicked = () => {
createInvoice(amount, message, currency, btcTicker.price_usd)
.then((success) => {
if (success) { close() }
})
}
}
const payClicked = () => {
payInvoice(payment_request)
.then(success => {
console.log('success: ', success)
const payClicked = () => {
payInvoice(payment_request)
.then((success) => {
if (success) { close() }
})
}
}
const paymentRequestOnChange = (payreq) => {
setPaymentRequest(payreq)
if (payreq.length === 124) { fetchInvoice(payreq) }
}
const paymentRequestOnChange = (payreq) => {
setPaymentRequest(payreq)
if (payreq.length === 124) { fetchInvoice(payreq) }
}
const calculateAmount = (amount) => currency === 'btc' ? btc.satoshisToBtc(amount) : btc.satoshisToUsd(amount, btcTicker.price_usd)
const calculateAmount = value => (currency === 'btc' ? btc.satoshisToBtc(value) : btc.satoshisToUsd(value, btcTicker.price_usd))
return (
<div className={`${styles.formContainer} ${isOpen ? styles.open : ''}`}>
<div className={styles.container}>
<div className={styles.esc} onClick={close}>
<MdClose />
</div>
<div className={styles.content}>
<section className={styles.amountContainer}>
<label>
{
currency === 'btc' ?
return (
<div className={`${styles.formContainer} ${isOpen ? styles.open : ''}`}>
<div className={styles.container}>
<div className={styles.esc} onClick={close}>
<MdClose />
</div>
<div className={styles.content}>
<section className={styles.amountContainer}>
<label>
{
currency === 'btc' ?
<FaBitcoin />
:
<FaDollar />
}
</label>
<input
type='text'
size=''
style={
formType === 'pay' ?
{ width: '75%', fontSize: '100px' }
:
{ width: `${amount.length > 1 ? (amount.length * 15) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` }
}
value={formType === 'pay' ? calculateAmount(formInvoice.amount) : amount}
onChange={(event) => setAmount(event.target.value)}
readOnly={formType === 'pay'}
/>
</section>
{
formType === 'pay' ?
<section className={styles.inputContainer}>
<label>Request:</label>
<input
type='text'
placeholder='Payment Request'
value={payment_request}
onChange={(event) => paymentRequestOnChange(event.target.value)}
/>
</section>
:
<section className={styles.inputContainer}>
<label>For:</label>
<input
type='text'
placeholder='Dinner, Rent, etc'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</section>
}
{
formType === 'pay' ?
<section className={styles.buttonGroup}>
<div className={styles.button} onClick={payClicked}>
Pay
</div>
</section>
:
<section className={styles.buttonGroup}>
<div className={styles.button} onClick={requestClicked}>
Request
</div>
</section>
}
</div>
}
</label>
<input
type='text'
size=''
style={
formType === 'pay' ?
{ width: '75%', fontSize: '100px' }
:
{ width: `${amount.length > 1 ? (amount.length * 15) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` }
}
value={formType === 'pay' ? calculateAmount(formInvoice.amount) : amount}
onChange={event => setAmount(event.target.value)}
readOnly={formType === 'pay'}
/>
</section>
{
formType === 'pay' ?
<section className={styles.inputContainer}>
<label>Request:</label>
<input
type='text'
placeholder='Payment Request'
value={payment_request}
onChange={(event) => paymentRequestOnChange(event.target.value)}
/>
</section>
:
<section className={styles.inputContainer}>
<label>For:</label>
<input
type='text'
placeholder='Dinner, Rent, etc'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</section>
}
{
formType === 'pay' ?
<section className={styles.buttonGroup}>
<div className={styles.button} onClick={payClicked}>
Pay
</div>
</section>
:
<section className={styles.buttonGroup}>
<div className={styles.button} onClick={requestClicked}>
Request
</div>
</section>
}
</div>
</div>
)
}
</div>
)
}
Form.propTypes = {}
Form.propTypes = {
form: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
formType: PropTypes.string,
amount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
message: PropTypes.string,
payment_request: PropTypes.string,
setAmount: PropTypes.func.isRequired,
setMessage: PropTypes.func.isRequired,
setPaymentRequest: PropTypes.func.isRequired,
currency: PropTypes.string,
btcTicker: PropTypes.object,
isOpen: PropTypes.bool.isRequired,
close: PropTypes.func.isRequired,
createInvoice: PropTypes.func.isRequired,
payInvoice: PropTypes.func.isRequired,
fetchInvoice: PropTypes.func.isRequired,
formInvoice: PropTypes.object.isRequired
}
export default Form
export default Form

2
app/routes/app/components/components/Form/index.js

@ -1,3 +1,3 @@
import Form from './Form'
export default Form
export default Form

36
app/routes/app/components/components/Socket.js

@ -1,26 +1,20 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import Websocket from 'react-websocket'
class Socket extends Component {
render() {
const onMessage = ({ event, data }) => {
console.log('data: ', data)
this.props.fetchChannels()
// switch(data.event) {
// case CHANNEL_DATA:
// console.log('channel data')
// if (data.update === 'chan_pending') {
// let zapNotification = new Notification({
// })
// }
// }
}
return (
<Websocket debug url='ws://localhost:3000/' onMessage={onMessage} />
)
const Socket = ({ fetchChannels }) => {
const onMessage = () => {
// TODO: Assumes only socket relationship is with channels. Actually flesh out socket logic
fetchChannels()
}
return (
<Websocket debug url='ws://localhost:3000/' onMessage={onMessage} />
)
}
Socket.propTypes = {
fetchChannels: PropTypes.func.isRequired
}
export default Socket
export default Socket

36
app/routes/app/containers/AppContainer.js

@ -10,27 +10,27 @@ import { fetchChannels } from '../../../reducers/channels'
import { setAmount, setMessage, setPubkey, setPaymentRequest } from '../../../reducers/form'
const mapDispatchToProps = {
fetchTicker,
setCurrency,
fetchBalance,
fetchInfo,
setAmount,
setMessage,
setPubkey,
setPaymentRequest,
setForm,
createInvoice,
payInvoice,
fetchChannels,
fetchInvoice
fetchTicker,
setCurrency,
fetchBalance,
fetchInfo,
setAmount,
setMessage,
setPubkey,
setPaymentRequest,
setForm,
createInvoice,
payInvoice,
fetchChannels,
fetchInvoice
}
const mapStateToProps = (state) => ({
ticker: state.ticker,
balance: state.balance,
payment: state.payment,
form: state.form,
invoice: state.invoice
ticker: state.ticker,
balance: state.balance,
payment: state.payment,
form: state.form,
invoice: state.invoice
})
export default connect(mapStateToProps, mapDispatchToProps)(App)

2
app/routes/app/index.js

@ -1,3 +1,3 @@
import AppContainer from './containers/AppContainer'
export default AppContainer
export default AppContainer

43
app/routes/wallet/components/Wallet.js

@ -1,26 +1,25 @@
// @flow
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ReactSVG from 'react-svg'
import { FaCircle } from 'react-icons/lib/fa'
import Peers from './components/peers'
import Channels from './components/channels'
import Peers from './components/Peers'
import Channels from './components/Channels'
import styles from './Wallet.scss'
class Wallet extends Component {
componentWillMount() {
const { fetchInfo, fetchPeers, fetchChannels } = this.props
fetchInfo()
fetchPeers()
fetchChannels()
}
componentWillMount() {
const { fetchInfo, fetchPeers, fetchChannels } = this.props
fetchInfo()
fetchPeers()
fetchChannels()
}
render() {
const {
info,
const {
info,
ticker,
peers: { peersLoading, peers, peer, peerForm },
channels: { channelsLoading, channels, channel, channelForm, pendingChannels },
peers: { peersLoading, peers, peer, peerForm },
channels: { channelsLoading, channels, channel, channelForm, pendingChannels },
setPeer,
setChannel,
peerModalOpen,
@ -31,13 +30,13 @@ class Wallet extends Component {
disconnectRequest,
allChannels,
openChannel
} = this.props
} = this.props
return (
<div className={styles.wallet}>
<section className={styles.header}>
<ReactSVG path='../resources/zap_2.svg' />
<h1>{info.data.identity_pubkey}</h1>
<ReactSVG path='../resources/zap_2.svg' />
<h1>{info.data.identity_pubkey}</h1>
</section>
<section className={styles.walletData}>
<Peers
@ -71,5 +70,11 @@ class Wallet extends Component {
}
}
Wallet.propTypes = {
fetchInfo: PropTypes.func.isRequired,
fetchPeers: PropTypes.func.isRequired,
fetchChannels: PropTypes.func.isRequired
}
export default Wallet
export default Wallet

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

@ -1,5 +1,5 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { TiPlus } from 'react-icons/lib/ti'
import ChannelModal from './components/ChannelModal'
import ChannelForm from './components/ChannelForm'
@ -8,67 +8,72 @@ import OpenPendingChannel from './components/OpenPendingChannel'
import ClosedPendingChannel from './components/ClosedPendingChannel'
import styles from './Channels.scss'
class Channels extends Component {
render() {
const {
ticker,
peers,
channelsLoading,
channels,
modalChannel,
setChannel,
channelModalOpen,
channelForm,
setChannelForm,
pendingChannels,
allChannels,
openChannel
} = this.props
const Channels = ({
ticker,
peers,
channelsLoading,
modalChannel,
setChannel,
channelModalOpen,
channelForm,
setChannelForm,
allChannels,
openChannel
}) => (
<div className={styles.channels}>
<ChannelModal isOpen={channelModalOpen} resetChannel={setChannel} channel={modalChannel} />
<ChannelForm form={channelForm} setForm={setChannelForm} ticker={ticker} peers={peers} openChannel={openChannel} />
<div className={styles.header}>
<h3>Channels</h3>
<div
className={`${styles.openChannel} hint--top`}
data-hint='Open a channel'
onClick={() => setChannelForm({ isOpen: true })}
>
<TiPlus />
</div>
</div>
<ul>
{
!channelsLoading ?
allChannels.map((channel, index) => {
if (channel.hasOwnProperty('blocks_till_open')) {
return (
<OpenPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else if (channel.hasOwnProperty('closing_txid')) {
return (
<ClosedPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else {
return (
<Channel
key={channel.chan_id}
ticker={ticker}
channel={channel}
setChannel={setChannel}
/>
)
}
})
:
'Loading...'
}
</ul>
</div>
)
return (
<div className={styles.channels}>
<ChannelModal isOpen={channelModalOpen} resetChannel={setChannel} channel={modalChannel} />
<ChannelForm form={channelForm} setForm={setChannelForm} ticker={ticker} peers={peers} openChannel={openChannel} />
<div className={styles.header}>
<h3>Channels</h3>
<div
className={`${styles.openChannel} hint--top`}
data-hint='Open a channel'
onClick={() => setChannelForm({ isOpen: true })}
>
<TiPlus />
</div>
</div>
<ul>
{
!channelsLoading ?
allChannels.map((channel, index) => {
if (channel.hasOwnProperty('blocks_till_open')) {
return (
<OpenPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else if (channel.hasOwnProperty('closing_txid')) {
return (
<ClosedPendingChannel key={index} channel={channel} ticker={ticker} />
)
} else {
return (
<Channel
key={channel.chan_id}
ticker={ticker}
channel={channel}
setChannel={setChannel}
/>
)
}
})
:
'Loading...'
}
</ul>
</div>
)
}
Channels.propTypes = {
ticker: PropTypes.object.isRequired,
peers: PropTypes.array.isRequired,
channelsLoading: PropTypes.bool.isRequired,
modalChannel: PropTypes.object,
setChannel: PropTypes.func.isRequired,
channelModalOpen: PropTypes.bool.isRequired,
channelForm: PropTypes.object.isRequired,
setChannelForm: PropTypes.func.isRequired,
allChannels: PropTypes.array.isRequired,
openChannel: PropTypes.func.isRequired
}
export default Channels

2
app/routes/wallet/components/components/Channels/Channels.scss

@ -42,4 +42,4 @@
font-weight: 400;
margin-bottom: 10px;
}
}
}

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

@ -1,65 +1,65 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { btc } from '../../../../../../../utils'
import styles from './Channel.scss'
class Channel extends Component {
render() {
const { ticker, channel, setChannel } = this.props
return (
<li className={styles.channel} onClick={() => setChannel(channel)}>
<h1 className={styles.status}>Status: Open</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_pubkey}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
}
}
const Channel = ({ ticker, channel, setChannel }) => (
<li className={styles.channel} onClick={() => setChannel(channel)}>
<h1 className={styles.status}>Status: Open</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_pubkey}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
Channel.propTypes = {
ticker: PropTypes.object.isRequired,
channel: PropTypes.object.isRequired,
setChannel: PropTypes.func.isRequired
}
export default Channel
export default Channel

2
app/routes/wallet/components/components/Channels/components/Channel/index.js

@ -1,3 +1,3 @@
import Channel from './Channel'
export default Channel
export default Channel

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

@ -1,125 +1,126 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import { FaUser, FaBitcoin, FaDollar } from 'react-icons/lib/fa'
import { usd, btc } from '../../../../../../../utils'
import styles from './ChannelForm.scss'
class ChannelForm extends Component {
render() {
const submitClicked = () => {
const { form: { node_key, local_amt, push_amt }, openChannel, ticker } = this.props
console.log('ticker: ', ticker)
const localamt = ticker.currency === 'btc' ? btc.btcToSatoshis(local_amt) : btc.btcToSatoshis(usd.usdToBtc(local_amt, ticker.btcTicker.price_usd))
const pushamt = ticker.currency === 'btc' ? btc.btcToSatoshis(push_amt) : btc.btcToSatoshis(usd.usdToBtc(push_amt, ticker.btcTicker.price_usd))
openChannel({ pubkey: node_key, localamt, pushamt }).then(channel => {
if (channel.data) { setForm({ isOpen: false }) }
})
}
const customStyles = {
overlay: {
cursor: 'pointer',
overflowY: 'auto'
},
content : {
top: 'auto',
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
padding: '40px'
}
}
const ChannelForm = ({ form, setForm, ticker, peers, openChannel }) => {
const submitClicked = () => {
const { form: { node_key, local_amt, push_amt }, openChannel, ticker } = this.props
const localamt = ticker.currency === 'btc' ? btc.btcToSatoshis(local_amt) : btc.btcToSatoshis(usd.usdToBtc(local_amt, ticker.btcTicker.price_usd))
const pushamt = ticker.currency === 'btc' ? btc.btcToSatoshis(push_amt) : btc.btcToSatoshis(usd.usdToBtc(push_amt, ticker.btcTicker.price_usd))
const { form, setForm, ticker, peers, openChannel } = this.props
openChannel({ pubkey: node_key, localamt, pushamt }).then(channel => {
if (channel.data) { setForm({ isOpen: false }) }
})
}
return (
<div>
<ReactModal
isOpen={form.isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp={true}
shouldCloseOnOverlayClick={true}
onRequestClose={() => setForm({ isOpen: false })}
parentSelector={() => document.body}
style={customStyles}
>
<div className={styles.form}>
<h1 className={styles.title}>Open a new channel</h1>
<section className={styles.pubkey}>
<label><FaUser /></label>
<input
type='text'
size=''
placeholder='Peer public key'
value={form.node_key}
onChange={(event) => setForm({ node_key: event.target.value })}
/>
</section>
<section className={styles.local}>
<label>
{
ticker.currency === 'btc' ?
<FaBitcoin />
:
<FaDollar />
}
</label>
<input
type='text'
size=''
placeholder='Local amount'
value={form.local_amt}
onChange={(event) => setForm({ local_amt: event.target.value })}
/>
</section>
<section className={styles.push}>
<label>
{
ticker.currency === 'btc' ?
<FaBitcoin />
:
<FaDollar />
}
</label>
<input
type='text'
size=''
placeholder='Push amount'
value={form.push_amt}
onChange={(event) => setForm({ push_amt: event.target.value })}
/>
</section>
const customStyles = {
overlay: {
cursor: 'pointer',
overflowY: 'auto'
},
content : {
top: 'auto',
left: '20%',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
padding: '40px'
}
}
return (
<div>
<ReactModal
isOpen={form.isOpen}
contentLabel="No Overlay Click Modal"
ariaHideApp={true}
shouldCloseOnOverlayClick={true}
onRequestClose={() => setForm({ isOpen: false })}
parentSelector={() => document.body}
style={customStyles}
>
<div className={styles.form}>
<h1 className={styles.title}>Open a new channel</h1>
<ul className={styles.peers}>
<h2>Connected Peers</h2>
<section className={styles.pubkey}>
<label><FaUser /></label>
<input
type='text'
size=''
placeholder='Peer public key'
value={form.node_key}
onChange={(event) => setForm({ node_key: event.target.value })}
/>
</section>
<section className={styles.local}>
<label>
{
peers.length ?
peers.map(peer =>
<li key={peer.peer_id} className={styles.peer} onClick={() => setForm({ node_key: peer.pub_key })}>
<h4>{peer.address}</h4>
<h1>{peer.pub_key}</h1>
</li>
)
:
null
ticker.currency === 'btc' ?
<FaBitcoin />
:
<FaDollar />
}
</ul>
</label>
<input
type='text'
size=''
placeholder='Local amount'
value={form.local_amt}
onChange={(event) => setForm({ local_amt: event.target.value })}
/>
</section>
<section className={styles.push}>
<label>
{
ticker.currency === 'btc' ?
<FaBitcoin />
:
<FaDollar />
}
</label>
<input
type='text'
size=''
placeholder='Push amount'
value={form.push_amt}
onChange={(event) => setForm({ push_amt: event.target.value })}
/>
</section>
<div className={styles.buttonGroup}>
<div className={styles.button} onClick={submitClicked}>
Submit
</div>
</div>
<ul className={styles.peers}>
<h2>Connected Peers</h2>
{
peers.length ?
peers.map(peer =>
<li key={peer.peer_id} className={styles.peer} onClick={() => setForm({ node_key: peer.pub_key })}>
<h4>{peer.address}</h4>
<h1>{peer.pub_key}</h1>
</li>
)
:
null
}
</ul>
<div className={styles.buttonGroup}>
<div className={styles.button} onClick={submitClicked}>Submit</div>
</div>
</ReactModal>
</div>
)
}
</div>
</ReactModal>
</div>
)
}
ChannelForm.propTypes = {
form: PropTypes.object.isRequired,
setForm: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired,
peers: PropTypes.array.isRequired,
openChannel: PropTypes.func.isRequired
}
export default ChannelForm

2
app/routes/wallet/components/components/Channels/components/ChannelForm/index.js

@ -1,3 +1,3 @@
import ChannelForm from './ChannelForm'
export default ChannelForm
export default ChannelForm

2
app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.js

@ -90,4 +90,4 @@ class ChannelModal extends Component {
}
export default ChannelModal
export default ChannelModal

2
app/routes/wallet/components/components/Channels/components/ChannelModal/ChannelModal.scss

@ -121,4 +121,4 @@
text-align: center;
margin-top: 50px;
text-transform: uppercase;
}
}

2
app/routes/wallet/components/components/Channels/components/ChannelModal/index.js

@ -1,3 +1,3 @@
import ChannelModal from './ChannelModal'
export default ChannelModal
export default ChannelModal

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

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

2
app/routes/wallet/components/components/Channels/components/ClosedPendingChannel/index.js

@ -1,3 +1,3 @@
import ClosedPendingChannel from './ClosedPendingChannel'
export default ClosedPendingChannel
export default ClosedPendingChannel

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

@ -1,66 +1,66 @@
// @flow
import { shell } from 'electron'
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { btc } from '../../../../../../../utils'
import styles from './OpenPendingChannel.scss'
class OpenPendingChannel extends Component {
render() {
const { ticker, channel: { channel } } = this.props
return (
<li className={styles.channel} onClick={() => shell.openExternal(`https://testnet.smartbit.com.au/tx/${channel.channel_point.split(':')[0]}`)}>
<h1 className={styles.pending}>Status: Pending</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_node_pub}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
}
}
const OpenPendingChannel = ({ ticker, channel: { channel } }) => (
<li className={styles.channel} onClick={() => shell.openExternal(`https://testnet.smartbit.com.au/tx/${channel.channel_point.split(':')[0]}`)}>
<h1 className={styles.pending}>Status: Pending</h1>
<div className={styles.left}>
<section className={styles.remotePubkey}>
<span>Remote Pubkey</span>
<h4>{channel.remote_node_pub}</h4>
</section>
<section className={styles.channelPoint}>
<span>Channel Point</span>
<h4>{channel.channel_point}</h4>
</section>
</div>
<div className={styles.right}>
<section className={styles.capacity}>
<span>Capacity</span>
<h2>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.capacity)
:
btc.satoshisToUsd(channel.capacity, ticker.btcTicker.price_usd)
}
</h2>
</section>
<div className={styles.balances}>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.local_balance)
:
btc.satoshisToUsd(channel.local_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Local</span>
</section>
<section>
<h4>
{
ticker.currency === 'btc' ?
btc.satoshisToBtc(channel.remote_balance)
:
btc.satoshisToUsd(channel.remote_balance, ticker.btcTicker.price_usd)
}
</h4>
<span>Remote</span>
</section>
</div>
</div>
</li>
)
OpenPendingChannel.propTypes = {
ticker: PropTypes.object.isRequired,
channel: PropTypes.object.isRequired
}
export default OpenPendingChannel
export default OpenPendingChannel

2
app/routes/wallet/components/components/Channels/components/OpenPendingChannel/index.js

@ -1,3 +1,3 @@
import OpenPendingChannel from './OpenPendingChannel'
export default OpenPendingChannel
export default OpenPendingChannel

2
app/routes/wallet/components/components/Channels/index.js

@ -1,3 +1,3 @@
import Channels from './Channels'
export default Channels
export default Channels

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

@ -1,51 +1,56 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { TiPlus } from 'react-icons/lib/ti'
import PeerModal from './components/PeerModal'
import PeerForm from './components/PeerForm'
import Peer from './components/Peer'
import styles from './Peers.scss'
class Peers extends Component {
render() {
const {
peersLoading,
peers,
peer,
setPeer,
modalPeer,
peerModalOpen,
peerForm,
setPeerForm,
connect,
disconnect,
} = this.props
const Peers = ({
peersLoading,
peers,
setPeer,
modalPeer,
peerModalOpen,
peerForm,
setPeerForm,
connect,
disconnect
}) => (
<div className={styles.peers}>
<PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={modalPeer} disconnect={disconnect} />
<PeerForm form={peerForm} setForm={setPeerForm} connect={connect} />
<div className={styles.header}>
<h3>Peers</h3>
<div
className={`${styles.connectPeer} hint--top`}
data-hint='Connect to a peer'
onClick={() => setPeerForm({ isOpen: true })}
>
<TiPlus />
</div>
</div>
<ul>
{
!peersLoading ?
peers.map(peer => <Peer key={peer.peer_id} peer={peer} setPeer={setPeer} />)
:
'Loading...'
}
</ul>
</div>
)
return (
<div className={styles.peers}>
<PeerModal isOpen={peerModalOpen} resetPeer={setPeer} peer={modalPeer} disconnect={disconnect} />
<PeerForm form={peerForm} setForm={setPeerForm} connect={connect} />
<div className={styles.header}>
<h3>Peers</h3>
<div
className={`${styles.connectPeer} hint--top`}
data-hint='Connect to a peer'
onClick={() => setPeerForm({ isOpen: true })}
>
<TiPlus />
</div>
</div>
<ul>
{
!peersLoading ?
peers.map(peer => <Peer key={peer.peer_id} peer={peer} setPeer={setPeer} />)
:
'Loading...'
}
</ul>
</div>
)
}
Peers.propTypes = {
peersLoading: PropTypes.bool.isRequired,
peers: PropTypes.array.isRequired,
setPeer: PropTypes.func.isRequired,
modalPeer: PropTypes.object,
peerModalOpen: PropTypes.bool.isRequired,
peerForm: PropTypes.object.isRequired,
setPeerForm: PropTypes.func.isRequired,
connect: PropTypes.func.isRequired,
disconnect: PropTypes.func.isRequired
}
export default Peers

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

@ -42,4 +42,4 @@
font-weight: 400;
margin-bottom: 10px;
}
}
}

27
app/routes/wallet/components/components/Peers/components/Peer/Peer.js

@ -1,18 +1,17 @@
// @flow
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import styles from './Peer.scss'
class Peer extends Component {
render() {
const { peer, setPeer } = this.props
return (
<li className={styles.peer} onClick={() => setPeer(peer)}>
<h4>{peer.address}</h4>
<h1>{peer.pub_key}</h1>
</li>
)
}
}
const Peer = ({ peer, setPeer }) => (
<li className={styles.peer} onClick={() => setPeer(peer)}>
<h4>{peer.address}</h4>
<h1>{peer.pub_key}</h1>
</li>
)
Peer.propTypes = {
peer: PropTypes.object.isRequired,
setPeer: PropTypes.func.isRequired
}
export default Peer
export default Peer

2
app/routes/wallet/components/components/Peers/components/Peer/Peer.scss

@ -31,4 +31,4 @@
font-weight: 200;
color: $main;
}
}
}

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

@ -1,3 +1,3 @@
import Peer from './Peer'
export default Peer
export default Peer

2
app/routes/wallet/components/components/Peers/components/PeerForm/PeerForm.scss

@ -66,4 +66,4 @@
border-right: 1px solid lighten($main, 20%);
}
}
}
}

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

@ -1,3 +1,3 @@
import PeerForm from './PeerForm'
export default PeerForm
export default PeerForm

2
app/routes/wallet/components/components/Peers/components/PeerModal/PeerModal.scss

@ -70,4 +70,4 @@
background: darken($red, 10%);
}
}
}
}

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

@ -1,3 +1,3 @@
import PeerModal from './PeerModal'
export default PeerModal
export default PeerModal

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

@ -1,3 +1,3 @@
import Peers from './Peers'
export default Peers
export default Peers

68
app/routes/wallet/containers/WalletContainer.js

@ -1,51 +1,51 @@
import { connect } from 'react-redux'
import { fetchInfo } from '../../../reducers/info'
import {
fetchPeers,
setPeer,
peersSelectors,
setPeerForm,
connectRequest,
disconnectRequest
fetchPeers,
setPeer,
peersSelectors,
setPeerForm,
connectRequest,
disconnectRequest
} from '../../../reducers/peers'
import {
fetchChannels,
fetchPendingChannels,
setChannel,
channelsSelectors,
setChannelForm,
openChannel
fetchChannels,
fetchPendingChannels,
setChannel,
channelsSelectors,
setChannelForm,
openChannel
} from '../../../reducers/channels'
import Wallet from '../components/Wallet'
const mapDispatchToProps = {
fetchInfo,
fetchPeers,
setPeer,
connectRequest,
disconnectRequest,
fetchChannels,
fetchPendingChannels,
setChannel,
openChannel,
setPeerForm,
setChannelForm
fetchInfo,
fetchPeers,
setPeer,
connectRequest,
disconnectRequest,
fetchChannels,
fetchPendingChannels,
setChannel,
openChannel,
setPeerForm,
setChannelForm
}
const mapStateToProps = (state) => ({
info: state.info,
ticker: state.ticker,
peers: state.peers,
channels: state.channels,
info: state.info,
ticker: state.ticker,
peers: state.peers,
channels: state.channels,
allChannels: channelsSelectors.allChannels(state),
allChannels: channelsSelectors.allChannels(state),
peerModalOpen: peersSelectors.peerModalOpen(state),
channelModalOpen: channelsSelectors.channelModalOpen(state),
peerModalOpen: peersSelectors.peerModalOpen(state),
channelModalOpen: channelsSelectors.channelModalOpen(state)
})
export default connect(mapStateToProps, mapDispatchToProps)(Wallet)

2
app/routes/wallet/index.js

@ -1,3 +1,3 @@
import WalletContainer from './containers/WalletContainer'
export default WalletContainer
export default WalletContainer

2
app/store/configureStore.dev.js

@ -35,7 +35,7 @@ const configureStore = (initialState?: counterStateType) => {
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
actionCreators,
actionCreators
})
: compose;
/* eslint-enable no-underscore-dangle */

6
app/utils/index.js

@ -2,6 +2,6 @@ import btc from './btc'
import usd from './usd'
export default {
btc,
usd
}
btc,
usd
}

6
app/utils/usd.js

@ -1,9 +1,9 @@
export function usdToBtc(usd, rate) {
if (usd == undefined || usd === '') return
if (usd === undefined || usd === '') return null
return (usd / rate).toFixed(8)
}
export default {
usdToBtc
}
usdToBtc
}

2
internals/scripts/CheckNodeEnv.js

@ -7,9 +7,11 @@ export default function CheckNodeEnv(expectedEnv: string) {
}
if (process.env.NODE_ENV !== expectedEnv) {
/* eslint-disable */
console.log(chalk.whiteBright.bgRed.bold(
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
));
/* eslint-enable */
process.exit(2);
}
}

396
package-lock.json

@ -408,9 +408,9 @@
}
},
"aria-query": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz",
"integrity": "sha1-heMVLNjMW6sY2+1hzZxPzlT6ecM=",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.0.tgz",
"integrity": "sha512-/r2lHl09V3o74+2MLKEdewoj37YZqiQZnfen1O4iNlrOjUgeKuu1U2yF3iKh6HJxqF+OXkLMfQv65Z/cvxD6vA==",
"dev": true,
"requires": {
"ast-types-flow": "0.0.7"
@ -2781,12 +2781,12 @@
"dev": true
},
"cli-cursor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
"integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"dev": true,
"requires": {
"restore-cursor": "1.0.1"
"restore-cursor": "2.0.0"
}
},
"cli-width": {
@ -4618,9 +4618,9 @@
}
},
"emoji-regex": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.0.tgz",
"integrity": "sha512-Vja85njef5T0kGfRUFkyl0etU9+49L1LNKR5oE41wAGRtJR64/a+JX3I8YCIur/uXj4Kt4cNe5i8bfd58ilgKQ==",
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
"integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==",
"dev": true
},
"emojis-list": {
@ -4888,48 +4888,61 @@
}
},
"eslint": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz",
"integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.4.1.tgz",
"integrity": "sha1-mc1+r8/8ov+Zpcj18qR01jZLS9M=",
"dev": true,
"requires": {
"ajv": "5.2.2",
"babel-code-frame": "6.22.0",
"chalk": "1.1.3",
"concat-stream": "1.6.0",
"cross-spawn": "5.1.0",
"debug": "2.6.8",
"doctrine": "2.0.0",
"escope": "3.6.0",
"espree": "3.4.3",
"eslint-scope": "3.7.1",
"espree": "3.5.0",
"esquery": "1.0.0",
"estraverse": "4.2.0",
"esutils": "2.0.2",
"file-entry-cache": "2.0.0",
"functional-red-black-tree": "1.0.1",
"glob": "7.1.2",
"globals": "9.18.0",
"ignore": "3.3.3",
"imurmurhash": "0.1.4",
"inquirer": "0.12.0",
"is-my-json-valid": "2.16.0",
"inquirer": "3.2.1",
"is-resolvable": "1.0.0",
"js-yaml": "3.7.0",
"js-yaml": "3.9.1",
"json-stable-stringify": "1.0.1",
"levn": "0.3.0",
"lodash": "4.17.4",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"natural-compare": "1.4.0",
"optionator": "0.8.2",
"path-is-inside": "1.0.2",
"pluralize": "1.2.1",
"progress": "1.1.8",
"pluralize": "4.0.0",
"progress": "2.0.0",
"require-uncached": "1.0.3",
"shelljs": "0.7.8",
"strip-bom": "3.0.0",
"semver": "5.3.0",
"strip-json-comments": "2.0.1",
"table": "3.8.3",
"text-table": "0.2.0",
"user-home": "2.0.0"
"table": "4.0.1",
"text-table": "0.2.0"
},
"dependencies": {
"ajv": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz",
"integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=",
"dev": true,
"requires": {
"co": "4.6.0",
"fast-deep-equal": "1.0.0",
"json-schema-traverse": "0.3.1",
"json-stable-stringify": "1.0.1"
}
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@ -4943,28 +4956,41 @@
"supports-color": "2.0.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"esprima": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
"dev": true
},
"js-yaml": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz",
"integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==",
"dev": true,
"requires": {
"argparse": "1.0.9",
"esprima": "4.0.0"
}
}
}
},
"eslint-config-airbnb": {
"version": "15.0.2",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-15.0.2.tgz",
"integrity": "sha512-4nI0Jp3ekTPuYKa2r8R8jq/CsDDaCwXkzV0V0BFyFSKJlQclAqJaJFXM/E6EjSFzVnK2PYNlEiIR3524Z0i2Mw==",
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz",
"integrity": "sha512-m0q9fiMBzDAIbirlGnpJNWToIhdhJmXXnMG+IFflYzzod9231ZhtmGKegKg8E9T8F1YuVaDSU1FnCm5b9iXVhQ==",
"dev": true,
"requires": {
"eslint-config-airbnb-base": "11.2.0"
"eslint-config-airbnb-base": "11.3.1"
}
},
"eslint-config-airbnb-base": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.2.0.tgz",
"integrity": "sha1-GancRIGib3CQRUXsBAEWh2AY+FM=",
"dev": true
"version": "11.3.1",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.1.tgz",
"integrity": "sha512-BXVH7PV5yiLjnkv49iOLJ8dWp+ljZf310ytQpqwrunFADiEbWRyN0tPGDU36FgEbdLvhJDWcJOngYDzPF4shDw==",
"dev": true,
"requires": {
"eslint-restricted-globals": "0.1.1"
}
},
"eslint-formatter-pretty": {
"version": "1.1.0",
@ -5236,17 +5262,17 @@
"dev": true
},
"eslint-plugin-jsx-a11y": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz",
"integrity": "sha1-SpOfduwSUBBSiCMzG/lIzFczgLY=",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz",
"integrity": "sha512-5I9SpoP7gT4wBFOtXT8/tXNPYohHBVfyVfO17vkbC7r9kEIxYJF12D3pKqhk8+xnk12rfxKClS3WCFpVckFTPQ==",
"dev": true,
"requires": {
"aria-query": "0.5.0",
"aria-query": "0.7.0",
"array-includes": "3.0.3",
"ast-types-flow": "0.0.7",
"axobject-query": "0.1.0",
"damerau-levenshtein": "1.0.4",
"emoji-regex": "6.5.0",
"emoji-regex": "6.5.1",
"jsx-ast-utils": "1.4.1"
}
},
@ -5257,20 +5283,47 @@
"dev": true
},
"eslint-plugin-react": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.1.0.tgz",
"integrity": "sha1-J3cKzzn1/UnNCvQIPOWBBOs5DUw=",
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.2.1.tgz",
"integrity": "sha512-7hN8YJO7bkxPdPfuSRz+xWKC0xk1BBp8yn8ehXaxklcMFdIoIQnhtBXc3iv042CGQH4LbKFMgDdOAjoAnnqr7Q==",
"dev": true,
"requires": {
"doctrine": "2.0.0",
"has": "1.0.1",
"jsx-ast-utils": "1.4.1"
"jsx-ast-utils": "2.0.0"
},
"dependencies": {
"jsx-ast-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz",
"integrity": "sha1-7Aaj1gzzB+XhGdrHutgeifCW8Pg=",
"dev": true,
"requires": {
"array-includes": "3.0.3"
}
}
}
},
"eslint-restricted-globals": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
"integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
"dev": true
},
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
"integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
"dev": true,
"requires": {
"esrecurse": "4.2.0",
"estraverse": "4.2.0"
}
},
"espree": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz",
"integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.0.tgz",
"integrity": "sha1-mDWGJb3QVYYeon4oZ+pyn69GPY0=",
"dev": true,
"requires": {
"acorn": "5.1.1",
@ -5397,12 +5450,6 @@
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.0.tgz",
"integrity": "sha1-ODXxJ6vwdb/ggtCu1EhAV8eOPIk="
},
"exit-hook": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
"integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
"dev": true
},
"expand-brackets": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
@ -5723,13 +5770,12 @@
}
},
"figures": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
"integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"dev": true,
"requires": {
"escape-string-regexp": "1.0.5",
"object-assign": "4.1.1"
"escape-string-regexp": "1.0.5"
}
},
"file-entry-cache": {
@ -7141,6 +7187,12 @@
"is-callable": "1.1.3"
}
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"gather-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gather-stream/-/gather-stream-1.0.0.tgz",
@ -7172,21 +7224,6 @@
"globule": "1.2.0"
}
},
"generate-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
"integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
"dev": true
},
"generate-object-property": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
"integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
"dev": true,
"requires": {
"is-property": "1.0.2"
}
},
"get-caller-file": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
@ -8119,37 +8156,62 @@
"dev": true
},
"inquirer": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
"integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.2.1.tgz",
"integrity": "sha512-QgW3eiPN8gpj/K5vVpHADJJgrrF0ho/dZGylikGX7iqAdRgC9FVKYKWFLx6hZDBFcOLEoSqINYrVPeFAeG/PdA==",
"dev": true,
"requires": {
"ansi-escapes": "1.4.0",
"ansi-regex": "2.1.1",
"chalk": "1.1.3",
"cli-cursor": "1.0.2",
"ansi-escapes": "2.0.0",
"chalk": "2.0.1",
"cli-cursor": "2.1.0",
"cli-width": "2.1.0",
"figures": "1.7.0",
"external-editor": "2.0.4",
"figures": "2.0.0",
"lodash": "4.17.4",
"readline2": "1.0.1",
"run-async": "0.1.0",
"rx-lite": "3.1.2",
"string-width": "1.0.2",
"strip-ansi": "3.0.1",
"mute-stream": "0.0.7",
"run-async": "2.3.0",
"rx-lite": "4.0.8",
"rx-lite-aggregates": "4.0.8",
"string-width": "2.1.1",
"strip-ansi": "4.0.0",
"through": "2.3.8"
},
"dependencies": {
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"ansi-escapes": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz",
"integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=",
"dev": true
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"ansi-styles": "2.2.1",
"escape-string-regexp": "1.0.5",
"has-ansi": "2.0.0",
"strip-ansi": "3.0.1",
"supports-color": "2.0.0"
"is-fullwidth-code-point": "2.0.0",
"strip-ansi": "4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "3.0.0"
}
}
}
@ -8335,26 +8397,6 @@
"is-extglob": "1.0.0"
}
},
"is-my-json-valid": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz",
"integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=",
"dev": true,
"requires": {
"generate-function": "2.0.0",
"generate-object-property": "1.2.0",
"jsonpointer": "4.0.1",
"xtend": "4.0.1"
},
"dependencies": {
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"dev": true
}
}
},
"is-npm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
@ -8447,12 +8489,6 @@
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
"dev": true
},
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
@ -9492,12 +9528,6 @@
"integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=",
"dev": true
},
"jsonpointer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
"dev": true
},
"JSONStream": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz",
@ -10435,9 +10465,9 @@
}
},
"mute-stream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
"integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
"nan": {
@ -10935,10 +10965,13 @@
"dev": true
},
"onetime": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"dev": true,
"requires": {
"mimic-fn": "1.1.0"
}
},
"opener": {
"version": "1.4.3",
@ -11333,9 +11366,9 @@
}
},
"pluralize": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
"integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-4.0.0.tgz",
"integrity": "sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=",
"dev": true
},
"pn": {
@ -12038,9 +12071,9 @@
"dev": true
},
"progress": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
"integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
"dev": true
},
"progress-stream": {
@ -12642,26 +12675,6 @@
}
}
},
"readline2": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
"integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=",
"dev": true,
"requires": {
"code-point-at": "1.1.0",
"is-fullwidth-code-point": "1.0.0",
"mute-stream": "0.0.5"
}
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"dev": true,
"requires": {
"resolve": "1.3.3"
}
},
"redbox-react": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/redbox-react/-/redbox-react-1.4.3.tgz",
@ -13053,13 +13066,13 @@
"dev": true
},
"restore-cursor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
"integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"dev": true,
"requires": {
"exit-hook": "1.1.1",
"onetime": "1.1.0"
"onetime": "2.0.1",
"signal-exit": "3.0.2"
}
},
"rgb2hex": {
@ -13097,12 +13110,12 @@
}
},
"run-async": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
"integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
"dev": true,
"requires": {
"once": "1.4.0"
"is-promise": "2.1.0"
}
},
"rx": {
@ -13112,11 +13125,20 @@
"dev": true
},
"rx-lite": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
"integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
"dev": true
},
"rx-lite-aggregates": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
"dev": true,
"requires": {
"rx-lite": "4.0.8"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
@ -13471,17 +13493,6 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"shelljs": {
"version": "0.7.8",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
"integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
"dev": true,
"requires": {
"glob": "7.1.2",
"interpret": "1.0.3",
"rechoir": "0.6.2"
}
},
"shellwords": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.0.tgz",
@ -14581,9 +14592,9 @@
}
},
"table": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
"integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz",
"integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=",
"dev": true,
"requires": {
"ajv": "4.11.8",
@ -15293,15 +15304,6 @@
"integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=",
"dev": true
},
"user-home": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
"integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=",
"dev": true,
"requires": {
"os-homedir": "1.0.2"
}
},
"utf8-byte-length": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",

11
package.json

@ -139,18 +139,18 @@
"electron-devtools-installer": "^2.2.0",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.1",
"eslint": "^4.4.1",
"eslint-config-airbnb": "^15.1.0",
"eslint-formatter-pretty": "^1.1.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-plugin-compat": "^1.0.4",
"eslint-plugin-flowtype": "^2.33.0",
"eslint-plugin-flowtype-errors": "^3.3.0",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jest": "^20.0.3",
"eslint-plugin-jsx-a11y": "5.0.3",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.1.0",
"eslint-plugin-react": "^7.2.1",
"express": "^4.15.3",
"extract-text-webpack-plugin": "^2.1.0",
"fbjs-scripts": "^0.8.0",
@ -188,6 +188,7 @@
"font-awesome": "^4.7.0",
"history": "^4.6.3",
"moment-timezone": "^0.5.13",
"prop-types": "^15.5.10",
"qrcode.react": "^0.7.1",
"react": "^15.6.1",
"react-addons-css-transition-group": "^15.6.0",

13
test/actions/__snapshots__/counter.spec.js.snap

@ -1,13 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`actions should decrement should create decrement action 1`] = `
Object {
"type": "DECREMENT_COUNTER",
}
`;
exports[`actions should increment should create increment action 1`] = `
Object {
"type": "INCREMENT_COUNTER",
}
`;

41
test/actions/counter.spec.js

@ -1,41 +0,0 @@
import { spy } from 'sinon';
import * as actions from '../../app/actions/counter';
describe('actions', () => {
it('should increment should create increment action', () => {
expect(actions.increment()).toMatchSnapshot();
});
it('should decrement should create decrement action', () => {
expect(actions.decrement()).toMatchSnapshot();
});
it('should incrementIfOdd should create increment action', () => {
const fn = actions.incrementIfOdd();
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
const getState = () => ({ counter: 1 });
fn(dispatch, getState);
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
});
it('should incrementIfOdd shouldnt create increment action if counter is even', () => {
const fn = actions.incrementIfOdd();
const dispatch = spy();
const getState = () => ({ counter: 2 });
fn(dispatch, getState);
expect(dispatch.called).toBe(false);
});
// There's no nice way to test this at the moment...
it('should incrementAsync', done => {
const fn = actions.incrementAsync(1);
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
fn(dispatch);
setTimeout(() => {
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
done();
}, 5);
});
});

68
test/components/Counter.spec.js

@ -1,68 +0,0 @@
import { spy } from 'sinon';
import React from 'react';
import { shallow } from 'enzyme';
import { BrowserRouter as Router } from 'react-router-dom';
import renderer from 'react-test-renderer';
import Counter from '../../app/components/Counter';
function setup() {
const actions = {
increment: spy(),
incrementIfOdd: spy(),
incrementAsync: spy(),
decrement: spy()
};
const component = shallow(<Counter counter={1} {...actions} />);
return {
component,
actions,
buttons: component.find('button'),
p: component.find('.counter')
};
}
describe('Counter component', () => {
it('should should display count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^1$/);
});
it('should first button should call increment', () => {
const { buttons, actions } = setup();
buttons.at(0).simulate('click');
expect(actions.increment.called).toBe(true);
});
it('should match exact snapshot', () => {
const { actions } = setup();
const tree = renderer
.create(
<div>
<Router>
<Counter counter={1} {...actions} />
</Router>
</div>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should second button should call decrement', () => {
const { buttons, actions } = setup();
buttons.at(1).simulate('click');
expect(actions.decrement.called).toBe(true);
});
it('should third button should call incrementIfOdd', () => {
const { buttons, actions } = setup();
buttons.at(2).simulate('click');
expect(actions.incrementIfOdd.called).toBe(true);
});
it('should fourth button should call incrementAsync', () => {
const { buttons, actions } = setup();
buttons.at(3).simulate('click');
expect(actions.incrementAsync.called).toBe(true);
});
});

63
test/components/__snapshots__/Counter.spec.js.snap

@ -1,63 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Counter component should match exact snapshot 1`] = `
<div>
<div>
<div
className="backButton"
data-tid="backButton"
>
<a
href="/"
onClick={[Function]}
>
<i
className="fa fa-arrow-left fa-3x"
/>
</a>
</div>
<div
className="counter counter"
data-tid="counter"
>
1
</div>
<div
className="btnGroup"
>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-plus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-minus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
odd
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
async
</button>
</div>
</div>
</div>
`;

57
test/containers/CounterPage.spec.js

@ -1,57 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import CounterPage from '../../app/containers/CounterPage';
import { configureStore } from '../../app/store/configureStore';
function setup(initialState) {
const store = configureStore(initialState);
const history = createBrowserHistory();
const app = mount(
<Provider store={store}>
<ConnectedRouter history={history}>
<CounterPage />
</ConnectedRouter>
</Provider>
);
return {
app,
buttons: app.find('button'),
p: app.find('.counter')
};
}
describe('containers', () => {
describe('App', () => {
it('should display initial count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^0$/);
});
it('should display updated count after increment button click', () => {
const { buttons, p } = setup();
buttons.at(0).simulate('click');
expect(p.text()).toMatch(/^1$/);
});
it('should display updated count after descrement button click', () => {
const { buttons, p } = setup();
buttons.at(1).simulate('click');
expect(p.text()).toMatch(/^-1$/);
});
it('shouldnt change if even and if odd button clicked', () => {
const { buttons, p } = setup();
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^0$/);
});
it('should change if odd and if odd button clicked', () => {
const { buttons, p } = setup({ counter: 1 });
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^2$/);
});
});
});

9
test/reducers/__snapshots__/counter.spec.js.snap

@ -1,9 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`;
exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`;
exports[`reducers counter should handle initial state 1`] = `0`;
exports[`reducers counter should handle unknown action type 1`] = `1`;

22
test/reducers/counter.spec.js

@ -1,22 +0,0 @@
import counter from '../../app/reducers/counter';
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter';
describe('reducers', () => {
describe('counter', () => {
it('should handle initial state', () => {
expect(counter(undefined, {})).toMatchSnapshot();
});
it('should handle INCREMENT_COUNTER', () => {
expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle DECREMENT_COUNTER', () => {
expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle unknown action type', () => {
expect(counter(1, { type: 'unknown' })).toMatchSnapshot();
});
});
});

10
webpack.config.base.js

@ -36,8 +36,8 @@ export default {
extensions: ['.js', '.jsx', '.json'],
modules: [
path.join(__dirname, 'app'),
'node_modules',
],
'node_modules'
]
},
plugins: [
@ -45,6 +45,6 @@ export default {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
}),
new webpack.NamedModulesPlugin(),
],
};
new webpack.NamedModulesPlugin()
]
}

2
webpack.config.main.prod.js

@ -58,5 +58,5 @@ export default merge.smart(baseConfig, {
node: {
__dirname: false,
__filename: false
},
}
});

Loading…
Cancel
Save