Browse Source

Merge pull request #323 from LN-Zap/feature/multi-currency-support

Feature/multi currency support
renovate/lint-staged-8.x
JimmyMow 7 years ago
committed by GitHub
parent
commit
18b8df49a9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      .eslintrc
  2. 21
      app/.eslintrc
  3. 10
      app/animated_checkmark.scss
  4. 57
      app/components/Activity/ActivityModal.js
  5. 27
      app/components/Activity/ActivityModal.scss
  6. 94
      app/components/Activity/Countdown.js
  7. 16
      app/components/Activity/Countdown.scss
  8. 107
      app/components/Activity/InvoiceModal.js
  9. 152
      app/components/Activity/InvoiceModal.scss
  10. 83
      app/components/Activity/PaymentModal.js
  11. 126
      app/components/Activity/PaymentModal.scss
  12. 91
      app/components/Activity/TransactionModal.js
  13. 135
      app/components/Activity/TransactionModal.scss
  14. 26
      app/components/Form/Form.js
  15. 59
      app/components/Form/Form.scss
  16. 219
      app/components/Form/Pay.js
  17. 182
      app/components/Form/Pay.scss
  18. 152
      app/components/Form/PayForm.js
  19. 166
      app/components/Form/PayForm.scss
  20. 124
      app/components/Form/Request.js
  21. 147
      app/components/Form/Request.scss
  22. 64
      app/components/Form/RequestForm.js
  23. 103
      app/components/Form/RequestForm.scss
  24. 5
      app/components/ModalRoot/ModalRoot.scss
  25. 7
      app/components/ModalRoot/SuccessfulSendPayment.scss
  26. 4
      app/components/ModalRoot/WalletDetails.js
  27. 8
      app/components/Onboarding/FormContainer.js
  28. 8
      app/components/Onboarding/Onboarding.js
  29. 4
      app/components/Onboarding/Syncing.js
  30. 22
      app/components/Value/Value.js
  31. 0
      app/components/Value/Value.scss
  32. 3
      app/components/Value/index.js
  33. 168
      app/components/Wallet/ReceiveModal.js
  34. 151
      app/components/Wallet/ReceiveModal.scss
  35. 160
      app/components/Wallet/Wallet.js
  36. 81
      app/components/Wallet/Wallet.scss
  37. 1
      app/icons/copy.svg
  38. 16
      app/icons/hand.svg
  39. 1
      app/icons/link.svg
  40. 13
      app/icons/paper_plane.svg
  41. 1
      app/icons/x.svg
  42. 1
      app/icons/zap.svg
  43. 3
      app/lnd/lib/rpc.proto
  44. 7
      app/lnd/methods/index.js
  45. 1
      app/lnd/methods/paymentsController.js
  46. 16
      app/reducers/activity.js
  47. 23
      app/reducers/address.js
  48. 14
      app/reducers/invoice.js
  49. 63
      app/reducers/payform.js
  50. 53
      app/reducers/payment.js
  51. 37
      app/reducers/requestform.js
  52. 34
      app/reducers/ticker.js
  53. 8
      app/reducers/transaction.js
  54. 49
      app/routes/activity/components/Activity.js
  55. 24
      app/routes/activity/components/components/Invoice/Invoice.js
  56. 74
      app/routes/activity/components/components/Modal/Invoice/Invoice.js
  57. 106
      app/routes/activity/components/components/Modal/Invoice/Invoice.scss
  58. 3
      app/routes/activity/components/components/Modal/Invoice/index.js
  59. 69
      app/routes/activity/components/components/Modal/Modal.js
  60. 13
      app/routes/activity/components/components/Modal/Modal.scss
  61. 52
      app/routes/activity/components/components/Modal/Payment/Payment.js
  62. 61
      app/routes/activity/components/components/Modal/Payment/Payment.scss
  63. 3
      app/routes/activity/components/components/Modal/Payment/index.js
  64. 64
      app/routes/activity/components/components/Modal/Transaction/Transaction.js
  65. 72
      app/routes/activity/components/components/Modal/Transaction/Transaction.scss
  66. 3
      app/routes/activity/components/components/Modal/Transaction/index.js
  67. 3
      app/routes/activity/components/components/Modal/index.js
  68. 24
      app/routes/activity/components/components/Payment/Payment.js
  69. 24
      app/routes/activity/components/components/Transaction/Transaction.js
  70. 25
      app/routes/activity/components/components/Transaction/Transaction.scss
  71. 45
      app/routes/activity/containers/ActivityContainer.js
  72. 19
      app/routes/app/components/App.js
  73. 76
      app/routes/app/containers/AppContainer.js
  74. 3
      app/rpc.proto
  75. 4
      app/utils/blockExplorer.js
  76. 116
      app/utils/btc.js
  77. 29
      package.json
  78. 52
      test/components/Form.spec.js
  79. 46
      test/components/Form/Pay.spec.js
  80. 21
      test/components/Form/Request.spec.js
  81. 5
      test/reducers/__snapshots__/payment.spec.js.snap
  82. 70
      test/reducers/__snapshots__/ticker.spec.js.snap
  83. 11
      test/reducers/form.spec.js
  84. 514
      yarn.lock

9
.eslintrc

@ -23,6 +23,7 @@
"indent": 2, "indent": 2,
"jsx-quotes": ["error", "prefer-single"], "jsx-quotes": ["error", "prefer-single"],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react/no-did-mount-set-state": 0,
"jsx-a11y/no-static-element-interactions": 0, "jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/no-noninteractive-element-interactions": 0, "jsx-a11y/no-noninteractive-element-interactions": 0,
"jsx-a11y/click-events-have-key-events": 0, "jsx-a11y/click-events-have-key-events": 0,
@ -41,12 +42,14 @@
"import/no-extraneous-dependencies": 0, "import/no-extraneous-dependencies": 0,
"no-new": 0, "no-new": 0,
"compat/compat": "error", "compat/compat": "error",
"flowtype-errors/show-errors": "error", "prefer-destructuring": ["error", {
"flowtype-errors/enforce-min-coverage": ["error", 20] "array": false,
"object": true
}],
"prefer-promise-reject-errors": 0
}, },
"plugins": [ "plugins": [
"flowtype", "flowtype",
"flowtype-errors",
"import", "import",
"promise", "promise",
"compat", "compat",

21
app/.eslintrc

@ -1,22 +1,3 @@
{ {
"rules": { "rules": {}
"flowtype/boolean-style": [2, "boolean"],
"flowtype/define-flow-type": 1,
"flowtype/delimiter-dangle": [2, "never"],
"flowtype/generic-spacing": [2, "never"],
"flowtype/no-primitive-constructor-types": 2,
"flowtype/no-weak-types": 1,
"flowtype/object-type-delimiter": [2, "comma"],
"flowtype/require-parameter-type": 0,
"flowtype/require-return-type": 0,
"flowtype/require-valid-file-annotation": 0,
"flowtype/semi": [2, "always"],
"flowtype/space-after-type-colon": [2, "always"],
"flowtype/space-before-generic-bracket": [2, "never"],
"flowtype/space-before-type-colon": [2, "never"],
"flowtype/union-intersection-spacing": [2, "always"],
"flowtype/use-flow-type": 2,
"flowtype/valid-syntax": 2,
"flowtype-errors/show-errors": 2
}
} }

10
app/animated_checkmark.scss

@ -5,19 +5,19 @@ $curve: cubic-bezier(0.650, 0.000, 0.450, 1.000);
stroke-dashoffset: 166; stroke-dashoffset: 166;
stroke-width: 2; stroke-width: 2;
stroke-miterlimit: 10; stroke-miterlimit: 10;
stroke: $main; stroke: $green;
fill: none; fill: none;
animation: stroke .6s $curve forwards; animation: stroke .6s $curve forwards;
} }
.checkmark { .checkmark {
width: 56px; width: 20px;
height: 56px; height: 20px;
border-radius: 50%; border-radius: 50%;
stroke-width: 2; stroke-width: 2;
stroke: #fff; stroke: #fff;
stroke-miterlimit: 10; stroke-miterlimit: 10;
box-shadow: inset 0px 0px 0px $main; box-shadow: inset 0px 0px 0px $green;
animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both; animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
} }
@ -45,6 +45,6 @@ $curve: cubic-bezier(0.650, 0.000, 0.450, 1.000);
@keyframes fill { @keyframes fill {
100% { 100% {
box-shadow: inset 0px 0px 0px 30px $main; box-shadow: inset 0px 0px 0px 30px $green;
} }
} }

57
app/components/Activity/ActivityModal.js

@ -0,0 +1,57 @@
import React from 'react'
import PropTypes from 'prop-types'
import Isvg from 'react-inlinesvg'
import x from 'icons/x.svg'
import TransactionModal from './TransactionModal'
import PaymentModal from './PaymentModal'
import InvoiceModal from './InvoiceModal'
import styles from './ActivityModal.scss'
const ActivityModal = ({
modalType,
modalProps,
ticker,
currentTicker,
hideActivityModal,
toggleCurrencyProps
}) => {
const MODAL_COMPONENTS = {
TRANSACTION: TransactionModal,
PAYMENT: PaymentModal,
INVOICE: InvoiceModal
}
if (!modalType) { return null }
const SpecificModal = MODAL_COMPONENTS[modalType]
return (
<div className={styles.container}>
<div className={styles.closeContainer}>
<span onClick={() => hideActivityModal()}>
<Isvg src={x} />
</span>
</div>
<SpecificModal
{...modalProps}
ticker={ticker}
currentTicker={currentTicker}
toggleCurrencyProps={toggleCurrencyProps}
/>
</div>
)
}
ActivityModal.propTypes = {
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
toggleCurrencyProps: PropTypes.object.isRequired,
modalType: PropTypes.string,
modalProps: PropTypes.object.isRequired,
hideActivityModal: PropTypes.func.isRequired
}
export default ActivityModal

27
app/components/Activity/ActivityModal.scss

@ -0,0 +1,27 @@
@import '../../variables.scss';
.container {
position: relative;
height: 100vh;
background: $bluegrey;
}
.closeContainer {
text-align: right;
padding: 20px 40px 0px;
span {
cursor: pointer;
opacity: 1.0;
transition: 0.25s all;
&:hover {
opacity: 0.5;
}
}
svg {
color: $white;
}
}

94
app/components/Activity/Countdown.js

@ -0,0 +1,94 @@
import React from 'react'
import PropTypes from 'prop-types'
import styles from './Countdown.scss'
class Countdown extends React.Component {
constructor(props) {
super(props)
this.state = {
days: null,
hours: null,
minutes: null,
seconds: null,
expired: false,
interval: null
}
this.timerInterval = this.timerInterval.bind(this)
}
componentDidMount() {
const interval = setInterval(this.timerInterval, 1000)
// store interval in the state so it can be accessed later
this.setState({ interval })
}
componentWillUnmount() {
// use interval from the state to clear the interval
clearInterval(this.state.interval)
}
timerInterval() {
const convertTwoDigits = n => (n > 9 ? n : `0${n}`.slice(-2))
const now = new Date().getTime()
const distance = (this.props.countDownDate * 1000) - now
if (distance <= 0) {
this.setState({ expired: true })
clearInterval(this.state.interval)
return
}
const days = convertTwoDigits(Math.floor(distance / (1000 * 60 * 60 * 24)))
const hours = convertTwoDigits(Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)))
const minutes = convertTwoDigits(Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)))
const seconds = convertTwoDigits(Math.floor((distance % (1000 * 60)) / 1000))
this.setState({
days,
hours,
minutes,
seconds
})
}
render() {
const {
days,
hours,
minutes,
seconds,
expired
} = this.state
if (expired) { return <span className={`${styles.container} ${styles.expired}`}>Expired</span> }
if (!days && !hours && !minutes && !seconds) { return <span className={styles.container} /> }
return (
<span className={styles.container}>
<i className={styles.caption}>Expires in</i>
<i>
{days > 0 && `${days}:`}
</i>
<i>
{hours > 0 && `${hours}:`}
</i>
<i>
{minutes > 0 && `${minutes}:`}
</i>
<i>
{seconds >= 0 && `${seconds}`}
</i>
</span>
)
}
}
Countdown.propTypes = {
countDownDate: PropTypes.number.isRequired
}
export default Countdown

16
app/components/Activity/Countdown.scss

@ -0,0 +1,16 @@
@import '../../variables.scss';
.container {
display: block;
text-align: center;
font-size: 12px;
min-height: 12px;
&.expired {
color: $red;
}
}
.caption {
margin-right: 4px;
}

107
app/components/Activity/InvoiceModal.js

@ -0,0 +1,107 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import QRCode from 'qrcode.react'
import copy from 'copy-to-clipboard'
import { showNotification } from 'notifications'
import { FaAngleDown } from 'react-icons/lib/fa'
import Value from 'components/Value'
import Countdown from './Countdown'
import styles from './InvoiceModal.scss'
const InvoiceModal = ({
invoice,
ticker,
currentTicker,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
}) => {
const copyPaymentRequest = () => {
copy(invoice.payment_request)
showNotification('Noice', 'Successfully copied to clipboard')
}
const countDownDate = (parseInt(invoice.creation_date, 10) + parseInt(invoice.expiry, 10))
return (
<div className={styles.container}>
<div className={styles.content}>
<section className={styles.left}>
<h2>Payment Request</h2>
<QRCode
value={invoice.payment_request}
renderAs='svg'
size={150}
bgColor='transparent'
fgColor='white'
level='L'
className={styles.qrcode}
/>
<Countdown countDownDate={countDownDate} />
</section>
<section className={styles.right}>
<div className={styles.details}>
<section className={styles.amount}>
<h1>
<Value value={invoice.value} currency={ticker.currency} currentTicker={currentTicker} />
</h1>
<section className={styles.currentCurrency} onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}>
<span>{currencyName}</span><span><FaAngleDown /></span>
</section>
<ul className={showCurrencyFilters && styles.active}>
{
currentCurrencyFilters.map(filter =>
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>{filter.name}</li>)
}
</ul>
</section>
<section className={styles.date}>
<p>
<Moment format='MM/DD/YYYY'>{invoice.creation_date * 1000}</Moment>
</p>
<p className={styles.notPaid}>
{!invoice.settled && 'Not Paid'}
</p>
</section>
</div>
<div className={styles.memo}>
<h4>Memo</h4>
<p>{invoice.memo}</p>
</div>
<div className={styles.request}>
<h4>Request</h4>
<p>{invoice.payment_request}</p>
</div>
</section>
</div>
<div className={styles.actions}>
<div>Save as image</div>
<div onClick={copyPaymentRequest}>Copy Request</div>
</div>
</div>
)
}
InvoiceModal.propTypes = {
invoice: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
toggleCurrencyProps: PropTypes.object.isRequired
}
export default InvoiceModal

152
app/components/Activity/InvoiceModal.scss

@ -0,0 +1,152 @@
@import '../../variables.scss';
.container {
color: $white;
}
.content {
display: flex;
flex-direction: row;
align-items: center;
background: $spaceblue;
width: 85%;
margin: 50px auto;
padding: 30px 0;
.left {
width: 25%;
padding: 0 60px;
h2 {
text-align: center;
margin-bottom: 20px;
}
.qrcode {
margin-bottom: 20px;
}
}
.right {
width: 75%;
min-height: 220px;
border-left: 1px solid $spaceborder;
padding: 10px 60px;
.details {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 40px;
.amount {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
h1 {
font-size: 40px;
margin-right: 10px;
}
.currentCurrency {
cursor: pointer;
transition: 0.25s all;
&:hover {
opacity: 0.5;
}
span {
font-size: 14px;
&:nth-child(1) {
font-weight: bold;
}
}
}
ul {
visibility: hidden;
position: absolute;
top: 40px;
right: -50px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: #191919;
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid #0f0f0f;
&:hover {
background: #0f0f0f;
}
}
}
}
.date {
font-size: 12px;
text-align: right;
.notPaid {
color: #FF8A65;
margin-top: 5px;
}
}
}
.memo, .request {
h4 {
font-size: 10px;
margin-bottom: 10px;
}
p {
word-wrap: break-word;
max-width: 450px;
}
}
.memo {
margin-bottom: 40px;
p {
font-size: 20px;
}
}
.request p {
font-size: 10px;
max-width: 450px;
line-height: 1.5;
}
}
}
.actions {
display: flex;
flex-direction: row;
justify-content: center;
div {
text-align: center;
margin: 35px 10px;
width: 235px;
padding: 20px 10px;
background: #31343f;
cursor: pointer;
transition: 0.25s all;
&:hover {
background: darken(#31343f, 5%);
}
}
}

83
app/components/Activity/PaymentModal.js

@ -0,0 +1,83 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { FaAngleDown } from 'react-icons/lib/fa'
import Isvg from 'react-inlinesvg'
import paperPlane from 'icons/paper_plane.svg'
import zap from 'icons/zap.svg'
import Value from 'components/Value'
import styles from './PaymentModal.scss'
const PaymentModal = ({
payment,
ticker,
currentTicker,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
}) => (
<div className={styles.container}>
<header className={styles.header}>
<section>
<Isvg src={paperPlane} />
<span>Sent</span>
</section>
<section className={styles.details}>
<div>
<Isvg src={zap} />
<span className={styles.zap}>Lightning Network</span>
</div>
<div>
<Value value={payment.fee} currency={ticker.currency} currentTicker={currentTicker} />
<span> {currencyName} fee</span>
</div>
</section>
</header>
<div className={styles.amount}>
<h1>
<i className={`${styles.symbol} ${payment.value > 0 && styles.active}`}>-</i>
<Value value={payment.value} currency={ticker.currency} currentTicker={currentTicker} />
</h1>
<section className={styles.currentCurrency} onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}>
<span>{currencyName}</span><span><FaAngleDown /></span>
<ul className={showCurrencyFilters && styles.active}>
{
currentCurrencyFilters.map(filter =>
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>{filter.name}</li>)
}
</ul>
</section>
</div>
<div className={styles.date}>
<Moment format='LLL'>{payment.creation_date * 1000}</Moment>
</div>
<footer className={styles.footer}>
<p>{payment.payment_preimage}</p>
</footer>
</div>
)
PaymentModal.propTypes = {
payment: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
toggleCurrencyProps: PropTypes.object.isRequired
}
export default PaymentModal

126
app/components/Activity/PaymentModal.scss

@ -0,0 +1,126 @@
@import '../../variables.scss';
@import '../../variables.scss';
.container {
color: $white;
font-size: 12px;
width: 75%;
margin: 0 auto;
background: $spaceblue;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 20px;
section {
&:nth-child(1) {
font-size: 16px;
color: $green;
svg {
width: 16px;
height: 16px;
vertical-align: top;
fill: $green;
}
span:nth-child(2) {
margin-left: 5px;
}
}
&.details {
text-align: right;
div:nth-child(1) {
margin-bottom: 5px;
}
svg {
width: 12px;
height: 12px;
vertical-align: middle;
}
.zap {
margin-left: 5px;
cursor: pointer;
transition: all 0.25s;
}
}
}
}
.amount {
margin-top: 50px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 20px;
h1 {
font-size: 40px;
}
section {
font-size: 20px;
margin-left: 10px;
position: relative;
cursor: pointer;
&:hover {
span {
opacity: 0.5;
}
}
span {
transition: all 0.25s;
}
ul {
visibility: hidden;
position: absolute;
top: 40px;
right: -50px;
font-size: 12px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: #191919;
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid #0f0f0f;
&:hover {
background: #0f0f0f;
}
}
}
}
}
.date {
text-align: center;
padding: 20px;
}
.footer {
background: #31343f;
margin: 20px 0 50px 0;
padding: 20px;
text-align: center;
p {
cursor: pointer;
}
}

91
app/components/Activity/TransactionModal.js

@ -0,0 +1,91 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { FaAngleDown } from 'react-icons/lib/fa'
import Isvg from 'react-inlinesvg'
import paperPlane from 'icons/paper_plane.svg'
import link from 'icons/link.svg'
import { blockExplorer } from 'utils'
import Value from 'components/Value'
import styles from './TransactionModal.scss'
const TransactionModal = ({
transaction,
ticker,
currentTicker,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
}) => (
<div className={styles.container}>
<header className={styles.header}>
<section>
<Isvg src={paperPlane} />
<span>Sent</span>
</section>
<section className={styles.details}>
<div>
<Isvg src={link} />
<span className={styles.link} onClick={() => blockExplorer.showTransaction(transaction.tx_hash)}>On-Chain</span>
</div>
<div>
<Value value={transaction.total_fees} currency={ticker.currency} currentTicker={currentTicker} />
<span> {currencyName} fee</span>
</div>
</section>
</header>
<div className={styles.amount}>
<h1>
<i className={`${styles.symbol} ${transaction.amount > 0 && styles.active}`}>
{
transaction.amount > 0 ?
'+'
:
'-'
}
</i>
<Value value={transaction.amount} currency={ticker.currency} currentTicker={currentTicker} />
</h1>
<section className={styles.currentCurrency} onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}>
<span>{currencyName}</span><span><FaAngleDown /></span>
<ul className={showCurrencyFilters && styles.active}>
{
currentCurrencyFilters.map(filter =>
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>{filter.name}</li>)
}
</ul>
</section>
</div>
<div className={styles.date}>
<Moment format='LLL'>{transaction.time_stamp * 1000}</Moment>
</div>
<footer className={styles.footer}>
<p onClick={() => blockExplorer.showTransaction(transaction.tx_hash)}>{transaction.tx_hash}</p>
</footer>
</div>
)
TransactionModal.propTypes = {
transaction: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
toggleCurrencyProps: PropTypes.object.isRequired
}
export default TransactionModal

135
app/components/Activity/TransactionModal.scss

