Browse Source

fix(activity): grouped data by day and redesign payment, invoice and transaction components

renovate/lint-staged-8.x
Jack Mallers 7 years ago
parent
commit
e85a9b2dd1
  1. 2
      app/components/Wallet/ReceiveModal.js
  2. 7
      app/components/Wallet/Wallet.scss
  3. 72
      app/reducers/activity.js
  4. 41
      app/routes/activity/components/Activity.js
  5. 14
      app/routes/activity/components/Activity.scss
  6. 23
      app/routes/activity/components/components/Activity.scss
  7. 45
      app/routes/activity/components/components/Invoice/Invoice.js
  8. 84
      app/routes/activity/components/components/Payment/Payment.js
  9. 24
      app/routes/activity/components/components/Transaction/Transaction.js
  10. 3
      app/routes/activity/containers/ActivityContainer.js

2
app/components/Wallet/ReceiveModal.js

@ -106,7 +106,7 @@ ReceiveModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
pubkey: PropTypes.string,
address: PropTypes.string.isRequired,
alias: PropTypes.string.isRequired,
alias: PropTypes.string,
closeReceiveModal: PropTypes.func.isRequired
}

7
app/components/Wallet/Wallet.scss

@ -125,16 +125,17 @@
font-size: 16px;
font-weight: bold;
color: $white;
background: $spaceblue;
padding: 15px;
background: #31343F;
padding: 10px;
width: 100px;
text-align: center;
border-radius: 5px;
cursor: pointer;
opacity: 0.5;
transition: all 0.25s;
&:hover {
opacity: 0.5;
opacity: 1;
}
&:nth-child(1) {

72
app/reducers/activity.js

@ -10,8 +10,7 @@ const initialState = {
{ key: 'ALL_ACTIVITY', name: 'All' },
{ key: 'SENT_ACTIVITY', name: 'Sent' },
{ key: 'REQUESTED_ACTIVITY', name: 'Requested' },
{ key: 'PENDING_ACTIVITY', name: 'Pending' },
{ key: 'FUNDED_ACTIVITY', name: 'Funding Transactions' }
{ key: 'PENDING_ACTIVITY', name: 'Pending' }
],
modal: {
modalType: null,
@ -128,8 +127,30 @@ const allActivity = createSelector(
return false
})
return searchedArr.sort((a, b) => {
// this will return the correct timestamp to use when sorting (time_stamp, creation_date, or settle_date)
// return searchedArr.sort((a, b) => {
// // this will return the correct timestamp to use when sorting (time_stamp, creation_date, or settle_date)
// function returnTimestamp(transaction) {
// // if on-chain txn
// if (Object.prototype.hasOwnProperty.call(transaction, 'time_stamp')) { return transaction.time_stamp }
// // if invoice that has been paid
// if (transaction.settled) { return transaction.settle_date }
// // if invoice that has not been paid or an LN payment
// return transaction.creation_date
// }
// const aTimestamp = returnTimestamp(a)
// const bTimestamp = returnTimestamp(b)
// // console.log('aTimestamp: ', aTimestamp)
// // console.log('bTimestamp: ', bTimestamp)
// console.log('date: ', new Date(aTimestamp * 1000))
// return bTimestamp - aTimestamp
// })
if (!searchedArr.length) { return [] }
const groups = searchedArr.reduce((groups, el) => {
function returnTimestamp(transaction) {
// if on-chain txn
if (Object.prototype.hasOwnProperty.call(transaction, 'time_stamp')) { return transaction.time_stamp }
@ -139,11 +160,34 @@ const allActivity = createSelector(
return transaction.creation_date
}
const aTimestamp = returnTimestamp(a)
const bTimestamp = returnTimestamp(b)
const months = ['Jan', 'Feb', 'Mar', 'April', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
return bTimestamp - aTimestamp
const d = new Date(returnTimestamp(el) * 1000)
const date = d.getDate()
const title = `${months[d.getMonth()]} ${date}, ${d.getFullYear()}`
if (!groups[title]) {
groups[title] = []
}
groups[title].push({
el
})
return groups
}, {})
const groupArrays = Object.keys(groups).map((title) => {
return {
title,
activity: groups[title]
}
})
console.log('groupArrays: ', groupArrays)
return groupArrays.sort((a, b) => new Date(b.title).getTime() - new Date(a.title).getTime())
}
)
@ -171,23 +215,11 @@ const pendingActivity = createSelector(
invoices => invoices.filter(invoice => !invoice.settled && !invoiceExpired(invoice))
)
const fundedActivity = createSelector(
transactionsSelector,
channelsSelector,
(transactions, channels) => {
const fundingTxIds = channels.map(channel => channel.channel_point.split(':')[0])
const fundingTxs = transactions.filter(transaction => fundingTxIds.includes(transaction.tx_hash))
return fundingTxs.sort((a, b) => b.time_stamp - a.time_stamp)
}
)
const FILTERS = {
ALL_ACTIVITY: allActivity,
SENT_ACTIVITY: sentActivity,
REQUESTED_ACTIVITY: invoiceActivity,
PENDING_ACTIVITY: pendingActivity,
FUNDED_ACTIVITY: fundedActivity
PENDING_ACTIVITY: pendingActivity
}
activitySelectors.currentActivity = createSelector(

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

@ -27,17 +27,40 @@ class Activity extends Component {
}
renderActivity(activity) {
const { ticker, currentTicker, showActivityModal } = this.props
const {
ticker,
currentTicker,
showActivityModal,
network,
currencyName
} = this.props
if (Object.prototype.hasOwnProperty.call(activity, 'block_hash')) {
// activity is an on-chain tx
return <Transaction transaction={activity} ticker={ticker} currentTicker={currentTicker} showActivityModal={showActivityModal} />
return (
<Transaction
transaction={activity}
ticker={ticker}
currentTicker={currentTicker}
showActivityModal={showActivityModal}
currencyName={currencyName}
/>
)
} else if (Object.prototype.hasOwnProperty.call(activity, 'payment_request')) {
// activity is an LN invoice
return <Invoice invoice={activity} ticker={ticker} currentTicker={currentTicker} showActivityModal={showActivityModal} />
}
// activity is an LN payment
return <Payment payment={activity} ticker={ticker} currentTicker={currentTicker} showActivityModal={showActivityModal} />
return (
<Payment
payment={activity}
ticker={ticker}
currentTicker={currentTicker}
showActivityModal={showActivityModal}
nodes={network.nodes}
currencyName={currencyName}
/>
)
}
render() {
@ -51,9 +74,12 @@ class Activity extends Component {
changeFilter,
currentActivity,
network,
walletProps
} = this.props
console.log('network: ', network)
if (!balance.channelBalance || !balance.walletBalance) { return <LoadingBolt /> }
return (
@ -78,9 +104,14 @@ class Activity extends Component {
</header>
<ul className={`${styles.activityContainer} ${filterPulldown && styles.pulldown}`}>
{
currentActivity.map((activity, index) => (
currentActivity.map((activityBlock, index) => (
<li className={styles.activity} key={index}>
{this.renderActivity(activity)}
<h2>{activityBlock.title}</h2>
<ul>
{
activityBlock.activity.map(activity => <li>{this.renderActivity(activity.el)}</li>)
}
</ul>
</li>
))
}

14
app/routes/activity/components/Activity.scss

@ -96,10 +96,16 @@
}
.activity {
padding: 0 40px;
&:hover {
position: relative;
padding: 0 60px;
margin-bottom: 30px;
h2 {
color: $white;
font-size: 10px;
font-weight: bold;
border-bottom: 0.2px solid #A0A0A0;
padding: 10px 0;
}
.left, .center, .right {

23
app/routes/activity/components/components/Activity.scss

@ -15,6 +15,17 @@
&.unpaid {
opacity: 0.5;
}
.pendingIcon {
position: absolute;
left: -5%;
top: 30%;
svg {
width: 10.5px;
height: 10.5px;
}
}
}
.clock {
@ -65,7 +76,14 @@
}
.title {
margin-bottom: 5px;
margin-bottom: 10px;
font-size: 14px;
font-family: Roboto;
}
.subtitle {
opacity: 0.5;
font-size: 10px;
}
.icon, h3, span {
@ -118,14 +136,13 @@
.amount {
display: flex;
flex-direction: column;
flex: 1;
text-align: right;
font-size: 12px;
color: $white;
span {
&:nth-child(1) {
margin-bottom: 5px;
margin-bottom: 10px;
}
&:nth-child(2) {

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

@ -2,6 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { btc } from 'utils'
import Isvg from 'react-inlinesvg'
import { FaBolt } from 'react-icons/lib/fa'
import Value from 'components/Value'
@ -13,35 +15,22 @@ const Invoice = ({
invoice, ticker, currentTicker, showActivityModal
}) => (
<div className={`${styles.container} ${!invoice.settled && styles.unpaid}`} onClick={() => showActivityModal('INVOICE', { invoice })}>
<div className={styles.date}>
<section>
{
invoice.settled ?
<Isvg src={checkmarkIcon} />
:
<i className='hint--top' data-hint='Request has not been paid'>
<Isvg src={clockIcon} />
</i>
}
</section>
<section>
<Moment format='MMM'>{invoice.creation_date * 1000}</Moment> <Moment format='D'>{invoice.creation_date * 1000}</Moment>
</section>
</div>
{
!invoice.settled && (
<div className={styles.pendingIcon}>
<Isvg src={checkmarkIcon} />
</div>
)
}
<div className={styles.data}>
<div className={styles.title}>
<i className={`${styles.icon} hint--top`} data-hint='Lightning Network request'>
<FaBolt />
</i>
<h3>
{ invoice.settled ? 'Received' : 'Requested' }
{ invoice.settled ? 'Received payment' : 'Requested payment' }
</h3>
<span>
{ticker.currency}
</span>
</div>
<div className={styles.subtitle}>
{invoice.r_hash.toString('hex')}
<Moment format='h:mm a'>{invoice.settled ? invoice.settled_date * 1000 : invoice.creation_date * 1000}</Moment>
</div>
</div>
<div className={`${styles.amount} ${invoice.settled ? styles.positive : styles.negative}`}>
@ -53,12 +42,10 @@ const Invoice = ({
currentTicker={currentTicker}
/>
</span>
<span className='hint--bottom' data-hint='Invoice fee'>
<Value
value={invoice.fee}
currency={ticker.currency}
currentTicker={currentTicker}
/>
<span>
<span>
${btc.convert('sats', 'usd', invoice.value, currentTicker.price_usd)}
</span>
</span>
</div>
</div>

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

@ -1,7 +1,10 @@
import React from 'react'
import PropTypes from 'prop-types'
import find from 'lodash/find'
import Moment from 'react-moment'
import 'moment-timezone'
import { btc } from 'utils'
import Isvg from 'react-inlinesvg'
import { FaBolt } from 'react-icons/lib/fa'
import Value from 'components/Value'
@ -9,52 +12,47 @@ import checkmarkIcon from 'icons/check_circle.svg'
import styles from '../Activity.scss'
const Payment = ({
payment, ticker, currentTicker, showActivityModal
}) => (
<div className={styles.container} onClick={() => showActivityModal('PAYMENT', { payment })}>
<div className={styles.date}>
<section>
<Isvg src={checkmarkIcon} />
</section>
<section>
<Moment format='MMM'>{payment.creation_date * 1000}</Moment> <Moment format='D'>{payment.creation_date * 1000}</Moment>
</section>
</div>
<div className={styles.data}>
<div className={styles.title}>
<i className={`${styles.icon} hint--top`} data-hint='Lightning Network payment'>
<FaBolt />
</i>
<h3>
Sent
</h3>
<span>
{ticker.currency}
</span>
payment, ticker, currentTicker, showActivityModal, nodes, currencyName
}) => {
console.log('nodes: ', nodes)
const displayNodeName = (pubkey) => {
const node = find(nodes, n => pubkey === n.pub_key)
if (node && node.alias.length) { return node.alias }
return pubkey.substring(0, 10)
}
return (
<div className={styles.container} onClick={() => showActivityModal('PAYMENT', { payment })}>
<div className={styles.data}>
<div className={styles.title}>
<h3>
{displayNodeName(payment.path[payment.path.length - 1])}
</h3>
</div>
<div className={styles.subtitle}>
<Moment format='h:mm a'>{payment.creation_date * 1000}</Moment>
</div>
</div>
<div className={styles.subtitle}>
{payment.payment_hash.toString('hex')}
<div className={styles.amount}>
<span className='hint--top' data-hint='Payment amount'>
<i className={styles.minus}>-</i>
<Value
value={payment.value}
currency={ticker.currency}
currentTicker={currentTicker}
/>
<i> {currencyName}</i>
</span>
<span className='hint--bottom' data-hint='Payment fee'>
${btc.convert('sats', 'usd', payment.value, currentTicker.price_usd)}
</span>
</div>
</div>
<div className={styles.amount}>
<span className='hint--top' data-hint='Payment amount'>
<i className={styles.minus}>-</i>
<Value
value={payment.value}
currency={ticker.currency}
currentTicker={currentTicker}
/>
</span>
<span className='hint--bottom' data-hint='Payment fee'>
<Value
value={payment.fee}
currency={ticker.currency}
currentTicker={currentTicker}
/>
</span>
</div>
</div>
)
)
}
Payment.propTypes = {
payment: PropTypes.object.isRequired,

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

@ -2,6 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import Moment from 'react-moment'
import 'moment-timezone'
import { btc } from 'utils'
import Isvg from 'react-inlinesvg'
import { FaChain } from 'react-icons/lib/fa'
import Value from 'components/Value'
@ -12,28 +14,14 @@ const Transaction = ({
transaction, ticker, currentTicker, showActivityModal
}) => (
<div className={styles.container} onClick={() => showActivityModal('TRANSACTION', { transaction })}>
<div className={styles.date}>
<section>
<Isvg src={checkmarkIcon} />
</section>
<section>
<Moment format='MMM'>{transaction.time_stamp * 1000}</Moment> <Moment format='D'>{transaction.time_stamp * 1000}</Moment>
</section>
</div>
<div className={styles.data}>
<div className={styles.title}>
<i className={`${styles.icon} hint--top`} data-hint='On-chain transaction'>
<FaChain />
</i>
<h3>
{ transaction.amount > 0 ? 'Received' : 'Sent' }
</h3>
<span>
{ticker.currency}
</span>
</div>
<div className={styles.subtitle}>
{transaction.tx_hash}
<Moment format='h:mm a'>{transaction.time_stamp * 1000}</Moment>
</div>
</div>
<div className={`${styles.amount} ${transaction.amount > 0 ? styles.positive : styles.negative}`}>
@ -46,11 +34,7 @@ const Transaction = ({
/>
</span>
<span className='hint--bottom' data-hint='Transaction fee'>
<Value
value={transaction.total_fees}
currency={ticker.currency}
currentTicker={currentTicker}
/>
${btc.convert('sats', 'usd', transaction.amount, currentTicker.price_usd)}
</span>
</div>
</div>

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

@ -60,10 +60,13 @@ const mapStateToProps = state => ({
ticker: state.ticker,
network: state.network,
paymentModalOpen: paymentSelectors.paymentModalOpen(state),
invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state),
currentTicker: tickerSelectors.currentTicker(state),
currencyName: tickerSelectors.currencyName(state),
currentActivity: activitySelectors.currentActivity(state)(state),
nonActiveFilters: activitySelectors.nonActiveFilters(state),

Loading…
Cancel
Save