on master help sign in
ui: Transaction view redesign
commit5cc71690b1a5814026ee0d39f4d931c8399b2348 authortomholford <tomholford@users.noreply.github.com> dateMon 11 Oct 2021, 21:29 (5 years ago) parent841a01a
10 files changed+310−60
package-lock.json+5 package.json+1 src/form/formatters.ts+11 src/lib/constants.ts+25 src/lib/router.js+1−1 src/views/TransactionHistory.scss−5 src/views/TransactionHistory.tsx−54 src/views/Transactions/TransactionHistory.scss+138 src/views/Transactions/TransactionHistory.tsx+59 src/views/Transactions/TransactionRow.tsx+70
package-lock.json+5
@@ -12348,6 +12348,11 @@
1234812348 "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==",
1234912349 "optional": true
1235012350 },
12351+ "date-fns": {
12352+ "version": "2.25.0",
12353+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.25.0.tgz",
12354+ "integrity": "sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w=="
12355+ },
1235112356 "debug": {
1235212357 "version": "2.6.9",
1235312358 "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
package.json+1
@@ -47,6 +47,7 @@
4747 "buffer": "^5.6.1",
4848 "classnames": "^2.2.6",
4949 "copy-to-clipboard": "^3.2.0",
50+ "date-fns": "^2.25.0",
5051 "ethereum-blockies-base64": "^1.0.2",
5152 "ethereumjs-util": "^7.0.10",
5253 "file-saver": "^2.0.0",
src/form/formatters.ts+11
@@ -104,3 +104,14 @@ export const ticketToSegments = (ticket: string): string[] => {
104104export const ticketFromSegments = (segments: string[]): string => {
105105 return ensureSigPrefix(segments.join('-'));
106106};
107+
108+export const capitalize = (s: string) => {
109+ return s.charAt(0).toUpperCase() + s.slice(1);
110+};
111+
112+export const titleize = (s: string): string => {
113+ return s
114+ .split(' ')
115+ .map(_s => capitalize(_s))
116+ .join(' ');
117+};
src/lib/constants.ts+25
@@ -128,6 +128,28 @@ const INVITES_PER_PAGE = 7;
128128const DEFAULT_NUM_INVITES = 5;
129129const DEFAULT_CSV_NAME = 'urbit_invites.csv';
130130
131+const TRANSACTION_STATUS_ICONS = {
132+ confirmed: 'Checkmark',
133+ pending: 'Clock',
134+ sending: 'ArrowEast',
135+ failed: 'X',
136+ unknown: 'NullIcon',
137+};
138+
139+const TRANSACTION_TYPE_ICONS = {
140+ spawn: 'ArrowEast',
141+ 'configure-keys': 'Gear',
142+ 'transfer-point': 'Swap',
143+ 'set-management-proxy': 'ArrowRefresh',
144+};
145+
146+const TRANSACTION_TYPE_TITLES = {
147+ spawn: 'Point Issued',
148+ 'configure-keys': 'Network Keys Configured',
149+ 'transfer-point': 'Point Transferred',
150+ 'set-management-proxy': 'Management Key Changed',
151+};
152+
131153export {
132154 CHECK_BLOCK_EVERY_MS,
133155 DEFAULT_GAS_PRICE_GWEI,
@@ -163,4 +185,7 @@ export {
163185 INVITES_PER_PAGE,
164186 DEFAULT_NUM_INVITES,
165187 DEFAULT_CSV_NAME,
188+ TRANSACTION_STATUS_ICONS,
189+ TRANSACTION_TYPE_ICONS,
190+ TRANSACTION_TYPE_TITLES,
166191};
src/lib/router.js+1−1
@@ -26,7 +26,7 @@ import Adopt from 'views/Adopt';
2626import Bitcoin from 'views/Bitcoin/Bitcoin';
2727import Xpub from 'views/Bitcoin/Xpub';
2828import SignTransaction from 'views/Bitcoin/SignTransaction';
29−import TransactionHistory from 'views/TransactionHistory';
29+import TransactionHistory from 'views/Transactions/TransactionHistory';
3030
3131import { ROUTE_NAMES } from './routeNames';
3232
src/views/TransactionHistory.scssdeleted−5
@@ -1,5 +0,0 @@
1−@import 'style/shared/variables.scss';
2−
3−.transaction-history {
4−
5−}
src/views/TransactionHistory.tsxdeleted−54
@@ -1,54 +0,0 @@
1−import React, { useEffect, useState } from 'react';
2−import { Row, Box } from '@tlon/indigo-react';
3−
4−import * as need from 'lib/need';
5−import { useWallet } from 'store/wallet';
6−import useRoller from 'lib/useRoller';
7−import { useLocalRouter } from 'lib/LocalRouter';
8−
9−import View from 'components/View';
10−import L2BackHeader from 'components/L2/Headers/L2BackHeader';
11−import { RollerTransaction } from '@urbit/roller-api';
12−
13−import './TransactionHistory';
14−
15−const TransactionHistory = () => {
16− const { pop }: any = useLocalRouter();
17− const { api } = useRoller();
18− const { wallet }: any = useWallet();
19− const address = need.addressFromWallet(wallet);
20− const [transactions, setTransactions] = useState<RollerTransaction[]>([]);
21−
22− useEffect(() => {
23− const getTransactionHistory = async () => {
24− const txns = await api.getHistory(address);
25− setTransactions(txns);
26− };
27−
28− getTransactionHistory();
29− }, [api, address]);
30−
31− return (
32− <View
33− pop={pop}
34− hideBack
35− inset
36− className="transaction-history"
37− header={<L2BackHeader />}>
38− {transactions.map(({ ship, status, type, hash }) => (
39− <Box className="transaction">
40− <Row className="title-row">
41− <Box className="title">{type}</Box>
42− <Box className="hash">{hash}</Box>
43− </Row>
44− <Row className="info-row">
45− <Box className="status">{status}</Box>
46− <Box className="date"></Box>
47− </Row>
48− </Box>
49− ))}
50− </View>
51− );
52−};
53−
54−export default TransactionHistory;
src/views/Transactions/TransactionHistory.scssnew file+138
@@ -0,0 +1,138 @@
1+@import 'style/shared/variables.scss';
2+
3+.transaction-history {
4+
5+ .transaction-container {
6+ width: 100%;
7+ }
8+
9+ .transaction-row {
10+ display: grid;
11+ grid-template-areas: "icon info";
12+ grid-template-columns: 18px auto;
13+ grid-template-rows: auto;
14+ column-gap: 16px;
15+ margin-top: 8px;
16+ margin-bottom: 8px;
17+ padding-top: 8px;
18+ padding-bottom: 8px;
19+
20+ .icon {
21+ grid-area: icon;
22+ padding-top: 3px;
23+
24+ svg {
25+ height: 18px;
26+ width: 18px;
27+ }
28+ }
29+
30+ .info {
31+ grid-area: info;
32+
33+ }
34+
35+ .title-row {
36+ display: flex;
37+ flex-direction: row;
38+ justify-content: space-between;
39+ align-items: center;
40+ margin-bottom: 8px;
41+
42+ .title {
43+ font-family: Inter;
44+ font-size: 14px;
45+ font-style: normal;
46+ font-weight: 400;
47+ line-height: 24px;
48+ letter-spacing: -0.011em;
49+ }
50+
51+ .status {
52+ .badge {
53+ width: 78px;
54+ display: flex;
55+ flex-flow: row nowrap;
56+ justify-content: space-between;
57+ align-items: center;
58+ font-family: Inter;
59+ font-size: 14px;
60+ font-style: normal;
61+ font-weight: 500;
62+ line-height: 18px;
63+ letter-spacing: 0em;
64+ text-align: center;
65+ color: white;
66+ background-color: black;
67+ padding: 2px;
68+ border-radius: 2px;
69+
70+ svg {
71+ height: 14px;
72+ width: 14px;
73+ padding-top: 4px;
74+ path {
75+ fill: white;
76+ }
77+ }
78+
79+ &.failed {
80+ background-color: rgb(246,200,196);
81+ color: rgb(236,81,66);
82+ width: 58px;
83+
84+
85+ svg {
86+ path {
87+ fill: rgb(236,81,66);;
88+ }
89+ }
90+ }
91+
92+ &.pending {
93+ background-color: $rollup-yellow;
94+ color: #000;
95+ padding: 4px 8px;
96+ width: 100px;
97+ display: flex;
98+ flex-direction: row;
99+ justify-content: space-between;
100+ align-items: center;
101+
102+ svg {
103+ path {
104+ fill: #000;
105+ }
106+ }
107+ }
108+ }
109+ }
110+ }
111+
112+ .info-row {
113+ display: flex;
114+ flex-direction: row;
115+ justify-content: space-between;
116+ align-items: center;
117+
118+ .hash {
119+ font-family: Inter;
120+ font-size: 14px;
121+ font-style: normal;
122+ font-weight: 400;
123+ line-height: 16px;
124+ letter-spacing: -0.011em;
125+ color: rgba(0,0,0,0.6);
126+ }
127+
128+ .date {
129+ font-family: Inter;
130+ font-size: 14px;
131+ font-style: normal;
132+ font-weight: 400;
133+ line-height: 16px;
134+ letter-spacing: -0.011em;
135+ }
136+ }
137+ }
138+}
src/views/Transactions/TransactionHistory.tsxnew file+59
@@ -0,0 +1,59 @@
1+import React, { useCallback, useEffect, useState } from 'react';
2+
3+import * as need from 'lib/need';
4+import { useWallet } from 'store/wallet';
5+import useRoller from 'lib/useRoller';
6+import { useLocalRouter } from 'lib/LocalRouter';
7+
8+import View from 'components/View';
9+import L2BackHeader from 'components/L2/Headers/L2BackHeader';
10+import Window from 'components/L2/Window/Window';
11+import HeaderPane from 'components/L2/Window/HeaderPane';
12+import BodyPane from 'components/L2/Window/BodyPane';
13+import { RollerTransaction } from '@urbit/roller-api';
14+
15+import './TransactionHistory.scss';
16+import { TransactionRow } from './TransactionRow';
17+import { Box } from '@tlon/indigo-react';
18+
19+const TransactionHistory = () => {
20+ const { pop }: any = useLocalRouter();
21+ const { api } = useRoller();
22+ const { wallet }: any = useWallet();
23+ const address = need.addressFromWallet(wallet);
24+ const [transactions, setTransactions] = useState<RollerTransaction[]>([]);
25+
26+ const fetchTransactions = useCallback(async () => {
27+ const txns = await api.getHistory(address);
28+ const sortedTxns = txns.sort((a, b) => a.time - b.time)
29+ setTransactions(sortedTxns);
30+ }, [address, api]);
31+
32+ useEffect(() => {
33+ fetchTransactions();
34+ }, [fetchTransactions]);
35+
36+ return (
37+ <View
38+ pop={pop}
39+ hideBack
40+ inset
41+ className="transaction-history"
42+ header={<L2BackHeader />}>
43+ <Window>
44+ <HeaderPane>
45+ <h5>Transactions</h5>
46+ </HeaderPane>
47+ <BodyPane>
48+ <Box className="transaction-container">
49+ {transactions.map(tx => (
50+ <TransactionRow key={tx.time} {...tx} />
51+ ))}
52+ </Box>
53+ </BodyPane>
54+ </Window>
55+ </View>
56+ );
57+};
58+
59+export default TransactionHistory;
src/views/Transactions/TransactionRow.tsxnew file+70
@@ -0,0 +1,70 @@
1+import cn from 'classnames';
2+import { Box, Icon, Row } from '@tlon/indigo-react';
3+import { RollerTransaction } from '@urbit/roller-api';
4+import { format, fromUnixTime } from 'date-fns';
5+import { titleize } from 'form/formatters';
6+import {
7+ TRANSACTION_STATUS_ICONS,
8+ TRANSACTION_TYPE_ICONS,
9+ TRANSACTION_TYPE_TITLES,
10+} from 'lib/constants';
11+import { abbreviateAddress } from 'lib/utils/address';
12+import { useMemo } from 'react';
13+import { useRollerStore } from 'store/roller';
14+
15+export const TransactionRow = ({
16+ type,
17+ hash,
18+ status,
19+ time,
20+}: RollerTransaction) => {
21+ const { nextRoll } = useRollerStore();
22+
23+ const label = useMemo(() => {
24+ return TRANSACTION_TYPE_TITLES[type] || type;
25+ }, [type]);
26+
27+ const icon = useMemo(() => {
28+ return <Icon icon={TRANSACTION_TYPE_ICONS[type] || 'Bug'} />;
29+ }, [type]);
30+
31+ const shortHash = useMemo(() => {
32+ return abbreviateAddress(hash);
33+ }, [hash]);
34+
35+ const shortDate = useMemo(() => {
36+ if (!time) {
37+ return null;
38+ }
39+ const d = fromUnixTime(time / 1000);
40+ console.log(d.toLocaleTimeString());
41+ return format(d, 'MMMM dd');
42+ }, [time]);
43+
44+ const statusBadge = useMemo(() => {
45+ return (
46+ <div className={cn(['badge', status])}>
47+ <span>
48+ <Icon icon={TRANSACTION_STATUS_ICONS[status] || 'Bug'} />
49+ </span>
50+ <span>{status === 'pending' ? nextRoll : titleize(status)}</span>
51+ </div>
52+ );
53+ }, [status, nextRoll]);
54+
55+ return (
56+ <Box className="transaction-row">
57+ <Box className="icon">{icon}</Box>
58+ <Box className="info">
59+ <Row className="title-row">
60+ <Box className="title">{label}</Box>
61+ <Box className="status">{statusBadge}</Box>
62+ </Row>
63+ <Row className="info-row">
64+ <Box className="hash">{shortHash}</Box>
65+ <Box className="date">{shortDate}</Box>
66+ </Row>
67+ </Box>
68+ </Box>
69+ );
70+};