@ -0,0 +1,135 @@
@import '../../variables.scss';
.container {
color: $white;
font-size: 12px;
width: 75%;
margin: 0 auto;
background: $spaceblue;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 20px;
section {
&:nth-child(1) {
font-size: 16px;
color: $green;
svg {
width: 16px;
height: 16px;
vertical-align: top;
fill: $green;
}
span:nth-child(2) {
margin-left: 5px;
}
}
&.details {
text-align: right;
div:nth-child(1) {
margin-bottom: 5px;
}
svg {
width: 12px;
height: 12px;
vertical-align: middle;
}
.link {
text-decoration: underline;
margin-left: 5px;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.5;
}
}
}
}
}
.amount {
margin-top: 50px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 20px;
h1 {
font-size: 40px;
}
section {
font-size: 20px;
margin-left: 10px;
position: relative;
cursor: pointer;
&:hover {
span {
opacity: 0.5;
}
}
span {
transition: all 0.25s;
}
ul {
visibility: hidden;
position: absolute;
top: 40px;
right: -50px;
font-size: 12px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: #191919;
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid #0f0f0f;
&:hover {
background: #0f0f0f;
}
}
}
}
}
.date {
text-align: center;
margin: 20px 0 50px 0;
padding: 20px;
}
.footer {
background: #31343f;
padding: 20px;
text-align: center;
p {
text-decoration: underline;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.5;
}
}
}

26
app/components/Form/Form.js

@ -1,16 +1,17 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { MdClose } from 'react-icons/lib/md' import Isvg from 'react-inlinesvg'
import x from 'icons/x.svg'
import PayForm from './PayForm' import Pay from './Pay'
import RequestForm from './RequestForm' import Request from './Request'
import styles from './Form.scss' import styles from './Form.scss'
const FORM_TYPES = { const FORM_TYPES = {
PAY_FORM: PayForm, PAY_FORM: Pay,
REQUEST_FORM: RequestForm REQUEST_FORM: Request
} }
const Form = ({ formType, formProps, closeForm }) => { const Form = ({ formType, formProps, closeForm }) => {
@ -18,16 +19,13 @@ const Form = ({ formType, formProps, closeForm }) => {
const FormComponent = FORM_TYPES[formType] const FormComponent = FORM_TYPES[formType]
return ( return (
<div className={`${styles.outtercontainer} ${formType && styles.open}`}> <div className={`${styles.container} ${formType && styles.open}`}>
<div className={styles.innercontainer}> <div className={styles.closeContainer}>
<div className={styles.esc} onClick={closeForm}> <span onClick={closeForm}>
<MdClose /> <Isvg src={x} />
</div> </span>
<div className={styles.content}>
<FormComponent {...formProps} />
</div>
</div> </div>
<FormComponent {...formProps} />
</div> </div>
) )
} }

59
app/components/Form/Form.scss

@ -1,59 +1,26 @@
@import '../../variables.scss'; @import '../../variables.scss';
.outtercontainer { .container {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100vh;
background: $white;
z-index: 0;
opacity: 0;
transition: all 0.5s;
&.open {
opacity: 1;
z-index: 20;
}
}
.innercontainer {
position: relative; position: relative;
height: 100vh; height: 100vh;
margin: 5%; background: $spaceblue;
} }
.esc { .closeContainer {
position: absolute; text-align: right;
top: 0; padding: 20px 40px 0px;
right: 0;
color: $darkestgrey;
cursor: pointer;
padding: 20px;
border-radius: 50%;
&:hover { span {
color: $bluegrey; cursor: pointer;
background: $darkgrey; opacity: 1.0;
} transition: 0.25s all;
&:active { &:hover {
color: $white; opacity: 0.5;
background: $main; }
} }
svg { svg {
width: 32px; color: $white;
height: 32px;
} }
} }
.content {
width: 50%;
margin: 0 auto;
display: flex;
flex-direction: column;
height: 75vh;
justify-content: center;
align-items: center;
}

219
app/components/Form/Pay.js

@ -0,0 +1,219 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import find from 'lodash/find'
import Isvg from 'react-inlinesvg'
import paperPlane from 'icons/paper_plane.svg'
import link from 'icons/link.svg'
import { FaAngleDown } from 'react-icons/lib/fa'
import { btc } from 'utils'
import styles from './Pay.scss'
class Pay extends Component {
componentDidUpdate(prevProps) {
const {
isOnchain, isLn, payform: { payInput }, fetchInvoice
} = this.props
// If on-chain, focus on amount to let user know it's editable
if (isOnchain) { this.amountInput.focus() }
// If LN go retrieve invoice details
if ((prevProps.payform.payInput !== payInput) && isLn) {
fetchInvoice(payInput)
}
}
render() {
const {
payform: {
payInput,
showErrors,
invoice,
showCurrencyFilters
},
nodes,
ticker,
isOnchain,
isLn,
currentAmount,
usdAmount,
payFormIsValid: { errors, isValid },
currentCurrencyFilters,
currencyName,
setPayAmount,
onPayAmountBlur,
setPayInput,
onPayInputBlur,
setCurrencyFilters,
onPaySubmit,
setCurrency
} = this.props
const displayNodeName = (pubkey) => {
const node = find(nodes, n => n.pub_key === pubkey)
if (node && node.alias.length) { return node.alias }
return pubkey ? pubkey.substring(0, 10) : ''
}
const onCurrencyFilterClick = (currency) => {
if (!isLn) {
// change the input amount
setPayAmount(btc.convert(ticker.currency, currency, currentAmount))
}
setCurrency(currency)
setCurrencyFilters(false)
}
return (
<div className={styles.container}>
<header className={styles.header}>
<Isvg src={paperPlane} />
<h1>Make Payment</h1>
</header>
<div className={styles.content}>
<section className={styles.destination}>
<div className={styles.top}>
<label htmlFor='paymentRequest'>Destination</label>
<span className={`${styles.description} ${(isOnchain || isLn) && styles.active}`}>
{isOnchain &&
<i>
<Isvg src={link} />
<span>On-Chain (~10 minutes)</span>
</i>
}
{isLn &&
<i>
<span>
{displayNodeName(invoice.destination)} ({invoice.description})
</span>
</i>
}
</span>
</div>
<div className={styles.bottom}>
<textarea
type='text'
placeholder='Payment request or bitcoin address'
value={payInput}
onChange={event => setPayInput(event.target.value)}
onBlur={onPayInputBlur}
id='paymentRequest'
rows='2'
/>
<section className={`${styles.errorMessage} ${showErrors.payInput && styles.active}`}>
{showErrors.payInput &&
<span>{errors.payInput}</span>
}
</section>
</div>
</section>
<section className={styles.amount}>
<div className={styles.top}>
<label htmlFor='amount'>Amount</label>
<span />
</div>
<div className={styles.bottom}>
<input
type='number'
min='0'
ref={(input) => { this.amountInput = input }}
size=''
placeholder='0.00000000'
value={currentAmount || ''}
onChange={event => setPayAmount(event.target.value)}
onBlur={onPayAmountBlur}
id='amount'
readOnly={isLn}
/>
<div className={styles.currency}>
<section className={styles.currentCurrency} onClick={() => setCurrencyFilters(!showCurrencyFilters)}>
<span>{currencyName}</span><span><FaAngleDown /></span>
</section>
<ul className={showCurrencyFilters && styles.active}>
{
currentCurrencyFilters.map(filter =>
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>{filter.name}</li>)
}
</ul>
</div>
</div>
<div className={styles.usdAmount}>
{`${usdAmount || 0} USD`}
</div>
<section className={`${styles.errorMessage} ${styles.amount} ${showErrors.amount && styles.active}`}>
{showErrors.amount &&
<span>{errors.amount}</span>
}
</section>
</section>
<section className={styles.submit}>
<div className={`${styles.button} ${isValid && styles.active}`} onClick={onPaySubmit}>Pay</div>
</section>
</div>
</div>
)
}
}
Pay.propTypes = {
payform: PropTypes.shape({
amount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
payInput: PropTypes.string.isRequired,
invoice: PropTypes.object.isRequired,
showErrors: PropTypes.object.isRequired
}).isRequired,
currencyName: PropTypes.string.isRequired,
isOnchain: PropTypes.bool.isRequired,
isLn: PropTypes.bool.isRequired,
currentAmount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
usdAmount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
payFormIsValid: PropTypes.shape({
errors: PropTypes.object,
isValid: PropTypes.bool
}).isRequired,
setPayAmount: PropTypes.func.isRequired,
onPayAmountBlur: PropTypes.func.isRequired,
setPayInput: PropTypes.func.isRequired,
onPayInputBlur: PropTypes.func.isRequired,
fetchInvoice: PropTypes.func.isRequired,
onPaySubmit: PropTypes.func.isRequired,
setCurrencyFilters: PropTypes.func.isRequired,
setCurrency: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired,
nodes: PropTypes.array.isRequired,
currentCurrencyFilters: PropTypes.array.isRequired
}
export default Pay

182
app/components/Form/Pay.scss

@ -0,0 +1,182 @@
@import '../../variables.scss';
.container {
padding: 0 40px;
font-family: Roboto;
}
.header {
text-align: center;
padding-bottom: 20px;
color: $white;
border-bottom: 1px solid $spaceborder;
h1 {
font-size: 22px;
font-weight: 100;
margin-top: 10px;
letter-spacing: 1.5px;
}
}
.content {
margin-top: 50px;
color: $white;
.destination {
margin-bottom: 25px;
.description {
font-size: 12px;
line-height: 14px;
padding: 10px 15px;
min-height: 14px;
&.active {
background: #23252f;
border-radius: 10px;
min-height: 0;
}
}
svg {
width: 10px;
height: 10px;
line-height: 14px;
margin-right: 5px;
}
}
.amount .bottom {
display: flex;
flex-direction: row;
align-items: center;
input {
font-size: 40px;
max-width: 230px;
}
}
.top {
margin-bottom: 25px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
label {
font-size: 14px;
}
}
.bottom {
input, textarea {
background: transparent;
outline: none;
border: 0;
color: $gold;
-webkit-text-fill-color: $white;
font-size: 12px;
width: 100%;
font-weight: 200;
}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
text-shadow: none;
-webkit-text-fill-color: initial;
}
}
.currency {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
.currentCurrency {
cursor: pointer;
transition: 0.25s all;
&:hover {
opacity: 0.5;
}
span {
font-size: 14px;
&:nth-child(1) {
font-weight: bold;
}
}
}
ul {
visibility: hidden;
position: absolute;
top: 30px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: #191919;
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid #0f0f0f;
&:hover {
background: #0f0f0f;
}
}
}
}
.usdAmount {
margin-top: 20px;
opacity: 0.5;
}
.submit {
margin-top: 50px;
text-align: center;
.button {
width: 235px;
margin: 0 auto;
padding: 20px 10px;
background: #31343f;
opacity: 0.5;
cursor: pointer;
transition: 0.25s all;
&.active {
background: $gold;
opacity: 1.0;
&:hover {
background: darken($gold, 5%);
}
}
}
}
}
.errorMessage {
color: $red;
font-size: 12px;
min-height: 20px;
opacity: 0;
transition: all 0.25s ease;
&.amount {
margin-top: 10px;
}
&.active {
opacity: 1;
}
}

152
app/components/Form/PayForm.js

@ -1,152 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { FaBolt, FaChain } from 'react-icons/lib/fa'
import LoadingBolt from 'components/LoadingBolt'
import CurrencyIcon from 'components/CurrencyIcon'
import styles from './PayForm.scss'
class PayForm extends Component {
componentDidUpdate(prevProps) {
const {
isOnchain, isLn, payform: { payInput }, fetchInvoice
} = this.props
// If on-chain, focus on amount to let user know it's editable
if (isOnchain) { this.amountInput.focus() }
// If LN go retrieve invoice details
if ((prevProps.payform.payInput !== payInput) && isLn) {
fetchInvoice(payInput)
}
}
render() {
const {
payform: { amount, payInput, showErrors },
currency,
crypto,
isOnchain,
isLn,
currentAmount,
inputCaption,
showPayLoadingScreen,
payFormIsValid: { errors, isValid },
setPayAmount,
onPayAmountBlur,
setPayInput,
onPayInputBlur,
onPaySubmit
} = this.props
return (
<div className={styles.container}>
{showPayLoadingScreen && <LoadingBolt />}
<section className={`${styles.amountContainer} ${isLn ? styles.ln : ''} ${showErrors.amount && styles.error}`}>
<label htmlFor='amount'>
<CurrencyIcon currency={currency} crypto={crypto} />
</label>
<input
type='number'
min='0'
ref={(input) => { this.amountInput = input }}
size=''
style={
isLn ?
{ width: '75%', fontSize: '85px' }
:
{ width: `${amount.length > 1 ? (amount.length * 20) - 5 : 35}%`, fontSize: `${190 - (amount.length ** 2)}px` }
}
value={currentAmount}
onChange={event => setPayAmount(event.target.value)}
onBlur={onPayAmountBlur}
id='amount'
readOnly={isLn}
/>
</section>
<section className={`${styles.errorMessage} ${showErrors.amount && styles.active}`}>
{showErrors.amount &&
<span>{errors.amount}</span>
}
</section>
<div className={styles.inputContainer}>
<div className={styles.info}>
<span>{inputCaption}</span>
</div>
<aside className={styles.paymentIcon}>
{isOnchain &&
<i>
<span>on-chain</span>
<FaChain />
</i>
}
{isLn &&
<i>
<span>lightning network</span>
<FaBolt />
</i>
}
</aside>
<section className={`${styles.input} ${showErrors.payInput && styles.error}`}>
<input
type='text'
placeholder='Payment request or bitcoin address'
value={payInput}
onChange={event => setPayInput(event.target.value)}
onBlur={onPayInputBlur}
id='paymentRequest'
/>
</section>
<section className={`${styles.errorMessage} ${showErrors.payInput && styles.active}`}>
{showErrors.payInput &&
<span>{errors.payInput}</span>
}
</section>
</div>
<section className={styles.buttonGroup}>
<div className={`buttonPrimary ${styles.button} ${isValid && styles.active}`} onClick={onPaySubmit}>Pay</div>
</section>
</div>
)
}
}
PayForm.propTypes = {
payform: PropTypes.shape({
amount: PropTypes.string.isRequired,
payInput: PropTypes.string.isRequired,
showErrors: PropTypes.object.isRequired
}).isRequired,
currency: PropTypes.string.isRequired,
crypto: PropTypes.string.isRequired,
isOnchain: PropTypes.bool.isRequired,
isLn: PropTypes.bool.isRequired,
currentAmount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]).isRequired,
inputCaption: PropTypes.string.isRequired,
showPayLoadingScreen: PropTypes.bool.isRequired,
payFormIsValid: PropTypes.shape({
errors: PropTypes.object,
isValid: PropTypes.bool
}).isRequired,
setPayAmount: PropTypes.func.isRequired,
onPayAmountBlur: PropTypes.func.isRequired,
setPayInput: PropTypes.func.isRequired,
onPayInputBlur: PropTypes.func.isRequired,
fetchInvoice: PropTypes.func.isRequired,
onPaySubmit: PropTypes.func.isRequired
}
export default PayForm

166
app/components/Form/PayForm.scss

@ -1,166 +0,0 @@
@import '../../variables.scss';
.container {
margin: 0 auto;
display: flex;
flex-direction: column;
height: 75vh;
justify-content: center;
align-items: center;
}
.amountContainer {
position: relative;
color: $main;
display: flex;
justify-content: center;
min-height: 175px;
border-bottom: 1px solid transparent;
&.ln {
opacity: 0.75;
}
&.error {
border-color: $red;
}
label, input[type=number], input[type=text] {
color: inherit;
display: inline-block;
vertical-align: top;
padding: 0;
}
label {
svg {
width: 85px;
height: 85px;
}
svg[data-icon='ltc'] {
margin-right: 10px;
g {
transform: scale(1.75) translate(-5px, -5px);
}
}
}
input[type=number], input[type=text] {
width: 100px;
font-size: 180px;
border: none;
outline: 0;
-webkit-appearance: none;
font-weight: 200;
}
input[type=number] {
&::-webkit-inner-spin-button, &::-webkit-outer-spin-button {
-webkit-appearance: none;
}
}
}
.inputContainer {
position: relative;
width: 100%;
padding: 40px 0;
cursor: pointer;
.info {
margin-bottom: 10px;
min-height: 19px;
}
.paymentIcon {
position: absolute;
width: 20%;
left: calc(-12.5% - 75px);
top: 42px;
color: $main;
font-size: 50px;
text-align: center;
span {
text-transform: uppercase;
display: block;
font-size: 12px;
font-weight: 200;
}
}
}
.input {
display: flex;
justify-content: center;
font-size: 18px;
height: auto;
min-height: 55px;
border: 1px solid $traditionalgrey;
border-radius: 6px;
position: relative;
padding: 0 20px;
&.error {
border-color: $red;
}
label, input[type=number], input[type=text] {
font-size: inherit;
}
label {
padding-top: 19px;
padding-bottom: 12px;
color: $traditionalgrey;
}
input[type=text] {
width: 100%;
border: none;
outline: 0;
-webkit-appearance: none;
height: 55px;
padding: 0 10px;
}
}
.errorMessage {
margin: 10px 0;
min-height: 20px;
color: $red;
opacity: 0;
transition: all 0.25s ease;
&.active {
opacity: 1;
}
}
.buttonGroup {
width: 100%;
display: flex;
flex-direction: row;
border-radius: 6px;
overflow: hidden;
.button {
width: 100%;
margin-bottom: 20px;
font-weight: bold;
cursor: pointer;
text-transform: uppercase;
letter-spacing: .2px;
&:first-child {
border-right: 1px solid lighten($main, 20%);
}
&.active {
opacity: 1;
cursor: pointer;
}
}
}

124
app/components/Form/Request.js

@ -0,0 +1,124 @@
import React from 'react'
import PropTypes from 'prop-types'
import Isvg from 'react-inlinesvg'
import hand from 'icons/hand.svg'
import { FaAngleDown } from 'react-icons/lib/fa'
import { btc } from 'utils'
import styles from './Request.scss'
const Request = ({
requestform: { amount, memo, showCurrencyFilters },
ticker,
setRequestAmount,
setRequestMemo,
setCurrency,
setRequestCurrencyFilters,
currencyName,
requestUsdAmount,
currentCurrencyFilters,
onRequestSubmit
}) => {
const onCurrencyFilterClick = (currency) => {
// change the input amount
setRequestAmount(btc.convert(ticker.currency, currency, amount))
setCurrency(currency)
setRequestCurrencyFilters(false)
}
return (
<div className={styles.container}>
<header className={styles.header}>
<Isvg src={hand} />
<h1>Request Payment</h1>
</header>
<div className={styles.content}>
<section className={styles.memo}>
<div className={styles.top}>
<label htmlFor='memo'>Memo</label>
</div>
<div className={styles.bottom}>
<input
type='text'
placeholder='Dinner, Rent, etc'
value={memo}
onChange={event => setRequestMemo(event.target.value)}
id='memo'
/>
</div>
</section>
<section className={styles.amount}>
<div className={styles.top}>
<label htmlFor='amount'>Amount</label>
<span />
</div>
<div className={styles.bottom}>
<input
type='number'
value={amount || ''}
onChange={event => setRequestAmount(event.target.value)}
id='amount'
placeholder='0.00000000'
/>
<div className={styles.currency}>
<section className={styles.currentCurrency} onClick={() => setRequestCurrencyFilters(!showCurrencyFilters)}>
<span>{currencyName}</span><span><FaAngleDown /></span>
</section>
<ul className={showCurrencyFilters && styles.active}>
{
currentCurrencyFilters.map(filter =>
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>{filter.name}</li>)
}
</ul>
</div>
</div>
<div className={styles.usdAmount}>
{`${requestUsdAmount || 0} USD`}
</div>
</section>
<section className={styles.submit}>
<div className={`${styles.button} ${amount > 0 && styles.active}`} onClick={onRequestSubmit}>
Request
</div>
</section>
</div>
</div>
)
}
Request.propTypes = {
requestform: PropTypes.shape({
amount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
memo: PropTypes.string
}).isRequired,
requestUsdAmount: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
currencyName: PropTypes.string.isRequired,
currentCurrencyFilters: PropTypes.array.isRequired,
setRequestAmount: PropTypes.func.isRequired,
setRequestMemo: PropTypes.func.isRequired,
onRequestSubmit: PropTypes.func.isRequired,
setCurrency: PropTypes.func.isRequired,
setRequestCurrencyFilters: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired
}
export default Request

147
app/components/Form/Request.scss

@ -0,0 +1,147 @@
@import '../../variables.scss';
.container {
padding: 0 40px;
font-family: Roboto;
}
.header {
text-align: center;
padding-bottom: 20px;
color: $white;
border-bottom: 1px solid $spaceborder;
h1 {
font-size: 22px;
font-weight: 100;
margin-top: 10px;
letter-spacing: 1.5px;
}
}
.content {
margin-top: 50px;
color: $white;
.memo {
margin-bottom: 25px;
}
.amount .bottom {
display: flex;
flex-direction: row;
align-items: center;
input {
font-size: 40px;
max-width: 230px;
}
}
.top {
margin-bottom: 25px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
label {
font-size: 14px;
}
}
.bottom {
input {
background: transparent;
outline: none;
border: 0;
color: $gold;
-webkit-text-fill-color: $white;
font-size: 20px;
width: 100%;
font-weight: 200;
}
input::-webkit-input-placeholder {
text-shadow: none;
-webkit-text-fill-color: initial;
}
}
.currency {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
.currentCurrency {
cursor: pointer;
transition: 0.25s all;
&:hover {
opacity: 0.5;
}
span {
font-size: 14px;
&:nth-child(1) {
font-weight: bold;
}
}
}
ul {
visibility: hidden;
position: absolute;
top: 30px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: #191919;
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid #0f0f0f;
&:hover {
background: #0f0f0f;
}
}
}
}
.usdAmount {
margin-top: 20px;
opacity: 0.5;
}
.submit {
margin-top: 50px;
text-align: center;
.button {
width: 235px;
margin: 0 auto;
padding: 20px 10px;
background: #31343f;
opacity: 0.5;
cursor: pointer;
transition: 0.25s all;
&.active {
background: $gold;
opacity: 1.0;
&:hover {
background: darken($gold, 5%);
}
}
}
}
}

