Skip to content

Commit 5f823b4

Browse files
committed
Merge branch 'frontend-refactor-transaction'
2 parents 9d3e419 + 5b6afae commit 5f823b4

File tree

1 file changed

+131
-156
lines changed

1 file changed

+131
-156
lines changed

frontends/web/src/components/transactions/transaction.tsx

Lines changed: 131 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { Component } from 'react';
18+
import { useState } from 'react';
19+
import { useTranslation } from 'react-i18next';
1920
import * as accountApi from '../../api/account';
20-
import { translate, TranslateProps } from '../../decorators/translate';
2121
import { A } from '../anchor/anchor';
2222
import { Dialog } from '../dialog/dialog';
2323
import { CopyableInput } from '../copy/Copy';
@@ -30,176 +30,155 @@ import { Note } from './note';
3030
import parentStyle from './transactions.module.css';
3131
import style from './transaction.module.css';
3232

33-
interface State {
34-
transactionDialog: boolean;
35-
transactionInfo?: accountApi.ITransaction;
36-
}
33+
type Props = {
34+
accountCode: accountApi.AccountCode;
35+
index: number;
36+
explorerURL: string;
37+
} & accountApi.ITransaction;
3738

38-
interface TransactionProps extends accountApi.ITransaction {
39-
accountCode: accountApi.AccountCode;
40-
index: number;
41-
explorerURL: string;
42-
}
39+
export const Transaction = ({
40+
accountCode,
41+
index,
42+
internalID,
43+
explorerURL,
44+
type,
45+
amount,
46+
feeRatePerKb,
47+
numConfirmations,
48+
numConfirmationsComplete,
49+
time,
50+
addresses,
51+
status,
52+
note = '',
53+
}: Props) => {
54+
const { i18n, t } = useTranslation();
55+
const [transactionDialog, setTransactionDialog] = useState<boolean>(false);
56+
const [transactionInfo, setTransactionInfo] = useState<accountApi.ITransaction>();
4357

44-
type Props = TransactionProps & TranslateProps;
45-
46-
class Transaction extends Component<Props, State> {
47-
public readonly state: State = {
48-
transactionDialog: false,
49-
};
50-
51-
private parseTimeShort = (time: string) => {
58+
const parseTimeShort = (time: string) => {
5259
const options = {
5360
year: 'numeric',
5461
month: 'numeric',
5562
day: 'numeric',
5663
} as Intl.DateTimeFormatOptions;
57-
return new Date(Date.parse(time)).toLocaleString(this.props.i18n.language, options);
64+
return new Date(Date.parse(time)).toLocaleString(i18n.language, options);
5865
};
5966

60-
private showDetails = () => {
61-
accountApi.getTransaction(this.props.accountCode, this.props.internalID).then(transaction => {
67+
const showDetails = () => {
68+
accountApi.getTransaction(accountCode, internalID).then(transaction => {
6269
if (!transaction) {
63-
console.error('Unable to retrieve transaction ' + this.props.internalID);
70+
console.error('Unable to retrieve transaction ' + internalID);
6471
return null;
6572
}
66-
this.setState({
67-
transactionInfo: transaction,
68-
transactionDialog: true,
69-
});
73+
setTransactionInfo(transaction);
74+
setTransactionDialog(true);
7075
})
7176
.catch(console.error);
7277
};
7378

74-
private hideDetails = () => {
75-
this.setState({ transactionDialog: false });
76-
};
79+
const arrow = status === 'failed' ? (
80+
<Warning style={{ maxWidth: '18px' }} />
81+
) : type === 'receive' ? (
82+
<ArrowIn />
83+
) : type === 'send' ? (
84+
<ArrowOut />
85+
) : (
86+
<ArrowSelf />
87+
);
88+
const sign = ((type === 'send') && '−') || ((type === 'receive') && '+') || '';
89+
const typeClassName = (status === 'failed' && style.failed) || (type === 'send' && style.send) || (type === 'receive' && style.receive) || '';
90+
const sDate = time ? parseTimeShort(time) : '---';
91+
const statusText = t(`transaction.status.${status}`);
92+
const progress = numConfirmations < numConfirmationsComplete ? (numConfirmations / numConfirmationsComplete) * 100 : 100;
7793

78-
public render() {
79-
const {
80-
t,
81-
index,
82-
explorerURL,
83-
type,
84-
amount,
85-
feeRatePerKb,
86-
numConfirmations,
87-
numConfirmationsComplete,
88-
time,
89-
addresses,
90-
status,
91-
note = '',
92-
} = this.props;
93-
const {
94-
transactionDialog,
95-
transactionInfo,
96-
} = this.state;
97-
const arrow = status === 'failed' ? (
98-
<Warning style={{ maxWidth: '18px' }} />
99-
) : type === 'receive' ? (
100-
<ArrowIn />
101-
) : type === 'send' ? (
102-
<ArrowOut />
103-
) : (
104-
<ArrowSelf />
105-
);
106-
const sign = ((type === 'send') && '−') || ((type === 'receive') && '+') || '';
107-
const typeClassName = (status === 'failed' && style.failed) || (type === 'send' && style.send) || (type === 'receive' && style.receive) || '';
108-
const sDate = time ? this.parseTimeShort(time) : '---';
109-
const statusText = {
110-
complete: t('transaction.status.complete'),
111-
pending: t('transaction.status.pending'),
112-
failed: t('transaction.status.failed'),
113-
}[status];
114-
const progress = numConfirmations < numConfirmationsComplete ? (numConfirmations / numConfirmationsComplete) * 100 : 100;
115-
116-
return (
117-
<div className={[style.container, index === 0 ? style.first : ''].join(' ')}>
118-
<div className={[parentStyle.columns, style.row].join(' ')}>
119-
<div className={parentStyle.columnGroup}>
120-
<div className={parentStyle.type}>{arrow}</div>
121-
<div className={parentStyle.date}>
122-
<span className={style.columnLabel}>
123-
{t('transaction.details.date')}:
124-
</span>
125-
<span className={style.date}>{sDate}</span>
126-
</div>
127-
{ note ? (
128-
<div className={parentStyle.activity}>
129-
<span className={style.address}>
130-
{note}
131-
</span>
132-
</div>
133-
) : (
134-
<div className={parentStyle.activity}>
135-
<span className={style.label}>
136-
{t(type === 'receive' ? 'transaction.tx.received' : 'transaction.tx.sent')}
137-
</span>
138-
<span className={style.address}>
139-
{addresses[0]}
140-
{addresses.length > 1 && (
141-
<span className={style.badge}>
142-
(+{addresses.length - 1})
143-
</span>
144-
)}
145-
</span>
146-
</div>
147-
)}
148-
<div className={[parentStyle.action, parentStyle.hideOnMedium].join(' ')}>
149-
<button type="button" className={style.action} onClick={this.showDetails}>
150-
<ExpandIcon expand={!transactionDialog} />
151-
</button>
152-
</div>
94+
return (
95+
<div className={[style.container, index === 0 ? style.first : ''].join(' ')}>
96+
<div className={[parentStyle.columns, style.row].join(' ')}>
97+
<div className={parentStyle.columnGroup}>
98+
<div className={parentStyle.type}>{arrow}</div>
99+
<div className={parentStyle.date}>
100+
<span className={style.columnLabel}>
101+
{t('transaction.details.date')}:
102+
</span>
103+
<span className={style.date}>{sDate}</span>
153104
</div>
154-
<div className={parentStyle.columnGroup}>
155-
<div className={parentStyle.status}>
156-
<span className={style.columnLabel}>
157-
{t('transaction.details.status')}:
105+
{ note ? (
106+
<div className={parentStyle.activity}>
107+
<span className={style.address}>
108+
{note}
158109
</span>
159-
<ProgressRing
160-
className="m-right-quarter"
161-
width={14}
162-
value={progress}
163-
isComplete={numConfirmations >= numConfirmationsComplete}
164-
/>
165-
<span className={style.status}>{statusText}</span>
166110
</div>
167-
<div className={parentStyle.fiat}>
168-
<span className={`${style.fiat} ${typeClassName}`}>
169-
<FiatConversion amount={amount} sign={sign} noAction />
111+
) : (
112+
<div className={parentStyle.activity}>
113+
<span className={style.label}>
114+
{t(type === 'receive' ? 'transaction.tx.received' : 'transaction.tx.sent')}
170115
</span>
171-
</div>
172-
<div className={`${parentStyle.currency} ${typeClassName}`}>
173-
<span
174-
className={`${style.amount} ${style.amountOverflow}`}
175-
data-unit={` ${amount.unit}`}>
176-
{sign}
177-
<Amount amount={amount.amount} unit={amount.unit}/>
178-
<span className={style.currencyUnit}>&nbsp;{amount.unit}</span>
116+
<span className={style.address}>
117+
{addresses[0]}
118+
{addresses.length > 1 && (
119+
<span className={style.badge}>
120+
(+{addresses.length - 1})
121+
</span>
122+
)}
179123
</span>
180124
</div>
181-
<div className={[parentStyle.action, parentStyle.showOnMedium].join(' ')}>
182-
<button type="button" className={style.action} onClick={this.showDetails}>
183-
<ExpandIcon expand={!transactionDialog} />
184-
</button>
185-
</div>
125+
)}
126+
<div className={[parentStyle.action, parentStyle.hideOnMedium].join(' ')}>
127+
<button type="button" className={style.action} onClick={showDetails}>
128+
<ExpandIcon expand={!transactionDialog} />
129+
</button>
130+
</div>
131+
</div>
132+
<div className={parentStyle.columnGroup}>
133+
<div className={parentStyle.status}>
134+
<span className={style.columnLabel}>
135+
{t('transaction.details.status')}:
136+
</span>
137+
<ProgressRing
138+
className="m-right-quarter"
139+
width={14}
140+
value={progress}
141+
isComplete={numConfirmations >= numConfirmationsComplete}
142+
/>
143+
<span className={style.status}>{statusText}</span>
144+
</div>
145+
<div className={parentStyle.fiat}>
146+
<span className={`${style.fiat} ${typeClassName}`}>
147+
<FiatConversion amount={amount} sign={sign} noAction />
148+
</span>
149+
</div>
150+
<div className={`${parentStyle.currency} ${typeClassName}`}>
151+
<span
152+
className={`${style.amount} ${style.amountOverflow}`}
153+
data-unit={` ${amount.unit}`}>
154+
{sign}
155+
<Amount amount={amount.amount} unit={amount.unit}/>
156+
<span className={style.currencyUnit}>&nbsp;{amount.unit}</span>
157+
</span>
158+
</div>
159+
<div className={[parentStyle.action, parentStyle.showOnMedium].join(' ')}>
160+
<button type="button" className={style.action} onClick={showDetails}>
161+
<ExpandIcon expand={!transactionDialog} />
162+
</button>
186163
</div>
187164
</div>
188-
{/*
189-
Amount and Confirmations info are displayed using props data
190-
instead of transactionInfo because they are live updated.
191-
*/}
192-
<Dialog
193-
open={transactionDialog}
194-
title={t('transaction.details.title')}
195-
onClose={this.hideDetails}
196-
slim
197-
medium>
198-
{transactionInfo && <>
165+
</div>
166+
{/*
167+
Amount and Confirmations info are displayed using props data
168+
instead of transactionInfo because they are live updated.
169+
*/}
170+
<Dialog
171+
open={transactionDialog}
172+
title={t('transaction.details.title')}
173+
onClose={() => setTransactionDialog(false)}
174+
slim
175+
medium>
176+
{transactionInfo && (
177+
<>
199178
<Note
200-
accountCode={this.props.accountCode}
201-
internalID={this.props.internalID}
202-
note={this.props.note}
179+
accountCode={accountCode}
180+
internalID={internalID}
181+
note={note}
203182
/>
204183
<div className={style.detail}>
205184
<label>{t('transaction.details.type')}</label>
@@ -360,13 +339,9 @@ class Transaction extends Component<Props, State> {
360339
{t('transaction.explorerTitle')}
361340
</A>
362341
</div>
363-
</> }
364-
</Dialog>
365-
</div>
366-
);
367-
}
368-
}
369-
370-
const HOC = translate()(Transaction);
371-
372-
export { HOC as Transaction };
342+
</>
343+
)}
344+
</Dialog>
345+
</div>
346+
);
347+
};

0 commit comments

Comments
 (0)