on master help sign in
ui: address Design team feedback
2f5bbebtomholford
src/lib/constants.ts+3−3
@@ -131,20 +131,20 @@ const DEFAULT_CSV_NAME = 'urbit_invites.csv';
131const TRANSACTION_STATUS_ICONS = {
131const TRANSACTION_STATUS_ICONS = {
132 confirmed: 'Checkmark',
132 confirmed: 'Checkmark',
133 pending: 'Clock',
133 pending: 'Clock',
134− sending: 'ArrowEast',
134+ sending: 'ArrowNorth',
135 failed: 'X',
135 failed: 'X',
136 unknown: 'NullIcon',
136 unknown: 'NullIcon',
137};
137};
138
138
139const TRANSACTION_TYPE_ICONS = {
139const TRANSACTION_TYPE_ICONS = {
140− spawn: 'ArrowEast',
140+ spawn: 'Node',
141 'configure-keys': 'BootNode',
141 'configure-keys': 'BootNode',
142 'transfer-point': 'Swap',
142 'transfer-point': 'Swap',
143 'set-management-proxy': 'ArrowRefresh',
143 'set-management-proxy': 'ArrowRefresh',
144};
144};
145
145
146const TRANSACTION_TYPE_TITLES = {
146const TRANSACTION_TYPE_TITLES = {
147− spawn: 'Point Issued',
147+ spawn: 'Point Spawned',
148 'configure-keys': 'Network Keys Configured',
148 'configure-keys': 'Network Keys Configured',
149 'transfer-point': 'Point Transferred',
149 'transfer-point': 'Point Transferred',
150 'set-management-proxy': 'Management Key Changed',
150 'set-management-proxy': 'Management Key Changed',
src/views/Transactions/TransactionHistory.scss+45−8
@@ -13,10 +13,16 @@
13}
13}
14
14
15.transaction-history {
15.transaction-history {
16−
17 .loading {
16 .loading {
17+ &> svg {
18+ width: 24px;
19+ height: 24px;
20+ }
21+ }
22+
23+ .loading, .no-results {
18 width: 100%;
24 width: 100%;
19− min-height: 400px;
25+ min-height: 500px;
20 display: flex;
26 display: flex;
21 flex-flow: column nowrap;
27 flex-flow: column nowrap;
22 justify-content: center;
28 justify-content: center;
@@ -74,6 +80,15 @@
74
80
75 .transaction-container {
81 .transaction-container {
76 width: 100%;
82 width: 100%;
83+ height: 550px;
84+ overflow-y: scroll;
85+
86+ // hide the vertical scrollbar
87+ -ms-overflow-style: none; /* IE and Edge */
88+ scrollbar-width: none; /* Firefox */
89+ &::-webkit-scrollbar {
90+ display: none; /* Chrome */
91+ }
77 }
92 }
78
93
79 .address-row {
94 .address-row {
@@ -112,9 +127,8 @@
112
127
113 .sigil {
128 .sigil {
114 background-color: black;
129 background-color: black;
115− height: 14px;
130+ border-radius: 2px;
116− width: 14px;
117− padding: 4px;
131+ padding: 6px;
118 }
132 }
119 }
133 }
120
134
@@ -196,11 +210,27 @@
196 }
210 }
197 }
211 }
198
212
213+ &.confirmed {
214+ width: 92px;
215+ background: #009F65;
216+ }
217+
218+ &.sending {
219+ width: 74px;
220+ background: #00000033;
221+ color: black;
222+
223+ svg {
224+ path {
225+ fill: black;
226+ }
227+ }
228+ }
229+
199 &.failed {
230 &.failed {
200 background-color: rgb(246,200,196);
231 background-color: rgb(246,200,196);
201 color: rgb(236,81,66);
232 color: rgb(236,81,66);
202 width: 58px;
233 width: 58px;
203−
204
234
205 svg {
235 svg {
206 path {
236 path {
@@ -213,7 +243,6 @@
213 background-color: $rollup-yellow;
243 background-color: $rollup-yellow;
214 color: #000;
244 color: #000;
215 width: 100px;
245 width: 100px;
216−
217
246
218 svg {
247 svg {
219 path {
248 path {
@@ -230,9 +259,17 @@
230 flex-direction: row;
259 flex-direction: row;
231 justify-content: space-between;
260 justify-content: space-between;
232 align-items: center;
261 align-items: center;
262+
263+ .hash-container {
264+ display: flex;
265+ flex-flow: row nowrap;
266+ justify-content: space-between;
267+ align-items: center;
268+ width: 31%;
269+ }
233
270
234 .hash {
271 .hash {
235− font-family: Inter;
272+ font-family: Source Code Pro;
236 font-size: 14px;
273 font-size: 14px;
237 font-style: normal;
274 font-style: normal;
238 font-weight: 400;
275 font-weight: 400;
src/views/Transactions/TransactionHistory.tsx+71−66
@@ -14,12 +14,11 @@ import { RollerTransaction } from '@urbit/roller-api';
14
14
15import './TransactionHistory.scss';
15import './TransactionHistory.scss';
16import { TransactionRow } from './TransactionRow';
16import { TransactionRow } from './TransactionRow';
17−import { Box, Row } from '@tlon/indigo-react';
17+import { Box, LoadingSpinner, Row } from '@tlon/indigo-react';
18import { PatpRow } from './PatpRow';
18import { PatpRow } from './PatpRow';
19import Dropdown from 'components/L2/Dropdowns/Dropdown';
19import Dropdown from 'components/L2/Dropdowns/Dropdown';
20import Sigil from 'components/Sigil';
20import Sigil from 'components/Sigil';
21import { AddressRow } from './AddressRow';
21import { AddressRow } from './AddressRow';
22−import { abbreviateAddress } from 'lib/utils/address';
23
22
24interface GroupedTransactions {
23interface GroupedTransactions {
25 [ship: string]: RollerTransaction[];
24 [ship: string]: RollerTransaction[];
@@ -36,7 +35,7 @@ const TransactionHistory = () => {
36 const [selectedPatp, setSelectedPatp] = useState<string | null>(null);
35 const [selectedPatp, setSelectedPatp] = useState<string | null>(null);
37
36
38 const shortAddress = useMemo(() => {
37 const shortAddress = useMemo(() => {
39− return abbreviateAddress(address);
38+ return address.slice(0, 6);
40 }, [address]);
39 }, [address]);
41
40
42 const fetchTransactions = useCallback(async () => {
41 const fetchTransactions = useCallback(async () => {
@@ -85,75 +84,81 @@ const TransactionHistory = () => {
85 className="transaction-history"
84 className="transaction-history"
86 header={<L2BackHeader />}>
85 header={<L2BackHeader />}>
87 <Window>
86 <Window>
88− {loading ? (
89− <Box className={'loading'}>Loading...</Box>
90− ) : (
91− <>
87+ <>
92− <HeaderPane>
88+ <HeaderPane>
93− <Box className="transaction-header">
89+ <Box className="transaction-header">
94− <h5>Transactions</h5>
90+ <h5>Transactions</h5>
95− <Dropdown
91+ <Dropdown
96− className="transaction-dropdown"
92+ className="transaction-dropdown"
97− open={dropdownOpen}
93+ open={dropdownOpen}
98− value={selectedPatp ? selectedPatp : shortAddress}
94+ value={selectedPatp ? selectedPatp : shortAddress}
99− toggleOpen={() => setDropdownOpen(!dropdownOpen)}>
95+ toggleOpen={() => setDropdownOpen(!dropdownOpen)}>
100− <Box className="divider" />
96+ <Box className="divider" />
101− <Box className="points">
97+ <Box className="points">
102− <Row className="entry" onClick={() => selectPatp(null)}>
98+ <Row className="entry" onClick={() => selectPatp(null)}>
103− <Box>{shortAddress}</Box>
99+ <Box>{shortAddress}</Box>
104− <Row>
100+ <Row>
105− <Box
101+ <Box
106− background="black"
102+ background="black"
107− borderRadius="2px"
103+ borderRadius="2px"
108− width="24px"
104+ width="24px"
109− height="24px"></Box>
105+ height="24px"></Box>
110− </Row>
111 </Row>
106 </Row>
107+ </Row>
112− {txPatps.map(patp => {
108+ {txPatps.map(patp => {
113− return (
109+ return (
114− <Row
110+ <Row
115− className="entry"
111+ className="entry"
116− onClick={() => selectPatp(patp)}
112+ onClick={() => selectPatp(patp)}
117− key={patp}>
113+ key={patp}>
118− <Box>{patp}</Box>
114+ <Box>{patp}</Box>
119− <Row>
115+ <Row>
120− <Box className="sigil">
116+ <Box className="sigil">
121− <Sigil
117+ <Sigil
122− icon
118+ icon
123− patp={patp}
119+ patp={patp}
124− size={1}
120+ size={1}
125− colors={['#000', '#FFF']}
121+ colors={['#000', '#FFF']}
126− />
122+ />
127− </Box>
123+ </Box>
128− </Row>
129 </Row>
124 </Row>
125+ </Row>
130− );
126+ );
131− })}
127+ })}
132− </Box>
128+ </Box>
133− </Dropdown>
129+ </Dropdown>
130+ </Box>
131+ </HeaderPane>
132+ <BodyPane>
133+ {loading ? (
134+ <Box className={'loading'}>
135+ <LoadingSpinner />
134 </Box>
136 </Box>
135− </HeaderPane>
137+ ) : (
136− <BodyPane>
137 <Box className="transaction-container">
138 <Box className="transaction-container">
138 {!selectedPatp && <AddressRow address={address} />}
139 {!selectedPatp && <AddressRow address={address} />}
139− {filteredPatps.map(patp => {
140+ {filteredPatps && filteredPatps.length > 0 ? (
141+ filteredPatps.map(patp => {
140− return (
142+ return (
141− <Box key={patp}>
143+ <Box key={patp}>
142− <PatpRow patp={patp} />
144+ <PatpRow patp={patp} />
143− {txnsByPatp[patp]
145+ {txnsByPatp[patp]
144− .sort((a, b) => {
146+ .sort((a, b) => {
145− return a.time - b.time;
147+ return a.time - b.time;
146− })
148+ })
147− .map(tx => (
149+ .map(tx => (
148− <TransactionRow key={tx.time} {...tx} />
150+ <TransactionRow key={tx.time} {...tx} />
149− ))}
151+ ))}
150− </Box>
152+ </Box>
153+ );
151− );
154+ })
155+ ) : (
156+ <Box className="no-results">No transactions yet</Box>
152− })}
157+ )}
153 </Box>
158 </Box>
159+ )}
154− </BodyPane>
160+ </BodyPane>
155− </>
161+ </>
156− )}
157 </Window>
162 </Window>
158 </View>
163 </View>
159 );
164 );
src/views/Transactions/TransactionRow.tsx+33−5
@@ -1,4 +1,5 @@
1import cn from 'classnames';
1import cn from 'classnames';
2+import * as ob from 'urbit-ob';
2import { Box, Icon, Row } from '@tlon/indigo-react';
3import { Box, Icon, Row } from '@tlon/indigo-react';
3import { RollerTransaction } from '@urbit/roller-api';
4import { RollerTransaction } from '@urbit/roller-api';
4import { format, fromUnixTime } from 'date-fns';
5import { format, fromUnixTime } from 'date-fns';
@@ -11,8 +12,11 @@ import {
11import { abbreviateAddress } from 'lib/utils/address';
12import { abbreviateAddress } from 'lib/utils/address';
12import { useMemo } from 'react';
13import { useMemo } from 'react';
13import { useRollerStore } from 'store/roller';
14import { useRollerStore } from 'store/roller';
15+import LayerIndicator from 'components/L2/LayerIndicator';
16+import { isPlanet, isStar } from 'lib/utils/point';
14
17
15export const TransactionRow = ({
18export const TransactionRow = ({
19+ ship,
16 type,
20 type,
17 hash,
21 hash,
18 status,
22 status,
@@ -20,9 +24,24 @@ export const TransactionRow = ({
20}: RollerTransaction) => {
24}: RollerTransaction) => {
21 const { nextRoll } = useRollerStore();
25 const { nextRoll } = useRollerStore();
22
26
27+ // For spawn events (associated with the spawner), we want to show the spawnee tier;
28+ // For all other events we will show the event's ship's tier
29+ const pointLabel = useMemo(() => {
30+ const azimuthIndex = ob.patp2dec(`~${ship}`);
31+ if (type === 'spawn') {
32+ return isStar(azimuthIndex) ? 'Planet' : 'Star';
33+ } else {
34+ return isStar(azimuthIndex)
35+ ? 'Star'
36+ : isPlanet(azimuthIndex)
37+ ? 'Planet'
38+ : 'Galaxy';
39+ }
40+ }, [ship, type]);
41+
23 const label = useMemo(() => {
42 const label = useMemo(() => {
24− return TRANSACTION_TYPE_TITLES[type] || type;
43+ return TRANSACTION_TYPE_TITLES[type].replace('Point', pointLabel) || type;
25− }, [type]);
44+ }, [pointLabel, type]);
26
45
27 const icon = useMemo(() => {
46 const icon = useMemo(() => {
28 return <Icon icon={TRANSACTION_TYPE_ICONS[type] || 'Bug'} />;
47 return <Icon icon={TRANSACTION_TYPE_ICONS[type] || 'Bug'} />;
@@ -62,10 +81,16 @@ export const TransactionRow = ({
62 <span>
81 <span>
63 <Icon icon={TRANSACTION_STATUS_ICONS[status] || 'Bug'} />
82 <Icon icon={TRANSACTION_STATUS_ICONS[status] || 'Bug'} />
64 </span>
83 </span>
84+ <span>
85+ {status === 'pending'
86+ ? nextRoll
87+ : status === 'confirmed'
88+ ? 'Completed'
65− <span>{status === 'pending' ? nextRoll : titleize(status)}</span>
89+ : titleize(status)}
90+ </span>
66 </div>
91 </div>
67 );
92 );
68− }, [status, nextRoll]);
93+ }, [nextRoll, status]);
69
94
70 return (
95 return (
71 <Box className="transaction-row">
96 <Box className="transaction-row">
@@ -76,7 +101,10 @@ export const TransactionRow = ({
76 <Box className="status">{statusBadge}</Box>
101 <Box className="status">{statusBadge}</Box>
77 </Row>
102 </Row>
78 <Row className="info-row">
103 <Row className="info-row">
79− <Box className="hash">{shortHash}</Box>
104+ <Box className="hash-container">
105+ <Box className="hash">{shortHash}</Box>
106+ <LayerIndicator layer={2} size={'sm'} />
107+ </Box>
80 <Box className="date" title={longDate}>
108 <Box className="date" title={longDate}>
81 {shortDate}
109 {shortDate}
82 </Box>
110 </Box>