64
app/components/Form/RequestForm.js

@ -1,64 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import CurrencyIcon from 'components/CurrencyIcon'
import styles from './RequestForm.scss'
const RequestForm = ({
requestform: { amount, memo },
currency,
crypto,
setRequestAmount,
setRequestMemo,
onRequestSubmit
}) => (
<div className={styles.container}>
<section className={styles.amountContainer}>
<label htmlFor='amount'>
<CurrencyIcon currency={currency} crypto={crypto} />
</label>
<input
type='text'
size=''
style={{ width: `${amount.length > 1 ? (amount.length * 15) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` }}
value={amount}
onChange={event => setRequestAmount(event.target.value)}
id='amount'
/>
</section>
<section className={styles.inputContainer}>
<label htmlFor='memo'>
Request:
</label>
<input
type='text'
placeholder='Dinner, Rent, etc'
value={memo}
onChange={event => setRequestMemo(event.target.value)}
id='memo'
/>
</section>
<section className={styles.buttonGroup}>
<div className={`buttonPrimary ${styles.button}`} onClick={onRequestSubmit}>
Request
</div>
</section>
</div>
)
RequestForm.propTypes = {
requestform: PropTypes.shape({
amount: PropTypes.string.isRequired,
memo: PropTypes.string
}).isRequired,
currency: PropTypes.string.isRequired,
crypto: PropTypes.string.isRequired,
setRequestAmount: PropTypes.func.isRequired,
setRequestMemo: PropTypes.func.isRequired,
onRequestSubmit: PropTypes.func.isRequired
}
export default RequestForm

103
app/components/Form/RequestForm.scss

@ -1,103 +0,0 @@
@import '../../variables.scss';
.container {
margin: 0 auto;
display: flex;
flex-direction: column;
height: 75vh;
justify-content: center;
align-items: center;
}
.amountContainer {
color: $main;
display: flex;
justify-content: center;
min-height: 120px;
margin-bottom: 20px;
label, input[type=text] {
color: inherit;
display: inline-block;
vertical-align: top;
padding: 0;
}
label {
svg {
width: 85px;
height: 85px;
}
svg[data-icon='ltc'] {
margin-right: 10px;
g {
transform: scale(1.75) translate(-5px, -5px);
}
}
}
input[type=text] {
width: 100px;
font-size: 180px;
border: none;
outline: 0;
-webkit-appearance: none;
font-weight: 200;
}
}
.inputContainer {
width: 100%;
display: flex;
justify-content: center;
font-size: 18px;
height: auto;
min-height: 55px;
margin-bottom: 20px;
border: 1px solid $traditionalgrey;
border-radius: 6px;
position: relative;
padding: 0 20px;
label, input[type=text] {
font-size: inherit;
}
label {
padding-top: 19px;
padding-bottom: 12px;
color: $traditionalgrey;
}
input[type=text] {
width: 100%;
border: none;
outline: 0;
-webkit-appearance: none;
height: 55px;
padding: 0 10px;
}
}
.buttonGroup {
width: 100%;
display: flex;
flex-direction: row;
border-radius: 6px;
overflow: hidden;
.button {
width: 100%;
margin-bottom: 20px;
font-weight: bold;
cursor: pointer;
text-transform: uppercase;
letter-spacing: .2px;
&:first-child {
border-right: 1px solid lighten($main, 20%);
}
}
}

5
app/components/ModalRoot/ModalRoot.scss

@ -7,13 +7,14 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 10; z-index: 10;
background: #fff; background: $spaceblue;
} }
.content { .content {
position: relative; position: relative;
height: 100vh; height: 100vh;
margin: 5%; margin: 5%;
color: $white;
} }
.esc { .esc {
@ -32,7 +33,7 @@
&:active { &:active {
color: $white; color: $white;
background: $main; background: $gold;
} }
svg { svg {

7
app/components/ModalRoot/SuccessfulSendPayment.scss

@ -24,7 +24,7 @@
.button { .button {
text-align: center; text-align: center;
border-radius: 8px; border-radius: 8px;
background: $main; background: #31343f;
padding: 20px 10px; padding: 20px 10px;
font-weight: bold; font-weight: bold;
cursor: pointer; cursor: pointer;
@ -33,5 +33,10 @@
color: $white; color: $white;
width: 15%; width: 15%;
margin: 0 auto; margin: 0 auto;
transition: all 0.25s;
&:hover {
background: darken(#31343f, 5%);
}
} }
} }

4
app/components/ModalRoot/WalletDetails.js

@ -25,9 +25,7 @@ const WalletDetails = ({ info, address }) => (
</div> </div>
<div className={styles.right}> <div className={styles.right}>
<section> <section>
<h2> <h2>Network</h2>
Network
</h2>
</section> </section>
</div> </div>
</div> </div>

8
app/components/Onboarding/FormContainer.js

@ -7,7 +7,13 @@ import { FaAngleLeft, FaAngleRight } from 'react-icons/lib/fa'
import zapLogo from 'icons/zap_logo.svg' import zapLogo from 'icons/zap_logo.svg'
import styles from './FormContainer.scss' import styles from './FormContainer.scss'
const FormContainer = ({ title, description, back, next, children }) => ( const FormContainer = ({
title,
description,
back,
next,
children
}) => (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.titleBar} /> <div className={styles.titleBar} />

8
app/components/Onboarding/Onboarding.js

@ -24,8 +24,8 @@ const Onboarding = ({
case 1: case 1:
return ( return (
<FormContainer <FormContainer
title={'1. What should we call you?'} title='1. What should we call you?'
description={'Set your nickname to help others connect with you on the Lightning Network'} description='Set your nickname to help others connect with you on the Lightning Network'
back={null} back={null}
next={() => changeStep(2)} next={() => changeStep(2)}
> >
@ -35,8 +35,8 @@ const Onboarding = ({
case 2: case 2:
return ( return (
<FormContainer <FormContainer
title={'2. Autopilot'} title='2. Autopilot'
description={'Autopilot is an automatic network manager. Instead of manually adding people to build your network to make payments, enable autopilot to automatically connect you to the Lightning Network using 60% of your balance.'} // eslint-disable-line description='Autopilot is an automatic network manager. Instead of manually adding people to build your network to make payments, enable autopilot to automatically connect you to the Lightning Network using 60% of your balance.' // eslint-disable-line
back={() => changeStep(1)} back={() => changeStep(1)}
next={() => submit(alias, autopilot)} next={() => submit(alias, autopilot)}
> >

4
app/components/Onboarding/Syncing.js

@ -24,9 +24,9 @@ class Syncing extends Component {
<section className={styles.progressContainer}> <section className={styles.progressContainer}>
<h1>Syncing to the blockchain...</h1> <h1>Syncing to the blockchain...</h1>
<div className={styles.progressBar}> <div className={styles.progressBar}>
<div className={styles.progress} style={{ width: isNaN(syncPercentage) ? 0 : `${syncPercentage}%` }} /> <div className={styles.progress} style={{ width: Number.isNaN(syncPercentage) ? 0 : `${syncPercentage}%` }} />
</div> </div>
<h4>{isNaN(parseInt(syncPercentage, 10)) || syncPercentage.toString().length === 0 ? '' : `${syncPercentage}%`}</h4> <h4>{Number.isNaN(parseInt(syncPercentage, 10)) || syncPercentage.toString().length === 0 ? '' : `${syncPercentage}%`}</h4>
</section> </section>
</div> </div>
</div> </div>

22
app/components/Value/Value.js

@ -0,0 +1,22 @@
import React from 'react'
import PropTypes from 'prop-types'
import { btc } from 'utils'
const Value = ({ value, currency, currentTicker }) => {
if (currency === 'sats') { return <i>{value > 0 ? value : value * -1}</i> }
return (
<i>{btc.convert('sats', currency, value, currentTicker.price_usd)}</i>
)
}
Value.propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
currency: PropTypes.string.isRequired,
currentTicker: PropTypes.object.isRequired
}
export default Value

0
app/components/Value/Value.scss

3
app/components/Value/index.js

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

168
app/components/Wallet/ReceiveModal.js

@ -1,103 +1,111 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import QRCode from 'qrcode.react' import QRCode from 'qrcode.react'
import copyIcon from 'icons/copy.svg'
import Isvg from 'react-inlinesvg'
import x from 'icons/x.svg'
import { showNotification } from 'notifications' import { showNotification } from 'notifications'
import { FaCopy } from 'react-icons/lib/fa'
import { MdClose } from 'react-icons/lib/md'
import styles from './ReceiveModal.scss' import styles from './ReceiveModal.scss'
const ReceiveModal = ({ class ReceiveModal extends React.Component {
isOpen, hideActivityModal, pubkey, address, newAddress, qrCodeType, changeQrCode constructor(props) {
}) => { super(props)
const customStyles = {
overlay: { this.state = {
cursor: 'pointer' qrCodeType: 1
},
content: {
top: 'auto',
left: '0',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
borderRadius: 'none',
padding: '0'
} }
} }
const copyOnClick = (data) => { render() {
copy(data) const copyOnClick = (data) => {
showNotification('Noice', 'Successfully copied to clipboard') copy(data)
} showNotification('Noice', 'Successfully copied to clipboard')
}
return ( const changeQrCode = () => {
<ReactModal if (this.state.qrCodeType === 1) {
isOpen={isOpen} this.setState({ qrCodeType: 2 })
ariaHideApp } else {
shouldCloseOnOverlayClick this.setState({ qrCodeType: 1 })
contentLabel='No Overlay Click Modal' }
onRequestClose={() => hideActivityModal()} }
parentSelector={() => document.body}
style={customStyles} const {
> isOpen,
<div className={styles.closeContainer}> pubkey,
<span onClick={() => hideActivityModal()}> address,
<MdClose /> closeReceiveModal
</span> } = this.props
</div>
const { qrCodeType } = this.state
if (!isOpen) { return null }
return (
<div className={styles.container}> <div className={styles.container}>
<header> <div className={styles.closeContainer}>
<div className={styles.qrcodes}> <span onClick={closeReceiveModal}>
<QRCode value={qrCodeType === 1 ? address : pubkey} /> <Isvg src={x} />
</div> </span>
</div>
<ul className={styles.tabs}>
<li className={qrCodeType === 1 && styles.active} onClick={changeQrCode}> <div className={styles.content}>
Wallet address <section className={styles.left}>
</li> <header className={styles.header}>
<li className={qrCodeType === 2 && styles.active} onClick={changeQrCode}> <h2>JimmyMow</h2>
Node pubkey
</li> <div className={styles.qrCodeOptions}>
</ul> <div className={qrCodeType === 1 && styles.active} onClick={changeQrCode}>Node Pubkey</div>
</header> <div className={qrCodeType === 2 && styles.active} onClick={changeQrCode}>Deposit Address</div>
<section> </div>
<div className={styles.addressHeader}> </header>
<h4>Deposit Address</h4>
<span className={styles.newAddress} onClick={() => newAddress('np2wkh')}>New Address</span> <div className={styles.qrCodeContainer}>
</div> <QRCode
<p> value={qrCodeType === 1 ? pubkey : address}
<span>{address}</span> renderAs='svg'
<span onClick={() => copyOnClick(address)} className='hint--left' data-hint='Copy address'> size={150}
<FaCopy /> bgColor='transparent'
</span> fgColor='white'
</p> level='L'
</section> />
</div>
<section> </section>
<h4>Node Public Key</h4> <section className={styles.right}>
<p> <div className={styles.pubkey}>
<span>{pubkey}</span> <h4>Node Public Key</h4>
<span onClick={() => copyOnClick(pubkey)} className='hint--left' data-hint='Copy pubkey'> <p>
<FaCopy /> <span className={styles.data}>{pubkey}</span>
</span> <span onClick={() => copyOnClick(pubkey)} className={`${styles.copy} hint--left`} data-hint='Copy pubkey'>
</p> <Isvg src={copyIcon} />
</section> </span>
</p>
</div>
<div className={styles.address}>
<h4>Deposit Address</h4>
<p>
<span className={styles.data}>{address}</span>
<span onClick={() => copyOnClick(address)} className={`${styles.copy} hint--left`} data-hint='Copy address'>
<Isvg src={copyIcon} />
</span>
</p>
</div>
</section>
</div>
</div> </div>
</ReactModal> )
) }
} }
ReceiveModal.propTypes = { ReceiveModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
hideActivityModal: PropTypes.func.isRequired,
pubkey: PropTypes.string.isRequired, pubkey: PropTypes.string.isRequired,
address: PropTypes.string.isRequired, address: PropTypes.string.isRequired,
newAddress: PropTypes.func.isRequired, closeReceiveModal: PropTypes.func.isRequired
changeQrCode: PropTypes.func.isRequired,
qrCodeType: PropTypes.number.isRequired
} }
export default ReceiveModal export default ReceiveModal

151
app/components/Wallet/ReceiveModal.scss

@ -1,106 +1,129 @@
@import '../../variables.scss'; @import '../../variables.scss';
.container {
position: relative;
height: 100vh;
background: $bluegrey;
}
.closeContainer { .closeContainer {
background: $lightgrey;
text-align: right; text-align: right;
padding: 10px; padding: 20px 40px 0px;
span { span {
color: $darkestgrey;
font-size: 20px;
cursor: pointer; cursor: pointer;
} opacity: 1.0;
} transition: 0.25s all;
.container { &:hover {
header { opacity: 0.5;
background: $lightgrey;
padding: 10px 40px 40px;
text-align: center;
.qrcodes {
text-align: center;
margin-top: 20px;
} }
}
.tabs { svg {
display: flex; color: $white;
flex-direction: row; }
align-items: center; }
justify-content: center;
margin-top: 20px;
li {
margin: 0 20px;
color: $darkestgrey;
transition: all 0.25s;
&:hover { .content {
color: $black; display: flex;
} flex-direction: row;
align-items: center;
background: $spaceblue;
width: 85%;
margin: 10% auto 50px auto;
color: $white;
.left {
width: 25%;
padding: 30px 40px;
.header {
h2 {
text-align: center;
}
&.active { .qrCodeOptions {
color: $black; display: flex;
font-weight: bold; flex-direction: row;
justify-content: space-around;
margin: 20px 0;
div {
font-size: 10px;
opacity: 0.5;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.75;
}
&.active {
opacity: 1.0;
}
} }
} }
} }
.qrCodeContainer {
text-align: center;
}
} }
section { .right {
margin: 25px 0; width: 75%;
padding: 25px; min-height: 220px;
border-left: 1px solid $spaceborder;
padding: 30px 40px;
h4 { .pubkey, .address {
font-size: 14px; padding: 25px;
font-weight: bold;
letter-spacing: 1.5px;
margin-bottom: 10px;
span { h4 {
color: $blue; font-size: 12px;
cursor: pointer; margin-bottom: 10px;
span {
cursor: pointer;
}
} }
} }
p { p {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center;
font-family: 'Roboto'; font-family: 'Roboto';
font-size: 14px; font-size: 10px;
font-weight: 200; font-weight: 200;
background: $lightgrey; background: $bluegrey;
span { .data, .copy {
padding: 15px; padding: 15px;
} }
span:nth-child(1) { .data {
flex: 9; flex: 9;
overflow-x: scroll; overflow-x: scroll;
} }
span:nth-child(2) { .copy {
background: $darkgrey; background: #383b47;
color: $black; color: $white;
cursor: pointer; cursor: pointer;
transition: all 0.25s; transition: all 0.25s;
&:hover { &:hover {
background: $darkestgrey; background: lighten(#383b47, 5%);
} }
}
}
}
}
.addressHeader { svg {
display: flex; height: 12px;
flex-direction: row; width: 12px;
justify-content: space-between; }
}
.newAddress { }
text-decoration: underline;
font-size: 12px;
} }
} }

160
app/components/Wallet/Wallet.js

@ -1,111 +1,105 @@
import React, { Component } from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { FaAngleDown } from 'react-icons/lib/fa' import { FaAngleDown } from 'react-icons/lib/fa'
import Isvg from 'react-inlinesvg' import Isvg from 'react-inlinesvg'
import { btc } from 'utils' import { btc } from 'utils'
import Value from 'components/Value'
import AnimatedCheckmark from 'components/AnimatedCheckmark'
import bitcoinIcon from 'icons/bitcoin.svg' import bitcoinIcon from 'icons/bitcoin.svg'
import zapLogo from 'icons/zap_logo.svg' import zapLogo from 'icons/zap_logo.svg'
import qrCode from 'icons/qrcode.svg' import qrCode from 'icons/qrcode.svg'
import ReceiveModal from './ReceiveModal'
import styles from './Wallet.scss' import styles from './Wallet.scss'
class Wallet extends Component { const Wallet = ({
constructor(props) { balance,
super(props) info,
openReceiveModal,
this.state = { ticker,
modalOpen: false, currentTicker,
qrCodeType: 1 openPayForm,
} openRequestForm,
} showPayLoadingScreen,
showSuccessPayScreen
render() { }) => {
const { const usdAmount = btc.satoshisToUsd((parseInt(balance.walletBalance, 10) + parseInt(balance.channelBalance, 10)), currentTicker.price_usd)
balance,
address,
info,
newAddress,
currentTicker,
openPayForm,
openRequestForm
} = this.props
const { modalOpen, qrCodeType } = this.state
const usdAmount = btc.satoshisToUsd((parseInt(balance.walletBalance, 10) + parseInt(balance.channelBalance, 10)), currentTicker.price_usd)
const changeQrCode = () => {
const qrCodeNum = this.state.qrCodeType === 1 ? 2 : 1
this.setState({ qrCodeType: qrCodeNum }) return (
} <div className={styles.wallet}>
<div className={styles.content}>
<header className={styles.header}>
<section className={styles.logo}>
<Isvg className={styles.bitcoinLogo} src={zapLogo} />
</section>
return ( <section className={styles.user}>
<div className={styles.wallet}> <div>
{ <span>{info.data.alias}</span>
( <FaAngleDown />
modalOpen && </div>
<ReceiveModal </section>
isOpen={modalOpen} </header>
hideActivityModal={() => this.setState({ modalOpen: false })}
pubkey={info.data.identity_pubkey}
address={address}
newAddress={newAddress}
qrCodeType={qrCodeType}
changeQrCode={changeQrCode}
/>
)
}
<div className={styles.content}>
<header className={styles.header}>
<section className={styles.logo}>
<Isvg className={styles.bitcoinLogo} src={zapLogo} />
</section>
<section className={styles.user}>
<div>
<span>{info.data.alias}</span>
<FaAngleDown />
</div>
</section>
</header>
<div className={styles.left}> <div className={styles.left}>
<div className={styles.leftContent}> <div className={styles.leftContent}>
<Isvg className={styles.bitcoinLogo} src={bitcoinIcon} /> <Isvg className={styles.bitcoinLogo} src={bitcoinIcon} />
<div className={styles.details}> <div className={styles.details}>
<h1> <h1>
<span> <span>
{btc.satoshisToBtc(parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance))}BTC <Value
</span> value={parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance)}
<span onClick={() => this.setState({ modalOpen: true })}> currency={ticker.currency}
<Isvg className={styles.bitcoinLogo} src={qrCode} /> currentTicker={currentTicker}
</span> />
</h1> <i className={styles.currency}>{btc.renderCurrency(ticker.currency)}</i>
<span className={styles.usdValue}> ${usdAmount ? usdAmount.toLocaleString() : ''}</span> </span>
</div> <span onClick={openReceiveModal}>
<Isvg className={styles.bitcoinLogo} src={qrCode} />
</span>
</h1>
<span className={styles.usdValue}> ${usdAmount ? usdAmount.toLocaleString() : ''}</span>
</div> </div>
</div> </div>
<div className={styles.right}> </div>
<div className={styles.rightContent}> <div className={styles.right}>
<div className={styles.pay} onClick={openPayForm}>Pay</div> <div className={styles.rightContent}>
<div className={styles.request} onClick={openRequestForm}>Request</div> <div className={styles.pay} onClick={openPayForm}>Pay</div>
</div> <div className={styles.request} onClick={openRequestForm}>Request</div>
</div>
<div className={styles.notificationBox}>
{
showPayLoadingScreen &&
<span>
<section className={`${styles.spinner} ${styles.icon}`} />
<section>Sending your lightning payment...</section>
</span>
}
{
showSuccessPayScreen &&
<span>
<section className={styles.icon}><AnimatedCheckmark /></section>
<section>Successfully sent payment</section>
</span>
}
</div> </div>
</div> </div>
</div> </div>
) </div>
} )
} }
Wallet.propTypes = { Wallet.propTypes = {
balance: PropTypes.object.isRequired, balance: PropTypes.object.isRequired,
address: PropTypes.string.isRequired,
info: PropTypes.object.isRequired, info: PropTypes.object.isRequired,
newAddress: PropTypes.func.isRequired, ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,
openPayForm: PropTypes.func.isRequired, openPayForm: PropTypes.func.isRequired,
openRequestForm: PropTypes.func.isRequired openRequestForm: PropTypes.func.isRequired,
openReceiveModal: PropTypes.func.isRequired,
showPayLoadingScreen: PropTypes.bool.isRequired,
showSuccessPayScreen: PropTypes.bool.isRequired
} }
export default Wallet export default Wallet

81
app/components/Wallet/Wallet.scss

@ -79,12 +79,33 @@
opacity: 0.5; opacity: 0.5;
} }
} }
.currency {
margin-left: 2.5px;
}
} }
.usdValue { .tickerButtons {
font-size: 12px; display: flex;
margin-left: 10px; flex-direction: row;
font-style: italic;
section {
margin: 5px;
font-size: 10px;
border-radius: 5px;
border: 1px solid $white;
padding: 5px 10px;
cursor: pointer;
opacity: 0.5;
transition: 0.25s all;
&.active {
background: $main;
color: $spaceborder;
border-color: $spaceborder;
opacity: 1;
}
}
} }
} }
@ -120,6 +141,58 @@
margin-right: 20px; margin-right: 20px;
} }
} }
} }
.notificationBox {
text-align: right;
font-size: 12px;
section {
display: inline-block;
vertical-align: middle;
transition: all 0.25s;
}
.spinner {
height: 20px;
width: 20px;
border: 1px solid rgba(235, 184, 100, 0.1);
border-left-color: rgba(235, 184, 100, 0.4);
-webkit-border-radius: 999px;
-moz-border-radius: 999px;
border-radius: 999px;
-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;
}
.icon {
margin-right: 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);
}
}

