Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

WIP - Add Payment History dialog to Payments Tab (#2994) #3391

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/extensions/brave/locales/en-US/preferences.properties
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ syncEmptyText=Sync settings coming soon.
bitcoin=Bitcoin
bitcoinAdd=Use an existing Bitcoin wallet
bitcoinBuy=Buy Bitcoin
viewPaymentHistory=View Payment History
paymentHistoryTitle=Your Payment History
paymentHistoryFooterText=Your next payment submission is {{reconcileDate}}.
paymentHistoryOKText=OK
bitcoinAddress=Your Brave wallet address is:
bitcoinPaymentURL=Scan the QR code or copy this link:
bitcoinQR=Brave wallet QR code:
Expand All @@ -51,6 +55,9 @@ moneyAdd=Use your debit or credit card
add=Buy with Coinbase
addFundsTitle=Add Funds
addFunds=Add funds to your Brave Payments Account
date=Date
totalAmount=Total Amount
receiptLink=Receipt Link
advanced=Advanced
rank=Rank
views=Views
Expand Down
34 changes: 24 additions & 10 deletions app/ledger.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,16 +683,28 @@ var ledgerInfo = {
transactions:
[
/*
{ viewingId : undefined
, submissionStamp : undefined
, satoshis : undefined
, currency : undefined
, amount : undefined
, ballots :
{ 'publisher1' : undefined
...
{
viewingId: undefined,
surveyorId: undefined,
contribution: {
fiat: {
amount: undefined,
currency: undefined
},
rates: {
[currency]: undefined // bitcoin value in <currency>
},
satoshis: undefined,
fee: undefined
},
submissionStamp: undefined,
submissionId: undefined,
count: undefined,
satoshis: undefined,
votes: undefined,
ballots: {
[publisher]: undefined
}
}
, ...
*/
],
Expand Down Expand Up @@ -995,7 +1007,9 @@ var getPaymentInfo = () => {
info.address = client.getWalletAddress()
if ((amount) && (currency)) {
info = underscore.extend(info, { amount: amount, currency: currency })
if ((body.rates) && (body.rates[currency])) info.btc = (amount / body.rates[currency]).toFixed(8)
if ((body.rates) && (body.rates[currency])) {
info.btc = (amount / body.rates[currency]).toFixed(8)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need @mrose17 to review this part

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess this is obsolete now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. no longer needed.

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't quite understand this change. it looks like 1 line got split to 3, but no other changes... what are my eyes not seeing? thanks!

Copy link
Contributor Author

@willy-b willy-b Aug 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops! I had added a line for btcPrice there, then removed it after you added the transactions[n].contribution field that made btcPrice unnecessary. But it looks like I didn't restore the old code style properly :-)

A pure whitespace change also happened merging #3434 because we both added 'contribution' to the whitelist. But I needed to be up to date with master, so only change was whitespsce.

}
ledgerInfo._internal.paymentInfo = info
updateLedgerInfo()
Expand Down
128 changes: 126 additions & 2 deletions js/about/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,83 @@ class BitcoinDashboard extends ImmutableComponent {
}
}

class PaymentHistory extends ImmutableComponent {
get ledgerData () {
return this.props.ledgerData
}

render () {
const transactions = this.props.ledgerData.get('transactions')

return <div id='paymentHistory'>
<table className='sort'>
<thead>
<tr>
<th className='sort-header' data-l10n-id='date' />
<th className='sort-header' data-l10n-id='totalAmount' />
<th className='sort-header' data-l10n-id='receiptLink' />
</tr>
</thead>
<tbody>
{
transactions.map(function (row) {
return <PaymentHistoryRow transaction={row} ledgerData={this.props.ledgerData} />
}.bind(this))
}
</tbody>
</table>
</div>
}
}

class PaymentHistoryRow extends ImmutableComponent {

get transaction () {
return this.props.transaction
}

get timestamp () {
return this.transaction.get('submissionStamp')
}

get formattedDate () {
return formattedDateFromTimestamp(this.timestamp)
}

get numericDateStr () {
return (new Date(this.timestamp)).toLocaleDateString().replace(/\//g, '-')
}

get ledgerData () {
return this.props.ledgerData
}

get satoshis () {
return this.transaction.get('contribution').get('satoshis')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contribution and other child properties may not necessarily exist, so this would throw cannot read property 'get' of undefined errors. please use this.transaction.getIn(['contribution', ...]).

}

get currency () {
return this.transaction.get('contribution').get('fiat').get('currency')
Copy link
Member

@diracdeltas diracdeltas Aug 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above; if undefined, return an empty string for now.

}

get totalAmount () {
var fiatAmount = this.transaction.get('contribution').get('fiat').get('amount')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

return fiatAmount.toFixed(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is available directly now, via this.transaction.get('contribution').get('fiat').get('amount'). see docs/state.md

Copy link
Member

@diracdeltas diracdeltas Aug 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should check that fiatAmount is a Number. otherwise return an empty string?

}

render () {
var date = this.formattedDate
var totalAmountStr = this.totalAmount + ' ' + this.currency
var receiptFileName = 'brave_ledger' + this.numericDateStr + '.pdf'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is minor, but we prefer to use JS template strings here. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals


return <tr>
<td className='narrow' data-sort={this.timestamp}>{date}</td>
<td className='wide' data-sort={this.satoshis}>{totalAmountStr}</td>
<td className='wide'>{receiptFileName}</td>
</tr>
}
}

class GeneralTab extends ImmutableComponent {
enabled (keyArray) {
return keyArray.every((key) => getSetting(key, this.props.settings) === true)
Expand Down Expand Up @@ -498,6 +575,20 @@ class PaymentsTab extends ImmutableComponent {
return <Button l10nId={buttonText} className='primaryButton' onClick={onButtonClick.bind(this)} disabled={this.props.ledgerData.get('creating')} />
}

get paymentHistoryButton () {
const walletCreated = this.props.ledgerData.get('created') && !this.props.ledgerData.get('creating')
const walletTransactions = this.props.ledgerData.get('transactions')
const walletHasTransactions = walletTransactions && walletTransactions.size

if (!walletCreated || !walletHasTransactions) {
return null
}

const buttonText = 'viewPaymentHistory'
const onButtonClick = this.props.showOverlay.bind(this, 'paymentHistory')
return <Button className='paymentHistoryButton' l10nId={buttonText} onClick={onButtonClick.bind(this)} disabled={this.props.ledgerData.get('creating')} />
}

get walletStatus () {
let status = {}
if (this.props.ledgerData.get('created')) {
Expand Down Expand Up @@ -536,6 +627,27 @@ class PaymentsTab extends ImmutableComponent {
hideParentOverlay={this.props.hideOverlay.bind(this, 'addFunds')} />
}

get paymentHistoryContent () {
return <PaymentHistory ledgerData={this.props.ledgerData} />
}

get paymentHistoryFooter () {
let ledgerData = this.props.ledgerData
if (!ledgerData.get('reconcileStamp')) {
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
let nextReconcileDate = formattedDateFromTimestamp(ledgerData.get('reconcileStamp'))
let l10nDataArgs = {
reconcileDate: nextReconcileDate
}
return <div className='paymentHistoryFooter'>
<div className='nextPaymentSubmission'>
<span data-l10n-id='paymentHistoryFooterText' data-l10n-args={JSON.stringify(l10nDataArgs)} />
</div>
<Button l10nId='paymentHistoryOKText' className='okButton primaryButton' onClick={this.props.hideOverlay.bind(this, 'paymentHistory')} />
</div>
}

btcToCurrencyString (btc) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can simplify this using btcPrice

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually btcPrice is now OBE based on what's in master

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrose17 what do you mean? btcPrice was needed to convert balance/unconfirmed from BTC to local currency amount. if this has changed, please document modified ledger info properties in docs/state.md.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm comment above, @willy-b you can leave btcToCurrencyString unchanged

const balance = Number(btc || 0)
const currency = this.props.ledgerData.get('currency')
Expand All @@ -545,8 +657,7 @@ class PaymentsTab extends ImmutableComponent {
if (balance === 0) {
return `0 ${currency}`
}
if (this.props.ledgerData.get('btc') &&
typeof this.props.ledgerData.get('amount') === 'number') {
if (this.props.ledgerData.get('btc') && typeof this.props.ledgerData.get('amount') === 'number') {
const btcValue = this.props.ledgerData.get('btc') / this.props.ledgerData.get('amount')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suspect that sometime post-beta we will need to use something like https://github.com/xsolla/currency-format in order to format properly...

return `${(balance / btcValue).toFixed(2)} ${currency}`
}
Expand Down Expand Up @@ -582,6 +693,7 @@ class PaymentsTab extends ImmutableComponent {
{this.btcToCurrencyString(this.props.ledgerData.get('balance'))}
</span>
{this.walletButton}
{this.paymentHistoryButton}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this button should not show up if there is empty payment history (ex: before wallet has been created)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</td>
<td>
<SettingsList>
Expand Down Expand Up @@ -616,6 +728,11 @@ class PaymentsTab extends ImmutableComponent {
? <ModalOverlay title={'addFunds'} content={this.overlayContent} onHide={this.props.hideOverlay.bind(this, 'addFunds')} />
: null
}
{
this.enabled && this.props.paymentHistoryOverlayVisible
? <ModalOverlay title={'paymentHistoryTitle'} customTitleClasses={'paymentHistory'} content={this.paymentHistoryContent} footer={this.paymentHistoryFooter} onHide={this.props.hideOverlay.bind(this, 'paymentHistory')} />
: null
}
<div className='titleBar'>
<div className='sectionTitle pull-left' data-l10n-id='publisherPaymentsTitle' value='publisherPaymentsTitle' />
<div className='pull-left' id='enablePaymentsSwitch'>
Expand Down Expand Up @@ -997,6 +1114,7 @@ class AboutPreferences extends React.Component {
let hash = window.location.hash ? window.location.hash.slice(1) : ''
this.state = {
bitcoinOverlayVisible: false,
paymentHistoryOverlayVisible: false,
addFundsOverlayVisible: false,
preferenceTab: hash.toUpperCase() in preferenceTabs ? hash : preferenceTabs.GENERAL,
hintNumber: this.getNextHintNumber(),
Expand Down Expand Up @@ -1109,6 +1227,7 @@ class AboutPreferences extends React.Component {
braveryDefaults={braveryDefaults} ledgerData={ledgerData}
onChangeSetting={this.onChangeSetting}
bitcoinOverlayVisible={this.state.bitcoinOverlayVisible}
paymentHistoryOverlayVisible={this.state.paymentHistoryOverlayVisible}
addFundsOverlayVisible={this.state.addFundsOverlayVisible}
showOverlay={this.setOverlayVisible.bind(this, true)}
hideOverlay={this.setOverlayVisible.bind(this, false)} />
Expand All @@ -1134,4 +1253,9 @@ class AboutPreferences extends React.Component {
}
}

let formattedDateFromTimestamp = function (timestamp) {
var date = new Date(timestamp)
return date.toLocaleDateString()
}

module.exports = <AboutPreferences />
17 changes: 13 additions & 4 deletions js/components/modalOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,19 @@ class ModalOverlay extends ImmutableComponent {
close = <button type='button' className='close pull-right' onClick={this.props.onHide}><span>&times;</span></button>
title = <div className='sectionTitle' data-l10n-id={this.props.title} />
}
return <div className='dialog'>
{close}
{title}
{this.props.content}
let customTitleClassesStr = (this.props.customTitleClasses ? this.props.customTitleClasses : '')

return <div className={'dialog ' + customTitleClassesStr}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something in this ModalOverlay change is breaking the Buy with Coinbase modal overlay:
screen shot 2016-08-26 at 4 29 46 pm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, glad you caught this :-).
Fixed by 2a729d9#diff-4b50ff14dadc9756719a577a72f520d6R513

<div className='dialog-header'>
{close}
{title}
</div>
<div className='dialog-body'>
{this.props.content}
</div>
<div className='dialog-footer'>
{this.props.footer}
</div>
</div>
}

Expand Down
77 changes: 77 additions & 0 deletions less/about/preferences.less
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,61 @@ table.sortableTable {
overflow-y: scroll;
}

.modal .dialog.paymentHistory .sectionTitle {
text-align: left;
}

span.paymentHistoryButton.browserButton {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not overflow when the window is narrow:
screen shot 2016-08-25 at 10 42 37 am

Copy link
Contributor Author

@willy-b willy-b Aug 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

FIXED in fb828b5#diff-4b50ff14dadc9756719a577a72f520d6R466

BTW, this screenshot points out another issue: The currency for the balance is BTC when the wallet is empty, and always USD when non-empty (just confirmed locally).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@willy-b before the wallet is created, we have no idea what the user's currency is, so it defaults to BTC. it could just say 'empty' or something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this now looks awkwardly aligned with the select box but that can be saved for a follow-up issue.
screen shot 2016-08-26 at 4 12 24 pm

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes...

Add Funds should be "Add funds..." and use smaller text. Button should match height of budget menu. Said menu should move up to align with "Add funds..." button.

display: block;
color: @braveOrange;
font-size: 14px;
white-space: nowrap;
}

.paymentHistoryFooter span.okButton.primaryButton {
background-color: gray;
float: right;
margin-top: -10px;
display: inline;
padding-top: 5px;
padding-left: 35px;
padding-right: 35px;
padding-bottom: 5px;
}

div.nextPaymentSubmission {
display: inline;
}
.nextPaymentSubmission span {
font-size: 12px;
display: inline;
}

.modal .dialog.paymentHistory {
padding: 0px;

.dialog-header {
background-color: #EEE;
height: 20px;
padding-bottom: 10px;
}

.dialog-body {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs overflow-y: scroll; or else this happens:
screen shot 2016-08-26 at 4 27 33 pm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your recommendation fixed this: 2a729d9#diff-4b50ff14dadc9756719a577a72f520d6R500

background-color: #FFF;
height: 300px;
}

.dialog-footer {
background-color: #EEE;
height: 5px;
}

.dialog-header, .dialog-body, .dialog-footer {
padding: 20px;
}
}


#paymentsContainer {
position: relative;
overflow-x: hidden;
Expand Down Expand Up @@ -485,6 +540,28 @@ table.sortableTable {
}
}

#paymentHistory {
background-color: #FFF;
tr {
th {
color: @darkGray;
font-weight: 500;
border-bottom: 2px solid @lightGray;
text-align: left;
width: 18%;
}
td {
text-align: left;
&.narrow {
color: @darkGray;
}
&.wide {
color: #777;
}
}
}
}

.sort {
text-align: left;
}
Expand Down