Browse Source

Merge pull request #865 from mrfelton/feat/ui-crypto-dropdown

feat(ui): add Dropdown component
renovate/lint-staged-8.x
JimmyMow 6 years ago
committed by GitHub
parent
commit
050190ab81
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 51
      app/components/Activity/InvoiceModal/InvoiceModal.js
  2. 46
      app/components/Activity/InvoiceModal/InvoiceModal.scss
  3. 48
      app/components/Activity/PaymentModal/PaymentModal.js
  4. 46
      app/components/Activity/PaymentModal/PaymentModal.scss
  5. 47
      app/components/Activity/TransactionModal/TransactionModal.js
  6. 46
      app/components/Activity/TransactionModal/TransactionModal.scss
  7. 35
      app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js
  8. 49
      app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss
  9. 48
      app/components/Form/Pay/Pay.js
  10. 49
      app/components/Form/Pay/Pay.scss
  11. 43
      app/components/Form/Request/Request.js
  12. 51
      app/components/Form/Request/Request.scss
  13. 3
      app/components/UI/Button.js
  14. 181
      app/components/UI/Dropdown.js
  15. 3
      app/components/UI/GlobalStyle.js
  16. 10
      app/components/UI/Page.js
  17. 216
      app/components/Wallet/Wallet.js
  18. 169
      app/components/Wallet/Wallet.scss
  19. 7
      app/containers/Activity.js
  20. 58
      app/containers/App.js
  21. 21
      app/reducers/activity.js
  22. 18
      app/reducers/contactsform.js
  23. 15
      app/reducers/info.js
  24. 17
      app/reducers/payform.js
  25. 18
      app/reducers/requestform.js
  26. 36
      app/reducers/ticker.js
  27. 6
      app/themes/dark.js
  28. 6
      app/themes/light.js
  29. 1
      package.json
  30. 58
      stories/components/dropdown.stories.js
  31. 7
      test/unit/components/Form.spec.js
  32. 24
      test/unit/components/Form/Pay.spec.js
  33. 14
      test/unit/components/Form/Request.spec.js
  34. 30
      test/unit/components/UI/Dropdown.spec.js
  35. 4
      test/unit/components/UI/__snapshots__/Button.spec.js.snap
  36. 81
      test/unit/components/UI/__snapshots__/Dropdown.spec.js.snap
  37. 7
      test/unit/components/UI/__snapshots__/Page.spec.js.snap
  38. 40
      test/unit/reducers/__snapshots__/activity.spec.js.snap
  39. 3
      test/unit/reducers/__snapshots__/info.spec.js.snap
  40. 11
      test/unit/reducers/activity.spec.js
  41. 9
      yarn.lock

51
app/components/Activity/InvoiceModal/InvoiceModal.js

