Browse Source

feature(countdown): add countdown component for the countdown till the invoice expires

renovate/lint-staged-8.x
Jack Mallers 7 years ago
parent
commit
fade245dc4
  1. 83
      app/components/Activity/Countdown.js
  2. 16
      app/components/Activity/Countdown.scss
  3. 6
      app/components/Activity/InvoiceModal.js
  4. 9
      app/components/Activity/InvoiceModal.scss
  5. 24
      app/components/Activity/PaymentModal.js
  6. 84
      app/components/Activity/PaymentModal.scss
  7. 37
      app/components/Activity/TransactionModal.js
  8. 127
      app/components/Activity/TransactionModal.scss
  9. 8
      app/components/Value/Value.js
  10. 2
      app/components/Wallet/ReceiveModal.scss
  11. 3
      app/lnd/lib/rpc.proto
  12. 3
      app/rpc.proto
  13. 4
      app/utils/blockExplorer.js

83
app/components/Activity/Countdown.js

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

16
app/components/Activity/Countdown.scss

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

6
app/components/Activity/InvoiceModal.js

@ -11,6 +11,7 @@ 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'
@ -32,10 +33,13 @@ const InvoiceModal = ({
showNotification('Noice', 'Successfully copied to clipboard')
}
const countDownDate = (parseInt(invoice.creation_date) + parseInt(invoice.expiry))
return (
<div className={styles.container}>
<div className={styles.content}>
<section className={styles.left}>
<h2>Payment Request</h2>
<QRCode
value={invoice.payment_request}
renderAs='svg'
@ -43,7 +47,9 @@ const InvoiceModal = ({
bgColor='transparent'
fgColor='white'
level='L'
className={styles.qrcode}
/>
<Countdown countDownDate={countDownDate} />
</section>
<section className={styles.right}>
<div className={styles.details}>

9
app/components/Activity/InvoiceModal.scss

@ -16,6 +16,15 @@
.left {
width: 25%;
padding: 0 60px;
h2 {
text-align: center;
margin-bottom: 20px;
}
.qrcode {
margin-bottom: 20px;
}
}
.right {

24
app/components/Activity/PaymentModal.js

@ -6,6 +6,8 @@ import 'moment-timezone'
import { FaAngleDown } from 'react-icons/lib/fa'
import { showTransaction, showBlock } from 'utils/blockExplorer'
import Value from 'components/Value'
import styles from './PaymentModal.scss'
@ -23,14 +25,15 @@ const PaymentModal = ({
onCurrencyFilterClick
}
}) => {
console.log('payment: ', payment)
return (
<div className={styles.container}>
<div className={styles.content}>
<section className={styles.left} />
<section className={styles.right}>
<section className={styles.top}>
<div className={styles.details}>
<section className={styles.amount}>
<h1>
<i className={styles.symbol}>-</i>
<Value value={payment.value} currency={ticker.currency} currentTicker={currentTicker} />
</h1>
<section className={styles.currentCurrency} onClick={() => setActivityModalCurrencyFilters(!showCurrencyFilters)}>
@ -43,13 +46,26 @@ const PaymentModal = ({
}
</ul>
</section>
<section className={styles.date}>
<section className={styles.fee}>
<p>Sent</p>
<p>
<Moment format='MM/DD/YYYY'>{payment.creation_date * 1000}</Moment>
<Value value={payment.fee} currency={ticker.currency} currentTicker={currentTicker} />
<span> {currencyName} fee</span>
</p>
</section>
</div>
</section>
<section className={styles.bottom}>
<div className={styles.txHash}>
<h4>Memo</h4>
<p>{payment.memo}</p>
</div>
<div className={styles.blockHash}>
<h4>Proof</h4>
<p>{payment.payment_preimage}</p>
</div>
</section>
</div>
</div>
)

84
app/components/Activity/PaymentModal.scss

@ -5,23 +5,12 @@
}
.content {
display: flex;
flex-direction: row;
align-items: center;
background: $spaceblue;
width: 85%;
margin: 50px auto;
padding: 30px 0;
padding-top: 30px;
.left {
width: 25%;
padding: 0 60px;
}
.right {
width: 75%;
min-height: 220px;
border-left: 1px solid $spaceborder;
.top {
padding: 10px 60px;
.details {
@ -36,6 +25,10 @@
align-items: center;
position: relative;
.symbol {
color: $red;
}
h1 {
font-size: 40px;
margin-right: 10px;
@ -83,7 +76,7 @@
}
}
.date {
.fee {
font-size: 12px;
text-align: right;
@ -91,53 +84,40 @@
color: #FF8A65;
margin-top: 5px;
}
}
}
.memo, .request {
h4 {
font-size: 10px;
margin-bottom: 10px;
}
p {
margin-bottom: 5px;
p {
word-wrap: break-word;
max-width: 450px;
&:nth-child(2) {
opacity: 0.5;
}
}
}
}
}
}
.memo {
margin-bottom: 40px;
p {
font-size: 20px;
}
}
.bottom {
background: #31343f;
padding: 40px;
.request p {
.txHash, .blockHash {
margin: 40px 0;
h4 {
font-size: 10px;
max-width: 450px;
line-height: 1.5;
margin-bottom: 10px;
}
}
}
.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%);
p {
font-size: 14px;
text-decoration: underline;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.5;
}
}
}
}

37
app/components/Activity/TransactionModal.js

@ -6,11 +6,13 @@ import 'moment-timezone'
import { FaAngleDown } from 'react-icons/lib/fa'
import { showTransaction, showBlock } from 'utils/blockExplorer'
import Value from 'components/Value'
import styles from './PaymentModal.scss'
import styles from './TransactionModal.scss'
const PaymentModal = ({
const TransactionModal = ({
transaction,
ticker,
currentTicker,
@ -23,15 +25,21 @@ const PaymentModal = ({
onCurrencyFilterClick
}
}) => {
console.log('transaction: ', transaction)
return (
<div className={styles.container}>
<div className={styles.content}>
<section className={styles.left} />
<section className={styles.right}>
<section className={styles.top}>
<div className={styles.details}>
<section 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)}>
@ -45,18 +53,31 @@ const PaymentModal = ({
</ul>
</section>
<section className={styles.date}>
<p>{transaction.num_confirmations} {transaction.num_confirmations > 1 ? 'confirmations' : 'confirmation'}</p>
<p>
<Moment format='MM/DD/YYYY'>{transaction.time_stamp * 1000}</Moment>
<Value value={transaction.total_fees} currency={ticker.currency} currentTicker={currentTicker} />
<span> {currencyName} fee</span>
</p>
</section>
</div>
</section>
<section className={styles.bottom}>
<div className={styles.txHash}>
<h4>Transaction</h4>
<p onClick={() => showTransaction(transaction.tx_hash)}>{transaction.tx_hash}</p>
</div>
<div className={styles.blockHash}>
<h4>Block</h4>
<p onClick={() => showBlock(transaction.block_hash)}>{transaction.block_hash}</p>
</div>
</section>
</div>
</div>
)
}
PaymentModal.propTypes = {
TransactionModal.propTypes = {
transaction: PropTypes.object.isRequired,
ticker: PropTypes.object.isRequired,
currentTicker: PropTypes.object.isRequired,
@ -64,4 +85,4 @@ PaymentModal.propTypes = {
toggleCurrencyProps: PropTypes.object.isRequired
}
export default PaymentModal
export default TransactionModal

127
app/components/Activity/TransactionModal.scss

@ -0,0 +1,127 @@
@import '../../variables.scss';
.container {
color: $white;
}
.content {
background: $spaceblue;
width: 85%;
margin: 50px auto;
padding-top: 30px;
.top {
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;
.symbol {
color: $red;
&.active {
color: $green;
}
}
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;
}
p {
margin-bottom: 5px;
&:nth-child(2) {
opacity: 0.5;
}
}
}
}
}
}
.bottom {
background: #31343f;
padding: 40px;
.txHash, .blockHash {
margin: 40px 0;
h4 {
font-size: 10px;
margin-bottom: 10px;
}
p {
font-size: 14px;
text-decoration: underline;
cursor: pointer;
transition: all 0.25s;
&:hover {
opacity: 0.5;
}
}
}
}

8
app/components/Value/Value.js

@ -2,7 +2,13 @@ import React from 'react'
import PropTypes from 'prop-types'
import { btc } from 'utils'
const Value = ({ value, currency, currentTicker }) => (<i>{btc.convert('sats', currency, value, currentTicker.price_usd)}</i>)
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([

2
app/components/Wallet/ReceiveModal.scss

@ -31,7 +31,7 @@
align-items: center;
background: $spaceblue;
width: 85%;
margin: 15% auto 50px auto;
margin: 10% auto 50px auto;
color: $white;
.left {

3
app/lnd/lib/rpc.proto

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

3
app/rpc.proto

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

4
app/utils/blockExplorer.js

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

Loading…
Cancel
Save