parent
ce1975fbb4
commit
930dac110a
@ -0,0 +1,25 @@ |
|||||||
|
import React, { PureComponent } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import classnames from 'classnames' |
||||||
|
|
||||||
|
export default class Card extends PureComponent { |
||||||
|
static propTypes = { |
||||||
|
className: PropTypes.string, |
||||||
|
overrideClassName: PropTypes.bool, |
||||||
|
title: PropTypes.string, |
||||||
|
children: PropTypes.node, |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { className, overrideClassName, title } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={classnames({ 'card': !overrideClassName }, className)}> |
||||||
|
<div className="card__title"> |
||||||
|
{ title } |
||||||
|
</div> |
||||||
|
{ this.props.children } |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './card.component' |
@ -0,0 +1,11 @@ |
|||||||
|
.card { |
||||||
|
border-radius: 4px; |
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08); |
||||||
|
padding: 16px 8px; |
||||||
|
|
||||||
|
&__title { |
||||||
|
border-bottom: 1px solid #d8d8d8; |
||||||
|
padding-bottom: 4px; |
||||||
|
text-transform: capitalize; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './transaction-activity-log.component' |
@ -0,0 +1,53 @@ |
|||||||
|
.transaction-activity-log { |
||||||
|
&__card { |
||||||
|
background: $white; |
||||||
|
} |
||||||
|
|
||||||
|
&__activities-container { |
||||||
|
padding-top: 8px; |
||||||
|
} |
||||||
|
|
||||||
|
&__activity { |
||||||
|
padding: 4px 0; |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
align-items: center; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
&::after { |
||||||
|
content: ''; |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
top: 0; |
||||||
|
height: 100%; |
||||||
|
width: 6px; |
||||||
|
border-right: 1px solid $scorpion; |
||||||
|
} |
||||||
|
|
||||||
|
&:first-child::after { |
||||||
|
height: 50%; |
||||||
|
top: 50%; |
||||||
|
} |
||||||
|
|
||||||
|
&:last-child::after { |
||||||
|
height: 50%; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&__activity-icon { |
||||||
|
width: 13px; |
||||||
|
height: 13px; |
||||||
|
margin-right: 6px; |
||||||
|
border-radius: 50%; |
||||||
|
background: $scorpion; |
||||||
|
} |
||||||
|
|
||||||
|
&__activity-text { |
||||||
|
color: $scorpion; |
||||||
|
font-size: .75rem; |
||||||
|
} |
||||||
|
|
||||||
|
b { |
||||||
|
font-weight: 500; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
import React, { PureComponent } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import { getActivities } from './transaction-activity-log.util' |
||||||
|
import Card from '../card' |
||||||
|
|
||||||
|
export default class TransactionActivityLog extends PureComponent { |
||||||
|
static contextTypes = { |
||||||
|
t: PropTypes.func, |
||||||
|
} |
||||||
|
|
||||||
|
static propTypes = { |
||||||
|
transaction: PropTypes.object, |
||||||
|
} |
||||||
|
|
||||||
|
state = { |
||||||
|
activities: [], |
||||||
|
} |
||||||
|
|
||||||
|
componentDidMount () { |
||||||
|
this.setActivites() |
||||||
|
} |
||||||
|
|
||||||
|
componentDidUpdate (prevProps) { |
||||||
|
const { transaction: { history: prevHistory = [] } = {} } = prevProps |
||||||
|
const { transaction: { history = [] } = {} } = this.props |
||||||
|
|
||||||
|
if (prevHistory.length !== history.length) { |
||||||
|
this.setActivites() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
setActivites () { |
||||||
|
const activities = getActivities(this.props.transaction) |
||||||
|
this.setState({ activities }) |
||||||
|
} |
||||||
|
|
||||||
|
renderActivity (activity, index) { |
||||||
|
return ( |
||||||
|
<div |
||||||
|
key={index} |
||||||
|
className="transaction-activity-log__activity" |
||||||
|
> |
||||||
|
<div className="transaction-activity-log__activity-icon" /> |
||||||
|
{ this.renderActivityText(activity) } |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderActivityText (activity) { |
||||||
|
const { eventKey, value, valueDescriptionKey } = activity |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="transaction-activity-log__activity-text"> |
||||||
|
{ `Transaction ` } |
||||||
|
<b>{ `${eventKey}` }</b> |
||||||
|
{ |
||||||
|
valueDescriptionKey && value |
||||||
|
? ( |
||||||
|
<span> |
||||||
|
{ ` with a ${valueDescriptionKey} of ` } |
||||||
|
<b>{ value }</b> |
||||||
|
. |
||||||
|
</span> |
||||||
|
) : '.' |
||||||
|
} |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { t } = this.context |
||||||
|
const { activities } = this.state |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="transaction-activity-log"> |
||||||
|
<Card |
||||||
|
title={t('activityLog')} |
||||||
|
className="transaction-activity-log__card" |
||||||
|
> |
||||||
|
<div className="transaction-activity-log__activities-container"> |
||||||
|
{ |
||||||
|
activities.map((activity, index) => ( |
||||||
|
this.renderActivity(activity, index) |
||||||
|
)) |
||||||
|
} |
||||||
|
</div> |
||||||
|
</Card> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
// path constants
|
||||||
|
const STATUS_PATH = '/status' |
||||||
|
const GAS_PRICE_PATH = '/txParams/gasPrice' |
||||||
|
|
||||||
|
// status constants
|
||||||
|
const STATUS_UNAPPROVED = 'unapproved' |
||||||
|
const STATUS_SUBMITTED = 'submitted' |
||||||
|
const STATUS_CONFIRMED = 'confirmed' |
||||||
|
const STATUS_DROPPED = 'dropped' |
||||||
|
|
||||||
|
// op constants
|
||||||
|
const REPLACE_OP = 'replace' |
||||||
|
|
||||||
|
const eventPathsHash = { |
||||||
|
[STATUS_PATH]: true, |
||||||
|
[GAS_PRICE_PATH]: true, |
||||||
|
} |
||||||
|
|
||||||
|
const statusHash = { |
||||||
|
[STATUS_SUBMITTED]: true, |
||||||
|
[STATUS_CONFIRMED]: true, |
||||||
|
[STATUS_DROPPED]: true, |
||||||
|
} |
||||||
|
|
||||||
|
function eventCreator (eventKey, timestamp, value, valueDescriptionKey) { |
||||||
|
return { |
||||||
|
eventKey, |
||||||
|
timestamp, |
||||||
|
value, |
||||||
|
valueDescriptionKey, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export function getActivities (transaction) { |
||||||
|
const { history = [] } = transaction |
||||||
|
|
||||||
|
return history.reduce((acc, base) => { |
||||||
|
// First history item should be transaction creation
|
||||||
|
if (!Array.isArray(base) && base.status === STATUS_UNAPPROVED && base.txParams) { |
||||||
|
const { time, txParams: { value } = {} } = base |
||||||
|
return acc.concat(eventCreator('created', time, value, 'value')) |
||||||
|
} else if (Array.isArray(base)) { |
||||||
|
const events = [] |
||||||
|
|
||||||
|
base.forEach(entry => { |
||||||
|
const { op, path, value, timestamp } = entry |
||||||
|
|
||||||
|
if (path in eventPathsHash && op === REPLACE_OP) { |
||||||
|
switch (path) { |
||||||
|
case STATUS_PATH: { |
||||||
|
if (value in statusHash) { |
||||||
|
events.push(eventCreator(value, timestamp)) |
||||||
|
} |
||||||
|
|
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
case GAS_PRICE_PATH: { |
||||||
|
events.push(eventCreator('updated', timestamp, value, 'gasPrice')) |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
default: { |
||||||
|
events.push(eventCreator(value, timestamp)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return acc.concat(events) |
||||||
|
} |
||||||
|
|
||||||
|
return acc |
||||||
|
}, []) |
||||||
|
} |
Loading…
Reference in new issue