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