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