JimmyMow
7 years ago
committed by
GitHub
84 changed files with 3033 additions and 1730 deletions
@ -1,22 +1,3 @@ |
|||
{ |
|||
"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 |
|||
} |
|||
"rules": {} |
|||
} |
|||
|
@ -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 |
@ -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; |
|||
} |
|||
} |
|||
|
@ -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 |
@ -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; |
|||
} |
@ -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 |
@ -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%); |
|||
} |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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 |
@ -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%); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -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 |
@ -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%); |
|||
} |
|||
} |
|||
} |
@ -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,0 +1,3 @@ |
|||
import Value from './Value' |
|||
|
|||
export default Value |
@ -1,103 +1,111 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import ReactModal from 'react-modal' |
|||
import copy from 'copy-to-clipboard' |
|||
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 { FaCopy } from 'react-icons/lib/fa' |
|||
import { MdClose } from 'react-icons/lib/md' |
|||
|
|||
import styles from './ReceiveModal.scss' |
|||
|
|||
const ReceiveModal = ({ |
|||
isOpen, hideActivityModal, pubkey, address, newAddress, qrCodeType, changeQrCode |
|||
}) => { |
|||
const customStyles = { |
|||
overlay: { |
|||
cursor: 'pointer' |
|||
}, |
|||
content: { |
|||
top: 'auto', |
|||
left: '0', |
|||
right: '0', |
|||
bottom: 'auto', |
|||
width: '40%', |
|||
margin: '50px auto', |
|||
borderRadius: 'none', |
|||
padding: '0' |
|||
class ReceiveModal extends React.Component { |
|||
constructor(props) { |
|||
super(props) |
|||
|
|||
this.state = { |
|||
qrCodeType: 1 |
|||
} |
|||
} |
|||
|
|||
const copyOnClick = (data) => { |
|||
copy(data) |
|||
showNotification('Noice', 'Successfully copied to clipboard') |
|||
} |
|||
render() { |
|||
const copyOnClick = (data) => { |
|||
copy(data) |
|||
showNotification('Noice', 'Successfully copied to clipboard') |
|||
} |
|||
|
|||
return ( |
|||
<ReactModal |
|||
isOpen={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> |
|||
const changeQrCode = () => { |
|||
if (this.state.qrCodeType === 1) { |
|||
this.setState({ qrCodeType: 2 }) |
|||
} else { |
|||
this.setState({ qrCodeType: 1 }) |
|||
} |
|||
} |
|||
|
|||
const { |
|||
isOpen, |
|||
pubkey, |
|||
address, |
|||
closeReceiveModal |
|||
} = this.props |
|||
|
|||
const { qrCodeType } = this.state |
|||
|
|||
if (!isOpen) { return null } |
|||
|
|||
return ( |
|||
<div className={styles.container}> |
|||
<header> |
|||
<div className={styles.qrcodes}> |
|||
<QRCode value={qrCodeType === 1 ? address : pubkey} /> |
|||
</div> |
|||
|
|||
<ul className={styles.tabs}> |
|||
<li className={qrCodeType === 1 && styles.active} onClick={changeQrCode}> |
|||
Wallet address |
|||
</li> |
|||
<li className={qrCodeType === 2 && styles.active} onClick={changeQrCode}> |
|||
Node pubkey |
|||
</li> |
|||
</ul> |
|||
</header> |
|||
<section> |
|||
<div className={styles.addressHeader}> |
|||
<h4>Deposit Address</h4> |
|||
<span className={styles.newAddress} onClick={() => newAddress('np2wkh')}>New Address</span> |
|||
</div> |
|||
<p> |
|||
<span>{address}</span> |
|||
<span onClick={() => copyOnClick(address)} className='hint--left' data-hint='Copy address'> |
|||
<FaCopy /> |
|||
</span> |
|||
</p> |
|||
</section> |
|||
|
|||
<section> |
|||
<h4>Node Public Key</h4> |
|||
<p> |
|||
<span>{pubkey}</span> |
|||
<span onClick={() => copyOnClick(pubkey)} className='hint--left' data-hint='Copy pubkey'> |
|||
<FaCopy /> |
|||
</span> |
|||
</p> |
|||
</section> |
|||
<div className={styles.closeContainer}> |
|||
<span onClick={closeReceiveModal}> |
|||
<Isvg src={x} /> |
|||
</span> |
|||
</div> |
|||
|
|||
<div className={styles.content}> |
|||
<section className={styles.left}> |
|||
<header className={styles.header}> |
|||
<h2>JimmyMow</h2> |
|||
|
|||
<div className={styles.qrCodeOptions}> |
|||
<div className={qrCodeType === 1 && styles.active} onClick={changeQrCode}>Node Pubkey</div> |
|||
<div className={qrCodeType === 2 && styles.active} onClick={changeQrCode}>Deposit Address</div> |
|||
</div> |
|||
</header> |
|||
|
|||
<div className={styles.qrCodeContainer}> |
|||
<QRCode |
|||
value={qrCodeType === 1 ? pubkey : address} |
|||
renderAs='svg' |
|||
size={150} |
|||
bgColor='transparent' |
|||
fgColor='white' |
|||
level='L' |
|||
/> |
|||
</div> |
|||
</section> |
|||
<section className={styles.right}> |
|||
<div className={styles.pubkey}> |
|||
<h4>Node Public Key</h4> |
|||
<p> |
|||
<span className={styles.data}>{pubkey}</span> |
|||
<span onClick={() => copyOnClick(pubkey)} className={`${styles.copy} hint--left`} data-hint='Copy pubkey'> |
|||
<Isvg src={copyIcon} /> |
|||
</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> |
|||
</ReactModal> |
|||
) |
|||
) |
|||
} |
|||
} |
|||
|
|||
ReceiveModal.propTypes = { |
|||
isOpen: PropTypes.bool.isRequired, |
|||
hideActivityModal: PropTypes.func.isRequired, |
|||
pubkey: PropTypes.string.isRequired, |
|||
address: PropTypes.string.isRequired, |
|||
newAddress: PropTypes.func.isRequired, |
|||
changeQrCode: PropTypes.func.isRequired, |
|||
qrCodeType: PropTypes.number.isRequired |
|||
closeReceiveModal: PropTypes.func.isRequired |
|||
} |
|||
|
|||
export default ReceiveModal |
|||
|
@ -1,106 +1,129 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.container { |
|||
position: relative; |
|||
height: 100vh; |
|||
background: $bluegrey; |
|||
} |
|||
|
|||
.closeContainer { |
|||
background: $lightgrey; |
|||
text-align: right; |
|||
padding: 10px; |
|||
padding: 20px 40px 0px; |
|||
|
|||
span { |
|||
color: $darkestgrey; |
|||
font-size: 20px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
opacity: 1.0; |
|||
transition: 0.25s all; |
|||
|
|||
.container { |
|||
header { |
|||
background: $lightgrey; |
|||
padding: 10px 40px 40px; |
|||
text-align: center; |
|||
|
|||
.qrcodes { |
|||
text-align: center; |
|||
margin-top: 20px; |
|||
&:hover { |
|||
opacity: 0.5; |
|||
} |
|||
} |
|||
|
|||
.tabs { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-top: 20px; |
|||
|
|||
li { |
|||
margin: 0 20px; |
|||
color: $darkestgrey; |
|||
transition: all 0.25s; |
|||
svg { |
|||
color: $white; |
|||
} |
|||
} |
|||
|
|||
&:hover { |
|||
color: $black; |
|||
} |
|||
.content { |
|||
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 { |
|||
color: $black; |
|||
font-weight: bold; |
|||
.qrCodeOptions { |
|||
display: flex; |
|||
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 { |
|||
margin: 25px 0; |
|||
padding: 25px; |
|||
.right { |
|||
width: 75%; |
|||
min-height: 220px; |
|||
border-left: 1px solid $spaceborder; |
|||
padding: 30px 40px; |
|||
|
|||
h4 { |
|||
font-size: 14px; |
|||
font-weight: bold; |
|||
letter-spacing: 1.5px; |
|||
margin-bottom: 10px; |
|||
.pubkey, .address { |
|||
padding: 25px; |
|||
|
|||
span { |
|||
color: $blue; |
|||
cursor: pointer; |
|||
h4 { |
|||
font-size: 12px; |
|||
margin-bottom: 10px; |
|||
|
|||
span { |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
p { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
font-family: 'Roboto'; |
|||
font-size: 14px; |
|||
font-size: 10px; |
|||
font-weight: 200; |
|||
background: $lightgrey; |
|||
background: $bluegrey; |
|||
|
|||
span { |
|||
.data, .copy { |
|||
padding: 15px; |
|||
} |
|||
|
|||
span:nth-child(1) { |
|||
.data { |
|||
flex: 9; |
|||
overflow-x: scroll; |
|||
} |
|||
|
|||
span:nth-child(2) { |
|||
background: $darkgrey; |
|||
color: $black; |
|||
.copy { |
|||
background: #383b47; |
|||
color: $white; |
|||
cursor: pointer; |
|||
transition: all 0.25s; |
|||
|
|||
&:hover { |
|||
background: $darkestgrey; |
|||
background: lighten(#383b47, 5%); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.addressHeader { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
|
|||
.newAddress { |
|||
text-decoration: underline; |
|||
font-size: 12px; |
|||
svg { |
|||
height: 12px; |
|||
width: 12px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,111 +1,105 @@ |
|||
import React, { Component } from 'react' |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import { FaAngleDown } from 'react-icons/lib/fa' |
|||
import Isvg from 'react-inlinesvg' |
|||
|
|||
import { btc } from 'utils' |
|||
import Value from 'components/Value' |
|||
import AnimatedCheckmark from 'components/AnimatedCheckmark' |
|||
|
|||
import bitcoinIcon from 'icons/bitcoin.svg' |
|||
import zapLogo from 'icons/zap_logo.svg' |
|||
import qrCode from 'icons/qrcode.svg' |
|||
import ReceiveModal from './ReceiveModal' |
|||
|
|||
import styles from './Wallet.scss' |
|||
|
|||
class Wallet extends Component { |
|||
constructor(props) { |
|||
super(props) |
|||
|
|||
this.state = { |
|||
modalOpen: false, |
|||
qrCodeType: 1 |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const { |
|||
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 |
|||
const Wallet = ({ |
|||
balance, |
|||
info, |
|||
openReceiveModal, |
|||
ticker, |
|||
currentTicker, |
|||
openPayForm, |
|||
openRequestForm, |
|||
showPayLoadingScreen, |
|||
showSuccessPayScreen |
|||
}) => { |
|||
const usdAmount = btc.satoshisToUsd((parseInt(balance.walletBalance, 10) + parseInt(balance.channelBalance, 10)), currentTicker.price_usd) |
|||
|
|||
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 ( |
|||
<div className={styles.wallet}> |
|||
{ |
|||
( |
|||
modalOpen && |
|||
<ReceiveModal |
|||
isOpen={modalOpen} |
|||
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> |
|||
<section className={styles.user}> |
|||
<div> |
|||
<span>{info.data.alias}</span> |
|||
<FaAngleDown /> |
|||
</div> |
|||
</section> |
|||
</header> |
|||
|
|||
<div className={styles.left}> |
|||
<div className={styles.leftContent}> |
|||
<Isvg className={styles.bitcoinLogo} src={bitcoinIcon} /> |
|||
<div className={styles.details}> |
|||
<h1> |
|||
<span> |
|||
{btc.satoshisToBtc(parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance))}BTC |
|||
</span> |
|||
<span onClick={() => this.setState({ modalOpen: true })}> |
|||
<Isvg className={styles.bitcoinLogo} src={qrCode} /> |
|||
</span> |
|||
</h1> |
|||
<span className={styles.usdValue}>≈ ${usdAmount ? usdAmount.toLocaleString() : ''}</span> |
|||
</div> |
|||
<div className={styles.left}> |
|||
<div className={styles.leftContent}> |
|||
<Isvg className={styles.bitcoinLogo} src={bitcoinIcon} /> |
|||
<div className={styles.details}> |
|||
<h1> |
|||
<span> |
|||
<Value |
|||
value={parseFloat(balance.walletBalance) + parseFloat(balance.channelBalance)} |
|||
currency={ticker.currency} |
|||
currentTicker={currentTicker} |
|||
/> |
|||
<i className={styles.currency}>{btc.renderCurrency(ticker.currency)}</i> |
|||
</span> |
|||
<span onClick={openReceiveModal}> |
|||
<Isvg className={styles.bitcoinLogo} src={qrCode} /> |
|||
</span> |
|||
</h1> |
|||
<span className={styles.usdValue}>≈ ${usdAmount ? usdAmount.toLocaleString() : ''}</span> |
|||
</div> |
|||
</div> |
|||
<div className={styles.right}> |
|||
<div className={styles.rightContent}> |
|||
<div className={styles.pay} onClick={openPayForm}>Pay</div> |
|||
<div className={styles.request} onClick={openRequestForm}>Request</div> |
|||
</div> |
|||
</div> |
|||
<div className={styles.right}> |
|||
<div className={styles.rightContent}> |
|||
<div className={styles.pay} onClick={openPayForm}>Pay</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> |
|||
) |
|||
} |
|||
|
|||
Wallet.propTypes = { |
|||
balance: PropTypes.object.isRequired, |
|||
address: PropTypes.string.isRequired, |
|||
info: PropTypes.object.isRequired, |
|||
newAddress: PropTypes.func.isRequired, |
|||
ticker: PropTypes.object.isRequired, |
|||
currentTicker: PropTypes.object.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 |
|||
|
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 803 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 282 B |
@ -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 |
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,3 +0,0 @@ |
|||
import Invoice from './Invoice' |
|||
|
|||
export default Invoice |
@ -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 |
@ -1,13 +0,0 @@ |
|||
@import '../../../../../variables.scss'; |
|||
|
|||
.closeContainer { |
|||
background: $lightgrey; |
|||
text-align: right; |
|||
padding: 10px; |
|||
|
|||
span { |
|||
color: $darkestgrey; |
|||
font-size: 20px; |
|||
cursor: pointer; |
|||
} |
|||
} |
@ -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 |
@ -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; |
|||
} |
|||
} |
@ -1,3 +0,0 @@ |
|||
import Payment from './Payment' |
|||
|
|||
export default Payment |
@ -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 |
@ -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; |
|||
} |
|||
} |
@ -1,3 +0,0 @@ |
|||
import Transaction from './Transaction' |
|||
|
|||
export default Transaction |
@ -1,3 +0,0 @@ |
|||
import Modal from './Modal' |
|||
|
|||
export default Modal |
@ -1,63 +1,67 @@ |
|||
import React from 'react' |
|||
import { shallow } from 'enzyme' |
|||
|
|||
import PayForm from '../../../app/components/Form/PayForm' |
|||
import Pay from '../../../app/components/Form/Pay' |
|||
|
|||
const defaultProps = { |
|||
payform: { |
|||
amount: '', |
|||
amount: 0, |
|||
payInput: '', |
|||
invoice: {}, |
|||
showErrors: {} |
|||
}, |
|||
currency: 'BTC', |
|||
crypto: 'BTC', |
|||
currency: {}, |
|||
crypto: {}, |
|||
nodes: [], |
|||
ticker: {}, |
|||
|
|||
isOnchain: false, |
|||
isLn: false, |
|||
currentAmount: '0', |
|||
isLn: true, |
|||
currentAmount: 0, |
|||
usdAmount: 0, |
|||
inputCaption: '', |
|||
showPayLoadingScreen: false, |
|||
showPayLoadingScreen: true, |
|||
payFormIsValid: {}, |
|||
currentCurrencyFilters: [], |
|||
currencyName: '', |
|||
|
|||
setPayAmount: () => {}, |
|||
onPayAmountBlur: () => {}, |
|||
setPayInput: () => {}, |
|||
onPayInputBlur: () => {}, |
|||
setCurrencyFilters: () => {}, |
|||
fetchInvoice: () => {}, |
|||
setCurrency: () => {}, |
|||
|
|||
onPayAmountBlur: () => {}, |
|||
|
|||
onPayInputBlur: () => {}, |
|||
|
|||
onPaySubmit: () => {} |
|||
} |
|||
|
|||
describe('Form', () => { |
|||
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.contains('lightning network')).toBe(false) |
|||
expect(el.contains('on-chain')).toBe(false) |
|||
}) |
|||
}) |
|||
|
|||
describe('should show lightning with a lightning input', () => { |
|||
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.contains('lightning network')).toBe(true) |
|||
expect(el.contains('on-chain')).toBe(false) |
|||
}) |
|||
}) |
|||
|
|||
describe('should show on-chain with an on-chain input', () => { |
|||
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.contains('lightning network')).toBe(false) |
|||
expect(el.contains('on-chain')).toBe(true) |
|||
}) |
|||
}) |
|||
}) |
Loading…
Reference in new issue