jackmallers
7 years ago
committed by
GitHub
42 changed files with 1325 additions and 622 deletions
@ -0,0 +1,145 @@ |
|||||
|
import { createSelector } from 'reselect' |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Initial State
|
||||
|
// ------------------------------------
|
||||
|
const initialState = { |
||||
|
filterPulldown: false, |
||||
|
filter: { key: 'ALL_ACTIVITY', name: 'All Activity' }, |
||||
|
filters: [ |
||||
|
{ key: 'ALL_ACTIVITY', name: 'All Activity' }, |
||||
|
{ key: 'LN_ACTIVITY', name: 'LN Activity' }, |
||||
|
{ key: 'PAYMENT_ACTIVITY', name: 'LN Payments' }, |
||||
|
{ key: 'INVOICE_ACTIVITY', name: 'LN Invoices' }, |
||||
|
{ key: 'TRANSACTION_ACTIVITY', name: 'On-chain Activity' } |
||||
|
], |
||||
|
modal: { |
||||
|
modalType: null, |
||||
|
modalProps: {} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Constants
|
||||
|
// ------------------------------------
|
||||
|
export const SHOW_ACTIVITY_MODAL = 'SHOW_ACTIVITY_MODAL' |
||||
|
export const HIDE_ACTIVITY_MODAL = 'HIDE_ACTIVITY_MODAL' |
||||
|
|
||||
|
export const CHANGE_FILTER = 'CHANGE_FILTER' |
||||
|
|
||||
|
export const TOGGLE_PULLDOWN = 'TOGGLE_PULLDOWN' |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Actions
|
||||
|
// ------------------------------------
|
||||
|
export function showActivityModal(modalType, modalProps) { |
||||
|
return { |
||||
|
type: SHOW_ACTIVITY_MODAL, |
||||
|
modalType, |
||||
|
modalProps |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function hideActivityModal() { |
||||
|
return { |
||||
|
type: HIDE_ACTIVITY_MODAL |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function changeFilter(filter) { |
||||
|
return { |
||||
|
type: CHANGE_FILTER, |
||||
|
filter |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function toggleFilterPulldown() { |
||||
|
return { |
||||
|
type: TOGGLE_PULLDOWN |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Action Handlers
|
||||
|
// ------------------------------------
|
||||
|
const ACTION_HANDLERS = { |
||||
|
[SHOW_ACTIVITY_MODAL]: (state, { modalType, modalProps }) => ({ ...state, modal: { modalType, modalProps } }), |
||||
|
[HIDE_ACTIVITY_MODAL]: state => ({ ...state, modal: { modalType: null, modalProps: {} } }), |
||||
|
[CHANGE_FILTER]: (state, { filter }) => ({ ...state, filter, filterPulldown: false }), |
||||
|
[TOGGLE_PULLDOWN]: state => ({ ...state, filterPulldown: !state.filterPulldown }) |
||||
|
} |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Selectors
|
||||
|
// ------------------------------------
|
||||
|
const activitySelectors = {} |
||||
|
const filtersSelector = state => state.activity.filters |
||||
|
const filterSelector = state => state.activity.filter |
||||
|
const paymentsSelector = state => state.payment.payments |
||||
|
const invoicesSelector = state => state.invoice.invoices |
||||
|
const transactionsSelector = state => state.transaction.transactions |
||||
|
|
||||
|
const allActivity = createSelector( |
||||
|
paymentsSelector, |
||||
|
invoicesSelector, |
||||
|
transactionsSelector, |
||||
|
(payments, invoices, transactions) => [...payments, ...invoices, ...transactions].sort((a, b) => { |
||||
|
const aTimestamp = Object.prototype.hasOwnProperty.call(a, 'time_stamp') ? a.time_stamp : a.creation_date |
||||
|
const bTimestamp = Object.prototype.hasOwnProperty.call(b, 'time_stamp') ? b.time_stamp : b.creation_date |
||||
|
|
||||
|
return bTimestamp - aTimestamp |
||||
|
}) |
||||
|
) |
||||
|
|
||||
|
const lnActivity = createSelector( |
||||
|
paymentsSelector, |
||||
|
invoicesSelector, |
||||
|
(payments, invoices) => [...payments, ...invoices].sort((a, b) => b.creation_date - a.creation_date) |
||||
|
) |
||||
|
|
||||
|
const paymentActivity = createSelector( |
||||
|
paymentsSelector, |
||||
|
payments => payments |
||||
|
) |
||||
|
|
||||
|
const invoiceActivity = createSelector( |
||||
|
invoicesSelector, |
||||
|
invoices => invoices |
||||
|
) |
||||
|
|
||||
|
const transactionActivity = createSelector( |
||||
|
transactionsSelector, |
||||
|
transactions => transactions |
||||
|
) |
||||
|
|
||||
|
const FILTERS = { |
||||
|
ALL_ACTIVITY: allActivity, |
||||
|
LN_ACTIVITY: lnActivity, |
||||
|
PAYMENT_ACTIVITY: paymentActivity, |
||||
|
INVOICE_ACTIVITY: invoiceActivity, |
||||
|
TRANSACTION_ACTIVITY: transactionActivity |
||||
|
} |
||||
|
|
||||
|
activitySelectors.currentActivity = createSelector( |
||||
|
filterSelector, |
||||
|
filter => FILTERS[filter.key] |
||||
|
) |
||||
|
|
||||
|
activitySelectors.nonActiveFilters = createSelector( |
||||
|
filtersSelector, |
||||
|
filterSelector, |
||||
|
(filters, filter) => filters.filter(f => f.key !== filter.key) |
||||
|
) |
||||
|
|
||||
|
|
||||
|
export { activitySelectors } |
||||
|
|
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Reducer
|
||||
|
// ------------------------------------
|
||||
|
export default function activityReducer(state = initialState, action) { |
||||
|
const handler = ACTION_HANDLERS[action.type] |
||||
|
|
||||
|
return handler ? handler(state, action) : state |
||||
|
} |
@ -0,0 +1,92 @@ |
|||||
|
import { ipcRenderer } from 'electron' |
||||
|
import { btc, usd } from '../utils' |
||||
|
import { setForm, resetForm } from './form' |
||||
|
import { showModal } from './modal' |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Constants
|
||||
|
// ------------------------------------
|
||||
|
export const GET_TRANSACTIONS = 'GET_TRANSACTIONS' |
||||
|
export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS' |
||||
|
|
||||
|
export const SEND_TRANSACTION = 'SEND_TRANSACTION' |
||||
|
|
||||
|
export const TRANSACTION_SUCCESSFULL = 'TRANSACTION_SUCCESSFULL' |
||||
|
export const TRANSACTION_FAILED = 'TRANSACTION_FAILED' |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Actions
|
||||
|
// ------------------------------------
|
||||
|
export function getTransactions() { |
||||
|
return { |
||||
|
type: GET_TRANSACTIONS |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function sendTransaction() { |
||||
|
return { |
||||
|
type: SEND_TRANSACTION |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Send IPC event for payments
|
||||
|
export const fetchTransactions = () => (dispatch) => { |
||||
|
dispatch(getTransactions()) |
||||
|
ipcRenderer.send('lnd', { msg: 'transactions' }) |
||||
|
} |
||||
|
|
||||
|
// Receive IPC event for payments
|
||||
|
export const receiveTransactions = (event, { transactions }) => dispatch => dispatch({ type: RECEIVE_TRANSACTIONS, transactions }) |
||||
|
|
||||
|
export const sendCoins = ({ value, addr, currency, rate }) => (dispatch) => { |
||||
|
const amount = currency === 'usd' ? btc.btcToSatoshis(usd.usdToBtc(value, rate)) : btc.btcToSatoshis(value) |
||||
|
dispatch(sendTransaction()) |
||||
|
ipcRenderer.send('lnd', { msg: 'sendCoins', data: { amount, addr } }) |
||||
|
} |
||||
|
|
||||
|
// Receive IPC event for successful payment
|
||||
|
// TODO: Add payment to state, not a total re-fetch
|
||||
|
export const transactionSuccessful = (event, { amount, addr, txid }) => (dispatch) => { |
||||
|
// Get the new list of transactions (TODO dont do an entire new fetch)
|
||||
|
fetchTransactions() |
||||
|
// Close the form modal once the payment was succesful
|
||||
|
dispatch(setForm({ modalOpen: false })) |
||||
|
// Show successful payment state
|
||||
|
dispatch(showModal('SUCCESSFUL_SEND_COINS', { txid, amount, addr })) |
||||
|
// TODO: Add successful on-chain payment to payments list once payments list supports on-chain and LN
|
||||
|
// dispatch({ type: PAYMENT_SUCCESSFULL, payment: { amount, addr, txid, pending: true } })
|
||||
|
dispatch({ type: TRANSACTION_SUCCESSFULL }) |
||||
|
// Reset the payment form
|
||||
|
dispatch(resetForm()) |
||||
|
} |
||||
|
|
||||
|
export const transactionError = () => (dispatch) => { |
||||
|
dispatch({ type: TRANSACTION_FAILED }) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Action Handlers
|
||||
|
// ------------------------------------
|
||||
|
const ACTION_HANDLERS = { |
||||
|
[GET_TRANSACTIONS]: state => ({ ...state, transactionLoading: true }), |
||||
|
[SEND_TRANSACTION]: state => ({ ...state, sendingTransaction: true }), |
||||
|
[RECEIVE_TRANSACTIONS]: (state, { transactions }) => ({ ...state, transactionLoading: false, transactions }), |
||||
|
[TRANSACTION_SUCCESSFULL]: state => ({ ...state, sendingTransaction: false }), |
||||
|
[TRANSACTION_FAILED]: state => ({ ...state, sendingTransaction: false }) |
||||
|
} |
||||
|
|
||||
|
// ------------------------------------
|
||||
|
// Reducer
|
||||
|
// ------------------------------------
|
||||
|
const initialState = { |
||||
|
sendingTransaction: false, |
||||
|
transactionLoading: false, |
||||
|
transactions: [] |
||||
|
} |
||||
|
|
||||
|
export default function transactionReducer(state = initialState, action) { |
||||
|
const handler = ACTION_HANDLERS[action.type] |
||||
|
|
||||
|
return handler ? handler(state, action) : state |
||||
|
} |
@ -0,0 +1,131 @@ |
|||||
|
@import '../../../../variables.scss'; |
||||
|
|
||||
|
.container { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
cursor: pointer; |
||||
|
max-width: 960px; |
||||
|
margin: 0 auto; |
||||
|
height: 76px; |
||||
|
align-items: center; |
||||
|
border-bottom: 1px solid $grey; |
||||
|
font-size: 14px; |
||||
|
transition: background-color .1s linear; |
||||
|
transition-delay: .1s; |
||||
|
color: $darkestgrey; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.clock { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: -100px; |
||||
|
width: 100px; |
||||
|
height: 77px; |
||||
|
|
||||
|
svg { |
||||
|
font-size: 18px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.date { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding-right: 50px; |
||||
|
|
||||
|
time { |
||||
|
text-transform: uppercase; |
||||
|
|
||||
|
&:nth-child(1) { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 38px; |
||||
|
font-size: 18px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(2) { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.data { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
flex: 6; |
||||
|
justify-content: space-evenly; |
||||
|
|
||||
|
.title { |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
|
||||
|
.icon, h3, span { |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
|
||||
|
h3, span { |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
display: inline-block; |
||||
|
flex: none; |
||||
|
position: relative; |
||||
|
width: 36px; |
||||
|
height: 36px; |
||||
|
border: 1px solid $darkestgrey; |
||||
|
border-radius: 50%; |
||||
|
margin-right: 15px; |
||||
|
|
||||
|
svg { |
||||
|
color: $main; |
||||
|
font-size: 16px; |
||||
|
vertical-align: middle; |
||||
|
display: flex; |
||||
|
top: 0; |
||||
|
left: 50%; |
||||
|
height: 100%; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
h3 { |
||||
|
display: inline-block; |
||||
|
|
||||
|
&:after { |
||||
|
content: " "; |
||||
|
display: inline-block; |
||||
|
width: 3px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
text-transform: uppercase; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.amount { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
flex: 1; |
||||
|
text-align: right; |
||||
|
font-size: 16px; |
||||
|
|
||||
|
&.positive span:nth-child(1) { |
||||
|
font-weight: bold; |
||||
|
color: $main; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
&:nth-child(2) { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
import { FaBolt, FaClockO } from 'react-icons/lib/fa' |
||||
|
import { btc } from '../../../../../utils' |
||||
|
import styles from '../Activity.scss' |
||||
|
|
||||
|
const Invoice = ({ invoice, ticker, currentTicker, showActivityModal }) => ( |
||||
|
<div className={styles.container} onClick={() => showActivityModal('INVOICE', { invoice })}> |
||||
|
{ |
||||
|
!invoice.settled ? |
||||
|
<div className={styles.clock}> |
||||
|
<i className='hint--top' data-hint='Request has not been paid'> |
||||
|
<FaClockO /> |
||||
|
</i> |
||||
|
</div> |
||||
|
: |
||||
|
null |
||||
|
} |
||||
|
<div className={styles.date}> |
||||
|
<Moment format='D'> |
||||
|
{invoice.creation_date * 1000} |
||||
|
</Moment> |
||||
|
<Moment format='MMMM'> |
||||
|
{invoice.creation_date * 1000} |
||||
|
</Moment> |
||||
|
</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' } |
||||
|
</h3> |
||||
|
<span> |
||||
|
{ticker.currency} |
||||
|
</span> |
||||
|
</div> |
||||
|
<div className={styles.subtitle}> |
||||
|
{invoice.r_hash.toString('hex')} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className={`${styles.amount} ${invoice.settled ? styles.positive : styles.negative}`}> |
||||
|
<span className='hint--top' data-hint='Invoice amount'> |
||||
|
+ |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(invoice.value, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(invoice.value) |
||||
|
} |
||||
|
</span> |
||||
|
<span className='hint--bottom' data-hint='Invoice fee'> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(invoice.fee, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(invoice.fee) |
||||
|
} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
|
||||
|
Invoice.propTypes = { |
||||
|
invoice: PropTypes.object.isRequired, |
||||
|
ticker: PropTypes.object.isRequired, |
||||
|
currentTicker: PropTypes.object.isRequired, |
||||
|
showActivityModal: PropTypes.func.isRequired |
||||
|
} |
||||
|
|
||||
|
export default Invoice |
@ -0,0 +1,3 @@ |
|||||
|
import Invoice from './Invoice' |
||||
|
|
||||
|
export default Invoice |
@ -1,110 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import PropTypes from 'prop-types' |
|
||||
import Moment from 'react-moment' |
|
||||
import 'moment-timezone' |
|
||||
import { MdCheck } from 'react-icons/lib/md' |
|
||||
import QRCode from 'qrcode.react' |
|
||||
import Modal from './Modal' |
|
||||
import CurrencyIcon from '../../../../components/CurrencyIcon' |
|
||||
import { btc } from '../../../../utils' |
|
||||
import styles from './Invoices.scss' |
|
||||
|
|
||||
const Invoices = ({ |
|
||||
invoice, |
|
||||
invoices, |
|
||||
ticker, |
|
||||
setInvoice, |
|
||||
invoiceModalOpen, |
|
||||
currentTicker |
|
||||
}) => ( |
|
||||
<div> |
|
||||
<Modal isOpen={invoiceModalOpen} resetObject={setInvoice}> |
|
||||
{ |
|
||||
invoice ? |
|
||||
<div className={styles.invoiceModal}> |
|
||||
<h3>{invoice.memo}</h3> |
|
||||
<h1> |
|
||||
<CurrencyIcon currency={ticker.currency} crypto={ticker.crypto} styles={{ verticalAlign: 'top' }} /> |
|
||||
<span className={styles.value}> |
|
||||
{ |
|
||||
ticker.currency === 'usd' ? |
|
||||
btc.satoshisToUsd(invoice.value, currentTicker.price_usd) |
|
||||
: |
|
||||
btc.satoshisToBtc(invoice.value) |
|
||||
} |
|
||||
</span> |
|
||||
</h1> |
|
||||
<div className={styles.qrcode}> |
|
||||
<QRCode value={invoice.payment_request} size={200} /> |
|
||||
<input |
|
||||
readOnly |
|
||||
className={styles.paymentRequest} |
|
||||
onClick={event => event.target.select()} |
|
||||
defaultValue={invoice.payment_request} |
|
||||
/> |
|
||||
</div> |
|
||||
<div className={styles.settled}> |
|
||||
{ |
|
||||
invoice.settled ? |
|
||||
<p><MdCheck style={{ verticalAlign: 'top' }} /> Paid</p> |
|
||||
: |
|
||||
<p>Not Paid</p> |
|
||||
} |
|
||||
</div> |
|
||||
<p className={styles.date}> |
|
||||
Created on |
|
||||
<Moment format='MMM Do'>{invoice.creation_date * 1000}</Moment> |
|
||||
</p> |
|
||||
</div> |
|
||||
: |
|
||||
null |
|
||||
} |
|
||||
</Modal> |
|
||||
<ul className={styles.invoices}> |
|
||||
<li className={styles.invoiceTitles}> |
|
||||
<div className={styles.left}> |
|
||||
<div>Payment Request</div> |
|
||||
</div> |
|
||||
<div className={styles.center}> |
|
||||
<div>Memo</div> |
|
||||
</div> |
|
||||
<div className={styles.right}> |
|
||||
<div>Amount</div> |
|
||||
</div> |
|
||||
</li> |
|
||||
{ |
|
||||
invoices.map((invoiceItem, index) => ( |
|
||||
<li key={index} className={styles.invoice} onClick={() => setInvoice(invoiceItem)}> |
|
||||
<div className={styles.left}> |
|
||||
<div className={styles.path}>{`${invoiceItem.payment_request.substring(0, 75)}...`}</div> |
|
||||
</div> |
|
||||
<div className={styles.center}> |
|
||||
<div>{invoiceItem.memo}</div> |
|
||||
</div> |
|
||||
<div className={styles.right}> |
|
||||
<div className={invoiceItem.settled ? styles.settled : null}> |
|
||||
{ |
|
||||
ticker.currency === 'usd' ? |
|
||||
btc.satoshisToUsd(invoiceItem.value, currentTicker.price_usd) |
|
||||
: |
|
||||
btc.satoshisToBtc(invoiceItem.value) |
|
||||
} |
|
||||
</div> |
|
||||
</div> |
|
||||
</li> |
|
||||
)) |
|
||||
} |
|
||||
</ul> |
|
||||
</div> |
|
||||
) |
|
||||
|
|
||||
Invoices.propTypes = { |
|
||||
invoice: PropTypes.object, |
|
||||
invoices: PropTypes.array.isRequired, |
|
||||
ticker: PropTypes.object.isRequired, |
|
||||
setInvoice: PropTypes.func.isRequired, |
|
||||
invoiceModalOpen: PropTypes.bool.isRequired, |
|
||||
currentTicker: PropTypes.object.isRequired |
|
||||
} |
|
||||
|
|
||||
export default Invoices |
|
@ -1,125 +0,0 @@ |
|||||
@import '../../../../variables.scss'; |
|
||||
|
|
||||
.invoiceModal { |
|
||||
padding: 40px; |
|
||||
|
|
||||
h3 { |
|
||||
font-size: 24px; |
|
||||
color: $black; |
|
||||
font-weight: bold; |
|
||||
text-align: center; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
h1 { |
|
||||
text-align: center; |
|
||||
color: $main; |
|
||||
margin: 20px 20px 60px 0; |
|
||||
|
|
||||
svg { |
|
||||
font-size: 30px; |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
span svg[data-icon='ltc'] { |
|
||||
width: 30px; |
|
||||
height: 30px; |
|
||||
vertical-align: top; |
|
||||
|
|
||||
g { |
|
||||
transform: scale(1.75) translate(-5px, -5px); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.value { |
|
||||
font-size: 60px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.qrcode { |
|
||||
text-align: center; |
|
||||
|
|
||||
.paymentRequest { |
|
||||
text-align: center; |
|
||||
font-size: 0.5vw; |
|
||||
margin-top: 20px; |
|
||||
padding: 5px; |
|
||||
border-radius: 5px; |
|
||||
background: $lightgrey; |
|
||||
border: 1px solid $darkgrey; |
|
||||
display: block; |
|
||||
width: 100%; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.settled { |
|
||||
text-align: center; |
|
||||
color: $main; |
|
||||
text-transform: uppercase; |
|
||||
font-size: 20px; |
|
||||
margin: 30px 0; |
|
||||
font-weight: bold; |
|
||||
|
|
||||
svg { |
|
||||
line-height: 20px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.date { |
|
||||
text-align: center; |
|
||||
|
|
||||
time { |
|
||||
margin-left: 3px; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.invoices { |
|
||||
width: 75%; |
|
||||
margin: 0 auto; |
|
||||
background: $white; |
|
||||
} |
|
||||
|
|
||||
.invoice, .invoiceTitles { |
|
||||
display: flex; |
|
||||
flex-direction: 'row'; |
|
||||
padding: 35px 10px; |
|
||||
border-bottom: 1px solid $grey; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
.left { |
|
||||
flex: 7; |
|
||||
} |
|
||||
|
|
||||
.center { |
|
||||
flex: 2; |
|
||||
} |
|
||||
|
|
||||
.right { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.settled { |
|
||||
color: $main; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.invoiceTitles { |
|
||||
border: none; |
|
||||
|
|
||||
.left, .center, .right { |
|
||||
color: $black; |
|
||||
text-transform: uppercase; |
|
||||
font-size: 16px; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.invoice { |
|
||||
cursor: pointer; |
|
||||
|
|
||||
&:hover { |
|
||||
background: #f0f0f0; |
|
||||
} |
|
||||
} |
|
@ -1,41 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import PropTypes from 'prop-types' |
|
||||
import ReactModal from 'react-modal' |
|
||||
|
|
||||
const Modal = ({ isOpen, resetObject, children }) => { |
|
||||
const customStyles = { |
|
||||
overlay: { |
|
||||
cursor: 'pointer' |
|
||||
}, |
|
||||
content: { |
|
||||
top: 'auto', |
|
||||
left: '20%', |
|
||||
right: '0', |
|
||||
bottom: 'auto', |
|
||||
width: '40%', |
|
||||
margin: '50px auto' |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<ReactModal |
|
||||
isOpen={isOpen} |
|
||||
contentLabel='No Overlay Click Modal' |
|
||||
ariaHideApp |
|
||||
shouldCloseOnOverlayClick |
|
||||
onRequestClose={() => resetObject(null)} |
|
||||
parentSelector={() => document.body} |
|
||||
style={customStyles} |
|
||||
> |
|
||||
{children} |
|
||||
</ReactModal> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
Modal.propTypes = { |
|
||||
isOpen: PropTypes.bool.isRequired, |
|
||||
resetObject: PropTypes.func.isRequired, |
|
||||
children: PropTypes.object |
|
||||
} |
|
||||
|
|
||||
export default Modal |
|
@ -0,0 +1,61 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
|
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
|
||||
|
import QRCode from 'qrcode.react' |
||||
|
|
||||
|
import { MdCheck } from 'react-icons/lib/md' |
||||
|
|
||||
|
import CurrencyIcon from '../../../../../../components/CurrencyIcon' |
||||
|
import { btc } from '../../../../../../utils' |
||||
|
|
||||
|
import styles from './Invoice.scss' |
||||
|
|
||||
|
|
||||
|
const Invoice = ({ invoice, ticker, currentTicker }) => ( |
||||
|
<div className={styles.container}> |
||||
|
<h3>{invoice.memo}</h3> |
||||
|
<h1> |
||||
|
<CurrencyIcon currency={ticker.currency} crypto={ticker.crypto} styles={{ verticalAlign: 'top' }} /> |
||||
|
<span className={styles.value}> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(invoice.value, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(invoice.value) |
||||
|
} |
||||
|
</span> |
||||
|
</h1> |
||||
|
<div className={styles.qrcode}> |
||||
|
<QRCode value={invoice.payment_request} size={200} /> |
||||
|
<input |
||||
|
readOnly |
||||
|
className={styles.paymentRequest} |
||||
|
onClick={event => event.target.select()} |
||||
|
defaultValue={invoice.payment_request} |
||||
|
/> |
||||
|
</div> |
||||
|
<div className={styles.settled}> |
||||
|
{ |
||||
|
invoice.settled ? |
||||
|
<p><MdCheck style={{ verticalAlign: 'top' }} /> Paid</p> |
||||
|
: |
||||
|
<p>Not Paid</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 |
@ -0,0 +1,75 @@ |
|||||
|
@import '../../../../../../variables.scss'; |
||||
|
|
||||
|
.container { |
||||
|
padding: 40px; |
||||
|
|
||||
|
h3 { |
||||
|
font-size: 24px; |
||||
|
color: $black; |
||||
|
font-weight: bold; |
||||
|
text-align: center; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: $main; |
||||
|
margin: 20px 20px 60px 0; |
||||
|
|
||||
|
svg { |
||||
|
font-size: 30px; |
||||
|
vertical-align: top; |
||||
|
} |
||||
|
|
||||
|
span svg[data-icon='ltc'] { |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
vertical-align: top; |
||||
|
|
||||
|
g { |
||||
|
transform: scale(1.75) translate(-5px, -5px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.value { |
||||
|
font-size: 60px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.qrcode { |
||||
|
text-align: center; |
||||
|
|
||||
|
.paymentRequest { |
||||
|
text-align: center; |
||||
|
font-size: 0.5vw; |
||||
|
margin-top: 20px; |
||||
|
padding: 5px; |
||||
|
border-radius: 5px; |
||||
|
background: $lightgrey; |
||||
|
border: 1px solid $darkgrey; |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.settled { |
||||
|
text-align: center; |
||||
|
color: $main; |
||||
|
text-transform: uppercase; |
||||
|
font-size: 20px; |
||||
|
margin: 30px 0; |
||||
|
font-weight: bold; |
||||
|
|
||||
|
svg { |
||||
|
line-height: 20px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.date { |
||||
|
text-align: center; |
||||
|
|
||||
|
time { |
||||
|
margin-left: 3px; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
import Invoice from './Invoice' |
||||
|
|
||||
|
export default Invoice |
@ -0,0 +1,57 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
import ReactModal from 'react-modal' |
||||
|
|
||||
|
import Transaction from './Transaction' |
||||
|
import Payment from './Payment' |
||||
|
import Invoice from './Invoice' |
||||
|
|
||||
|
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: '20%', |
||||
|
right: '0', |
||||
|
bottom: 'auto', |
||||
|
width: '40%', |
||||
|
margin: '50px auto' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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} |
||||
|
> |
||||
|
<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 |
@ -0,0 +1,44 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
|
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
|
||||
|
import CurrencyIcon from '../../../../../../components/CurrencyIcon' |
||||
|
import { btc } from '../../../../../../utils' |
||||
|
|
||||
|
import styles from './Payment.scss' |
||||
|
|
||||
|
|
||||
|
const Payment = ({ payment, ticker, currentTicker }) => ( |
||||
|
<div className={styles.container}> |
||||
|
<h3>{payment.payment_hash}</h3> |
||||
|
<h1> |
||||
|
<CurrencyIcon currency={ticker.currency} crypto={ticker.crypto} styles={{ verticalAlign: 'top' }} /> |
||||
|
<span className={styles.value}> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(payment.value, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(payment.value) |
||||
|
} |
||||
|
</span> |
||||
|
</h1> |
||||
|
<dl> |
||||
|
<dt>Fee</dt> |
||||
|
<dd>{payment.fee}</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 |
@ -0,0 +1,55 @@ |
|||||
|
@import '../../../../../../variables.scss'; |
||||
|
|
||||
|
.container { |
||||
|
padding: 40px; |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: $main; |
||||
|
margin: 60px 30px 60px 0; |
||||
|
|
||||
|
svg { |
||||
|
font-size: 30px; |
||||
|
vertical-align: top; |
||||
|
} |
||||
|
|
||||
|
span svg[data-icon='ltc'] { |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
vertical-align: top; |
||||
|
|
||||
|
g { |
||||
|
transform: scale(1.75) translate(-5px, -5px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.value { |
||||
|
font-size: 80px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
h3 { |
||||
|
font-size: 14px; |
||||
|
text-align: center; |
||||
|
color: $black; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
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: 19px 0; |
||||
|
margin-left: 0; |
||||
|
border-top: 1px solid $darkgrey; |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
import Payment from './Payment' |
||||
|
|
||||
|
export default Payment |
@ -0,0 +1,54 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
|
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
|
||||
|
import CurrencyIcon from '../../../../../../components/CurrencyIcon' |
||||
|
import { btc } from '../../../../../../utils' |
||||
|
|
||||
|
import styles from './Transaction.scss' |
||||
|
|
||||
|
|
||||
|
const Transaction = ({ transaction, ticker, currentTicker }) => ( |
||||
|
<div className={styles.container}> |
||||
|
<h2> |
||||
|
{ |
||||
|
transaction.amount < 0 ? |
||||
|
'Sent' |
||||
|
: |
||||
|
'Received' |
||||
|
} |
||||
|
</h2> |
||||
|
<h3>{transaction.tx_hash}</h3> |
||||
|
<h1> |
||||
|
<CurrencyIcon currency={ticker.currency} crypto={ticker.crypto} styles={{ verticalAlign: 'top' }} /> |
||||
|
<span className={styles.value}> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(transaction.amount, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(transaction.amount) |
||||
|
} |
||||
|
</span> |
||||
|
</h1> |
||||
|
<dl> |
||||
|
<dt>Confirmations</dt> |
||||
|
<dd>{transaction.num_confirmations}</dd> |
||||
|
<dt>Fee</dt> |
||||
|
<dd>{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 |
@ -0,0 +1,63 @@ |
|||||
|
@import '../../../../../../variables.scss'; |
||||
|
|
||||
|
.container { |
||||
|
padding: 40px; |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: $main; |
||||
|
margin: 60px 30px 60px 0; |
||||
|
|
||||
|
svg { |
||||
|
font-size: 30px; |
||||
|
vertical-align: top; |
||||
|
} |
||||
|
|
||||
|
span svg[data-icon='ltc'] { |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
vertical-align: top; |
||||
|
|
||||
|
g { |
||||
|
transform: scale(1.75) translate(-5px, -5px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.value { |
||||
|
font-size: 75px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
h3 { |
||||
|
font-size: 14px; |
||||
|
text-align: center; |
||||
|
color: $black; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
h2 { |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
text-transform: uppercase; |
||||
|
letter-spacing: 1.5px; |
||||
|
font-size: 24px; |
||||
|
} |
||||
|
|
||||
|
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: 19px 0; |
||||
|
margin-left: 0; |
||||
|
border-top: 1px solid $darkgrey; |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
import Transaction from './Transaction' |
||||
|
|
||||
|
export default Transaction |
@ -0,0 +1,3 @@ |
|||||
|
import Modal from './Modal' |
||||
|
|
||||
|
export default Modal |
@ -0,0 +1,64 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
import { FaBolt } from 'react-icons/lib/fa' |
||||
|
import { btc } from '../../../../../utils' |
||||
|
import styles from '../Activity.scss' |
||||
|
|
||||
|
const Payment = ({ payment, ticker, currentTicker, showActivityModal }) => ( |
||||
|
<div className={styles.container} onClick={() => showActivityModal('PAYMENT', { payment })}> |
||||
|
<div className={styles.date}> |
||||
|
<Moment format='D'> |
||||
|
{payment.creation_date * 1000} |
||||
|
</Moment> |
||||
|
<Moment format='MMMM'> |
||||
|
{payment.creation_date * 1000} |
||||
|
</Moment> |
||||
|
</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> |
||||
|
</div> |
||||
|
<div className={styles.subtitle}> |
||||
|
{payment.payment_hash.toString('hex')} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className={styles.amount}> |
||||
|
<span className='hint--top' data-hint='Payment amount'> |
||||
|
- |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(payment.value, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(payment.value) |
||||
|
} |
||||
|
</span> |
||||
|
<span className='hint--bottom' data-hint='Payment fee'> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(payment.fee, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(payment.fee) |
||||
|
} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
|
||||
|
Payment.propTypes = { |
||||
|
payment: PropTypes.object.isRequired, |
||||
|
ticker: PropTypes.object.isRequired, |
||||
|
currentTicker: PropTypes.object.isRequired, |
||||
|
showActivityModal: PropTypes.func.isRequired |
||||
|
} |
||||
|
|
||||
|
export default Payment |
@ -0,0 +1,3 @@ |
|||||
|
import Payment from './Payment' |
||||
|
|
||||
|
export default Payment |
@ -1,112 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import PropTypes from 'prop-types' |
|
||||
import Moment from 'react-moment' |
|
||||
import 'moment-timezone' |
|
||||
import Modal from './Modal' |
|
||||
import CurrencyIcon from '../../../../components/CurrencyIcon' |
|
||||
import { btc } from '../../../../utils' |
|
||||
import styles from './Payments.scss' |
|
||||
|
|
||||
const Payments = ({ |
|
||||
payment, |
|
||||
payments, |
|
||||
ticker, |
|
||||
setPayment, |
|
||||
paymentModalOpen, |
|
||||
currentTicker |
|
||||
}) => ( |
|
||||
<div> |
|
||||
<Modal isOpen={paymentModalOpen} resetObject={setPayment}> |
|
||||
{ |
|
||||
payment ? |
|
||||
<div className={styles.paymentModal}> |
|
||||
<h3>{payment.payment_hash}</h3> |
|
||||
<h1> |
|
||||
<CurrencyIcon currency={ticker.currency} crypto={ticker.crypto} styles={{ verticalAlign: 'top' }} /> |
|
||||
<span className={styles.value}> |
|
||||
{ |
|
||||
ticker.currency === 'usd' ? |
|
||||
btc.satoshisToUsd(payment.value, currentTicker.price_usd) |
|
||||
: |
|
||||
btc.satoshisToBtc(payment.value) |
|
||||
} |
|
||||
</span> |
|
||||
</h1> |
|
||||
<dl> |
|
||||
<dt>Fee</dt> |
|
||||
<dd>{payment.fee}</dd> |
|
||||
<dt>Date</dt> |
|
||||
<dd> |
|
||||
<Moment format='MMM Do'>{payment.creation_date * 1000}</Moment> |
|
||||
</dd> |
|
||||
</dl> |
|
||||
</div> |
|
||||
: |
|
||||
null |
|
||||
} |
|
||||
</Modal> |
|
||||
<ul className={styles.payments}> |
|
||||
<li className={styles.paymentTitles}> |
|
||||
<div className={styles.left}> |
|
||||
<div>Public Key</div> |
|
||||
</div> |
|
||||
<div className={styles.center}> |
|
||||
<div>Date</div> |
|
||||
</div> |
|
||||
<div className={styles.center}> |
|
||||
<div>Fee</div> |
|
||||
</div> |
|
||||
<div className={styles.right}> |
|
||||
<div>Amount</div> |
|
||||
</div> |
|
||||
</li> |
|
||||
{ |
|
||||
payments.map((paymentItem, index) => |
|
||||
( |
|
||||
<li key={index} className={styles.payment} onClick={() => setPayment(paymentItem)}> |
|
||||
<div className={styles.left}> |
|
||||
<div className={styles.path}>{paymentItem.path[0]}</div> |
|
||||
</div> |
|
||||
<div className={styles.center}> |
|
||||
<div className={styles.date}> |
|
||||
<Moment format='MMM Do'>{paymentItem.creation_date * 1000}</Moment> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div className={styles.right}> |
|
||||
<span className={styles.fee}> |
|
||||
{ |
|
||||
ticker.currency === 'usd' ? |
|
||||
btc.satoshisToUsd(paymentItem.fee, currentTicker.price_usd) |
|
||||
: |
|
||||
btc.satoshisToBtc(paymentItem.fee) |
|
||||
} |
|
||||
</span> |
|
||||
</div> |
|
||||
<div className={styles.right}> |
|
||||
<span className={styles.value}> |
|
||||
{ |
|
||||
ticker.currency === 'usd' ? |
|
||||
btc.satoshisToUsd(paymentItem.value, currentTicker.price_usd) |
|
||||
: |
|
||||
btc.satoshisToBtc(paymentItem.value) |
|
||||
} |
|
||||
</span> |
|
||||
</div> |
|
||||
</li> |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
</ul> |
|
||||
</div> |
|
||||
) |
|
||||
|
|
||||
Payments.propTypes = { |
|
||||
payment: PropTypes.object, |
|
||||
payments: PropTypes.array.isRequired, |
|
||||
ticker: PropTypes.object.isRequired, |
|
||||
setPayment: PropTypes.func.isRequired, |
|
||||
paymentModalOpen: PropTypes.bool.isRequired, |
|
||||
currentTicker: PropTypes.object.isRequired |
|
||||
} |
|
||||
|
|
||||
export default Payments |
|
@ -1,102 +0,0 @@ |
|||||
@import '../../../../variables.scss'; |
|
||||
|
|
||||
.paymentModal { |
|
||||
padding: 40px; |
|
||||
|
|
||||
h1 { |
|
||||
text-align: center; |
|
||||
color: $main; |
|
||||
margin: 60px 30px 60px 0; |
|
||||
|
|
||||
svg { |
|
||||
font-size: 30px; |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
span svg[data-icon='ltc'] { |
|
||||
width: 30px; |
|
||||
height: 30px; |
|
||||
vertical-align: top; |
|
||||
|
|
||||
g { |
|
||||
transform: scale(1.75) translate(-5px, -5px); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.value { |
|
||||
font-size: 80px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
h3 { |
|
||||
font-size: 14px; |
|
||||
text-align: center; |
|
||||
color: $black; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
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: 19px 0; |
|
||||
margin-left: 0; |
|
||||
border-top: 1px solid $darkgrey; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.payments { |
|
||||
width: 75%; |
|
||||
margin: 0 auto; |
|
||||
background: $white; |
|
||||
} |
|
||||
|
|
||||
.payment, .paymentTitles { |
|
||||
display: flex; |
|
||||
flex-direction: 'row'; |
|
||||
padding: 35px 10px; |
|
||||
border-bottom: 1px solid $grey; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
.left, .center, .right { |
|
||||
display: inline-block; |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
.center, .right { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.left { |
|
||||
flex: 7; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.paymentTitles { |
|
||||
border: none; |
|
||||
|
|
||||
.left, .center, .right { |
|
||||
color: $black; |
|
||||
text-transform: uppercase; |
|
||||
font-size: 16px; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
.payment { |
|
||||
cursor: pointer; |
|
||||
|
|
||||
&:hover { |
|
||||
background: #f0f0f0; |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,64 @@ |
|||||
|
import React from 'react' |
||||
|
import PropTypes from 'prop-types' |
||||
|
import Moment from 'react-moment' |
||||
|
import 'moment-timezone' |
||||
|
import { FaChain } from 'react-icons/lib/fa' |
||||
|
import { btc } from '../../../../../utils' |
||||
|
import styles from '../Activity.scss' |
||||
|
|
||||
|
const Transaction = ({ transaction, ticker, currentTicker, showActivityModal }) => ( |
||||
|
<div className={styles.container} onClick={() => showActivityModal('TRANSACTION', { transaction })}> |
||||
|
<div className={styles.date}> |
||||
|
<Moment format='D'> |
||||
|
{transaction.time_stamp * 1000} |
||||
|
</Moment> |
||||
|
<Moment format='MMMM'> |
||||
|
{transaction.time_stamp * 1000} |
||||
|
</Moment> |
||||
|
</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} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className={`${styles.amount} ${transaction.amount > 0 ? styles.positive : styles.negative}`}> |
||||
|
<span className='hint--top' data-hint='Transaction amount'> |
||||
|
{ transaction.amount > 0 ? '+' : '' } |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(transaction.amount, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(transaction.amount) |
||||
|
} |
||||
|
</span> |
||||
|
<span className='hint--bottom' data-hint='Transaction fee'> |
||||
|
{ |
||||
|
ticker.currency === 'usd' ? |
||||
|
btc.satoshisToUsd(transaction.total_fees, currentTicker.price_usd) |
||||
|
: |
||||
|
btc.satoshisToBtc(transaction.total_fees) |
||||
|
} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
|
||||
|
Transaction.propTypes = { |
||||
|
transaction: PropTypes.object.isRequired, |
||||
|
ticker: PropTypes.object.isRequired, |
||||
|
currentTicker: PropTypes.object.isRequired, |
||||
|
showActivityModal: PropTypes.func.isRequired |
||||
|
} |
||||
|
|
||||
|
export default Transaction |
@ -0,0 +1,111 @@ |
|||||
|
@import '../../../../../variables.scss'; |
||||
|
|
||||
|
.container { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
cursor: pointer; |
||||
|
max-width: 960px; |
||||
|
margin: 0 auto; |
||||
|
height: 76px; |
||||
|
align-items: center; |
||||
|
border-bottom: 1px solid $grey; |
||||
|
font-size: 14px; |
||||
|
transition: background-color .1s linear; |
||||
|
transition-delay: .1s; |
||||
|
color: $darkestgrey; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
display: block; |
||||
|
margin-right: 20px; |
||||
|
flex: none; |
||||
|
position: relative; |
||||
|
width: 36px; |
||||
|
height: 36px; |
||||
|
border: 1px solid $darkestgrey; |
||||
|
border-radius: 50%; |
||||
|
|
||||
|
svg { |
||||
|
color: $main; |
||||
|
font-size: 16px; |
||||
|
vertical-align: middle; |
||||
|
display: flex; |
||||
|
top: 0; |
||||
|
left: 50%; |
||||
|
height: 100%; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.data { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex: 6 0; |
||||
|
|
||||
|
.title { |
||||
|
flex: 8 0; |
||||
|
} |
||||
|
|
||||
|
.subtitle { |
||||
|
padding-left: 20px; |
||||
|
flex: 2 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.subtitle, .date { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.date { |
||||
|
padding-left: 20px; |
||||
|
flex: 1 0; |
||||
|
} |
||||
|
|
||||
|
.amount { |
||||
|
flex: 1 0; |
||||
|
padding-left: 20px; |
||||
|
|
||||
|
&.negative { |
||||
|
font-weight: 200; |
||||
|
} |
||||
|
|
||||
|
&.positive { |
||||
|
color: $main; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
section { |
||||
|
display: inline-block; |
||||
|
vertical-align: top; |
||||
|
text-align: right; |
||||
|
font-size: 0; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
|
||||
|
&:nth-child(1) { |
||||
|
width: 20%; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(2) { |
||||
|
width: 80%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
svg { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
display: block; |
||||
|
|
||||
|
&:nth-child(1) { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(2) { |
||||
|
font-size: 10px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,3 @@ |
|||||
|
import Transaction from './Transaction' |
||||
|
|
||||
|
export default Transaction |
Loading…
Reference in new issue