1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 19:26:13 +02:00

Add ActivityLog component

This commit is contained in:
Alexander Tseung 2018-08-25 18:00:38 -07:00
parent ce1975fbb4
commit 930dac110a
10 changed files with 264 additions and 1 deletions

View File

@ -17,6 +17,9 @@
"accountSelectionRequired": {
"message": "You need to select an account!"
},
"activityLog": {
"message": "activity log"
},
"address": {
"message": "Address"
},

View File

@ -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>
)
}
}

View File

@ -0,0 +1 @@
export { default } from './card.component'

View File

@ -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;
}
}

View File

@ -2,6 +2,8 @@
@import './button-group/index';
@import './card/index';
@import './confirm-page-container/index';
@import './export-text-container/index';
@ -24,6 +26,8 @@
@import './tabs/index';
@import './transaction-activity-log/index';
@import './transaction-view/index';
@import './transaction-view-balance/index';

View File

@ -80,7 +80,6 @@
justify-content: center;
position: relative;
flex: 0 0 auto;
padding: 8px;
.sender-to-recipient {
&__party {

View File

@ -0,0 +1 @@
export { default } from './transaction-activity-log.component'

View File

@ -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;
}
}

View File

@ -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>
)
}
}

View File

@ -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
}, [])
}