aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/_locales/en/messages.json3
-rw-r--r--ui/app/components/card/card.component.js25
-rw-r--r--ui/app/components/card/index.js1
-rw-r--r--ui/app/components/card/index.scss11
-rw-r--r--ui/app/components/index.scss4
-rw-r--r--ui/app/components/sender-to-recipient/index.scss1
-rw-r--r--ui/app/components/transaction-activity-log/index.js1
-rw-r--r--ui/app/components/transaction-activity-log/index.scss53
-rw-r--r--ui/app/components/transaction-activity-log/transaction-activity-log.component.js91
-rw-r--r--ui/app/components/transaction-activity-log/transaction-activity-log.util.js75
10 files changed, 264 insertions, 1 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 14e867b33..ad276306f 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -17,6 +17,9 @@
"accountSelectionRequired": {
"message": "You need to select an account!"
},
+ "activityLog": {
+ "message": "activity log"
+ },
"address": {
"message": "Address"
},
diff --git a/ui/app/components/card/card.component.js b/ui/app/components/card/card.component.js
new file mode 100644
index 000000000..bb7241da1
--- /dev/null
+++ b/ui/app/components/card/card.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/card/index.js b/ui/app/components/card/index.js
new file mode 100644
index 000000000..c3ca6e3f4
--- /dev/null
+++ b/ui/app/components/card/index.js
@@ -0,0 +1 @@
+export { default } from './card.component'
diff --git a/ui/app/components/card/index.scss b/ui/app/components/card/index.scss
new file mode 100644
index 000000000..68d972709
--- /dev/null
+++ b/ui/app/components/card/index.scss
@@ -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;
+ }
+}
diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss
index cb4065fd9..e39dfe091 100644
--- a/ui/app/components/index.scss
+++ b/ui/app/components/index.scss
@@ -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';
diff --git a/ui/app/components/sender-to-recipient/index.scss b/ui/app/components/sender-to-recipient/index.scss
index 656e30ddf..6f128f729 100644
--- a/ui/app/components/sender-to-recipient/index.scss
+++ b/ui/app/components/sender-to-recipient/index.scss
@@ -80,7 +80,6 @@
justify-content: center;
position: relative;
flex: 0 0 auto;
- padding: 8px;
.sender-to-recipient {
&__party {
diff --git a/ui/app/components/transaction-activity-log/index.js b/ui/app/components/transaction-activity-log/index.js
new file mode 100644
index 000000000..f39f8098c
--- /dev/null
+++ b/ui/app/components/transaction-activity-log/index.js
@@ -0,0 +1 @@
+export { default } from './transaction-activity-log.component'
diff --git a/ui/app/components/transaction-activity-log/index.scss b/ui/app/components/transaction-activity-log/index.scss
new file mode 100644
index 000000000..fb24b77e2
--- /dev/null
+++ b/ui/app/components/transaction-activity-log/index.scss
@@ -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;
+ }
+}
diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js
new file mode 100644
index 000000000..4cba8cf15
--- /dev/null
+++ b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js
new file mode 100644
index 000000000..fe780788a
--- /dev/null
+++ b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js
@@ -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
+ }, [])
+}