mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Add ActivityLog component
This commit is contained in:
parent
ce1975fbb4
commit
930dac110a
@ -17,6 +17,9 @@
|
|||||||
"accountSelectionRequired": {
|
"accountSelectionRequired": {
|
||||||
"message": "You need to select an account!"
|
"message": "You need to select an account!"
|
||||||
},
|
},
|
||||||
|
"activityLog": {
|
||||||
|
"message": "activity log"
|
||||||
|
},
|
||||||
"address": {
|
"address": {
|
||||||
"message": "Address"
|
"message": "Address"
|
||||||
},
|
},
|
||||||
|
25
ui/app/components/card/card.component.js
Normal file
25
ui/app/components/card/card.component.js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
1
ui/app/components/card/index.js
Normal file
1
ui/app/components/card/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './card.component'
|
11
ui/app/components/card/index.scss
Normal file
11
ui/app/components/card/index.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
@import './button-group/index';
|
@import './button-group/index';
|
||||||
|
|
||||||
|
@import './card/index';
|
||||||
|
|
||||||
@import './confirm-page-container/index';
|
@import './confirm-page-container/index';
|
||||||
|
|
||||||
@import './export-text-container/index';
|
@import './export-text-container/index';
|
||||||
@ -24,6 +26,8 @@
|
|||||||
|
|
||||||
@import './tabs/index';
|
@import './tabs/index';
|
||||||
|
|
||||||
|
@import './transaction-activity-log/index';
|
||||||
|
|
||||||
@import './transaction-view/index';
|
@import './transaction-view/index';
|
||||||
|
|
||||||
@import './transaction-view-balance/index';
|
@import './transaction-view-balance/index';
|
||||||
|
@ -80,7 +80,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
.sender-to-recipient {
|
.sender-to-recipient {
|
||||||
&__party {
|
&__party {
|
||||||
|
1
ui/app/components/transaction-activity-log/index.js
Normal file
1
ui/app/components/transaction-activity-log/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './transaction-activity-log.component'
|
53
ui/app/components/transaction-activity-log/index.scss
Normal file
53
ui/app/components/transaction-activity-log/index.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
Block a user