1
app/icons/copy.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>

After

Width:  |  Height:  |  Size: 351 B

16
app/icons/hand.svg

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="14px" viewBox="0 0 32 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>Group 20 Copy</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Generate-Request-Invoice" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-496.000000, -77.000000)">
<g id="Group-9" transform="translate(423.000000, 78.000000)" stroke="#FFFFFF" stroke-width="0.75">
<g id="Group-20-Copy" transform="translate(74.000000, 0.000000)">
<path d="M6,1.07778574 C10.0254503,0.706033518 13.3396626,1.66678494 15.9426369,3.96004002 C17.6659089,3.96004002 19.2069713,3.96004002 20.5658241,3.96004002 C22.6041032,3.96004002 22.3477222,7 20.5658241,7 C19.377892,7 16.9999136,7 13.4318889,7" id="Path-5"></path>
<path d="M2.10931173,9.9245745 L6,9.9245745 L6,0 L0,0 C0.208329331,5.0351144 0.965722589,8.36844774 2.27217977,10" id="Path-2"></path>
<path d="M6,8.84278961 C11.4341037,10.9475965 15.0785314,12 16.933283,12 C19.7154105,12 27.7492176,7.1560437 29.2844267,6.15768359 C30.8196358,5.15932348 29.5244956,4 28.7375913,4 C28.2129884,4 25.9404234,4.75236148 21.9198962,6.25708445" id="Path-4"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

1
app/icons/link.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>

After

Width:  |  Height:  |  Size: 371 B

13
app/icons/paper_plane.svg

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>send</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Pay-(LN)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-502.000000, -68.000000)" stroke-linecap="round" stroke-linejoin="round">
<g id="send" transform="translate(502.000000, 69.000000)" stroke="#FFFFFF" stroke-width="0.75">
<path d="M20.5,0.5 L9.5,11.5" id="Shape"></path>
<polygon id="Shape" points="21 0 13.65 21 9.45 11.55 0 7.35"></polygon>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 803 B

1
app/icons/x.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>

After

Width:  |  Height:  |  Size: 299 B

1
app/icons/zap.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-zap"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>

After

Width:  |  Height:  |  Size: 282 B

3
app/lnd/lib/rpc.proto

@ -1348,6 +1348,9 @@ message Payment {
/// The fee paid for this payment in satoshis /// The fee paid for this payment in satoshis
int64 fee = 5 [json_name = "fee"]; int64 fee = 5 [json_name = "fee"];
/// The payment preimage
string payment_preimage = 6 [json_name = "payment_preimage"];
} }
message ListPaymentsRequest { message ListPaymentsRequest {

7
app/lnd/methods/index.js

@ -47,11 +47,8 @@ export default function (lnd, meta, event, msg, data) {
networkController.queryRoutes(lnd, meta, { networkController.queryRoutes(lnd, meta, {
pubkey: invoiceData.destination, pubkey: invoiceData.destination,
amount: invoiceData.num_satoshis amount: invoiceData.num_satoshis
}) }))
) .then(routes => event.sender.send('receiveInvoiceAndQueryRoutes', routes))
.then(routes =>
event.sender.send('receiveInvoiceAndQueryRoutes', routes)
)
.catch(error => console.log('getInvoiceAndQueryRoutes invoice error: ', error)) .catch(error => console.log('getInvoiceAndQueryRoutes invoice error: ', error))
break break
case 'newaddress': case 'newaddress':

1
app/lnd/methods/paymentsController.js

@ -14,6 +14,7 @@ export function sendPaymentSync(lnd, meta, { paymentRequest }) {
if (!data || !data.payment_route) { reject({ error: data.payment_error }) } if (!data || !data.payment_route) { reject({ error: data.payment_error }) }
console.log('data: ', data)
resolve(data) resolve(data)
}) })
}) })

16
app/reducers/activity.js

@ -15,7 +15,8 @@ const initialState = {
], ],
modal: { modal: {
modalType: null, modalType: null,
modalProps: {} modalProps: {},
showCurrencyFilters: false
}, },
searchText: '' searchText: ''
} }
@ -30,6 +31,8 @@ export const CHANGE_FILTER = 'CHANGE_FILTER'
export const TOGGLE_PULLDOWN = 'TOGGLE_PULLDOWN' export const TOGGLE_PULLDOWN = 'TOGGLE_PULLDOWN'
export const SET_ACTIVITY_MODAL_CURRENCY_FILTERS = 'SET_ACTIVITY_MODAL_CURRENCY_FILTERS'
export const UPDATE_SEARCH_TEXT = 'UPDATE_SEARCH_TEXT' export const UPDATE_SEARCH_TEXT = 'UPDATE_SEARCH_TEXT'
// ------------------------------------ // ------------------------------------
@ -69,6 +72,13 @@ export function updateSearchText(searchText) {
} }
} }
export function setActivityModalCurrencyFilters(showCurrencyFilters) {
return {
type: SET_ACTIVITY_MODAL_CURRENCY_FILTERS,
showCurrencyFilters
}
}
// ------------------------------------ // ------------------------------------
// Action Handlers // Action Handlers
// ------------------------------------ // ------------------------------------
@ -78,6 +88,10 @@ const ACTION_HANDLERS = {
[CHANGE_FILTER]: (state, { filter }) => ({ ...state, filter, filterPulldown: false }), [CHANGE_FILTER]: (state, { filter }) => ({ ...state, filter, filterPulldown: false }),
[TOGGLE_PULLDOWN]: state => ({ ...state, filterPulldown: !state.filterPulldown }), [TOGGLE_PULLDOWN]: state => ({ ...state, filterPulldown: !state.filterPulldown }),
[SET_ACTIVITY_MODAL_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => (
{ ...state, modal: { modalType: state.modal.modalType, modalProps: state.modal.modalProps, showCurrencyFilters } }
),
[UPDATE_SEARCH_TEXT]: (state, { searchText }) => ({ ...state, searchText }) [UPDATE_SEARCH_TEXT]: (state, { searchText }) => ({ ...state, searchText })
} }

23
app/reducers/address.js