@ -1,18 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
import QRCode from 'qrcode.react'
import copy from 'copy-to-clipboard'
import { showNotification } from 'lib/utils/notifications'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import Value from 'components/Value'
import Dropdown from 'components/UI/Dropdown'
import { FormattedDate, FormattedTime, FormattedMessage } from 'react-intl'
import Countdown from '../Countdown'
import messages from './messages'
import styles from './InvoiceModal.scss'
const InvoiceModal = ({
@ -20,13 +15,7 @@ const InvoiceModal = ({
ticker,
currentTicker,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
toggleCurrencyProps: { currencyFilters, setCurrency }
}) => {
const copyPaymentRequest = () => {
copy(invoice.payment_request)
@ -56,30 +45,18 @@ const InvoiceModal = ({
<section className={styles.right}>
<div className={styles.details}>
<section className={styles.amount}>
<h1>
<Value
value={invoice.finalAmount}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
</h1>
<section
className={styles.currentCurrency}
onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}
>
<span>{currencyName}</span>
<span>
<FaAngleDown />
</span>
</section>
<ul className={showCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
<Value
value={invoice.finalAmount}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
<Dropdown
activeKey={ticker.currency}
items={currencyFilters}
onChange={setCurrency}
ml={2}
/>
</section>
<section className={styles.date}>
<p>

46
app/components/Activity/InvoiceModal/InvoiceModal.scss

@ -46,51 +46,7 @@
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: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid var(--lightestBackground);
&:hover {
background: var(--lightestBackground);
}
}
}
font-size: 40px;
}
.date {

48
app/components/Activity/PaymentModal/PaymentModal.js

@ -1,16 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import Dropdown from 'components/UI/Dropdown'
import PaperPlane from 'components/Icon/PaperPlane'
import Zap from 'components/Icon/Zap'
import Value from 'components/Value'
import { FormattedDate, FormattedTime, FormattedMessage } from 'react-intl'
import messages from './messages'
import styles from './PaymentModal.scss'
const PaymentModal = ({
@ -18,13 +13,7 @@ const PaymentModal = ({
ticker,
currentTicker,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
toggleCurrencyProps: { currencyName, currencyFilters, setCurrency }
}) => (
<div className={styles.container}>
<header className={styles.header}>
@ -55,31 +44,14 @@ const PaymentModal = ({
</header>
<div className={styles.amount}>
<h1>
<i className={`${styles.symbol} ${payment.value > 0 ? styles.active : undefined}`}>-</i>
<Value
value={payment.value}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
</h1>
<section
className={styles.currentCurrency}
onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}
>
<span>{currencyName}</span>
<span>
<FaAngleDown />
</span>
<ul className={showCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</section>
<i className={`${styles.symbol} ${payment.value > 0 ? styles.active : undefined}`}>-</i>
<Value
value={payment.value}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
<Dropdown activeKey={ticker.currency} items={currencyFilters} onChange={setCurrency} ml={2} />
</div>
<div className={styles.date}>

46
app/components/Activity/PaymentModal/PaymentModal.scss

@ -60,51 +60,7 @@
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: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid var(--lightestBackground);
&:hover {
background: var(--lightestBackground);
}
}
}
}
font-size: 40px;
}
.date {

47
app/components/Activity/TransactionModal/TransactionModal.js

@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import Dropdown from 'components/UI/Dropdown'
import PaperPlane from 'components/Icon/PaperPlane'
import Hand from 'components/Icon/Hand'
import ChainLink from 'components/Icon/ChainLink'
@ -19,13 +19,7 @@ const TransactionModal = ({
currentTicker,
network,
toggleCurrencyProps: {
setActivityModalCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick
}
toggleCurrencyProps: { currencyName, currencyFilters, setCurrency }
}) => (
<div className={styles.container}>
<header className={styles.header}>
@ -72,33 +66,16 @@ const TransactionModal = ({
</header>
<div className={styles.amount}>
<h1>
<i className={`${styles.symbol} ${transaction.received ? styles.active : undefined}`}>
{transaction.received ? '+' : '-'}
</i>
<Value
value={transaction.amount}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
</h1>
<section
className={styles.currentCurrency}
onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}
>
<span>{currencyName}</span>
<span>
<FaAngleDown />
</span>
<ul className={showCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</section>
<i className={`${styles.symbol} ${transaction.received ? styles.active : undefined}`}>
{transaction.received ? '+' : '-'}
</i>
<Value
value={transaction.amount}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
<Dropdown activeKey={ticker.currency} items={currencyFilters} onChange={setCurrency} ml={2} />
</div>
<div className={styles.date}>

46
app/components/Activity/TransactionModal/TransactionModal.scss

@ -68,51 +68,7 @@
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: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
border-bottom: 1px solid var(--lightestBackground);
&:hover {
background: var(--lightestBackground);
}
}
}
}
font-size: 40px;
}
.date {

35
app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js

@ -1,11 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import FaExclamationCircle from 'react-icons/lib/fa/exclamation-circle'
import AmountInput from 'components/AmountInput'
import Button from 'components/UI/Button'
import Dropdown from 'components/UI/Dropdown'
import { FormattedNumber, FormattedMessage } from 'react-intl'
import messages from './messages'
@ -37,14 +37,7 @@ class SubmitChannelForm extends React.Component {
ticker,
toggleCurrencyProps: {
setContactsCurrencyFilters,
showCurrencyFilters,
currencyName,
currentCurrencyFilters,
onCurrencyFilterClick,
contactFormFiatAmount
}
toggleCurrencyProps: { currencyFilters, onCurrencyFilterClick, contactFormFiatAmount }
} = this.props
const renderTitle = () => {
@ -126,24 +119,12 @@ class SubmitChannelForm extends React.Component {
onChangeEvent={updateContactCapacity}
ref={this.amountInput}
/>
<div className={styles.currency}>
<section
className={styles.currentCurrency}
onClick={() => setContactsCurrencyFilters(!showCurrencyFilters)}
>
<span>{currencyName}</span>
<span>
<FaAngleDown />
</span>
</section>
<ul className={showCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</div>
<Dropdown
activeKey={ticker.currency}
items={currencyFilters}
onChange={onCurrencyFilterClick}
ml={2}
/>
</div>
<div className={styles.fiatAmount}>
{'≈ '}

49
app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss

@ -1,7 +1,6 @@
@import 'styles/variables.scss';
.content {
padding: 0 40px;
color: var(--primaryText);
margin: 0 auto;
width: 500px;
@ -72,7 +71,7 @@
input {
font-size: 25px;
max-width: 110px;
max-width: 180px;
background: transparent;
outline: none;
border: 1px solid #404040;
@ -91,52 +90,6 @@
-webkit-text-fill-color: initial;
}
.currency {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
padding-left: 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: 30px;
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
&:hover {
background: var(--lightestBackground);
}
}
}
}
.fiatAmount {
margin-top: 20px;
opacity: 0.75;

48
app/components/Form/Pay/Pay.js

@ -3,11 +3,11 @@ import PropTypes from 'prop-types'
import PaperPlane from 'components/Icon/PaperPlane'
import ChainLink from 'components/Icon/ChainLink'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import { btc } from 'lib/utils'
import AmountInput from 'components/AmountInput'
import Button from 'components/UI/Button'
import Dropdown from 'components/UI/Dropdown'
import { FormattedNumber, FormattedMessage, injectIntl } from 'react-intl'
import messages from './messages'
@ -46,28 +46,20 @@ class Pay extends Component {
render() {
const {
payform: { payInput, showErrors, invoice, showCurrencyFilters },
payform: { payInput, showErrors, invoice },
nodes,
ticker,
isOnchain,
isLn,
currentAmount,
fiatAmount,
payFormIsValid: { errors, isValid },
currentCurrencyFilters,
currencyName,
currencyFilters,
setPayAmount,
onPayAmountBlur,
setPayInput,
onPayInputBlur,
setCurrencyFilters,
onPaySubmit,
setCurrency,
intl
} = this.props
@ -87,9 +79,7 @@ class Pay extends Component {
// change the input amount
setPayAmount(btc.convert(ticker.currency, currency, currentAmount))
}
setCurrency(currency)
setCurrencyFilters(false)
}
return (
@ -164,24 +154,12 @@ class Pay extends Component {
onBlurEvent={onPayAmountBlur}
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 : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</div>
<Dropdown
activeKey={ticker.currency}
items={currencyFilters}
onChange={onCurrencyFilterClick}
ml={2}
/>
</div>
<div className={styles.fiatAmount}>
@ -221,7 +199,6 @@ Pay.propTypes = {
showErrors: PropTypes.object.isRequired
}).isRequired,
currencyName: PropTypes.string.isRequired,
isOnchain: PropTypes.bool.isRequired,
isLn: PropTypes.bool.isRequired,
currentAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
@ -230,21 +207,16 @@ Pay.propTypes = {
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
currencyFilters: PropTypes.array.isRequired
}
export default injectIntl(Pay)

49
app/components/Form/Pay/Pay.scss

@ -1,7 +1,6 @@
@import 'styles/variables.scss';
.container {
padding: 0 40px;
margin: 0 auto;
width: 500px;
}
@ -50,7 +49,6 @@
.amount .bottom {
display: flex;
flex-direction: row;
align-items: center;
input {
@ -95,53 +93,6 @@
}
}
.currency {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
margin-left: 12px;
.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;
z-index: z("form", "pay");
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
&:hover {
background: var(--darkestBackground);
}
}
}
}
.fiatAmount {
margin-top: 10px;
opacity: 0.5;

43
app/components/Form/Request/Request.js

@ -2,11 +2,11 @@ import React from 'react'
import PropTypes from 'prop-types'
import Hand from 'components/Icon/Hand'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import { btc } from 'lib/utils'
import AmountInput from 'components/AmountInput'
import Button from 'components/UI/Button'
import Dropdown from 'components/UI/Dropdown'
import { FormattedNumber, FormattedMessage, injectIntl } from 'react-intl'
import messages from './messages'
@ -14,27 +14,20 @@ import messages from './messages'
import styles from './Request.scss'
const Request = ({
requestform: { amount, memo, showCurrencyFilters },
requestform: { amount, memo },
ticker,
setRequestAmount,
setRequestMemo,
setCurrency,
setRequestCurrencyFilters,
currencyName,
requestFiatAmount,
currentCurrencyFilters,
currencyFilters,
onRequestSubmit,
intl
}) => {
const onCurrencyFilterClick = currency => {
// change the input amount
setRequestAmount(btc.convert(ticker.currency, currency, amount))
setCurrency(currency)
setRequestCurrencyFilters(false)
}
return (
@ -61,24 +54,12 @@ const Request = ({
currency={ticker.currency}
onChangeEvent={setRequestAmount}
/>
<div className={styles.currency}>
<section
className={styles.currentCurrency}
onClick={() => setRequestCurrencyFilters(!showCurrencyFilters)}
>
<span>{currencyName}</span>
<span>
<FaAngleDown />
</span>
</section>
<ul className={showCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</div>
<Dropdown
activeKey={ticker.currency}
items={currencyFilters}
onChange={onCurrencyFilterClick}
ml={2}
/>
</div>
<div className={styles.fiatAmount}>
@ -123,17 +104,13 @@ Request.propTypes = {
amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
memo: PropTypes.string
}).isRequired,
requestFiatAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
currencyName: PropTypes.string.isRequired,
currentCurrencyFilters: PropTypes.array.isRequired,
currencyFilters: 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
}

51
app/components/Form/Request/Request.scss

@ -1,7 +1,6 @@
@import 'styles/variables.scss';
.container {
padding: 0 40px;
margin: 0 auto;
width: 500px;
}
@ -55,6 +54,9 @@
}
.bottom {
display: flex;
align-items: center;
input {
background: transparent;
outline: none;
@ -74,53 +76,6 @@
}
}
.currency {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
margin-left: 12px;
.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;
z-index: z("form", "request");
&.active {
visibility: visible;
}
li {
padding: 8px 15px;
background: var(--lightBackground);
cursor: pointer;
transition: 0.25s hover;
&:hover {
background: var(--darkestBackground);
}
}
}
}
.fiatAmount {
margin-top: 10px;
opacity: 0.5;

3
app/components/UI/Button.js

@ -10,9 +10,6 @@ const Wrapper = styled(BaseButton)`
border-radius: 5;
font-weight: normal;
line-height: '18px';
&:focus {
box-shadow: 0 0 3px ${props => props.theme.lightningOrange};
}
&:disabled {
opacity: 0.5;
}

181
app/components/UI/Dropdown.js

@ -0,0 +1,181 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Box, Flex } from 'rebass'
import styled, { withTheme } from 'styled-components'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import FaAngleUp from 'react-icons/lib/fa/angle-up'
import Check from 'components/Icon/Check'
import Text from 'components/UI/Text'
/**
* Container
*/
const DropdownContainer = styled(Flex)({})
DropdownContainer.defaultProps = {
flexDirection: 'column',
flexWrap: 'none',
display: 'relative'
}
/**
* Button
*/
const DropdownButton = styled(Box)({
appearance: 'none',
display: 'inline-block',
textAlign: 'center',
lineHeight: 'inherit',
textDecoration: 'none',
border: 'none',
outline: 'none',
background: 'transparent',
color: 'inherit',
cursor: 'pointer'
})
DropdownButton.defaultProps = {
as: 'button',
m: 0,
px: 0,
py: 2,
textAlign: 'left'
}
/**
* Menu
*/
const MenuContainer = styled(Box)({
display: 'relative'
})
const Menu = styled(Box)({
cursor: 'pointer',
display: 'inline-block',
position: 'absolute',
'z-index': '999',
'min-width': '70px',
'list-style-type': 'none',
'border-radius': '3px',
'box-shadow': '0 3px 4px 0 rgba(30, 30, 30, 0.5)'
})
Menu.defaultProps = {
as: 'ul',
m: 0,
p: 0,
bg: 'lightestBackground'
}
/**
* MenuItem
*/
const MenuItem = styled(Box)`
cursor: pointer;
&:hover {
background-color: ${props => props.theme.colors.darkestBackground};
}
`
MenuItem.defaultProps = {
as: 'li',
px: 2,
py: 2
}
/**
* @render react
* @name Dropdown
* @example
* <Dropdown items={[
* {name: 'Item 1', key: 'key1'},
* {name: 'Item 2', key: 'key2'}
* ]} activeKey="key1" />
*/
class Dropdown extends React.Component {
state = {
isOpen: false
}
onChange = this.onChange.bind(this)
toggleMenu = this.toggleMenu.bind(this)
setWrapperRef = this.setWrapperRef.bind(this)
handleClickOutside = this.handleClickOutside.bind(this)
static propTypes = {
activeKey: PropTypes.string.isRequired,
items: PropTypes.arrayOf(
PropTypes.shape({
key: PropTypes.string.isRequired,
name: PropTypes.string.isRequired
})
).isRequired,
onChange: PropTypes.func
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside)
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside)
}
onChange(key) {
const { onChange, activeKey } = this.props
if (key !== activeKey) {
if (onChange) {
onChange(key)
}
}
this.setState({ isOpen: false })
}
setWrapperRef(node) {
this.wrapperRef = node
}
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
this.setState({ isOpen: false })
}
}
toggleMenu() {
const { isOpen } = this.state
this.setState({ isOpen: !isOpen })
}
render() {
const { isOpen } = this.state
const { activeKey, items, theme, ...rest } = this.props
const selectedItem = items.find(c => c.key === activeKey)
return (
<DropdownContainer ref={this.setWrapperRef} {...rest}>
<DropdownButton type="button" onClick={this.toggleMenu}>
<Text textAlign="left">
{selectedItem ? selectedItem.name : activeKey}{' '}
{isOpen ? <FaAngleUp /> : <FaAngleDown />}
</Text>
</DropdownButton>
{isOpen && (
<MenuContainer>
<Menu>
{items.map(item => {
return (
<MenuItem key={item.key} onClick={() => this.onChange(item.key)}>
<Flex alignItems="center">
<Text width="18px">
{activeKey === item.key && (
<Check height="0.95em" color={theme.colors.superGreen} />
)}
</Text>
<Text>{item.name}</Text>
</Flex>
</MenuItem>
)
})}
</Menu>
</MenuContainer>
)}
</DropdownContainer>
)
}
}
export default withTheme(Dropdown)

3
app/components/UI/GlobalStyle.js

@ -11,11 +11,10 @@ const GlobalStyle = createGlobalStyle`
position: relative;
box-sizing: border-box;
overflow-y: hidden;
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
font-family: 'Roboto', Arial, Helvetica, sans-serif;
font-size: 13px;
}
`

10
app/components/UI/Page.js

@ -12,9 +12,13 @@ const Page = props => (
{...props}
as="article"
alignItems="stretch"
width="950"
bg="white"
css={{ height: '600px', 'min-height': '700px', 'min-width': '950px' }}
bg="darkestBackground"
css={{
'min-height': '700px',
'min-width': '950px',
'overflow-y': 'hidden',
'box-shadow': '0 3px 4px 0 rgba(30, 30, 30, 0.5)'
}}
/>
)

216
app/components/Wallet/Wallet.js

@ -2,11 +2,13 @@ import React from 'react'
import PropTypes from 'prop-types'
import FaAngleUp from 'react-icons/lib/fa/angle-up'
import FaAngleDown from 'react-icons/lib/fa/angle-down'
import { Box, Flex } from 'rebass'
import { btc, blockExplorer } from 'lib/utils'
import Value from 'components/Value'
import Settings from 'components/Settings'
import Button from 'components/UI/Button'
import Text from 'components/UI/Text'
import Dropdown from 'components/UI/Dropdown'
import CheckAnimated from 'components/Icon/CheckAnimated'
import ZapLogo from 'components/Icon/ZapLogo'
@ -29,10 +31,8 @@ const Wallet = ({
showPayLoadingScreen,
showSuccessPayScreen,
successTransactionScreen,
currentCurrencyFilters,
currencyName,
currencyFilters,
setCurrency,
setWalletCurrencyFilters,
network,
settingsProps,
paymentTimeout,
@ -43,24 +43,22 @@ const Wallet = ({
currentTicker[ticker.fiatTicker].last
)
const onCurrencyFilterClick = currency => {
setCurrency(currency)
setWalletCurrencyFilters(false)
}
return (
<div className={`${styles.wallet}`}>
<div className={styles.content}>
<header className={styles.header}>
<section className={styles.logo}>
{theme === 'light' ? (
<ZapLogoBlack width="70px" height="32px" />
) : (
<ZapLogo width="70px" height="32px" />
)}
{info.data.testnet && <span className={styles.testnetPill}>Testnet</span>}
</section>
<Flex as="header" justifyContent="space-between">
<Flex as="section" alignItems="center">
{theme === 'light' ? (
<ZapLogoBlack width="70px" height="32px" />
) : (
<ZapLogo width="70px" height="32px" />
)}
{info.data.testnet && (
<Text color="superGreen" fontSize={1} ml={2}>
Testnet
</Text>
)}
</Flex>
<Box as="section">
<section className={styles.user}>
<div
className={`${styles.alias} ${settingsProps.settings.settingsOpen &&
@ -72,102 +70,93 @@ const Wallet = ({
</div>
{settingsProps.settings.settingsOpen && <Settings {...settingsProps} />}
</section>
</header>
</Box>
</Flex>
<div className={styles.left}>
<div className={styles.leftContent}>
<span onClick={openReceiveModal} className={styles.qrCode}>
<Qrcode width="20px" height="32px" />
</span>
<div className={styles.details}>
<h1>
<span>
<Value
value={parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance)}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
<section className={styles.currencyContainer}>
<i className={styles.currency}>{currencyName}</i>
<span onClick={() => setWalletCurrencyFilters(!info.showWalletCurrencyFilters)}>
<FaAngleDown />
</span>
<ul className={info.showWalletCurrencyFilters ? styles.active : undefined}>
{currentCurrencyFilters.map(filter => (
<li key={filter.key} onClick={() => onCurrencyFilterClick(filter.key)}>
{filter.name}
</li>
))}
</ul>
</section>
</span>
</h1>
{Boolean(fiatAmount) && (
<span>
{'≈ '}
<FormattedNumber
currency={ticker.fiatTicker}
style="currency"
value={fiatAmount}
/>
</span>
)}
</div>
</div>
</div>
<div className={styles.right}>
<div className={styles.rightContent}>
<Button onClick={openPayForm} variant="primary" my={10} mx={7.5} width={100}>
<FormattedMessage {...messages.pay} />
</Button>
<Button onClick={openRequestForm} variant="primary" my={10} mx={7.5} width={100}>
<FormattedMessage {...messages.request} />
</Button>
</div>
<div className={styles.notificationBox}>
{showPayLoadingScreen && (
<Flex as="header" justifyContent="space-between" mt={4}>
<Box as="section">
<Flex alignItems="baseline">
<Box onClick={openReceiveModal} className={styles.qrCode} mr={2}>
<Qrcode width="20px" height="20px" />
</Box>
<Flex flexDirection="column">
<Text fontSize="24px" letterSpacing={2}>
<Value
value={parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance)}
currency={ticker.currency}
currentTicker={currentTicker}
fiatTicker={ticker.fiatTicker}
/>
</Text>
</Flex>
<Dropdown
activeKey={ticker.currency}
items={currencyFilters}
onChange={setCurrency}
ml={2}
/>
</Flex>
<Box ml={30} mt={1}>
{Boolean(fiatAmount) && (
<span>
<div className={styles.spinnerContainer}>
<section className={`${styles.spinner} ${styles.icon}`} />
<span className={styles.timeout}>{paymentTimeout / 1000}</span>
</div>
<section>
<FormattedMessage {...messages.sending_tx} />
</section>
{'≈ '}
<FormattedNumber currency={ticker.fiatTicker} style="currency" value={fiatAmount} />
</span>
)}
{showSuccessPayScreen && (
<span>
<section className={styles.icon}>
<CheckAnimated />
</section>
<section>
<FormattedMessage {...messages.payment_success} />
</section>
</span>
)}
{successTransactionScreen.show && (
<span>
<section className={styles.icon}>
<CheckAnimated />
</section>
<section>
<span
className={styles.txLink}
onClick={() => {
return blockExplorer.showTransaction(network, successTransactionScreen.txid)
}}
>
<FormattedMessage {...messages.transaction_success} />
</span>
</section>
</span>
)}
</div>
</Box>
</Box>
<Box as="section">
<Button onClick={openPayForm} variant="primary" mx={7.5} width={100}>
<FormattedMessage {...messages.pay} />
</Button>
<Button onClick={openRequestForm} variant="primary" mx={7.5} width={100}>
<FormattedMessage {...messages.request} />
</Button>
</Box>
</Flex>
<Box mt={2}>
<div className={styles.notificationBox}>
{showPayLoadingScreen && (
<span>
<div className={styles.spinnerContainer}>
<section className={`${styles.spinner} ${styles.icon}`} />
<span className={styles.timeout}>{paymentTimeout / 1000}</span>
</div>
<section>
<FormattedMessage {...messages.sending_tx} />
</section>
</span>
)}
{showSuccessPayScreen && (
<span>
<section className={styles.icon}>
<CheckAnimated />
</section>
<section>
<FormattedMessage {...messages.payment_success} />
</section>
</span>
)}
{successTransactionScreen.show && (
<span>
<section className={styles.icon}>
<CheckAnimated />
</section>
<section>
<span
className={styles.txLink}
onClick={() => {
return blockExplorer.showTransaction(network, successTransactionScreen.txid)
}}
>
<FormattedMessage {...messages.transaction_success} />
</span>
</section>
</span>
)}
</div>
</div>
</Box>
</div>
)
}
@ -185,11 +174,10 @@ Wallet.propTypes = {
network: PropTypes.object.isRequired,
successTransactionScreen: PropTypes.object.isRequired,
settingsProps: PropTypes.object.isRequired,
currentCurrencyFilters: PropTypes.array.isRequired,
currencyFilters: PropTypes.array.isRequired,
currencyName: PropTypes.string.isRequired,
paymentTimeout: PropTypes.number.isRequired,
setCurrency: PropTypes.func.isRequired,
setWalletCurrencyFilters: PropTypes.func.isRequired
setCurrency: PropTypes.func.isRequired
}
export default Wallet

169
app/components/Wallet/Wallet.scss

@ -3,176 +3,27 @@
.wallet {
background: var(--lightBackground);
color: var(--primaryText);
transition: background 0.25s;
height: 150px;
height: 180px;
padding: 20px 40px;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
.logo span {
line-height: 24px;
svg {
width: 64px;
height: 24px;
vertical-align: top;
}
}
.testnetPill {
margin-left: 10px;
font-size: 10px;
border-radius: 100px;
color: rgb(57, 230, 115);
}
.user {
position: relative;
cursor: pointer;
transition: all 0.25s;
.aliasText:hover {
opacity: 0.5;
}
}
}
.left,
.right {
display: inline-block;
vertical-align: top;
width: 50%;
height: 150px;
.user {
position: relative;
cursor: pointer;
transition: all 0.25s;
.leftContent,
.rightContent {
padding: 25px 0;
.aliasText:hover {
opacity: 0.5;
}
}
.leftContent {
display: flex;
flex-direction: row;
.qrCode {
cursor: pointer;
}
.qrCode svg {
.qrCode {
cursor: pointer;
svg {
g {
fill: var(--gray);
}
}
.details {
display: flex;
flex-direction: column;
justify-content: center;
h1 {
display: flex;
flex-direction: row;
span:nth-child(1) {
font-size: 24px;
line-height: 32px;
font-weight: 500;
margin-left: 10px;
margin-bottom: 5px;
letter-spacing: 1.5px;
}
span:nth-child(2) svg {
color: var(--primaryText);
width: 20px;
height: 32px;
opacity: 1;
margin-left: 5px;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.5;
}
}
.currency {
margin-left: 2.5px;
}
.currencyContainer {
position: relative;
display: inline-block;
svg {
width: 25px;
height: 32px;
color: var(--white);
}
ul {
visibility: hidden;
position: absolute;
top: 30px;
&.active {
visibility: visible;
}
li {
font-size: 12px;
padding: 0 15px;
background: var(--lightestBackground);
cursor: pointer;
transition: 0.25s hover;
&:hover {
background: var(--darkestBackground);
}
}
}
}
}
.tickerButtons {
display: flex;
flex-direction: row;
section {
margin: 5px;
font-size: 10px;
border-radius: 5px;
border: 1px solid var(--white);
padding: 5px 10px;
cursor: pointer;
opacity: 0.5;
transition: 0.25s all;
&.active {
background: $main;
color: $spaceborder;
border-color: $spaceborder;
opacity: 1;
}
}
}
}
svg {
font-size: 100px;
color: $main;
}
}
.rightContent {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: right;
}
.notificationBox {

7
app/containers/Activity.js

@ -20,7 +20,6 @@ import {
import { walletAddress, openWalletModal } from 'reducers/address'
import { setFormType } from 'reducers/form'
import { payFormSelectors } from 'reducers/payform'
import { setWalletCurrencyFilters } from 'reducers/info'
import { setSettingsOpen, setActiveSubMenu, disableSubMenu } from 'reducers/settings'
import { setTheme, themeSelectors } from 'reducers/theme'
@ -47,7 +46,6 @@ const mapDispatchToProps = {
updateSearchActive,
updateSearchText,
setFormType,
setWalletCurrencyFilters,
setSettingsOpen,
setActiveSubMenu,
disableSubMenu,
@ -76,7 +74,7 @@ const mapStateToProps = state => ({
invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state),
currentTicker: tickerSelectors.currentTicker(state),
currentCurrencyFilters: tickerSelectors.currentCurrencyFilters(state),
currencyFilters: tickerSelectors.currencyFilters(state),
currencyName: tickerSelectors.currencyName(state),
@ -101,14 +99,13 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
showPayLoadingScreen: stateProps.showPayLoadingScreen,
showSuccessPayScreen: stateProps.payment.showSuccessPayScreen,
successTransactionScreen: stateProps.transaction.successTransactionScreen,
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyFilters: stateProps.currencyFilters,
currencyName: stateProps.currencyName,
network: stateProps.info.network,
paymentTimeout: stateProps.payment.paymentTimeout,
theme: stateProps.currentTheme,
setCurrency: dispatchProps.setCurrency,
setWalletCurrencyFilters: dispatchProps.setWalletCurrencyFilters,
walletAddress: dispatchProps.walletAddress,
openReceiveModal: dispatchProps.openWalletModal,
openPayForm: () => dispatchProps.setFormType('PAY_FORM'),

58
app/containers/App.js

@ -9,19 +9,8 @@ import { setCurrency, tickerSelectors, fetchTicker } from 'reducers/ticker'
import { closeWalletModal } from 'reducers/address'
import { fetchInfo, infoSelectors } from 'reducers/info'
import { setFormType } from 'reducers/form'
import {
setPayAmount,
setPayInput,
setCurrencyFilters,
updatePayErrors,
payFormSelectors
} from 'reducers/payform'
import {
setRequestAmount,
setRequestMemo,
setRequestCurrencyFilters,
requestFormSelectors
} from 'reducers/requestform'
import { setPayAmount, setPayInput, updatePayErrors, payFormSelectors } from 'reducers/payform'
import { setRequestAmount, setRequestMemo, requestFormSelectors } from 'reducers/requestform'
import { sendCoins } from 'reducers/transaction'
import { payInvoice } from 'reducers/payment'
import { createInvoice, fetchInvoice } from 'reducers/invoice'
@ -51,17 +40,12 @@ import {
updateContactCapacity,
setNode,
contactFormSelectors,
updateManualFormErrors,
setContactsCurrencyFilters
updateManualFormErrors
} from 'reducers/contactsform'
import { fetchBalance } from 'reducers/balance'
import { fetchDescribeNetwork } from 'reducers/network'
import { clearError } from 'reducers/error'
import {
hideActivityModal,
setActivityModalCurrencyFilters,
activitySelectors
} from 'reducers/activity'
import { hideActivityModal, activitySelectors } from 'reducers/activity'
import App from 'components/App'
import withLoading from 'components/withLoading'
@ -73,11 +57,9 @@ const mapDispatchToProps = {
setFormType,
setPayAmount,
setPayInput,
setCurrencyFilters,
updatePayErrors,
setRequestAmount,
setRequestMemo,
setRequestCurrencyFilters,
sendCoins,
payInvoice,
createInvoice,
@ -105,11 +87,9 @@ const mapDispatchToProps = {
setNode,
contactFormSelectors,
updateManualFormErrors,
setContactsCurrencyFilters,
setChannelFormType,
fetchDescribeNetwork,
hideActivityModal,
setActivityModalCurrencyFilters
hideActivityModal
}
const mapStateToProps = state => ({
@ -142,7 +122,7 @@ const mapStateToProps = state => ({
currentTheme: themeSelectors.currentTheme(state),
currentTicker: tickerSelectors.currentTicker(state),
currentCurrencyFilters: tickerSelectors.currentCurrencyFilters(state),
currencyFilters: tickerSelectors.currencyFilters(state),
currencyName: tickerSelectors.currencyName(state),
isOnchain: payFormSelectors.isOnchain(state),
@ -186,12 +166,11 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
showPayLoadingScreen: stateProps.showPayLoadingScreen,
payFormIsValid: stateProps.payFormIsValid,
payInputMin: stateProps.payInputMin,
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyFilters: stateProps.currencyFilters,
currencyName: stateProps.currencyName,
setPayAmount: dispatchProps.setPayAmount,
setPayInput: dispatchProps.setPayInput,
setCurrencyFilters: dispatchProps.setCurrencyFilters,
fetchInvoice: dispatchProps.fetchInvoice,
setCurrency: dispatchProps.setCurrency,
@ -250,15 +229,13 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
requestform: stateProps.requestform,
ticker: stateProps.ticker,
currentCurrencyFilters: stateProps.currentCurrencyFilters,
showCurrencyFilters: stateProps.showCurrencyFilters,
currencyFilters: stateProps.currencyFilters,
currencyName: stateProps.currencyName,
requestFiatAmount: stateProps.requestFiatAmount,
setRequestAmount: dispatchProps.setRequestAmount,
setRequestMemo: dispatchProps.setRequestMemo,
setCurrency: dispatchProps.setCurrency,
setRequestCurrencyFilters: dispatchProps.setRequestCurrencyFilters,
onRequestSubmit: () =>
dispatchProps.createInvoice(
@ -349,16 +326,9 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
hideActivityModal: dispatchProps.hideActivityModal,
toggleCurrencyProps: {
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyFilters: stateProps.currencyFilters,
currencyName: stateProps.currencyName,
showCurrencyFilters: stateProps.activity.modal.showCurrencyFilters,
setActivityModalCurrencyFilters: dispatchProps.setActivityModalCurrencyFilters,
setCurrencyFilters: dispatchProps.setCurrencyFilters,
onCurrencyFilterClick: currency => {
dispatchProps.setCurrency(currency)
dispatchProps.setActivityModalCurrencyFilters(false)
}
setCurrency: dispatchProps.setCurrency
}
}
@ -388,19 +358,15 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
ticker: stateProps.ticker,
toggleCurrencyProps: {
currentCurrencyFilters: stateProps.currentCurrencyFilters,
currencyFilters: stateProps.currencyFilters,
currencyName: stateProps.currencyName,
showCurrencyFilters: stateProps.contactsform.showCurrencyFilters,
contactFormFiatAmount: stateProps.contactFormFiatAmount,
setContactsCurrencyFilters: dispatchProps.setContactsCurrencyFilters,
setCurrencyFilters: dispatchProps.setCurrencyFilters,
setCurrency: dispatchProps.setCurrency,
onCurrencyFilterClick: currency => {
dispatchProps.updateContactCapacity(
btc.convert(stateProps.ticker.currency, currency, stateProps.contactsform.contactCapacity)
)
dispatchProps.setCurrency(currency)
dispatchProps.setContactsCurrencyFilters(false)
}
}
}

21
app/reducers/activity.js

@ -14,8 +14,7 @@ const initialState = {
],
modal: {
itemType: null,
itemId: null,
showCurrencyFilters: false
itemId: null
},
searchActive: false,
searchText: '',
@ -34,8 +33,6 @@ export const TOGGLE_PULLDOWN = 'TOGGLE_PULLDOWN'
export const TOGGLE_EXPIRED_REQUESTS = 'TOGGLE_EXPIRED_REQUESTS'
export const SET_ACTIVITY_MODAL_CURRENCY_FILTERS = 'SET_ACTIVITY_MODAL_CURRENCY_FILTERS'
export const UPDATE_SEARCH_ACTIVE = 'UPDATE_SEARCH_ACTIVE'
export const UPDATE_SEARCH_TEXT = 'UPDATE_SEARCH_TEXT'
@ -83,13 +80,6 @@ export function updateSearchText(searchText) {
}
}
export function setActivityModalCurrencyFilters(showCurrencyFilters) {
return {
type: SET_ACTIVITY_MODAL_CURRENCY_FILTERS,
showCurrencyFilters
}
}
export function toggleExpiredRequests() {
return {
type: TOGGLE_EXPIRED_REQUESTS
@ -112,15 +102,6 @@ const ACTION_HANDLERS = {
showExpiredRequests: !state.showExpiredRequests
}),
[SET_ACTIVITY_MODAL_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({
...state,
modal: {
itemType: state.modal.itemType,
itemId: state.modal.itemId,
showCurrencyFilters
}
}),
[UPDATE_SEARCH_ACTIVE]: (state, { searchActive }) => ({ ...state, searchActive }),
[UPDATE_SEARCH_TEXT]: (state, { searchText }) => ({ ...state, searchText })
}

18
app/reducers/contactsform.js

@ -19,9 +19,7 @@ const initialState = {
},
manualFormOpen: false,
submitChannelFormOpen: false,
showCurrencyFilters: false
submitChannelFormOpen: false
}
// Constants
@ -50,8 +48,6 @@ export const UPDATE_MANUAL_FORM_ERRORS = 'UPDATE_MANUAL_FORM_ERRORS'
export const UPDATE_MANUAL_FORM_SEARCH_QUERY = 'UPDATE_MANUAL_FORM_SEARCH_QUERY'
export const SET_CONTACTS_CURRENCY_FILTERS = 'SET_CONTACTS_CURRENCY_FILTERS'
// ------------------------------------
// Actions
// ------------------------------------
@ -145,13 +141,6 @@ export function updateManualFormErrors(errorsObject) {
}
}
export function setContactsCurrencyFilters(showCurrencyFilters) {
return {
type: SET_CONTACTS_CURRENCY_FILTERS,
showCurrencyFilters
}
}
// ------------------------------------
// Action Handlers
// ------------------------------------
@ -183,11 +172,6 @@ const ACTION_HANDLERS = {
[UPDATE_MANUAL_FORM_SEARCH_QUERY]: (state, { manualSearchQuery }) => ({
...state,
manualSearchQuery
}),
[SET_CONTACTS_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({
...state,
showCurrencyFilters
})
}

15
app/reducers/info.js

@ -8,7 +8,6 @@ import { walletAddress } from './address'
// ------------------------------------
export const GET_INFO = 'GET_INFO'
export const RECEIVE_INFO = 'RECEIVE_INFO'
export const SET_WALLET_CURRENCY_FILTERS = 'SET_WALLET_CURRENCY_FILTERS'
export const SET_HAS_SYNCED = 'SET_HAS_SYNCED'
// ------------------------------------
@ -20,13 +19,6 @@ export function getInfo() {
}
}
export function setWalletCurrencyFilters(showWalletCurrencyFilters) {
return {
type: SET_WALLET_CURRENCY_FILTERS,
showWalletCurrencyFilters
}
}
export const setHasSynced = hasSynced => {
return {
type: SET_HAS_SYNCED,
@ -85,10 +77,6 @@ const ACTION_HANDLERS = {
infoLoading: false,
network: data.testnet ? networks.testnet : networks.mainnet,
data
}),
[SET_WALLET_CURRENCY_FILTERS]: (state, { showWalletCurrencyFilters }) => ({
...state,
showWalletCurrencyFilters
})
}
@ -99,8 +87,7 @@ const initialState = {
infoLoading: false,
hasSynced: undefined,
network: {},
data: {},
showWalletCurrencyFilters: false
data: {}
}
// Selectors

17
app/reducers/payform.js

@ -18,8 +18,6 @@ const initialState = {
destination: ''
},
showCurrencyFilters: false,
showErrors: {
amount: false,
payInput: false
@ -31,11 +29,7 @@ const initialState = {
export const SET_PAY_AMOUNT = 'SET_PAY_AMOUNT'
export const SET_PAY_INPUT = 'SET_PAY_INPUT'
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 RESET_FORM = 'RESET_FORM'
// ------------------------------------
@ -62,13 +56,6 @@ export function setPayInvoice(invoice) {
}
}
export function setCurrencyFilters(showCurrencyFilters) {
return {
type: SET_PAY_CURRENCY_FILTERS,
showCurrencyFilters
}
}
export function updatePayErrors(errorsObject) {
return {
type: UPDATE_PAY_ERRORS,
@ -108,10 +95,6 @@ const ACTION_HANDLERS = {
invoice,
showErrors: Object.assign(state.showErrors, { amount: false })
}),
[SET_PAY_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({
...state,
showCurrencyFilters
}),
[UPDATE_PAY_ERRORS]: (state, { errorsObject }) => ({
...state,

18
app/reducers/requestform.js

@ -5,8 +5,7 @@ import { tickerSelectors } from './ticker'
// Initial State
const initialState = {
amount: '',
memo: '',
showCurrencyFilters: false
memo: ''
}
// Constants
@ -14,9 +13,6 @@ const initialState = {
export const SET_REQUEST_AMOUNT = 'SET_REQUEST_AMOUNT'
export const SET_REQUEST_MEMO = 'SET_REQUEST_MEMO'
export const SET_PAY_INVOICE = 'SET_PAY_INVOICE'
export const SET_REQUEST_CURRENCY_FILTERS = 'SET_REQUEST_CURRENCY_FILTERS'
export const RESET_FORM = 'RESET_FORM'
// ------------------------------------
@ -42,24 +38,12 @@ export function resetRequestForm() {
}
}
export function setRequestCurrencyFilters(showCurrencyFilters) {
return {
type: SET_REQUEST_CURRENCY_FILTERS,
showCurrencyFilters
}
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[SET_REQUEST_AMOUNT]: (state, { amount }) => ({ ...state, amount }),
[SET_REQUEST_MEMO]: (state, { memo }) => ({ ...state, memo }),
[SET_REQUEST_CURRENCY_FILTERS]: (state, { showCurrencyFilters }) => ({
...state,
showCurrencyFilters
}),
[RESET_FORM]: () => initialState
}

36
app/reducers/ticker.js

@ -92,13 +92,14 @@ const ACTION_HANDLERS = {
// Selectors
const tickerSelectors = {}
const cryptoSelector = state => state.ticker.crypto
const currencyFiltersSelector = state => state.ticker.currencyFilters
const currencySelector = state => state.ticker.currency
const currencyFiltersSelector = state => state.ticker.currencyFilters
const bitcoinTickerSelector = state => state.ticker.btcTicker
const litecoinTickerSelector = state => state.ticker.ltcTicker
const tickerLoading = state => state.ticker.tickerLoading
const tickerLoadingSelector = state => state.ticker.tickerLoading
tickerSelectors.tickerLoading = tickerLoading
tickerSelectors.currency = currencySelector
tickerSelectors.tickerLoading = tickerLoadingSelector
tickerSelectors.currentTicker = createSelector(
cryptoSelector,
@ -107,25 +108,30 @@ tickerSelectors.currentTicker = createSelector(
(crypto, btcTicker, ltcTicker) => (crypto === 'btc' ? btcTicker : ltcTicker)
)
tickerSelectors.currentCurrencyFilters = createSelector(
currencySelector,
tickerSelectors.currencyFilters = createSelector(
infoSelectors.networkSelector,
currencyFiltersSelector,
(currency, filters) => filters.filter(f => f.key !== currency)
(network, currencyFilters = []) => {
if (!network || !network.unitPrefix) {
return currencyFilters
}
return currencyFilters.map(item => {
item.name = `${network.unitPrefix}${item.name}`
return item
})
}
)
tickerSelectors.currencyName = createSelector(
currencySelector,
infoSelectors.networkSelector,
(currency, network) => {
tickerSelectors.currencyFilters,
(currency, currencyFilters = []) => {
let unit = currency
if (currency === 'btc') {
unit = 'BTC'
const selectedCurrency = currencyFilters.find(c => c.key === currency)
if (selectedCurrency) {
unit = selectedCurrency.name
}
if (currency === 'sats') {
unit = 'satoshis'
}
return `${network.unitPrefix}${unit}`
return unit
}
)

6
app/themes/dark.js

@ -17,6 +17,9 @@ const buttons = {
color: colors.lightningOrange,
'&:hover:enabled': {
backgroundColor: colors.highlight
},
'&:focus': {
backgroundColor: colors.highlight
}
},
primary: {
@ -28,6 +31,9 @@ const buttons = {
color: colors.white,
'&:hover:enabled': {
color: colors.lightningOrange
},
'&:focus': {
color: colors.lightningOrange
}
}
}

6
app/themes/light.js

@ -17,6 +17,9 @@ const buttons = {
color: colors.lightningOrange,
'&:hover:enabled': {
backgroundColor: colors.highlight
},
'&:focus': {
backgroundColor: colors.highlight
}
},
primary: {
@ -28,6 +31,9 @@ const buttons = {
color: colors.lightningOrange,
'&:hover:enabled': {
color: colors.black
},
'&:focus': {
color: colors.black
}
}
}

1
package.json

@ -211,6 +211,7 @@
"@babel/register": "^7.0.0",
"@commitlint/cli": "^7.2.1",
"@commitlint/config-conventional": "^7.1.2",
"@sambego/storybook-state": "^1.3.1",
"@storybook/addon-actions": "^4.0.2",
"@storybook/addon-console": "^1.1.0",
"@storybook/addon-info": "^4.0.2",

58
stories/components/dropdown.stories.js

@ -0,0 +1,58 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { StateDecorator, Store } from '@sambego/storybook-state'
import Dropdown from 'components/UI/Dropdown'
const store = new Store({
crypto: 'btc',
fiat: 'usd',
cryptoCurrencies: [
{
key: 'btc',
name: 'BTC'
},
{
key: 'bits',
name: 'bits'
},
{
key: 'sats',
name: 'satoshis'
}
],
fiatCurrencies: [
{
key: 'usd',
name: 'USD'
},
{
key: 'eur',
name: 'EUR'
},
{
key: 'gbp',
name: 'GBP'
}
]
})
storiesOf('Components.Dropdown', module)
.addDecorator(StateDecorator(store))
.add('Crypto', () => {
return (
<Dropdown
activeKey={store.get('crypto')}
items={store.get('cryptoCurrencies')}
onChange={crypto => store.set({ crypto })}
/>
)
})
.add('Fiat', () => {
return (
<Dropdown
activeKey={store.get('fiat')}
items={store.get('fiatCurrencies')}
onChange={fiat => store.set({ fiat })}
/>
)
})

7
test/unit/components/Form.spec.js

@ -29,12 +29,11 @@ const payFormProps = {
inputCaption: '',
showPayLoadingScreen: true,
payFormIsValid: {},
currentCurrencyFilters: [],
currencyFilters: [],
currencyName: '',
setPayAmount: () => {},
setPayInput: () => {},
setCurrencyFilters: () => {},
fetchInvoice: () => {},
setCurrency: () => {},
@ -49,15 +48,13 @@ const requestFormProps = {
requestform: {},
ticker: {},
currentCurrencyFilters: [],
showCurrencyFilters: true,
currencyFilters: [],
currencyName: '',
requestFiatAmount: '',
setRequestAmount: () => {},
setRequestMemo: () => {},
setCurrency: () => {},
setRequestCurrencyFilters: () => {},
onRequestSubmit: () => {}
}

24
test/unit/components/Form/Pay.spec.js

@ -2,8 +2,9 @@ import React from 'react'
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import 'jest-styled-components'
import { ThemeProvider } from 'styled-components'
import Pay from 'components/Form/Pay'
import { dark as theme } from 'themes'
import { dark } from 'themes'
import { mountWithIntl } from '../../__helpers__/intl-enzyme-test-helper'
configure({ adapter: new Adapter() })
@ -30,12 +31,11 @@ const defaultProps = {
inputCaption: '',
showPayLoadingScreen: true,
payFormIsValid: {},
currentCurrencyFilters: [],
currencyFilters: [],
currencyName: '',
setPayAmount: () => {},
setPayInput: () => {},
setCurrencyFilters: () => {},
fetchInvoice: () => {},
setCurrency: () => {},
@ -48,7 +48,11 @@ const defaultProps = {
describe('Form', () => {
describe('should show the form without an input', () => {
const el = mountWithIntl(<Pay {...defaultProps} theme={theme} />)
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...defaultProps} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)
@ -57,7 +61,11 @@ describe('Form', () => {
describe('should show lightning with a lightning input', () => {
const props = { ...defaultProps, isLn: true }
const el = mountWithIntl(<Pay {...props} theme={theme} />)
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...props} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)
@ -66,7 +74,11 @@ describe('Form', () => {
describe('should show on-chain with an on-chain input', () => {
const props = { ...defaultProps, isOnchain: true }
const el = mountWithIntl(<Pay {...props} theme={theme} />)
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...props} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)

14
test/unit/components/Form/Request.spec.js

@ -1,9 +1,9 @@
import React from 'react'
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import { ThemeProvider } from 'styled-components'
import Request from 'components/Form/Request'
import { dark } from 'themes'
import { mountWithIntl } from '../../__helpers__/intl-enzyme-test-helper'
configure({ adapter: new Adapter() })
@ -15,15 +15,13 @@ const defaultProps = {
fiatTicker: 'USD'
},
currentCurrencyFilters: [],
showCurrencyFilters: true,
currencyFilters: [],
currencyName: '',
requestFiatAmount: '',
setRequestAmount: () => {},
setRequestMemo: () => {},
setCurrency: () => {},
setRequestCurrencyFilters: () => {},
onRequestSubmit: () => {}
}
@ -31,7 +29,11 @@ const defaultProps = {
describe('Form', () => {
describe('should show request form when formType is REQUEST_FORM', () => {
const props = { ...defaultProps }
const el = mountWithIntl(<Request {...props} />)
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Request {...props} />
</ThemeProvider>
)
it('should contain Request', () => {
expect(el.contains('Request Payment')).toBe(true)
})

30
test/unit/components/UI/Dropdown.spec.js

@ -0,0 +1,30 @@
import React from 'react'
import Dropdown from 'components/UI/Dropdown'
import renderer from 'react-test-renderer'
import { dark } from 'themes'
const currencies = [
{
key: 'btc',
name: 'BTC'
},
{
key: 'bits',
name: 'bits'
},
{
key: 'sats',
name: 'satoshis'
}
]
const setCurrency = jest.fn()
describe('component.Dropdown', () => {
it('should render correctly', () => {
const tree = renderer
.create(<Dropdown theme={dark} activeKey="btc" items={currencies} onClick={setCurrency} />)
.toJSON()
expect(tree).toMatchSnapshot()
})
})

4
test/unit/components/UI/__snapshots__/Button.spec.js.snap

@ -33,10 +33,6 @@ exports[`component.UI.Button should render correctly 1`] = `
line-height: '18px';
}
.c0:focus {
box-shadow: 0 0 3px;
}
.c0:disabled {
opacity: 0.5;
}

81
test/unit/components/UI/__snapshots__/Dropdown.spec.js.snap

@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component.Dropdown should render correctly 1`] = `
.c2 {
font-size: m;
text-align: left;
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-wrap: none;
-ms-flex-wrap: none;
flex-wrap: none;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
}
.c1 {
margin: 0px;
padding-left: 0px;
padding-right: 0px;
padding-top: 8px;
padding-bottom: 8px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
display: inline-block;
text-align: center;
line-height: inherit;
-webkit-text-decoration: none;
text-decoration: none;
border: none;
outline: none;
background: transparent;
color: inherit;
cursor: pointer;
}
<div
className="c0"
display="relative"
onClick={[MockFunction]}
>
<button
className="c1"
onClick={[Function]}
type="button"
>
<div
className="c2"
fontSize="m"
>
BTC
<svg
fill="currentColor"
height="1em"
preserveAspectRatio="xMidYMid meet"
style={
Object {
"color": undefined,
"verticalAlign": "middle",
}
}
viewBox="0 0 40 40"
width="1em"
>
<g>
<path
d="m31 16.4q0 0.3-0.2 0.5l-10.4 10.4q-0.3 0.3-0.5 0.3t-0.6-0.3l-10.4-10.4q-0.2-0.2-0.2-0.5t0.2-0.5l1.2-1.1q0.2-0.2 0.5-0.2t0.5 0.2l8.8 8.8 8.7-8.8q0.3-0.2 0.5-0.2t0.6 0.2l1.1 1.1q0.2 0.2 0.2 0.5z"
/>
</g>
</svg>
</div>
</button>
</div>
`;

7
test/unit/components/UI/__snapshots__/Page.spec.js.snap

@ -2,11 +2,11 @@
exports[`component.UI.Page should render correctly 1`] = `
.c0 {
width: 950;
background-color: white;
height: 600px;
background-color: darkestBackground;
min-height: 700px;
min-width: 950px;
overflow-y: hidden;
box-shadow: 0 3px 4px 0 rgba(30,30,30,0.5);
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@ -19,6 +19,5 @@ exports[`component.UI.Page should render correctly 1`] = `
<article
className="c0"
width="950"
/>
`;

40
test/unit/reducers/__snapshots__/activity.spec.js.snap

@ -25,7 +25,6 @@ Object {
"modal": Object {
"itemId": null,
"itemType": null,
"showCurrencyFilters": false,
},
"searchActive": false,
"searchText": "",
@ -68,42 +67,6 @@ Object {
}
`;
exports[`reducers activityReducer should correctly setActivityModalCurrencyFilters 1`] = `
Object {
"filter": Object {
"key": "ALL_ACTIVITY",
"name": "All Activity",
},
"filterPulldown": false,
"filters": Array [
Object {
"key": "ALL_ACTIVITY",
"name": "all",
},
Object {
"key": "SENT_ACTIVITY",
"name": "sent",
},
Object {
"key": "REQUESTED_ACTIVITY",
"name": "requested",
},
Object {
"key": "PENDING_ACTIVITY",
"name": "pending",
},
],
"modal": Object {
"itemId": null,
"itemType": null,
"showCurrencyFilters": undefined,
},
"searchActive": false,
"searchText": "",
"showExpiredRequests": false,
}
`;
exports[`reducers activityReducer should correctly showActivityModal 1`] = `
Object {
"filter": Object {
@ -167,7 +130,6 @@ Object {
"modal": Object {
"itemId": null,
"itemType": null,
"showCurrencyFilters": false,
},
"searchActive": false,
"searchText": "",
@ -203,7 +165,6 @@ Object {
"modal": Object {
"itemId": null,
"itemType": null,
"showCurrencyFilters": false,
},
"searchActive": undefined,
"searchText": "",
@ -239,7 +200,6 @@ Object {
"modal": Object {
"itemId": null,
"itemType": null,
"showCurrencyFilters": false,
},
"searchActive": false,
"searchText": undefined,

3
test/unit/reducers/__snapshots__/info.spec.js.snap

@ -6,7 +6,6 @@ Object {
"hasSynced": undefined,
"infoLoading": true,
"network": Object {},
"showWalletCurrencyFilters": false,
}
`;
@ -32,7 +31,6 @@ Object {
"name": null,
"unitPrefix": "",
},
"showWalletCurrencyFilters": false,
}
`;
@ -42,6 +40,5 @@ Object {
"hasSynced": undefined,
"infoLoading": false,
"network": Object {},
"showWalletCurrencyFilters": false,
}
`;

11
test/unit/reducers/activity.spec.js

@ -3,7 +3,6 @@ import activityReducer, {
HIDE_ACTIVITY_MODAL,
CHANGE_FILTER,
TOGGLE_PULLDOWN,
SET_ACTIVITY_MODAL_CURRENCY_FILTERS,
UPDATE_SEARCH_ACTIVE,
UPDATE_SEARCH_TEXT
} from 'reducers/activity'
@ -26,10 +25,6 @@ describe('reducers', () => {
expect(TOGGLE_PULLDOWN).toEqual('TOGGLE_PULLDOWN')
})
it('should have SET_ACTIVITY_MODAL_CURRENCY_FILTERS', () => {
expect(SET_ACTIVITY_MODAL_CURRENCY_FILTERS).toEqual('SET_ACTIVITY_MODAL_CURRENCY_FILTERS')
})
it('should have UPDATE_SEARCH_ACTIVE', () => {
expect(UPDATE_SEARCH_ACTIVE).toEqual('UPDATE_SEARCH_ACTIVE')
})
@ -58,12 +53,6 @@ describe('reducers', () => {
).toMatchSnapshot()
})
it('should correctly setActivityModalCurrencyFilters', () => {
expect(
activityReducer(undefined, { type: SET_ACTIVITY_MODAL_CURRENCY_FILTERS })
).toMatchSnapshot()
})
it('should correctly updateSearchActive', () => {
expect(activityReducer(undefined, { type: UPDATE_SEARCH_ACTIVE })).toMatchSnapshot()
})

9
yarn.lock

@ -1101,6 +1101,13 @@
dependencies:
styled-system "^3.0.1"
"@sambego/storybook-state@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@sambego/storybook-state/-/storybook-state-1.3.1.tgz#9aec5f8e10e9df3f689eb70eff6903d41d9ce2a7"
integrity sha512-JFnu/AuAcpk8trsxUweFUGRF92a/qpg8ZpAclusOVs5jp7XvhiEsJehqGlqueytZpZyzFgTkla8Mjh/D98FaXQ==
dependencies:
uuid "^3.1.0"
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -16369,7 +16376,7 @@ uuid@3.2.1:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==
uuid@^3.0.1, uuid@^3.2.1, uuid@^3.3.2:
uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==

Loading…
Cancel
Save