@ -5,6 +5,9 @@ import { ipcRenderer } from 'electron'
export const GET_ADDRESS = 'GET_ADDRESS' export const GET_ADDRESS = 'GET_ADDRESS'
export const RECEIVE_ADDRESS = 'RECEIVE_ADDRESS' export const RECEIVE_ADDRESS = 'RECEIVE_ADDRESS'
export const OPEN_WALLET_MODAL = 'OPEN_WALLET_MODAL'
export const CLOSE_WALLET_MODAL = 'CLOSE_WALLET_MODAL'
// LND expects types to be sent as int, so this object will allow mapping from string to int // LND expects types to be sent as int, so this object will allow mapping from string to int
const addressTypes = { const addressTypes = {
p2wkh: 0, p2wkh: 0,
@ -21,6 +24,18 @@ export function getAddress() {
} }
} }
export function openWalletModal() {
return {
type: OPEN_WALLET_MODAL
}
}
export function closeWalletModal() {
return {
type: CLOSE_WALLET_MODAL
}
}
// Send IPC event for getinfo // Send IPC event for getinfo
export const newAddress = type => async (dispatch) => { export const newAddress = type => async (dispatch) => {
dispatch(getAddress()) dispatch(getAddress())
@ -35,7 +50,10 @@ export const receiveAddress = (event, address) => dispatch => dispatch({ type: R
// ------------------------------------ // ------------------------------------
const ACTION_HANDLERS = { const ACTION_HANDLERS = {
[GET_ADDRESS]: state => ({ ...state, addressLoading: true }), [GET_ADDRESS]: state => ({ ...state, addressLoading: true }),
[RECEIVE_ADDRESS]: (state, { address }) => ({ ...state, addressLoading: false, address }) [RECEIVE_ADDRESS]: (state, { address }) => ({ ...state, addressLoading: false, address }),
[OPEN_WALLET_MODAL]: state => ({ ...state, walletModal: true }),
[CLOSE_WALLET_MODAL]: state => ({ ...state, walletModal: false })
} }
// ------------------------------------ // ------------------------------------
@ -43,7 +61,8 @@ const ACTION_HANDLERS = {
// ------------------------------------ // ------------------------------------
const initialState = { const initialState = {
addressLoading: false, addressLoading: false,
address: '' address: '',
walletModal: false
} }
export default function addressReducer(state = initialState, action) { export default function addressReducer(state = initialState, action) {

14
app/reducers/invoice.js

@ -10,7 +10,7 @@ import { resetRequestForm } from './requestform'
import { setError } from './error' import { setError } from './error'
import { showNotification } from '../notifications' import { showNotification } from '../notifications'
import { btc, usd } from '../utils' import { btc } from '../utils'
// ------------------------------------ // ------------------------------------
// Constants // Constants
// ------------------------------------ // ------------------------------------
@ -96,8 +96,10 @@ export const fetchInvoices = () => (dispatch) => {
export const receiveInvoices = (event, { invoices }) => dispatch => dispatch({ type: RECEIVE_INVOICES, invoices }) export const receiveInvoices = (event, { invoices }) => dispatch => dispatch({ type: RECEIVE_INVOICES, invoices })
// Send IPC event for creating an invoice // Send IPC event for creating an invoice
export const createInvoice = (amount, memo, currency, rate) => (dispatch) => { export const createInvoice = (amount, memo, currency) => (dispatch) => {
const value = currency === 'btc' ? btc.btcToSatoshis(amount) : btc.btcToSatoshis(usd.usdToBtc(amount, rate)) // backend needs value in satoshis no matter what currency we are using
const value = btc.convert(currency, 'sats', amount)
dispatch(sendInvoice()) dispatch(sendInvoice())
ipcRenderer.send('lnd', { msg: 'createInvoice', data: { value, memo } }) ipcRenderer.send('lnd', { msg: 'createInvoice', data: { value, memo } })
} }
@ -189,6 +191,12 @@ invoiceSelectors.invoices = createSelector(
(invoices, invoicesSearchText) => invoices.filter(invoice => invoice.memo.includes(invoicesSearchText)) (invoices, invoicesSearchText) => invoices.filter(invoice => invoice.memo.includes(invoicesSearchText))
) )
invoiceSelectors.invoices = createSelector(
invoicesSelector,
invoicesSearchTextSelector,
(invoices, invoicesSearchText) => invoices.filter(invoice => invoice.memo.includes(invoicesSearchText))
)
export { invoiceSelectors } export { invoiceSelectors }
// ------------------------------------ // ------------------------------------

63
app/reducers/payform.js

@ -9,15 +9,19 @@ import { btc, bech32 } from '../utils'
// Initial State // Initial State
const initialState = { const initialState = {
amount: '0', amount: '',
payInput: '', payInput: '',
invoice: { invoice: {
payreq: '', payreq: '',
r_hash: '', r_hash: '',
amount: '0' amount: '0',
description: '',
destination: ''
}, },
showCurrencyFilters: false,
showErrors: { showErrors: {
amount: false, amount: false,
payInput: false payInput: false
@ -30,6 +34,8 @@ export const SET_PAY_AMOUNT = 'SET_PAY_AMOUNT'
export const SET_PAY_INPUT = 'SET_PAY_INPUT' export const SET_PAY_INPUT = 'SET_PAY_INPUT'
export const SET_PAY_INVOICE = 'SET_PAY_INVOICE' export const SET_PAY_INVOICE = 'SET_PAY_INVOICE'
export const SET_PAY_CURRENCY_FILTERS = 'SET_PAY_CURRENCY_FILTERS'
export const UPDATE_PAY_ERRORS = 'UPDATE_PAY_ERRORS' export const UPDATE_PAY_ERRORS = 'UPDATE_PAY_ERRORS'
export const RESET_FORM = 'RESET_FORM' export const RESET_FORM = 'RESET_FORM'
@ -58,6 +64,13 @@ export function setPayInvoice(invoice) {
} }
} }
export function setCurrencyFilters(showCurrencyFilters) {
return {
type: SET_PAY_CURRENCY_FILTERS,
showCurrencyFilters
}
}
export function updatePayErrors(errorsObject) { export function updatePayErrors(errorsObject) {
return { return {
type: UPDATE_PAY_ERRORS, type: UPDATE_PAY_ERRORS,
@ -85,6 +98,7 @@ const ACTION_HANDLERS = {
[SET_PAY_AMOUNT]: (state, { amount }) => ({ ...state, amount, showErrors: Object.assign(state.showErrors, { amount: false }) }), [SET_PAY_AMOUNT]: (state, { amount }) => ({ ...state, amount, showErrors: Object.assign(state.showErrors, { amount: false }) }),
[SET_PAY_INPUT]: (state, { payInput }) => ({ ...state, payInput, showErrors: Object.assign(state.showErrors, { payInput: false }) }), [SET_PAY_INPUT]: (state, { payInput }) => ({ ...state, payInput, showErrors: Object.assign(state.showErrors, { payInput: false }) }),
[SET_PAY_INVOICE]: (state, { invoice }) => ({ ...state, invoice, showErrors: Object.assign(state.showErrors, { amount: false }) }), [SET_PAY_INVOICE]: (state, { invoice }) => ({ ...state, invoice, showErrors: Object.assign(state.showErrors, { amount: false }) }),
[SET_PAY_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({ ...state, showCurrencyFilters }),
[UPDATE_PAY_ERRORS]: (state, { errorsObject }) => ({ ...state, showErrors: Object.assign(state.showErrors, errorsObject) }), [UPDATE_PAY_ERRORS]: (state, { errorsObject }) => ({ ...state, showErrors: Object.assign(state.showErrors, errorsObject) }),
@ -135,17 +149,58 @@ payFormSelectors.isLn = createSelector(
) )
payFormSelectors.currentAmount = createSelector( payFormSelectors.currentAmount = createSelector(
payFormSelectors.isLn,
payAmountSelector,
payInvoiceSelector,
currencySelector,
(isLn, amount, invoice, currency) => {
if (isLn) {
switch (currency) {
case 'btc':
return btc.satoshisToBtc((invoice.num_satoshis || 0))
case 'bits':
return btc.satoshisToBits((invoice.num_satoshis || 0))
case 'sats':
return invoice.num_satoshis
default:
return invoice.num_satoshis
}
}
return amount
}
)
payFormSelectors.usdAmount = createSelector(
payFormSelectors.isLn, payFormSelectors.isLn,
payAmountSelector, payAmountSelector,
payInvoiceSelector, payInvoiceSelector,
currencySelector, currencySelector,
tickerSelectors.currentTicker, tickerSelectors.currentTicker,
(isLn, amount, invoice, currency, ticker) => { (isLn, amount, invoice, currency, ticker) => {
if (!ticker || !ticker.price_usd) { return false }
if (isLn) { if (isLn) {
return currency === 'usd' ? btc.satoshisToUsd((invoice.num_satoshis || 0), ticker.price_usd) : btc.satoshisToBtc((invoice.num_satoshis || 0)) return btc.satoshisToUsd((invoice.num_satoshis || 0), ticker.price_usd)
} }
return amount return btc.convert(currency, 'usd', amount, ticker.price_usd)
}
)
payFormSelectors.payInputMin = createSelector(
currencySelector,
(currency) => {
switch (currency) {
case 'btc':
return '0.00000001'
case 'bits':
return '0.01'
case 'sats':
return '1'
default:
return '0'
}
} }
) )

53
app/reducers/payment.js

@ -3,7 +3,6 @@ import { ipcRenderer } from 'electron'
import { fetchBalance } from './balance' import { fetchBalance } from './balance'
import { setFormType } from './form' import { setFormType } from './form'
import { resetPayForm } from './payform' import { resetPayForm } from './payform'
import { showModal } from './modal'
import { setError } from './error' import { setError } from './error'
// ------------------------------------ // ------------------------------------
@ -19,6 +18,9 @@ export const SEND_PAYMENT = 'SEND_PAYMENT'
export const PAYMENT_SUCCESSFULL = 'PAYMENT_SUCCESSFULL' export const PAYMENT_SUCCESSFULL = 'PAYMENT_SUCCESSFULL'
export const PAYMENT_FAILED = 'PAYMENT_FAILED' export const PAYMENT_FAILED = 'PAYMENT_FAILED'
export const SHOW_SUCCESS_SCREEN = 'SHOW_SUCCESS_SCREEN'
export const HIDE_SUCCESS_SCREEN = 'HIDE_SUCCESS_SCREEN'
// ------------------------------------ // ------------------------------------
// Actions // Actions
// ------------------------------------ // ------------------------------------
@ -48,6 +50,18 @@ export function paymentSuccessfull(payment) {
} }
} }
export function showSuccessScreen() {
return {
type: SHOW_SUCCESS_SCREEN
}
}
export function hideSuccessScreen() {
return {
type: HIDE_SUCCESS_SCREEN
}
}
// Send IPC event for payments // Send IPC event for payments
export const fetchPayments = () => (dispatch) => { export const fetchPayments = () => (dispatch) => {
dispatch(getPayments()) dispatch(getPayments())
@ -62,11 +76,10 @@ export const receivePayments = (event, { payments }) => dispatch => dispatch({ t
export const paymentSuccessful = () => (dispatch) => { export const paymentSuccessful = () => (dispatch) => {
// Dispatch successful payment to stop loading screen // Dispatch successful payment to stop loading screen
dispatch(paymentSuccessfull()) dispatch(paymentSuccessfull())
// Close the form modal once the payment was succesful
dispatch(setFormType(null))
// Show successful payment state // Show successful payment state for 5 seconds
dispatch(showModal('SUCCESSFUL_SEND_PAYMENT')) dispatch(showSuccessScreen())
setTimeout(() => dispatch(hideSuccessScreen()), 5000)
// Refetch payments (TODO: dont do a full refetch, rather append new tx to list) // Refetch payments (TODO: dont do a full refetch, rather append new tx to list)
dispatch(fetchPayments()) dispatch(fetchPayments())
@ -82,18 +95,21 @@ export const paymentFailed = (event, { error }) => (dispatch) => {
dispatch(setError(error)) dispatch(setError(error))
} }
export const payInvoice = paymentRequest => (dispatch, getState) => { export const payInvoice = paymentRequest => (dispatch) => {
dispatch(sendPayment()) dispatch(sendPayment())
ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } }) ipcRenderer.send('lnd', { msg: 'sendPayment', data: { paymentRequest } })
// Close the form modal once the payment has been sent
dispatch(setFormType(null))
// if LND hangs on sending the payment we'll cut it after 10 seconds and return an error // if LND hangs on sending the payment we'll cut it after 10 seconds and return an error
setTimeout(() => { // setTimeout(() => {
const { payment } = getState() // const { payment } = getState()
if (payment.sendingPayment) { // if (payment.sendingPayment) {
dispatch(paymentFailed(null, { error: 'Shoot, we\'re having some trouble sending your payment.' })) // dispatch(paymentFailed(null, { error: 'Shoot, we\'re having some trouble sending your payment.' }))
} // }
}, 10000) // }, 10000)
} }
@ -101,12 +117,16 @@ export const payInvoice = paymentRequest => (dispatch, getState) => {
// Action Handlers // Action Handlers
// ------------------------------------ // ------------------------------------
const ACTION_HANDLERS = { const ACTION_HANDLERS = {
[SET_PAYMENT]: (state, { payment }) => ({ ...state, payment }),
[GET_PAYMENTS]: state => ({ ...state, paymentLoading: true }), [GET_PAYMENTS]: state => ({ ...state, paymentLoading: true }),
[SEND_PAYMENT]: state => ({ ...state, sendingPayment: true }),
[RECEIVE_PAYMENTS]: (state, { payments }) => ({ ...state, paymentLoading: false, payments }), [RECEIVE_PAYMENTS]: (state, { payments }) => ({ ...state, paymentLoading: false, payments }),
[SET_PAYMENT]: (state, { payment }) => ({ ...state, payment }),
[SEND_PAYMENT]: state => ({ ...state, sendingPayment: true }),
[PAYMENT_SUCCESSFULL]: state => ({ ...state, sendingPayment: false }), [PAYMENT_SUCCESSFULL]: state => ({ ...state, sendingPayment: false }),
[PAYMENT_FAILED]: state => ({ ...state, sendingPayment: false }) [PAYMENT_FAILED]: state => ({ ...state, sendingPayment: false }),
[SHOW_SUCCESS_SCREEN]: state => ({ ...state, showSuccessPayScreen: true }),
[HIDE_SUCCESS_SCREEN]: state => ({ ...state, showSuccessPayScreen: false })
} }
const paymentSelectors = {} const paymentSelectors = {}
@ -126,7 +146,8 @@ const initialState = {
sendingPayment: false, sendingPayment: false,
paymentLoading: false, paymentLoading: false,
payments: [], payments: [],
payment: null payment: null,
showSuccessPayScreen: false
} }
export default function paymentReducer(state = initialState, action) { export default function paymentReducer(state = initialState, action) {

37
app/reducers/requestform.js

@ -1,7 +1,12 @@
import { createSelector } from 'reselect'
import { btc } from 'utils'
import { tickerSelectors } from './ticker'
// Initial State // Initial State
const initialState = { const initialState = {
amount: '0', amount: '',
memo: '' memo: '',
showCurrencyFilters: false
} }
// Constants // Constants
@ -10,6 +15,8 @@ export const SET_REQUEST_AMOUNT = 'SET_REQUEST_AMOUNT'
export const SET_REQUEST_MEMO = 'SET_REQUEST_MEMO' export const SET_REQUEST_MEMO = 'SET_REQUEST_MEMO'
export const SET_PAY_INVOICE = 'SET_PAY_INVOICE' export const SET_PAY_INVOICE = 'SET_PAY_INVOICE'
export const SET_REQUEST_CURRENCY_FILTERS = 'SET_REQUEST_CURRENCY_FILTERS'
export const RESET_FORM = 'RESET_FORM' export const RESET_FORM = 'RESET_FORM'
// ------------------------------------ // ------------------------------------
@ -35,16 +42,42 @@ export function resetRequestForm() {
} }
} }
export function setRequestCurrencyFilters(showCurrencyFilters) {
return {
type: SET_REQUEST_CURRENCY_FILTERS,
showCurrencyFilters
}
}
// ------------------------------------ // ------------------------------------
// Action Handlers // Action Handlers
// ------------------------------------ // ------------------------------------
const ACTION_HANDLERS = { const ACTION_HANDLERS = {
[SET_REQUEST_AMOUNT]: (state, { amount }) => ({ ...state, amount }), [SET_REQUEST_AMOUNT]: (state, { amount }) => ({ ...state, amount }),
[SET_REQUEST_MEMO]: (state, { memo }) => ({ ...state, memo }), [SET_REQUEST_MEMO]: (state, { memo }) => ({ ...state, memo }),
[SET_REQUEST_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({ ...state, showCurrencyFilters }),
[RESET_FORM]: () => (initialState) [RESET_FORM]: () => (initialState)
} }
const requestFormSelectors = {}
const requestAmountSelector = state => state.requestform.amount
const currencySelector = state => state.ticker.currency
requestFormSelectors.usdAmount = createSelector(
requestAmountSelector,
currencySelector,
tickerSelectors.currentTicker,
(amount, currency, ticker) => {
if (!ticker || !ticker.price_usd) { return false }
return btc.convert(currency, 'usd', amount, ticker.price_usd)
}
)
export { requestFormSelectors }
// ------------------------------------ // ------------------------------------
// Reducer // Reducer
// ------------------------------------ // ------------------------------------

34
app/reducers/ticker.js

@ -77,6 +77,8 @@ const ACTION_HANDLERS = {
// Selectors // Selectors
const tickerSelectors = {} const tickerSelectors = {}
const cryptoSelector = state => state.ticker.crypto const cryptoSelector = state => state.ticker.crypto
const currencyFiltersSelector = state => state.ticker.currencyFilters
const currencySelector = state => state.ticker.currency
const bitcoinTickerSelector = state => state.ticker.btcTicker const bitcoinTickerSelector = state => state.ticker.btcTicker
const litecoinTickerSelector = state => state.ticker.ltcTicker const litecoinTickerSelector = state => state.ticker.ltcTicker
@ -87,6 +89,22 @@ tickerSelectors.currentTicker = createSelector(
(crypto, btcTicker, ltcTicker) => (crypto === 'btc' ? btcTicker : ltcTicker) (crypto, btcTicker, ltcTicker) => (crypto === 'btc' ? btcTicker : ltcTicker)
) )
tickerSelectors.currentCurrencyFilters = createSelector(
currencySelector,
currencyFiltersSelector,
(currency, filters) => filters.filter(f => f.key !== currency)
)
tickerSelectors.currencyName = createSelector(
currencySelector,
(currency) => {
if (currency === 'btc') { return 'BTC' }
if (currency === 'sats') { return 'satoshis' }
return currency
}
)
export { tickerSelectors } export { tickerSelectors }
// ------------------------------------ // ------------------------------------
@ -97,7 +115,21 @@ const initialState = {
currency: '', currency: '',
crypto: '', crypto: '',
btcTicker: null, btcTicker: null,
ltcTicker: null ltcTicker: null,
currencyFilters: [
{
key: 'btc',
name: 'BTC'
},
{
key: 'bits',
name: 'bits'
},
{
key: 'sats',
name: 'satoshis'
}
]
} }
export default function tickerReducer(state = initialState, action) { export default function tickerReducer(state = initialState, action) {

8
app/reducers/transaction.js

@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { showNotification } from '../notifications' import { showNotification } from '../notifications'
import { btc, usd } from '../utils' import { btc } from '../utils'
import { newAddress } from './address' import { newAddress } from './address'
import { fetchBalance } from './balance' import { fetchBalance } from './balance'
import { setFormType } from './form' import { setFormType } from './form'
@ -46,9 +46,11 @@ export const fetchTransactions = () => (dispatch) => {
export const receiveTransactions = (event, { transactions }) => dispatch => dispatch({ type: RECEIVE_TRANSACTIONS, transactions }) export const receiveTransactions = (event, { transactions }) => dispatch => dispatch({ type: RECEIVE_TRANSACTIONS, transactions })
export const sendCoins = ({ export const sendCoins = ({
value, addr, currency, rate value, addr, currency
}) => (dispatch) => { }) => (dispatch) => {
const amount = currency === 'usd' ? btc.btcToSatoshis(usd.usdToBtc(value, rate)) : btc.btcToSatoshis(value) // backend needs amount in satoshis no matter what currency we are using
const amount = btc.convert(currency, 'sats', value)
dispatch(sendTransaction()) dispatch(sendTransaction())
ipcRenderer.send('lnd', { msg: 'sendCoins', data: { amount, addr } }) ipcRenderer.send('lnd', { msg: 'sendCoins', data: { amount, addr } })
} }

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

@ -7,8 +7,6 @@ import Invoice from './components/Invoice'
import Payment from './components/Payment' import Payment from './components/Payment'
import Transaction from './components/Transaction' import Transaction from './components/Transaction'
import Modal from './components/Modal'
import styles from './Activity.scss' import styles from './Activity.scss'
class Activity extends Component { class Activity extends Component {
@ -44,45 +42,23 @@ class Activity extends Component {
render() { render() {
const { const {
ticker,
invoice: { invoiceLoading },
address: { address },
balance, balance,
info, activity: {
payment: { paymentLoading }, filters,
currentTicker, filter,
activity: { modal, filters, filter, filterPulldown }, filterPulldown
hideActivityModal, },
changeFilter, changeFilter,
currentActivity, currentActivity,
newAddress,
openPayForm, walletProps
openRequestForm
} = this.props } = this.props
if (invoiceLoading || paymentLoading) { return <LoadingBolt /> }
if (balance.balanceLoading) { return <LoadingBolt /> }
if (!balance.channelBalance || !balance.walletBalance) { return <LoadingBolt /> } if (!balance.channelBalance || !balance.walletBalance) { return <LoadingBolt /> }
return ( return (
<div> <div>
<Modal <Wallet {...walletProps} />
modalType={modal.modalType}
modalProps={modal.modalProps}
hideActivityModal={hideActivityModal}
ticker={ticker}
currentTicker={currentTicker}
/>
<Wallet
balance={balance}
address={address}
info={info}
newAddress={newAddress}
currentTicker={currentTicker}
openPayForm={openPayForm}
openRequestForm={openRequestForm}
/>
<div className={styles.activities}> <div className={styles.activities}>
<header className={styles.header}> <header className={styles.header}>
@ -122,22 +98,15 @@ Activity.propTypes = {
fetchBalance: PropTypes.func.isRequired, fetchBalance: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired, ticker: PropTypes.object.isRequired,
invoice: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired, currentTicker: PropTypes.object.isRequired,
showActivityModal: PropTypes.func.isRequired, showActivityModal: PropTypes.func.isRequired,
hideActivityModal: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired, changeFilter: PropTypes.func.isRequired,
newAddress: PropTypes.func.isRequired,
openPayForm: PropTypes.func.isRequired,
openRequestForm: PropTypes.func.isRequired,
activity: PropTypes.object.isRequired, activity: PropTypes.object.isRequired,
currentActivity: PropTypes.array.isRequired, currentActivity: PropTypes.array.isRequired,
address: PropTypes.object.isRequired,
balance: PropTypes.object.isRequired, balance: PropTypes.object.isRequired,
info: PropTypes.object.isRequired walletProps: PropTypes.object.isRequired
} }
export default Activity export default Activity

24
app/routes/activity/components/components/Invoice/Invoice.js

@ -4,7 +4,7 @@ import Moment from 'react-moment'
import 'moment-timezone' import 'moment-timezone'
import Isvg from 'react-inlinesvg' import Isvg from 'react-inlinesvg'
import { FaBolt } from 'react-icons/lib/fa' import { FaBolt } from 'react-icons/lib/fa'
import { btc } from 'utils' import Value from 'components/Value'
import checkmarkIcon from 'icons/check_circle.svg' import checkmarkIcon from 'icons/check_circle.svg'
import clockIcon from 'icons/clock.svg' import clockIcon from 'icons/clock.svg'
import styles from '../Activity.scss' import styles from '../Activity.scss'
@ -47,20 +47,18 @@ const Invoice = ({
<div className={`${styles.amount} ${invoice.settled ? styles.positive : styles.negative}`}> <div className={`${styles.amount} ${invoice.settled ? styles.positive : styles.negative}`}>
<span className='hint--top' data-hint='Invoice amount'> <span className='hint--top' data-hint='Invoice amount'>
<i className={styles.plus}>+</i> <i className={styles.plus}>+</i>
{ <Value
ticker.currency === 'usd' ? value={invoice.value}
btc.satoshisToUsd(invoice.value, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(invoice.value) />
}
</span> </span>
<span className='hint--bottom' data-hint='Invoice fee'> <span className='hint--bottom' data-hint='Invoice fee'>
{ <Value
ticker.currency === 'usd' ? value={invoice.fee}
btc.satoshisToUsd(invoice.fee, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(invoice.fee) />
}
</span> </span>
</div> </div>
</div> </div>

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

@ -1,74 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import QRCode from 'qrcode.react'
import copy from 'copy-to-clipboard'
import { showNotification } from 'notifications'
import { FaCircle, FaCopy } from 'react-icons/lib/fa'
import { btc } from 'utils'
import styles from './Invoice.scss'
const Invoice = ({ invoice, ticker, currentTicker }) => {
const copyPaymentRequest = () => {
copy(invoice.payment_request)
showNotification('Noice', 'Successfully copied to clipboard')
}
return (
<div className={styles.container}>
<div className={styles.settled}>
{
!invoice.settled &&
<p>
<FaCircle />
<span>Not Paid</span>
</p>
}
</div>
<header>
<h3>{invoice.memo}</h3>
<h1>
<span className={styles.value}>
{
ticker.currency === 'usd' ?
btc.satoshisToUsd(invoice.value, currentTicker.price_usd)
:
btc.satoshisToBtc(invoice.value)
}
</span>
<i>BTC</i>
</h1>
</header>
<div className={styles.qrcode}>
<QRCode value={invoice.payment_request} size={150} />
</div>
<div className={styles.input}>
<p className={styles.invoiceAddress}>
<span>{invoice.payment_request}</span>
<span onClick={copyPaymentRequest} className='hint--left' data-hint='Copy Invoice'>
<FaCopy />
</span>
</p>
</div>
<p className={styles.date}>
Created on
<Moment format='MMM Do'>{invoice.creation_date * 1000}</Moment>
</p>
</div>
)
}
Invoice.propTypes = {
invoice: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired
}
export default Invoice

106
app/routes/activity/components/components/Modal/Invoice/Invoice.scss

@ -1,106 +0,0 @@
@import '../../../../../../variables.scss';
.container {
.settled {
position: absolute;
top: 0;
padding: 10px 0 10px 40px;
color: $darkestgrey;
svg {
line-height: 20px;
font-size: 10px;
vertical-align: middle;
}
span {
font-size: 12px;
margin-left: 5px;
}
}
header {
background: $lightgrey;
padding-bottom: 20px;
}
h3 {
font-size: 20px;
color: $black;
font-weight: bold;
padding: 10px 40px;
}
h1 {
color: $main;
padding: 10px 40px;
.value {
font-size: 30px;
}
i {
margin-left: 2px;
vertical-align: top;
}
}
.qrcode {
text-align: center;
margin-top: 40px;
}
.input {
padding: 10px 40px;
.paymentRequest {
text-align: center;
font-size: 12px;
padding: 15px;
background: $lightgrey;
border: 1px solid transparent;
display: block;
width: 90%;
margin: 20px auto 0 auto;
}
}
.date {
text-align: center;
padding-bottom: 40px;
margin-top: 20px;
time {
margin-left: 3px;
}
}
.invoiceAddress {
display: flex;
flex-direction: row;
font-family: 'Roboto';
font-size: 14px;
font-weight: 200;
background: $lightgrey;
span {
padding: 15px;
}
span:nth-child(1) {
flex: 9;
overflow-x: scroll;
}
span:nth-child(2) {
background: $darkgrey;
color: $black;
cursor: pointer;
transition: all 0.25s;
&:hover {
background: $darkestgrey;
}
}
}
}

3
app/routes/activity/components/components/Modal/Invoice/index.js

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

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

@ -1,69 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import ReactModal from 'react-modal'
import { MdClose } from 'react-icons/lib/md'
import Transaction from './Transaction'
import Payment from './Payment'
import Invoice from './Invoice'
import styles from './Modal.scss'
const Modal = ({
modalType, modalProps, hideActivityModal, ticker, currentTicker
}) => {
const MODAL_COMPONENTS = {
TRANSACTION: Transaction,
PAYMENT: Payment,
INVOICE: Invoice
}
const customStyles = {
overlay: {
cursor: 'pointer'
},
content: {
top: 'auto',
left: '0',
right: '0',
bottom: 'auto',
width: '40%',
margin: '50px auto',
borderRadius: 'none',
padding: '0'
}
}
if (!modalType) { return null }
const SpecificModal = MODAL_COMPONENTS[modalType]
return (
<ReactModal
isOpen
ariaHideApp
shouldCloseOnOverlayClick
contentLabel='No Overlay Click Modal'
onRequestClose={() => hideActivityModal()}
parentSelector={() => document.body}
style={customStyles}
>
<div className={styles.closeContainer}>
<span onClick={() => hideActivityModal()}>
<MdClose />
</span>
</div>
<SpecificModal {...modalProps} ticker={ticker} currentTicker={currentTicker} />
</ReactModal>
)
}
Modal.propTypes = {
modalType: PropTypes.string,
modalProps: PropTypes.object.isRequired,
hideActivityModal: PropTypes.func.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired
}
export default Modal

13
app/routes/activity/components/components/Modal/Modal.scss

@ -1,13 +0,0 @@
@import '../../../../../variables.scss';
.closeContainer {
background: $lightgrey;
text-align: right;
padding: 10px;
span {
color: $darkestgrey;
font-size: 20px;
cursor: pointer;
}
}

52
app/routes/activity/components/components/Modal/Payment/Payment.js

@ -1,52 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { btc } from 'utils'
import styles from './Payment.scss'
const Payment = ({ payment, ticker, currentTicker }) => (
<div className={styles.container}>
<header>
<div className={styles.title}>
<h2>Sent</h2>
<h1>
<span className={styles.value}>
{
ticker.currency === 'usd' ?
btc.satoshisToUsd(payment.value, currentTicker.price_usd)
:
btc.satoshisToBtc(payment.value)
}
</span>
<i>
BTC
</i>
</h1>
</div>
<h3>{payment.payment_hash}</h3>
</header>
<dl>
<dt>Fee</dt>
<dd>{payment.fee}</dd>
<dt>Hops</dt>
<dd>{payment.path.length}</dd>
<dt>Date</dt>
<dd>
<Moment format='MMM Do'>{payment.creation_date * 1000}</Moment>
</dd>
</dl>
</div>
)
Payment.propTypes = {
payment: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired
}
export default Payment

61
app/routes/activity/components/components/Modal/Payment/Payment.scss

@ -1,61 +0,0 @@
@import '../../../../../../variables.scss';
.container {
header {
padding: 5px 40px 20px 40px;
background: $lightgrey;
.title {
display: flex;
flex-direction: row;
margin-bottom: 30px;
h2 {
text-transform: uppercase;
font-size: 24px;
margin-right: 10px;
}
}
}
h1 {
color: $main;
.value {
font-size: 24px;
}
i {
margin-left: 2px;
vertical-align: top;
}
}
h3 {
font-size: 14px;
text-align: center;
color: $darkestgrey;
}
dl {
padding: 40px 40px 40px 40px;
}
dt {
text-align: left;
float: left;
clear: left;
font-weight: 500;
padding: 20px 35px 19px 0;
color: $black;
font-weight: bold;
}
dd {
text-align: right;
font-weight: 400;
padding: 30px 0 10px 0;
margin-left: 0;
border-bottom: 1px solid $darkgrey;
}
}

3
app/routes/activity/components/components/Modal/Payment/index.js

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

64
app/routes/activity/components/components/Modal/Transaction/Transaction.js

@ -1,64 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { btc, blockExplorer } from 'utils'
import styles from './Transaction.scss'
const Transaction = ({ transaction, ticker, currentTicker }) => (
<div className={styles.container}>
<header>
<div className={styles.title}>
<h2>
{
transaction.amount < 0 ?
'Sent'
:
'Received'
}
</h2>
<h1>
<span className={styles.value}>
{
ticker.currency === 'usd' ?
btc.satoshisToUsd(transaction.amount, currentTicker.price_usd)
:
btc.satoshisToBtc(transaction.amount)
}
</span>
<i>BTC</i>
</h1>
</div>
<h3 onClick={() => blockExplorer.showTransaction(transaction.tx_hash)}>{transaction.tx_hash}</h3>
</header>
<dl>
<dt>Confirmations</dt>
<dd>{transaction.num_confirmations}</dd>
<dt>Fee</dt>
<dd>
{
ticker.currency === 'usd' ?
btc.satoshisToUsd(transaction.total_fees)
:
btc.satoshisToBtc(transaction.total_fees)
}
</dd>
<dt>Date</dt>
<dd>
<Moment format='MMM Do'>{transaction.time_stamp * 1000}</Moment>
</dd>
</dl>
</div>
)
Transaction.propTypes = {
transaction: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired
}
export default Transaction

72
app/routes/activity/components/components/Modal/Transaction/Transaction.scss

@ -1,72 +0,0 @@
@import '../../../../../../variables.scss';
.container {
header {
padding: 5px 40px 20px 40px;
background: $lightgrey;
.title {
display: flex;
flex-direction: row;
h2 {
text-transform: uppercase;
}
}
}
h1 {
text-align: center;
color: $main;
.value {
font-size: 24px;
}
i {
margin-left: 2px;
vertical-align: top;
}
}
h3 {
font-size: 14px;
text-align: center;
color: $darkestgrey;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
h2 {
text-align: center;
margin-right: 10px;
margin-bottom: 30px;
text-transform: uppercase;
font-size: 24px;
}
dl {
padding: 40px 40px 40px 40px;
}
dt {
text-align: left;
float: left;
clear: left;
font-weight: 500;
padding: 20px 35px 19px 0;
color: $black;
font-weight: bold;
}
dd {
text-align: right;
font-weight: 400;
padding: 30px 0 10px 0;
margin-left: 0;
border-bottom: 1px solid $darkgrey;
}
}

3
app/routes/activity/components/components/Modal/Transaction/index.js

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

3
app/routes/activity/components/components/Modal/index.js

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

24
app/routes/activity/components/components/Payment/Payment.js

@ -4,7 +4,7 @@ import Moment from 'react-moment'
import 'moment-timezone' import 'moment-timezone'
import Isvg from 'react-inlinesvg' import Isvg from 'react-inlinesvg'
import { FaBolt } from 'react-icons/lib/fa' import { FaBolt } from 'react-icons/lib/fa'
import { btc } from 'utils' import Value from 'components/Value'
import checkmarkIcon from 'icons/check_circle.svg' import checkmarkIcon from 'icons/check_circle.svg'
import styles from '../Activity.scss' import styles from '../Activity.scss'
@ -39,20 +39,18 @@ const Payment = ({
<div className={styles.amount}> <div className={styles.amount}>
<span className='hint--top' data-hint='Payment amount'> <span className='hint--top' data-hint='Payment amount'>
<i className={styles.minus}>-</i> <i className={styles.minus}>-</i>
{ <Value
ticker.currency === 'usd' ? value={payment.value}
btc.satoshisToUsd(payment.value, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(payment.value) />
}
</span> </span>
<span className='hint--bottom' data-hint='Payment fee'> <span className='hint--bottom' data-hint='Payment fee'>
{ <Value
ticker.currency === 'usd' ? value={payment.fee}
btc.satoshisToUsd(payment.fee, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(payment.fee) />
}
</span> </span>
</div> </div>
</div> </div>

24
app/routes/activity/components/components/Transaction/Transaction.js

@ -4,7 +4,7 @@ import Moment from 'react-moment'
import 'moment-timezone' import 'moment-timezone'
import Isvg from 'react-inlinesvg' import Isvg from 'react-inlinesvg'
import { FaChain } from 'react-icons/lib/fa' import { FaChain } from 'react-icons/lib/fa'
import { btc } from 'utils' import Value from 'components/Value'
import checkmarkIcon from 'icons/check_circle.svg' import checkmarkIcon from 'icons/check_circle.svg'
import styles from '../Activity.scss' import styles from '../Activity.scss'
@ -39,20 +39,18 @@ const Transaction = ({
<div className={`${styles.amount} ${transaction.amount > 0 ? styles.positive : styles.negative}`}> <div className={`${styles.amount} ${transaction.amount > 0 ? styles.positive : styles.negative}`}>
<span className='hint--top' data-hint='Transaction amount'> <span className='hint--top' data-hint='Transaction amount'>
<i className={transaction.amount > 0 ? styles.plus : styles.minus}>{ transaction.amount > 0 ? '+' : '-' }</i> <i className={transaction.amount > 0 ? styles.plus : styles.minus}>{ transaction.amount > 0 ? '+' : '-' }</i>
{ <Value
ticker.currency === 'usd' ? value={transaction.amount}
btc.satoshisToUsd(transaction.amount, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(transaction.amount) />
}
</span> </span>
<span className='hint--bottom' data-hint='Transaction fee'> <span className='hint--bottom' data-hint='Transaction fee'>
{ <Value
ticker.currency === 'usd' ? value={transaction.total_fees}
btc.satoshisToUsd(transaction.total_fees, currentTicker.price_usd) currency={ticker.currency}
: currentTicker={currentTicker}
btc.satoshisToBtc(transaction.total_fees) />
}
</span> </span>
</div> </div>
</div> </div>

25
app/routes/activity/components/components/Transaction/Transaction.scss

@ -75,27 +75,6 @@
font-weight: 500; font-weight: 500;
} }
section {
display: inline-block;
vertical-align: top;
text-align: right;
font-size: 0;
margin: 0;
padding: 0;
&:nth-child(1) {
width: 20%;
}
&:nth-child(2) {
width: 80%;
}
}
svg {
font-size: 20px;
}
span { span {
display: block; display: block;
@ -106,6 +85,10 @@
&:nth-child(2) { &:nth-child(2) {
font-size: 10px; font-size: 10px;
} }
.value {
font-size: 14px;
}
} }
} }

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

@ -1,5 +1,5 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { tickerSelectors } from 'reducers/ticker' import { setCurrency, tickerSelectors } from 'reducers/ticker'
import { fetchBalance } from 'reducers/balance' import { fetchBalance } from 'reducers/balance'
import { import {
fetchInvoices, fetchInvoices,
@ -20,12 +20,15 @@ import {
activitySelectors, activitySelectors,
updateSearchText updateSearchText
} from 'reducers/activity' } from 'reducers/activity'
import { newAddress } from 'reducers/address' import { newAddress, openWalletModal } from 'reducers/address'
import { setFormType } from 'reducers/form' import { setFormType } from 'reducers/form'
import { payFormSelectors } from 'reducers/payform'
import Activity from '../components/Activity' import Activity from '../components/Activity'
const mapDispatchToProps = { const mapDispatchToProps = {
setCurrency,
setPayment, setPayment,
setInvoice, setInvoice,
fetchPayments, fetchPayments,
@ -36,6 +39,7 @@ const mapDispatchToProps = {
changeFilter, changeFilter,
toggleFilterPulldown, toggleFilterPulldown,
newAddress, newAddress,
openWalletModal,
fetchBalance, fetchBalance,
updateSearchText, updateSearchText,
setFormType setFormType
@ -61,18 +65,35 @@ const mapStateToProps = state => ({
currentTicker: tickerSelectors.currentTicker(state), currentTicker: tickerSelectors.currentTicker(state),
currentActivity: activitySelectors.currentActivity(state)(state), currentActivity: activitySelectors.currentActivity(state)(state),
nonActiveFilters: activitySelectors.nonActiveFilters(state) nonActiveFilters: activitySelectors.nonActiveFilters(state),
showPayLoadingScreen: payFormSelectors.showPayLoadingScreen(state)
}) })
const mergeProps = (stateProps, dispatchProps, ownProps) => ({ const mergeProps = (stateProps, dispatchProps, ownProps) => {
...stateProps, const walletProps = {
...dispatchProps, balance: stateProps.balance,
...ownProps, address: stateProps.address.address,
info: stateProps.info,
ticker: stateProps.ticker,
currentTicker: stateProps.currentTicker,
showPayLoadingScreen: stateProps.showPayLoadingScreen,
showSuccessPayScreen: stateProps.payment.showSuccessPayScreen,
// action to open the pay form setCurrency: dispatchProps.setCurrency,
openPayForm: () => dispatchProps.setFormType('PAY_FORM'), newAddress: dispatchProps.newAddress,
// action to open the request form openReceiveModal: dispatchProps.openWalletModal,
openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM') openPayForm: () => dispatchProps.setFormType('PAY_FORM'),
}) openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM')
}
return {
...stateProps,
...dispatchProps,
...ownProps,
walletProps
}
}
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Activity) export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Activity)

19
app/routes/app/components/App.js

@ -11,11 +11,21 @@ import Network from 'components/Contacts/Network'
import ContactModal from 'components/Contacts/ContactModal' import ContactModal from 'components/Contacts/ContactModal'
import ContactsForm from 'components/Contacts/ContactsForm' import ContactsForm from 'components/Contacts/ContactsForm'
import ReceiveModal from 'components/Wallet/ReceiveModal'
import ActivityModal from 'components/Activity/ActivityModal'
import styles from './App.scss' import styles from './App.scss'
class App extends Component { class App extends Component {
componentWillMount() { componentWillMount() {
const { fetchTicker, fetchInfo, newAddress, fetchChannels, fetchBalance, fetchDescribeNetwork } = this.props const {
fetchTicker,
fetchInfo,
newAddress,
fetchChannels,
fetchBalance,
fetchDescribeNetwork
} = this.props
// fetch price ticker // fetch price ticker
fetchTicker() fetchTicker()
@ -48,6 +58,8 @@ class App extends Component {
contactModalProps, contactModalProps,
contactsFormProps, contactsFormProps,
networkTabProps, networkTabProps,
receiveModalProps,
activityModalProps,
children children
} = this.props } = this.props
@ -71,6 +83,9 @@ class App extends Component {
<Form formType={form.formType} formProps={formProps} closeForm={closeForm} /> <Form formType={form.formType} formProps={formProps} closeForm={closeForm} />
<ReceiveModal {...receiveModalProps} />
<ActivityModal {...activityModalProps} />
<div className={styles.content}> <div className={styles.content}>
{children} {children}
</div> </div>
@ -92,6 +107,8 @@ App.propTypes = {
contactModalProps: PropTypes.object, contactModalProps: PropTypes.object,
contactsFormProps: PropTypes.object, contactsFormProps: PropTypes.object,
networkTabProps: PropTypes.object, networkTabProps: PropTypes.object,
activityModalProps: PropTypes.object,
receiveModalProps: PropTypes.object,
newAddress: PropTypes.func.isRequired, newAddress: PropTypes.func.isRequired,
fetchInfo: PropTypes.func.isRequired, fetchInfo: PropTypes.func.isRequired,

76
app/routes/app/containers/AppContainer.js

@ -3,7 +3,7 @@ import { connect } from 'react-redux'
import { fetchTicker, setCurrency, tickerSelectors } from 'reducers/ticker' import { fetchTicker, setCurrency, tickerSelectors } from 'reducers/ticker'
import { newAddress } from 'reducers/address' import { newAddress, closeWalletModal } from 'reducers/address'
import { fetchInfo } from 'reducers/info' import { fetchInfo } from 'reducers/info'
@ -11,9 +11,9 @@ import { showModal, hideModal } from 'reducers/modal'
import { setFormType } from 'reducers/form' import { setFormType } from 'reducers/form'
import { setPayAmount, setPayInput, updatePayErrors, payFormSelectors } from 'reducers/payform' import { setPayAmount, setPayInput, setCurrencyFilters, updatePayErrors, payFormSelectors } from 'reducers/payform'
import { setRequestAmount, setRequestMemo } from 'reducers/requestform' import { setRequestAmount, setRequestMemo, setRequestCurrencyFilters, requestFormSelectors } from 'reducers/requestform'
import { sendCoins } from 'reducers/transaction' import { sendCoins } from 'reducers/transaction'
@ -52,6 +52,8 @@ import { fetchDescribeNetwork } from 'reducers/network'
import { clearError } from 'reducers/error' import { clearError } from 'reducers/error'
import { hideActivityModal, setActivityModalCurrencyFilters } from 'reducers/activity'
import App from '../components/App' import App from '../components/App'
const mapDispatchToProps = { const mapDispatchToProps = {
@ -59,6 +61,7 @@ const mapDispatchToProps = {
setCurrency, setCurrency,
newAddress, newAddress,
closeWalletModal,
fetchInfo, fetchInfo,
@ -69,11 +72,12 @@ const mapDispatchToProps = {
setPayAmount, setPayAmount,
setPayInput, setPayInput,
setCurrencyFilters,
updatePayErrors, updatePayErrors,
setRequestAmount, setRequestAmount,
setRequestMemo, setRequestMemo,
setRequestCurrencyFilters,
sendCoins, sendCoins,
payInvoice, payInvoice,
@ -102,10 +106,15 @@ const mapDispatchToProps = {
contactFormSelectors, contactFormSelectors,
updateManualFormErrors, updateManualFormErrors,
fetchDescribeNetwork fetchDescribeNetwork,
hideActivityModal,
setActivityModalCurrencyFilters
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
activity: state.activity,
lnd: state.lnd, lnd: state.lnd,
ticker: state.ticker, ticker: state.ticker,
@ -130,12 +139,17 @@ const mapStateToProps = state => ({
network: state.network, network: state.network,
currentTicker: tickerSelectors.currentTicker(state), currentTicker: tickerSelectors.currentTicker(state),
currentCurrencyFilters: tickerSelectors.currentCurrencyFilters(state),
currencyName: tickerSelectors.currencyName(state),
isOnchain: payFormSelectors.isOnchain(state), isOnchain: payFormSelectors.isOnchain(state),
isLn: payFormSelectors.isLn(state), isLn: payFormSelectors.isLn(state),
currentAmount: payFormSelectors.currentAmount(state), currentAmount: payFormSelectors.currentAmount(state),
usdAmount: payFormSelectors.usdAmount(state),
inputCaption: payFormSelectors.inputCaption(state), inputCaption: payFormSelectors.inputCaption(state),
showPayLoadingScreen: payFormSelectors.showPayLoadingScreen(state), showPayLoadingScreen: payFormSelectors.showPayLoadingScreen(state),
payFormIsValid: payFormSelectors.payFormIsValid(state), payFormIsValid: payFormSelectors.payFormIsValid(state),
payInputMin: payFormSelectors.payInputMin(state),
requestUsdAmount: requestFormSelectors.usdAmount(state),
syncPercentage: lndSelectors.syncPercentage(state), syncPercentage: lndSelectors.syncPercentage(state),
filteredNetworkNodes: contactFormSelectors.filteredNetworkNodes(state), filteredNetworkNodes: contactFormSelectors.filteredNetworkNodes(state),
@ -155,17 +169,25 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
payform: stateProps.payform, payform: stateProps.payform,
currency: stateProps.ticker.currency, currency: stateProps.ticker.currency,
crypto: stateProps.ticker.crypto, crypto: stateProps.ticker.crypto,
nodes: stateProps.network.nodes,
ticker: stateProps.ticker,
isOnchain: stateProps.isOnchain, isOnchain: stateProps.isOnchain,
isLn: stateProps.isLn, isLn: stateProps.isLn,
currentAmount: stateProps.currentAmount, currentAmount: stateProps.currentAmount,
usdAmount: stateProps.usdAmount,
inputCaption: stateProps.inputCaption, inputCaption: stateProps.inputCaption,
showPayLoadingScreen: stateProps.showPayLoadingScreen, showPayLoadingScreen: stateProps.showPayLoadingScreen,
payFormIsValid: stateProps.payFormIsValid, payFormIsValid: stateProps.payFormIsValid,
payInputMin: stateProps.payInputMin,
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyName: stateProps.currencyName,
setPayAmount: dispatchProps.setPayAmount, setPayAmount: dispatchProps.setPayAmount,
setPayInput: dispatchProps.setPayInput, setPayInput: dispatchProps.setPayInput,
setCurrencyFilters: dispatchProps.setCurrencyFilters,
fetchInvoice: dispatchProps.fetchInvoice, fetchInvoice: dispatchProps.fetchInvoice,
setCurrency: dispatchProps.setCurrency,
onPayAmountBlur: () => { onPayAmountBlur: () => {
// If the amount is now valid and showErrors was on, turn it off // If the amount is now valid and showErrors was on, turn it off
@ -218,11 +240,17 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
const requestFormProps = { const requestFormProps = {
requestform: stateProps.requestform, requestform: stateProps.requestform,
currency: stateProps.ticker.currency, ticker: stateProps.ticker,
crypto: stateProps.ticker.crypto,
currentCurrencyFilters: stateProps.currentCurrencyFilters,
showCurrencyFilters: stateProps.showCurrencyFilters,
currencyName: stateProps.currencyName,
requestUsdAmount: stateProps.requestUsdAmount,
setRequestAmount: dispatchProps.setRequestAmount, setRequestAmount: dispatchProps.setRequestAmount,
setRequestMemo: dispatchProps.setRequestMemo, setRequestMemo: dispatchProps.setRequestMemo,
setCurrency: dispatchProps.setCurrency,
setRequestCurrencyFilters: dispatchProps.setRequestCurrencyFilters,
onRequestSubmit: () => ( onRequestSubmit: () => (
dispatchProps.createInvoice( dispatchProps.createInvoice(
@ -290,6 +318,36 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
closingChannelIds: stateProps.channels.closingChannelIds closingChannelIds: stateProps.channels.closingChannelIds
} }
const activityModalProps = {
modalType: stateProps.activity.modal.modalType,
modalProps: stateProps.activity.modal.modalProps,
ticker: stateProps.ticker,
currentTicker: stateProps.currentTicker,
hideActivityModal: dispatchProps.hideActivityModal,
toggleCurrencyProps: {
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyName: stateProps.currencyName,
showCurrencyFilters: stateProps.activity.modal.showCurrencyFilters,
setActivityModalCurrencyFilters: dispatchProps.setActivityModalCurrencyFilters,
setCurrencyFilters: dispatchProps.setCurrencyFilters,
onCurrencyFilterClick: (currency) => {
dispatchProps.setCurrency(currency)
dispatchProps.setActivityModalCurrencyFilters(false)
}
}
}
const receiveModalProps = {
isOpen: stateProps.address.walletModal,
pubkey: stateProps.info.data.identity_pubkey,
address: stateProps.address.address,
newAddress: dispatchProps.newAddress,
closeReceiveModal: dispatchProps.closeWalletModal
}
return { return {
...stateProps, ...stateProps,
...dispatchProps, ...dispatchProps,
@ -301,6 +359,10 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
contactsFormProps, contactsFormProps,
// props for the contact modal // props for the contact modal
contactModalProps, contactModalProps,
// props for the receive modal
receiveModalProps,
// props for the activity modals
activityModalProps,
// Props to pass to the pay form // Props to pass to the pay form
formProps: formProps(stateProps.form.formType), formProps: formProps(stateProps.form.formType),
// action to close form // action to close form

3
app/rpc.proto

@ -1348,6 +1348,9 @@ message Payment {
/// The fee paid for this payment in satoshis /// The fee paid for this payment in satoshis
int64 fee = 5 [json_name = "fee"]; int64 fee = 5 [json_name = "fee"];
/// The payment preimage
string payment_preimage = 6 [json_name = "payment_preimage"];
} }
message ListPaymentsRequest { message ListPaymentsRequest {

4
app/utils/blockExplorer.js

@ -6,6 +6,9 @@ const mainnetUrl = 'https://smartbit.com.au'
const showTransaction = txid => const showTransaction = txid =>
shell.openExternal(`${testnetUrl}/tx/${txid}`) shell.openExternal(`${testnetUrl}/tx/${txid}`)
const showBlock = blockHash =>
shell.openExternal(`${testnetUrl}/block/${blockHash}`)
const showChannelClosing = channel => const showChannelClosing = channel =>
showTransaction(channel.closing_txid) showTransaction(channel.closing_txid)
@ -16,6 +19,7 @@ export default {
testnetUrl, testnetUrl,
mainnetUrl, mainnetUrl,
showTransaction, showTransaction,
showBlock,
showChannelClosing, showChannelClosing,
showChannelPoint showChannelPoint
} }

116
app/utils/btc.js

@ -1,11 +1,54 @@
import sb from 'satoshi-bitcoin' /* eslint-disable */
import sb from 'satoshi-bitcoin'
//////////////////////
// BTC to things /////
/////////////////////
export function btcToSatoshis(btc) { export function btcToSatoshis(btc) {
if (btc === undefined || btc === null || btc === '') return null if (btc === undefined || btc === null || btc === '') return null
return sb.toSatoshi(btc) return sb.toSatoshi(btc)
} }
export function btcToBits(btc) {
if (btc === undefined || btc === null || btc === '') return null
return satoshisToBits(sb.toSatoshi(btc))
}
export function btcToUsd(btc, price) {
const amount = parseFloat(btc * price).toFixed(2)
return (btc > 0 && amount <= 0) ? '< 0.01' : amount.toLocaleString('en')
}
////////////////////////////
// bits to things /////////
//////////////////////////
export function bitsToBtc(bits, price) {
if (bits === undefined || bits === null || bits === '') return null
const sats = bits * 100
return satoshisToBtc(sats, price)
}
export function bitsToSatoshis(bits, price) {
if (bits === undefined || bits === null || bits === '') return null
return bits * 100
}
export function bitsToUsd(bits, price) {
if (bits === undefined || bits === null || bits === '') return null
const sats = bits * 100
return satoshisToUsd(sats, price)
}
////////////////////////////
// satoshis to things /////
//////////////////////////
export function satoshisToBtc(satoshis) { export function satoshisToBtc(satoshis) {
if (satoshis === undefined || satoshis === null || satoshis === '') return null if (satoshis === undefined || satoshis === null || satoshis === '') return null
@ -13,9 +56,11 @@ export function satoshisToBtc(satoshis) {
return btcAmount > 0 ? btcAmount : btcAmount * -1 return btcAmount > 0 ? btcAmount : btcAmount * -1
} }
export function btcToUsd(btc, price) { export function satoshisToBits(satoshis) {
const amount = parseFloat(btc * price).toFixed(2) if (satoshis === undefined || satoshis === null || satoshis === '') return null
return (btc > 0 && amount <= 0) ? '< 0.01' : amount.toLocaleString('en')
const bitsAmount = satoshis / 100
return bitsAmount > 0 ? bitsAmount : bitsAmount * -1
} }
export function satoshisToUsd(satoshis, price) { export function satoshisToUsd(satoshis, price) {
@ -25,9 +70,70 @@ export function satoshisToUsd(satoshis, price) {
} }
export function renderCurrency(currency) {
switch (currency) {
case 'btc':
return 'BTC'
case 'bits':
return 'bits'
case 'sats':
return 'satoshis'
default:
return 'satoshis'
}
}
export function convert(from, to, amount, price) {
switch (from) {
case 'btc':
switch (to) {
case 'bits':
return btcToBits(amount)
case 'sats':
return btcToSatoshis(amount)
case 'usd':
return btcToUsd(amount, price)
}
break
case 'bits':
switch (to) {
case 'btc':
return bitsToBtc(amount)
case 'sats':
return bitsToSatoshis(amount)
case 'usd':
return bitsToUsd(amount, price)
}
break
case 'sats':
switch (to) {
case 'btc':
return satoshisToBtc(amount)
case 'bits':
return satoshisToBits(amount)
case 'usd':
return satoshisToUsd(amount, price)
}
break
default:
return ''
}
}
export default { export default {
btcToSatoshis, btcToSatoshis,
btcToBits,
btcToUsd,
bitsToBtc,
bitsToSatoshis,
bitsToUsd,
satoshisToBtc, satoshisToBtc,
satoshisToBits,
satoshisToUsd, satoshisToUsd,
btcToUsd
renderCurrency,
convert
} }

29
package.json

@ -11,7 +11,7 @@
"dev": "cross-env START_HOT=1 concurrently \"npm run start-renderer-dev\"", "dev": "cross-env START_HOT=1 concurrently \"npm run start-renderer-dev\"",
"flow": "flow", "flow": "flow",
"flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true", "flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true",
"lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .", "lint": "cross-env NODE_ENV=development eslint --cache --format=node_modules/eslint-formatter-pretty .",
"lint-ci": "npm run lint && npm run flow", "lint-ci": "npm run lint && npm run flow",
"lint-fix": "npm run lint -- --fix", "lint-fix": "npm run lint -- --fix",
"lint-styles": "stylelint app/*.css app/components/*.css --syntax scss", "lint-styles": "stylelint app/*.css app/components/*.css --syntax scss",
@ -130,7 +130,7 @@
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.24.1", "babel-core": "^6.24.1",
"babel-eslint": "^7.2.3", "babel-eslint": "^8.2.1",
"babel-jest": "^20.0.3", "babel-jest": "^20.0.3",
"babel-loader": "^7.1.0", "babel-loader": "^7.1.0",
"babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-add-module-exports": "^0.2.1",
@ -156,18 +156,17 @@
"electron-devtools-installer": "^2.2.1", "electron-devtools-installer": "^2.2.1",
"enzyme": "^2.9.1", "enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1", "enzyme-to-json": "^1.5.1",
"eslint": "^4.4.1", "eslint": "^4.16.0",
"eslint-config-airbnb": "^15.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-formatter-pretty": "^1.1.0", "eslint-formatter-pretty": "^1.3.0",
"eslint-import-resolver-webpack": "^0.8.3", "eslint-import-resolver-webpack": "^0.8.4",
"eslint-plugin-compat": "^1.0.4", "eslint-plugin-compat": "^2.2.0",
"eslint-plugin-flowtype": "^2.33.0", "eslint-plugin-flowtype": "^2.42.0",
"eslint-plugin-flowtype-errors": "^3.3.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-import": "^2.7.0", "eslint-plugin-jest": "^21.7.0",
"eslint-plugin-jest": "^20.0.3", "eslint-plugin-jsx-a11y": "6.0.3",
"eslint-plugin-jsx-a11y": "^5.1.1", "eslint-plugin-promise": "^3.6.0",
"eslint-plugin-promise": "^3.5.0", "eslint-plugin-react": "^7.6.1",
"eslint-plugin-react": "^7.2.1",
"express": "^4.15.3", "express": "^4.15.3",
"extract-text-webpack-plugin": "^3.0.1", "extract-text-webpack-plugin": "^3.0.1",
"fbjs-scripts": "^0.8.0", "fbjs-scripts": "^0.8.0",
@ -215,7 +214,7 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"moment-timezone": "^0.5.13", "moment-timezone": "^0.5.13",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qrcode.react": "^0.7.1", "qrcode.react": "0.8.0",
"react": "^15.6.1", "react": "^15.6.1",
"react-dom": "^15.6.1", "react-dom": "^15.6.1",
"react-hot-loader": "3.0.0-beta.6", "react-hot-loader": "3.0.0-beta.6",

52
test/components/Form.spec.js

@ -2,45 +2,57 @@ import React from 'react'
import { shallow } from 'enzyme' import { shallow } from 'enzyme'
import Form from '../../app/components/Form' import Form from '../../app/components/Form'
import PayForm from '../../app/components/Form/PayForm' import Pay from '../../app/components/Form/Pay'
import RequestForm from '../../app/components/Form/RequestForm' import Request from '../../app/components/Form/Request'
const payFormProps = { const payFormProps = {
payform: { payform: {
amount: '', amount: 0,
payInput: '', payInput: '',
invoice: {},
showErrors: {} showErrors: {}
}, },
currency: 'BTC', currency: {},
crypto: 'BTC', crypto: {},
nodes: [],
ticker: {},
isOnchain: false, isOnchain: false,
isLn: false, isLn: true,
currentAmount: '0', currentAmount: 0,
usdAmount: 0,
inputCaption: '', inputCaption: '',
showPayLoadingScreen: false, showPayLoadingScreen: true,
payFormIsValid: {}, payFormIsValid: {},
currentCurrencyFilters: [],
currencyName: '',
setPayAmount: () => {}, setPayAmount: () => {},
onPayAmountBlur: () => {},
setPayInput: () => {}, setPayInput: () => {},
onPayInputBlur: () => {}, setCurrencyFilters: () => {},
fetchInvoice: () => {}, fetchInvoice: () => {},
setCurrency: () => {},
onPayAmountBlur: () => {},
onPayInputBlur: () => {},
onPaySubmit: () => {} onPaySubmit: () => {}
} }
const requestFormProps = { const requestFormProps = {
requestform: { requestform: {},
amount: '', ticker: {},
memo: ''
}, currentCurrencyFilters: [],
currency: '', showCurrencyFilters: true,
crypto: '', currencyName: '',
requestUsdAmount: '',
setRequestAmount: () => {}, setRequestAmount: () => {},
setRequestMemo: () => {}, setRequestMemo: () => {},
setCurrency: () => {},
setRequestCurrencyFilters: () => {},
onRequestSubmit: () => {} onRequestSubmit: () => {}
} }
@ -55,16 +67,16 @@ describe('Form', () => {
describe('should show pay form when formType is PAY_FORM', () => { describe('should show pay form when formType is PAY_FORM', () => {
const props = { ...defaultProps, formType: 'PAY_FORM', formProps: payFormProps } const props = { ...defaultProps, formType: 'PAY_FORM', formProps: payFormProps }
const el = shallow(<Form {...props} />) const el = shallow(<Form {...props} />)
it('should contain PayForm', () => { it('should contain Pay', () => {
expect(el.find(PayForm)).toHaveLength(1) expect(el.find(Pay)).toHaveLength(1)
}) })
}) })
describe('should show request form when formType is REQUEST_FORM', () => { describe('should show request form when formType is REQUEST_FORM', () => {
const props = { ...defaultProps, formType: 'REQUEST_FORM', formProps: requestFormProps } const props = { ...defaultProps, formType: 'REQUEST_FORM', formProps: requestFormProps }
const el = shallow(<Form {...props} />) const el = shallow(<Form {...props} />)
it('should contain RequestForm', () => { it('should contain Request', () => {
expect(el.find(RequestForm)).toHaveLength(1) expect(el.find(Request)).toHaveLength(1)
}) })
}) })
}) })

46
test/components/Form/Payform.spec.js → test/components/Form/Pay.spec.js

@ -1,63 +1,67 @@
import React from 'react' import React from 'react'
import { shallow } from 'enzyme' import { shallow } from 'enzyme'
import PayForm from '../../../app/components/Form/PayForm' import Pay from '../../../app/components/Form/Pay'
const defaultProps = { const defaultProps = {
payform: { payform: {
amount: '', amount: 0,
payInput: '', payInput: '',
invoice: {},
showErrors: {} showErrors: {}
}, },
currency: 'BTC', currency: {},
crypto: 'BTC', crypto: {},
nodes: [],
ticker: {},
isOnchain: false, isOnchain: false,
isLn: false, isLn: true,
currentAmount: '0', currentAmount: 0,
usdAmount: 0,
inputCaption: '', inputCaption: '',
showPayLoadingScreen: false, showPayLoadingScreen: true,
payFormIsValid: {}, payFormIsValid: {},
currentCurrencyFilters: [],
currencyName: '',
setPayAmount: () => {}, setPayAmount: () => {},
onPayAmountBlur: () => {},
setPayInput: () => {}, setPayInput: () => {},
onPayInputBlur: () => {}, setCurrencyFilters: () => {},
fetchInvoice: () => {}, fetchInvoice: () => {},
setCurrency: () => {},
onPayAmountBlur: () => {},
onPayInputBlur: () => {},
onPaySubmit: () => {} onPaySubmit: () => {}
} }
describe('Form', () => { describe('Form', () => {
describe('should show the form without an input', () => { describe('should show the form without an input', () => {
const el = shallow(<PayForm {...defaultProps} />) const el = shallow(<Pay {...defaultProps} />)
it('should contain PayForm', () => { it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined) expect(el.find('input#paymentRequest').props.value).toBe(undefined)
expect(el.contains('lightning network')).toBe(false)
expect(el.contains('on-chain')).toBe(false)
}) })
}) })
describe('should show lightning with a lightning input', () => { describe('should show lightning with a lightning input', () => {
const props = { ...defaultProps, isLn: true } const props = { ...defaultProps, isLn: true }
const el = shallow(<PayForm {...props} />) const el = shallow(<Pay {...props} />)
it('should contain PayForm', () => { it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined) expect(el.find('input#paymentRequest').props.value).toBe(undefined)
expect(el.contains('lightning network')).toBe(true)
expect(el.contains('on-chain')).toBe(false)
}) })
}) })
describe('should show on-chain with an on-chain input', () => { describe('should show on-chain with an on-chain input', () => {
const props = { ...defaultProps, isOnchain: true } const props = { ...defaultProps, isOnchain: true }
const el = shallow(<PayForm {...props} />) const el = shallow(<Pay {...props} />)
it('should contain PayForm', () => { it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined) expect(el.find('input#paymentRequest').props.value).toBe(undefined)
expect(el.contains('lightning network')).toBe(false)
expect(el.contains('on-chain')).toBe(true)
}) })
}) })
}) })

21
test/components/Form/RequestForm.spec.js → test/components/Form/Request.spec.js

@ -1,18 +1,21 @@
import React from 'react' import React from 'react'
import { shallow } from 'enzyme' import { shallow } from 'enzyme'
import RequestForm from '../../../app/components/Form/RequestForm' import Request from '../../../app/components/Form/Request'
const defaultProps = { const defaultProps = {
requestform: { requestform: {},
amount: '', ticker: {},
showErrors: {}
}, currentCurrencyFilters: [],
currency: '', showCurrencyFilters: true,
crypto: '', currencyName: '',
requestUsdAmount: '',
setRequestAmount: () => {}, setRequestAmount: () => {},
setRequestMemo: () => {}, setRequestMemo: () => {},
setCurrency: () => {},
setRequestCurrencyFilters: () => {},
onRequestSubmit: () => {} onRequestSubmit: () => {}
} }
@ -20,8 +23,8 @@ const defaultProps = {
describe('Form', () => { describe('Form', () => {
describe('should show request form when formType is REQUEST_FORM', () => { describe('should show request form when formType is REQUEST_FORM', () => {
const props = { ...defaultProps } const props = { ...defaultProps }
const el = shallow(<RequestForm {...props} />) const el = shallow(<Request {...props} />)
it('should contain RequestForm', () => { it('should contain Request', () => {
expect(el.contains('Request')).toBe(true) expect(el.contains('Request')).toBe(true)
}) })
}) })

5
test/reducers/__snapshots__/payment.spec.js.snap

@ -6,6 +6,7 @@ Object {
"paymentLoading": true, "paymentLoading": true,
"payments": Array [], "payments": Array [],
"sendingPayment": false, "sendingPayment": false,
"showSuccessPayScreen": false,
} }
`; `;
@ -15,6 +16,7 @@ Object {
"paymentLoading": false, "paymentLoading": false,
"payments": Array [], "payments": Array [],
"sendingPayment": false, "sendingPayment": false,
"showSuccessPayScreen": false,
} }
`; `;
@ -27,6 +29,7 @@ Object {
2, 2,
], ],
"sendingPayment": false, "sendingPayment": false,
"showSuccessPayScreen": false,
} }
`; `;
@ -36,6 +39,7 @@ Object {
"paymentLoading": false, "paymentLoading": false,
"payments": Array [], "payments": Array [],
"sendingPayment": false, "sendingPayment": false,
"showSuccessPayScreen": false,
} }
`; `;
@ -45,5 +49,6 @@ Object {
"paymentLoading": false, "paymentLoading": false,
"payments": Array [], "payments": Array [],
"sendingPayment": false, "sendingPayment": false,
"showSuccessPayScreen": false,
} }
`; `;

70
test/reducers/__snapshots__/ticker.spec.js.snap

@ -5,6 +5,20 @@ Object {
"btcTicker": null, "btcTicker": null,
"crypto": "", "crypto": "",
"currency": "", "currency": "",
"currencyFilters": Array [
Object {
"key": "btc",
"name": "BTC",
},
Object {
"key": "bits",
"name": "bits",
},
Object {
"key": "sats",
"name": "satoshis",
},
],
"ltcTicker": null, "ltcTicker": null,
"tickerLoading": true, "tickerLoading": true,
} }
@ -15,6 +29,20 @@ Object {
"btcTicker": undefined, "btcTicker": undefined,
"crypto": "", "crypto": "",
"currency": "", "currency": "",
"currencyFilters": Array [
Object {
"key": "btc",
"name": "BTC",
},
Object {
"key": "bits",
"name": "bits",
},
Object {
"key": "sats",
"name": "satoshis",
},
],
"ltcTicker": undefined, "ltcTicker": undefined,
"tickerLoading": false, "tickerLoading": false,
} }
@ -25,6 +53,20 @@ Object {
"btcTicker": null, "btcTicker": null,
"crypto": "foo", "crypto": "foo",
"currency": "", "currency": "",
"currencyFilters": Array [
Object {
"key": "btc",
"name": "BTC",
},
Object {
"key": "bits",
"name": "bits",
},
Object {
"key": "sats",
"name": "satoshis",
},
],
"ltcTicker": null, "ltcTicker": null,
"tickerLoading": false, "tickerLoading": false,
} }
@ -35,6 +77,20 @@ Object {
"btcTicker": null, "btcTicker": null,
"crypto": "", "crypto": "",
"currency": "foo", "currency": "foo",
"currencyFilters": Array [
Object {
"key": "btc",
"name": "BTC",
},
Object {
"key": "bits",
"name": "bits",
},
Object {
"key": "sats",
"name": "satoshis",
},
],
"ltcTicker": null, "ltcTicker": null,
"tickerLoading": false, "tickerLoading": false,
} }
@ -45,6 +101,20 @@ Object {
"btcTicker": null, "btcTicker": null,
"crypto": "", "crypto": "",
"currency": "", "currency": "",
"currencyFilters": Array [
Object {
"key": "btc",
"name": "BTC",
},
Object {
"key": "bits",
"name": "bits",
},
Object {
"key": "sats",
"name": "satoshis",
},
],
"ltcTicker": null, "ltcTicker": null,
"tickerLoading": false, "tickerLoading": false,
} }

11
test/reducers/form.spec.js

@ -1,13 +1,4 @@
import formReducer, { import formReducer, { SET_FORM_TYPE } from '../../app/reducers/form'
SET_FORM_TYPE
} from '../../app/reducers/form'
// describe('reducers', () => {
// describe('formReducer', () => {
// }
// }
describe('reducers', () => { describe('reducers', () => {
describe('formReducer', () => { describe('formReducer', () => {

514
yarn.lock

@ -26,6 +26,75 @@
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30" resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30"
"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6"
dependencies:
"@babel/highlight" "7.0.0-beta.40"
"@babel/generator@7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea"
dependencies:
"@babel/types" "7.0.0-beta.40"
jsesc "^2.5.1"
lodash "^4.2.0"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/helper-function-name@7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6"
dependencies:
"@babel/helper-get-function-arity" "7.0.0-beta.40"
"@babel/template" "7.0.0-beta.40"
"@babel/types" "7.0.0-beta.40"
"@babel/helper-get-function-arity@7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e"
dependencies:
"@babel/types" "7.0.0-beta.40"
"@babel/highlight@7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255"
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^3.0.0"
"@babel/template@7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8"
dependencies:
"@babel/code-frame" "7.0.0-beta.40"
"@babel/types" "7.0.0-beta.40"
babylon "7.0.0-beta.40"
lodash "^4.2.0"
"@babel/traverse@^7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e"
dependencies:
"@babel/code-frame" "7.0.0-beta.40"
"@babel/generator" "7.0.0-beta.40"
"@babel/helper-function-name" "7.0.0-beta.40"
"@babel/types" "7.0.0-beta.40"
babylon "7.0.0-beta.40"
debug "^3.0.1"
globals "^11.1.0"
invariant "^2.2.0"
lodash "^4.2.0"
"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40":
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14"
dependencies:
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@types/node@^6.0.46": "@types/node@^6.0.46":
version "6.0.78" version "6.0.78"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470"
@ -90,9 +159,9 @@ acorn@^5.0.0, acorn@^5.0.3:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
acorn@^5.1.1: acorn@^5.4.0:
version "5.1.1" version "5.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
ajv-keywords@^1.0.0: ajv-keywords@^1.0.0:
version "1.5.1" version "1.5.1"
@ -120,7 +189,7 @@ ajv@^5.0.0:
co "^4.6.0" co "^4.6.0"
json-stable-stringify "^1.0.1" json-stable-stringify "^1.0.1"
ajv@^5.1.0, ajv@^5.5.1: ajv@^5.1.0, ajv@^5.3.0, ajv@^5.5.1:
version "5.5.2" version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies: dependencies:
@ -129,7 +198,7 @@ ajv@^5.1.0, ajv@^5.5.1:
fast-json-stable-stringify "^2.0.0" fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0" json-schema-traverse "^0.3.0"
ajv@^5.1.5, ajv@^5.2.0: ajv@^5.1.5:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.0.tgz#c1735024c5da2ef75cc190713073d44f098bf486" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.0.tgz#c1735024c5da2ef75cc190713073d44f098bf486"
dependencies: dependencies:
@ -204,6 +273,12 @@ ansi-styles@^3.1.0:
dependencies: dependencies:
color-convert "^1.0.0" color-convert "^1.0.0"
ansi-styles@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
dependencies:
color-convert "^1.9.0"
anymatch@^1.3.0: anymatch@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
@ -336,8 +411,8 @@ arrify@^1.0.0, arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
asap@~2.0.3: asap@~2.0.3:
version "2.0.5" version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
asar-integrity@0.2.3: asar-integrity@0.2.3:
version "0.2.3" version "0.2.3"
@ -480,14 +555,16 @@ babel-core@^6.0.0, babel-core@^6.24.1, babel-core@^6.7.2:
slash "^1.0.0" slash "^1.0.0"
source-map "^0.5.0" source-map "^0.5.0"
babel-eslint@^7.2.3: babel-eslint@^8.2.1:
version "7.2.3" version "8.2.2"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b"
dependencies: dependencies:
babel-code-frame "^6.22.0" "@babel/code-frame" "^7.0.0-beta.40"
babel-traverse "^6.23.1" "@babel/traverse" "^7.0.0-beta.40"
babel-types "^6.23.0" "@babel/types" "^7.0.0-beta.40"
babylon "^6.17.0" babylon "^7.0.0-beta.40"
eslint-scope "~3.7.1"
eslint-visitor-keys "^1.0.0"
babel-generator@^6.18.0, babel-generator@^6.21.0, babel-generator@^6.25.0: babel-generator@^6.18.0, babel-generator@^6.21.0, babel-generator@^6.25.0:
version "6.25.0" version "6.25.0"
@ -669,8 +746,8 @@ babel-jest@^20.0.3:
babel-preset-jest "^20.0.3" babel-preset-jest "^20.0.3"
babel-loader@^7.1.0: babel-loader@^7.1.0:
version "7.1.0" version "7.1.3"
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.0.tgz#3fbf2581f085774bd9642dca9990e6d6c1491144" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.3.tgz#ff5b440da716e9153abb946251a9ab7670037b16"
dependencies: dependencies:
find-cache-dir "^1.0.0" find-cache-dir "^1.0.0"
loader-utils "^1.0.2" loader-utils "^1.0.2"
@ -1264,8 +1341,8 @@ babel-preset-babili@^0.1.4:
lodash.isplainobject "^4.0.6" lodash.isplainobject "^4.0.6"
babel-preset-env@^1.5.1: babel-preset-env@^1.5.1:
version "1.5.2" version "1.6.1"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.5.2.tgz#cd4ae90a6e94b709f97374b33e5f8b983556adef" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
dependencies: dependencies:
babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-syntax-trailing-function-commas "^6.22.0" babel-plugin-syntax-trailing-function-commas "^6.22.0"
@ -1419,13 +1496,20 @@ babel-register@^6.24.1:
mkdirp "^0.5.1" mkdirp "^0.5.1"
source-map-support "^0.4.2" source-map-support "^0.4.2"
babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@~6.23.0: babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@~6.23.0:
version "6.23.0" version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
dependencies: dependencies:
core-js "^2.4.0" core-js "^2.4.0"
regenerator-runtime "^0.10.0" regenerator-runtime "^0.10.0"
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0, babel-template@^6.7.0: babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0, babel-template@^6.7.0:
version "6.25.0" version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
@ -1436,7 +1520,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0, babel-te
babylon "^6.17.2" babylon "^6.17.2"
lodash "^4.2.0" lodash "^4.2.0"
babel-traverse@^6.18.0, babel-traverse@^6.20.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.25.0: babel-traverse@^6.18.0, babel-traverse@^6.20.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0:
version "6.25.0" version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
dependencies: dependencies:
@ -1450,7 +1534,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.20.0, babel-traverse@^6.23.1, babel-tr
invariant "^2.2.0" invariant "^2.2.0"
lodash "^4.2.0" lodash "^4.2.0"
babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.25.0: babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
version "6.25.0" version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
dependencies: dependencies:
@ -1467,7 +1551,11 @@ babili-webpack-plugin@^0.1.2:
babel-preset-babili "^0.1.4" babel-preset-babili "^0.1.4"
webpack-sources "^1.0.1" webpack-sources "^1.0.1"
babylon@^6.13.0, babylon@^6.16.1, babylon@^6.17.0, babylon@^6.17.2: babylon@7.0.0-beta.40, babylon@^7.0.0-beta.40:
version "7.0.0-beta.40"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a"
babylon@^6.13.0, babylon@^6.16.1, babylon@^6.17.2:
version "6.17.3" version "6.17.3"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48"
@ -1730,12 +1818,11 @@ browserify-zlib@^0.1.4:
dependencies: dependencies:
pako "~0.2.0" pako "~0.2.0"
browserslist@2.1.4, browserslist@^2.1.2: browserify-zlib@^0.2.0:
version "2.1.4" version "0.2.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
dependencies: dependencies:
caniuse-lite "^1.0.30000670" pako "~1.0.5"
electron-to-chromium "^1.3.11"
browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
version "1.7.7" version "1.7.7"
@ -1744,6 +1831,13 @@ browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.3.6, browserslist@^1.5
caniuse-db "^1.0.30000639" caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7" electron-to-chromium "^1.2.7"
browserslist@^2.1.2:
version "2.1.4"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053"
dependencies:
caniuse-lite "^1.0.30000670"
electron-to-chromium "^1.3.11"
browserslist@^2.11.0: browserslist@^2.11.0:
version "2.11.0" version "2.11.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.0.tgz#50350d6873a82ebe0f3ae5483658c571ae5f9d7d" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.0.tgz#50350d6873a82ebe0f3ae5483658c571ae5f9d7d"
@ -1751,6 +1845,13 @@ browserslist@^2.11.0:
caniuse-lite "^1.0.30000784" caniuse-lite "^1.0.30000784"
electron-to-chromium "^1.3.30" electron-to-chromium "^1.3.30"
browserslist@^2.11.3:
version "2.11.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2"
dependencies:
caniuse-lite "^1.0.30000792"
electron-to-chromium "^1.3.30"
bs58@=2.0.0: bs58@=2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5" resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5"
@ -1903,10 +2004,14 @@ caniuse-api@^1.5.2:
lodash.memoize "^4.1.2" lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0" lodash.uniq "^4.5.0"
caniuse-db@1.0.30000671, caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000671" version "1.0.30000671"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000671.tgz#9f071bbc7b96994638ccbaf47829d58a1577a8ed" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000671.tgz#9f071bbc7b96994638ccbaf47829d58a1577a8ed"
caniuse-db@^1.0.30000794:
version "1.0.30000810"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000810.tgz#bd25830c41efab64339a2e381f49677343c84509"
caniuse-lite@^1.0.30000670: caniuse-lite@^1.0.30000670:
version "1.0.30000683" version "1.0.30000683"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000683.tgz#a7573707cf2acc9217ca6484d1dfbc9f13898364" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000683.tgz#a7573707cf2acc9217ca6484d1dfbc9f13898364"
@ -1915,6 +2020,10 @@ caniuse-lite@^1.0.30000784:
version "1.0.30000787" version "1.0.30000787"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000787.tgz#a76c4fa1d6ac00640447ec83c1e7c6b33dd615c5" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000787.tgz#a76c4fa1d6ac00640447ec83c1e7c6b33dd615c5"
caniuse-lite@^1.0.30000792:
version "1.0.30000810"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000810.tgz#47585fffce0e9f3593a6feea4673b945424351d9"
capture-stack-trace@^1.0.0: capture-stack-trace@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
@ -1972,6 +2081,14 @@ chalk@^2.0.1:
escape-string-regexp "^1.0.5" escape-string-regexp "^1.0.5"
supports-color "^4.0.0" supports-color "^4.0.0"
chalk@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
dependencies:
ansi-styles "^3.2.0"
escape-string-regexp "^1.0.5"
supports-color "^5.2.0"
chalk@^2.3.0: chalk@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
@ -2128,6 +2245,12 @@ color-convert@^1.0.0, color-convert@^1.3.0:
dependencies: dependencies:
color-name "^1.1.1" color-name "^1.1.1"
color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
dependencies:
color-name "^1.1.1"
color-convert@~0.5.0: color-convert@~0.5.0:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
@ -2755,7 +2878,7 @@ debug@2.6.9, debug@^2.1.3, debug@^2.2.0:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@^3.0.0, debug@^3.1.0: debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies: dependencies:
@ -2918,12 +3041,11 @@ doctrine@1.5.0:
esutils "^2.0.2" esutils "^2.0.2"
isarray "^1.0.0" isarray "^1.0.0"
doctrine@^2.0.0: doctrine@^2.0.2, doctrine@^2.1.0:
version "2.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
dependencies: dependencies:
esutils "^2.0.2" esutils "^2.0.2"
isarray "^1.0.0"
doiuse@^2.4.1: doiuse@^2.4.1:
version "2.6.0" version "2.6.0"
@ -3450,25 +3572,25 @@ escope@^3.6.0:
esrecurse "^4.1.0" esrecurse "^4.1.0"
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-config-airbnb-base@^11.3.0: eslint-config-airbnb-base@^12.1.0:
version "11.3.1" version "12.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.1.tgz#c0ab108c9beed503cb999e4c60f4ef98eda0ed30" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
dependencies: dependencies:
eslint-restricted-globals "^0.1.1" eslint-restricted-globals "^0.1.1"
eslint-config-airbnb@^15.1.0: eslint-config-airbnb@^16.1.0:
version "15.1.0" version "16.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz#fd432965a906e30139001ba830f58f73aeddae8e" resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz#2546bfb02cc9fe92284bf1723ccf2e87bc45ca46"
dependencies: dependencies:
eslint-config-airbnb-base "^11.3.0" eslint-config-airbnb-base "^12.1.0"
eslint-formatter-pretty@^1.1.0: eslint-formatter-pretty@^1.3.0:
version "1.1.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-1.1.0.tgz#ab4d06da02fed8c13ae9f0dc540a433ef7ed6f5e" resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-1.3.0.tgz#985d9e41c1f8475f4a090c5dbd2dfcf2821d607e"
dependencies: dependencies:
ansi-escapes "^1.4.0" ansi-escapes "^2.0.0"
chalk "^1.1.3" chalk "^2.1.0"
log-symbols "^1.0.2" log-symbols "^2.0.0"
plur "^2.1.2" plur "^2.1.2"
string-width "^2.0.0" string-width "^2.0.0"
@ -3479,9 +3601,9 @@ eslint-import-resolver-node@^0.3.1:
debug "^2.6.8" debug "^2.6.8"
resolve "^1.2.0" resolve "^1.2.0"
eslint-import-resolver-webpack@^0.8.3: eslint-import-resolver-webpack@^0.8.4:
version "0.8.3" version "0.8.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.8.3.tgz#ad61e28df378a474459d953f246fd43f92675385" resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.8.4.tgz#0f7cd74bc9d7fc1773e8d5fc25baf864b2f87a42"
dependencies: dependencies:
array-find "^1.0.0" array-find "^1.0.0"
debug "^2.6.8" debug "^2.6.8"
@ -3491,7 +3613,7 @@ eslint-import-resolver-webpack@^0.8.3:
interpret "^1.0.0" interpret "^1.0.0"
is-absolute "^0.2.3" is-absolute "^0.2.3"
lodash.get "^3.7.0" lodash.get "^3.7.0"
node-libs-browser "^1.0.0" node-libs-browser "^1.0.0 || ^2.0.0"
resolve "^1.2.0" resolve "^1.2.0"
semver "^5.3.0" semver "^5.3.0"
@ -3502,31 +3624,25 @@ eslint-module-utils@^2.1.1:
debug "^2.6.8" debug "^2.6.8"
pkg-dir "^1.0.0" pkg-dir "^1.0.0"
eslint-plugin-compat@^1.0.4: eslint-plugin-compat@^2.2.0:
version "1.0.4" version "2.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-1.0.4.tgz#76e52038119a5080e2612cc4141d687f4d140398" resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-2.2.0.tgz#d612a9988d9fba66bcd8e86fcc1b551753aac995"
dependencies: dependencies:
babel-runtime "^6.23.0" babel-runtime "^6.26.0"
browserslist "2.1.4" browserslist "^2.11.3"
caniuse-db "1.0.30000671" caniuse-db "^1.0.30000794"
mdn-browser-compat-data "^0.0.20"
requireindex "^1.1.0" requireindex "^1.1.0"
eslint-plugin-flowtype-errors@^3.3.0: eslint-plugin-flowtype@^2.42.0:
version "3.3.0" version "2.46.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype-errors/-/eslint-plugin-flowtype-errors-3.3.0.tgz#4699c7fb73a1a2a6a77582d8621cc29ffd0e9a19" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.1.tgz#c4f81d580cd89c82bc3a85a1ccf4ae3a915143a4"
dependencies:
babel-runtime "^6.23.0"
slash "^1.0.0"
eslint-plugin-flowtype@^2.33.0:
version "2.34.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.34.0.tgz#b9875f314652e5081623c9d2b18a346bbb759c09"
dependencies: dependencies:
lodash "^4.15.0" lodash "^4.15.0"
eslint-plugin-import@^2.7.0: eslint-plugin-import@^2.8.0:
version "2.7.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169"
dependencies: dependencies:
builtin-modules "^1.1.1" builtin-modules "^1.1.1"
contains-path "^0.1.0" contains-path "^0.1.0"
@ -3535,17 +3651,17 @@ eslint-plugin-import@^2.7.0:
eslint-import-resolver-node "^0.3.1" eslint-import-resolver-node "^0.3.1"
eslint-module-utils "^2.1.1" eslint-module-utils "^2.1.1"
has "^1.0.1" has "^1.0.1"
lodash.cond "^4.3.0" lodash "^4.17.4"
minimatch "^3.0.3" minimatch "^3.0.3"
read-pkg-up "^2.0.0" read-pkg-up "^2.0.0"
eslint-plugin-jest@^20.0.3: eslint-plugin-jest@^21.7.0:
version "20.0.3" version "21.12.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-20.0.3.tgz#ec15eba6ac0ab44a67ebf6e02672ca9d7e7cba29" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-21.12.2.tgz#325f7c6a5078aed51ea087c33c26792337b5ba37"
eslint-plugin-jsx-a11y@^5.1.1: eslint-plugin-jsx-a11y@6.0.3:
version "5.1.1" version "6.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz#5c96bb5186ca14e94db1095ff59b3e2bd94069b1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz#54583d1ae442483162e040e13cc31865465100e5"
dependencies: dependencies:
aria-query "^0.7.0" aria-query "^0.7.0"
array-includes "^3.0.3" array-includes "^3.0.3"
@ -3553,57 +3669,62 @@ eslint-plugin-jsx-a11y@^5.1.1:
axobject-query "^0.1.0" axobject-query "^0.1.0"
damerau-levenshtein "^1.0.0" damerau-levenshtein "^1.0.0"
emoji-regex "^6.1.0" emoji-regex "^6.1.0"
jsx-ast-utils "^1.4.0" jsx-ast-utils "^2.0.0"
eslint-plugin-promise@^3.5.0: eslint-plugin-promise@^3.6.0:
version "3.5.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.6.0.tgz#54b7658c8f454813dc2a870aff8152ec4969ba75"
eslint-plugin-react@^7.2.1: eslint-plugin-react@^7.6.1:
version "7.2.1" version "7.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.2.1.tgz#c2673526ed6571b08c69c5f453d03f5f13e8ddbe" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160"
dependencies: dependencies:
doctrine "^2.0.0" doctrine "^2.0.2"
has "^1.0.1" has "^1.0.1"
jsx-ast-utils "^2.0.0" jsx-ast-utils "^2.0.1"
prop-types "^15.6.0"
eslint-restricted-globals@^0.1.1: eslint-restricted-globals@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
eslint-scope@^3.7.1: eslint-scope@^3.7.1, eslint-scope@~3.7.1:
version "3.7.1" version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
dependencies: dependencies:
esrecurse "^4.1.0" esrecurse "^4.1.0"
estraverse "^4.1.1" estraverse "^4.1.1"
eslint@^4.4.1: eslint-visitor-keys@^1.0.0:
version "4.4.1" version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.4.1.tgz#99cd7eafcffca2ff99a5c8f5f2a474d6364b4bd3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
eslint@^4.16.0:
version "4.18.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.1.tgz#b9138440cb1e98b2f44a0d578c6ecf8eae6150b0"
dependencies: dependencies:
ajv "^5.2.0" ajv "^5.3.0"
babel-code-frame "^6.22.0" babel-code-frame "^6.22.0"
chalk "^1.1.3" chalk "^2.1.0"
concat-stream "^1.6.0" concat-stream "^1.6.0"
cross-spawn "^5.1.0" cross-spawn "^5.1.0"
debug "^2.6.8" debug "^3.1.0"
doctrine "^2.0.0" doctrine "^2.1.0"
eslint-scope "^3.7.1" eslint-scope "^3.7.1"
espree "^3.5.0" eslint-visitor-keys "^1.0.0"
espree "^3.5.2"
esquery "^1.0.0" esquery "^1.0.0"
estraverse "^4.2.0"
esutils "^2.0.2" esutils "^2.0.2"
file-entry-cache "^2.0.0" file-entry-cache "^2.0.0"
functional-red-black-tree "^1.0.1" functional-red-black-tree "^1.0.1"
glob "^7.1.2" glob "^7.1.2"
globals "^9.17.0" globals "^11.0.1"
ignore "^3.3.3" ignore "^3.3.3"
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
inquirer "^3.0.6" inquirer "^3.0.6"
is-resolvable "^1.0.0" is-resolvable "^1.0.0"
js-yaml "^3.9.1" js-yaml "^3.9.1"
json-stable-stringify "^1.0.1" json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0" levn "^0.3.0"
lodash "^4.17.4" lodash "^4.17.4"
minimatch "^3.0.2" minimatch "^3.0.2"
@ -3611,19 +3732,20 @@ eslint@^4.4.1:
natural-compare "^1.4.0" natural-compare "^1.4.0"
optionator "^0.8.2" optionator "^0.8.2"
path-is-inside "^1.0.2" path-is-inside "^1.0.2"
pluralize "^4.0.0" pluralize "^7.0.0"
progress "^2.0.0" progress "^2.0.0"
require-uncached "^1.0.3" require-uncached "^1.0.3"
semver "^5.3.0" semver "^5.3.0"
strip-ansi "^4.0.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
table "^4.0.1" table "^4.0.1"
text-table "~0.2.0" text-table "~0.2.0"
espree@^3.5.0: espree@^3.5.2:
version "3.5.0" version "3.5.3"
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.0.tgz#98358625bdd055861ea27e2867ea729faf463d8d" resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.3.tgz#931e0af64e7fbbed26b050a29daad1fc64799fa6"
dependencies: dependencies:
acorn "^5.1.1" acorn "^5.4.0"
acorn-jsx "^3.0.0" acorn-jsx "^3.0.0"
esprima@^2.6.0, esprima@^2.7.1: esprima@^2.6.0, esprima@^2.7.1:
@ -3655,7 +3777,7 @@ estraverse@^1.9.1:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: estraverse@^4.0.0, estraverse@^4.1.1:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
@ -3787,7 +3909,7 @@ express@^4.13.3, express@^4.15.2, express@^4.15.3:
utils-merge "1.0.0" utils-merge "1.0.0"
vary "~1.1.1" vary "~1.1.1"
extend@~3.0.0, extend@~3.0.1: extend@3.0.1, extend@~3.0.0, extend@~3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@ -3904,9 +4026,9 @@ fbjs-scripts@^0.8.0:
semver "^5.1.0" semver "^5.1.0"
through2 "^2.0.0" through2 "^2.0.0"
fbjs@^0.8.9: fbjs@^0.8.16, fbjs@^0.8.9:
version "0.8.12" version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies: dependencies:
core-js "^1.0.0" core-js "^1.0.0"
isomorphic-fetch "^2.1.1" isomorphic-fetch "^2.1.1"
@ -4308,7 +4430,11 @@ global@^4.3.0:
min-document "^2.19.0" min-document "^2.19.0"
process "~0.5.1" process "~0.5.1"
globals@^9.0.0, globals@^9.17.0: globals@^11.0.1, globals@^11.1.0:
version "11.3.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0"
globals@^9.0.0:
version "9.18.0" version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
@ -4480,6 +4606,10 @@ has-flag@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
has-gulplog@^0.1.0: has-gulplog@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce"
@ -4721,6 +4851,10 @@ https-browserify@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
humanize-plus@^1.8.1: humanize-plus@^1.8.1:
version "1.8.2" version "1.8.2"
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030" resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
@ -4729,11 +4863,11 @@ iconv-lite@0.4.13:
version "0.4.13" version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
iconv-lite@^0.4.17, iconv-lite@~0.4.13: iconv-lite@^0.4.17:
version "0.4.17" version "0.4.17"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d"
iconv-lite@^0.4.19: iconv-lite@^0.4.19, iconv-lite@~0.4.13:
version "0.4.19" version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
@ -5437,8 +5571,8 @@ js-base64@^2.1.8, js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
js-tokens@^3.0.0: js-tokens@^3.0.0:
version "3.0.1" version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
js-yaml@^3.10.0: js-yaml@^3.10.0:
version "3.10.0" version "3.10.0"
@ -5530,6 +5664,10 @@ jsesc@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
jsesc@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
jsesc@~0.5.0: jsesc@~0.5.0:
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
@ -5546,6 +5684,10 @@ json-schema@0.2.3:
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
json-stable-stringify@^1.0.1: json-stable-stringify@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
@ -5602,16 +5744,18 @@ jsprim@^1.2.2:
json-schema "0.2.3" json-schema "0.2.3"
verror "1.10.0" verror "1.10.0"
jsx-ast-utils@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
jsx-ast-utils@^2.0.0: jsx-ast-utils@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz#ec06a3d60cf307e5e119dac7bad81e89f096f0f8" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz#ec06a3d60cf307e5e119dac7bad81e89f096f0f8"
dependencies: dependencies:
array-includes "^3.0.3" array-includes "^3.0.3"
jsx-ast-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
dependencies:
array-includes "^3.0.3"
kind-of@^2.0.1: kind-of@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5"
@ -5805,10 +5949,6 @@ lodash.clonedeep@^4.3.2:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
lodash.defaults@^4.0.1: lodash.defaults@^4.0.1:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
@ -5953,6 +6093,12 @@ log-symbols@^1.0.2:
dependencies: dependencies:
chalk "^1.0.0" chalk "^1.0.0"
log-symbols@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
dependencies:
chalk "^2.0.1"
lolex@^1.6.0: lolex@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
@ -6038,6 +6184,12 @@ md5@^2.1.0:
crypt "~0.0.1" crypt "~0.0.1"
is-buffer "~1.1.1" is-buffer "~1.1.1"
mdn-browser-compat-data@^0.0.20:
version "0.0.20"
resolved "https://registry.yarnpkg.com/mdn-browser-compat-data/-/mdn-browser-compat-data-0.0.20.tgz#26e4a226d96cc5318fdc504d6dfd86907752da5c"
dependencies:
extend "3.0.1"
media-typer@0.3.0: media-typer@0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -6295,8 +6447,8 @@ node-emoji@^1.8.1:
lodash.toarray "^4.4.0" lodash.toarray "^4.4.0"
node-fetch@^1.0.1: node-fetch@^1.0.1:
version "1.7.1" version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd400d5" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
dependencies: dependencies:
encoding "^0.1.11" encoding "^0.1.11"
is-stream "^1.0.1" is-stream "^1.0.1"
@ -6327,29 +6479,29 @@ node-int64@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
node-libs-browser@^1.0.0: "node-libs-browser@^1.0.0 || ^2.0.0":
version "1.1.1" version "2.1.0"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df"
dependencies: dependencies:
assert "^1.1.1" assert "^1.1.1"
browserify-zlib "^0.1.4" browserify-zlib "^0.2.0"
buffer "^4.3.0" buffer "^4.3.0"
console-browserify "^1.1.0" console-browserify "^1.1.0"
constants-browserify "^1.0.0" constants-browserify "^1.0.0"
crypto-browserify "^3.11.0" crypto-browserify "^3.11.0"
domain-browser "^1.1.1" domain-browser "^1.1.1"
events "^1.0.0" events "^1.0.0"
https-browserify "0.0.1" https-browserify "^1.0.0"
os-browserify "^0.2.0" os-browserify "^0.3.0"
path-browserify "0.0.0" path-browserify "0.0.0"
process "^0.11.0" process "^0.11.10"
punycode "^1.2.4" punycode "^1.2.4"
querystring-es3 "^0.2.0" querystring-es3 "^0.2.0"
readable-stream "^2.0.5" readable-stream "^2.3.3"
stream-browserify "^2.0.1" stream-browserify "^2.0.1"
stream-http "^2.3.1" stream-http "^2.7.2"
string_decoder "^0.10.25" string_decoder "^1.0.0"
timers-browserify "^1.4.2" timers-browserify "^2.0.4"
tty-browserify "0.0.0" tty-browserify "0.0.0"
url "^0.11.0" url "^0.11.0"
util "^0.10.3" util "^0.10.3"
@ -6666,6 +6818,10 @@ os-browserify@^0.2.0:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
os-browserify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
os-homedir@^1.0.0, os-homedir@^1.0.1: os-homedir@^1.0.0, os-homedir@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
@ -6730,6 +6886,10 @@ pako@~0.2.0:
version "0.2.9" version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
param-case@2.1.x: param-case@2.1.x:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
@ -6908,9 +7068,9 @@ plur@^2.0.0, plur@^2.1.2:
dependencies: dependencies:
irregular-plurals "^1.0.0" irregular-plurals "^1.0.0"
pluralize@^4.0.0: pluralize@^7.0.0:
version "4.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
pn@^1.0.0: pn@^1.0.0:
version "1.0.0" version "1.0.0"
@ -7289,7 +7449,11 @@ process-nextick-args@~1.0.6:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
process@^0.11.0, process@~0.11.0: process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
process@^0.11.0, process@^0.11.10:
version "0.11.10" version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
@ -7309,18 +7473,26 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
promise@^7.1.1: promise@^7.1.1:
version "7.1.1" version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies: dependencies:
asap "~2.0.3" asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8: prop-types@^15.5.10, prop-types@^15.5.4:
version "15.5.10" version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies: dependencies:
fbjs "^0.8.9" fbjs "^0.8.9"
loose-envify "^1.3.1" loose-envify "^1.3.1"
prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.3.1"
object-assign "^4.1.1"
proxy-addr@~1.1.4: proxy-addr@~1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3"
@ -7390,11 +7562,11 @@ qr.js@0.0.0:
version "0.0.0" version "0.0.0"
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
qrcode.react@^0.7.1: qrcode.react@0.8.0:
version "0.7.1" version "0.8.0"
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-0.7.1.tgz#6fc5017f17025048919115581da5e375b0946742" resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-0.8.0.tgz#413b31cc3b62910e39513f7bead945e01c4c34fb"
dependencies: dependencies:
prop-types "^15.5.8" prop-types "^15.6.0"
qr.js "0.0.0" qr.js "0.0.0"
qs@6.4.0, qs@~6.4.0: qs@6.4.0, qs@~6.4.0:
@ -7700,6 +7872,18 @@ readable-stream@^2.2.2:
string_decoder "~1.0.3" string_decoder "~1.0.3"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.0.3"
util-deprecate "~1.0.1"
readdirp@^2.0.0: readdirp@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
@ -7770,6 +7954,10 @@ regenerator-runtime@^0.10.0:
version "0.10.5" version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
regenerator-transform@0.9.11: regenerator-transform@0.9.11:
version "0.9.11" version "0.9.11"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
@ -8534,6 +8722,16 @@ stream-http@^2.3.1:
to-arraybuffer "^1.0.0" to-arraybuffer "^1.0.0"
xtend "^4.0.0" xtend "^4.0.0"
stream-http@^2.7.2:
version "2.8.0"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10"
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
readable-stream "^2.3.3"
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
strict-uri-encode@^1.0.0: strict-uri-encode@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@ -8570,7 +8768,7 @@ string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31" version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
string_decoder@~1.0.0, string_decoder@~1.0.3: string_decoder@^1.0.0, string_decoder@~1.0.0, string_decoder@~1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies: dependencies:
@ -8812,6 +9010,12 @@ supports-color@^4.2.1:
dependencies: dependencies:
has-flag "^2.0.0" has-flag "^2.0.0"
supports-color@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a"
dependencies:
has-flag "^3.0.0"
svg-tags@^1.0.0: svg-tags@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
@ -9006,18 +9210,18 @@ timed-out@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
timers-browserify@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
dependencies:
process "~0.11.0"
timers-browserify@^2.0.2: timers-browserify@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86"
dependencies: dependencies:
setimmediate "^1.0.4" setimmediate "^1.0.4"
timers-browserify@^2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.6.tgz#241e76927d9ca05f4d959819022f5b3664b64bae"
dependencies:
setimmediate "^1.0.4"
tmp@^0.0.31: tmp@^0.0.31:
version "0.0.31" version "0.0.31"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
@ -9036,6 +9240,10 @@ to-fast-properties@^1.0.1:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
toggle-selection@^1.0.3: toggle-selection@^1.0.3:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
@ -9128,8 +9336,8 @@ typeforce@^1.11.3:
inherits "^2.0.1" inherits "^2.0.1"
ua-parser-js@^0.7.9: ua-parser-js@^0.7.9:
version "0.7.12" version "0.7.17"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
uglify-js@3.0.x: uglify-js@3.0.x:
version "3.0.15" version "3.0.15"

Loading…
Cancel
Save