on master help sign in
ux: filter TXs by patp
8b8af6etomholford
src/form/formatters.ts+4
@@ -106,6 +106,10 @@ export const ticketFromSegments = (segments: string[]): string => {
106};
106};
107
107
108export const capitalize = (s: string) => {
108export const capitalize = (s: string) => {
109+ if (!s || s === '') {
110+ return s;
111+ }
112+
109 return s.charAt(0).toUpperCase() + s.slice(1);
113 return s.charAt(0).toUpperCase() + s.slice(1);
110};
114};
111
115
src/views/Transactions/Gallery.tsxdeleted−20
@@ -1,20 +0,0 @@
1−import { Box, Icon } from '@tlon/indigo-react';
2−import { _iconIndex as iconIndex } from '@tlon/indigo-react';
3−
4−export const Gallery = () => {
5− const iconNames = Object.keys(iconIndex);
6− return (
7− <>
8− {iconNames.map(name => {
9− return (
10− <>
11− <Box padding={'5px'} marginBottom={'5px'} border={'solid 1px rgba(0,0,0,0.5)'}>
12− {name}
13− <Icon icon={name} height={'16px'} width={'16px'} />
14− </Box>
15− </>
16− );
17− })}
18− </>
19− );
20−};
src/views/Transactions/ShipRow.tsx → src/views/Transactions/PatpRow.tsxrenamed+3−3
@@ -1,10 +1,10 @@
1−import { Box, Row } from '@tlon/indigo-react';
1+import { Box } from '@tlon/indigo-react';
2import Sigil from 'components/Sigil';
2import Sigil from 'components/Sigil';
3
3
4−export const ShipRow = ({ patp }) => {
4+export const PatpRow = ({ patp }) => {
5 return (
5 return (
6 <>
6 <>
7− <Box className="ship-row">
7+ <Box className="patp-row">
8 <Box className="icon">
8 <Box className="icon">
9 <Box className="sigil">
9 <Box className="sigil">
10 {
10 {
src/views/Transactions/TransactionHistory.scss+59−1
@@ -14,11 +14,69 @@
14
14
15.transaction-history {
15.transaction-history {
16
16
17+ .loading {
18+ width: 100%;
19+ min-height: 400px;
20+ display: flex;
21+ flex-flow: column nowrap;
22+ justify-content: center;
23+ align-items: center;
24+ color: rgba(0,0,0,0.6);
25+ }
26+
27+ .transaction-header {
28+ display: flex;
29+ flex-flow: row nowrap;
30+ justify-content: space-between;
31+ width: 100%;
32+ }
33+
34+ .transaction-dropdown {
35+ font-size: 14px;
36+ position: relative;
37+
38+ .selector {
39+ font-family: "Source Code Pro", "Roboto mono", "Courier New", monospace;
40+ }
41+
42+ .content-border {
43+ .dropdown-content {
44+ .entry, .current-address {
45+ display: flex;
46+ flex-direction: row;
47+ justify-content: space-between;
48+ align-items: center;
49+ cursor: pointer;
50+
51+ padding: 8px 12px 8px 16px;
52+
53+ &:hover {
54+ opacity: 0.8;
55+ }
56+
57+ .sigil {
58+ background-color: black;
59+ height: 14px;
60+ width: 14px;
61+ padding: 4px;
62+ border-radius: $small-border-radius;
63+ }
64+ }
65+
66+ .divider {
67+ width: 100%;
68+ height: 1px;
69+ background-color: $light-gray;
70+ }
71+ }
72+ }
73+ }
74+
17 .transaction-container {
75 .transaction-container {
18 width: 100%;
76 width: 100%;
19 }
77 }
20
78
21− .ship-row {
79+ .patp-row {
22 @include tx-row();
80 @include tx-row();
23
81
24 .icon {
82 .icon {
src/views/Transactions/TransactionHistory.tsx+84−28
@@ -14,8 +14,10 @@ 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 } from '@tlon/indigo-react';
17+import { Box, Row } from '@tlon/indigo-react';
18−import { ShipRow } from './ShipRow';
18+import { PatpRow } from './PatpRow';
19+import Dropdown from 'components/L2/Dropdowns/Dropdown';
20+import Sigil from 'components/Sigil';
19
21
20interface GroupedTransactions {
22interface GroupedTransactions {
21 [ship: string]: RollerTransaction[];
23 [ship: string]: RollerTransaction[];
@@ -26,7 +28,21 @@ const TransactionHistory = () => {
26 const { api } = useRoller();
28 const { api } = useRoller();
27 const { wallet }: any = useWallet();
29 const { wallet }: any = useWallet();
28 const address = need.addressFromWallet(wallet);
30 const address = need.addressFromWallet(wallet);
31+ const [loading, setLoading] = useState(true);
29 const [transactions, setTransactions] = useState<RollerTransaction[]>([]);
32 const [transactions, setTransactions] = useState<RollerTransaction[]>([]);
33+ const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);
34+ const [selectedPatp, setSelectedPatp] = useState<string | null>(null);
35+
36+ const fetchTransactions = useCallback(async () => {
37+ const txns = await api.getHistory(address);
38+ setTransactions(txns);
39+ setLoading(false);
40+ }, [address, api]);
41+
42+ const selectPatp = useCallback(patp => {
43+ setSelectedPatp(patp);
44+ setDropdownOpen(false);
45+ }, []);
30
46
31 const txnsByPatp = useMemo(() => {
47 const txnsByPatp = useMemo(() => {
32 return transactions.reduce((memo, tx) => {
48 return transactions.reduce((memo, tx) => {
@@ -39,14 +55,17 @@ const TransactionHistory = () => {
39 }, {} as GroupedTransactions);
55 }, {} as GroupedTransactions);
40 }, [transactions]);
56 }, [transactions]);
41
57
42− const txKeys = useMemo(() => {
58+ const txPatps = useMemo(() => {
43 return Object.keys(txnsByPatp).sort();
59 return Object.keys(txnsByPatp).sort();
44 }, [txnsByPatp]);
60 }, [txnsByPatp]);
45
61
46− const fetchTransactions = useCallback(async () => {
62+ const filteredPatps = useMemo(() => {
47− const txns = await api.getHistory(address);
63+ if (!selectedPatp) {
48− setTransactions(txns);
64+ return txPatps;
49− }, [address, api]);
65+ }
66+
67+ return txPatps.filter(p => p === selectedPatp);
68+ }, [selectedPatp, txPatps]);
50
69
51 useEffect(() => {
70 useEffect(() => {
52 fetchTransactions();
71 fetchTransactions();
@@ -60,27 +79,64 @@ const TransactionHistory = () => {
60 className="transaction-history"
79 className="transaction-history"
61 header={<L2BackHeader />}>
80 header={<L2BackHeader />}>
62 <Window>
81 <Window>
63− <HeaderPane>
82+ {loading ? (
83+ <Box className={'loading'}>Loading...</Box>
84+ ) : (
85+ <>
86+ <HeaderPane>
87+ <Box className="transaction-header">
64− <h5>Transactions</h5>
88+ <h5>Transactions</h5>
89+ <Dropdown
90+ className="transaction-dropdown"
91+ open={dropdownOpen}
92+ value={selectedPatp}
93+ toggleOpen={() => setDropdownOpen(!dropdownOpen)}>
94+ <Box className="divider" />
95+ <Box className="points">
96+ {txPatps.map(patp => {
97+ return (
98+ <Row
99+ className="entry"
100+ onClick={() => selectPatp(patp)}
101+ key={patp}>
102+ <Box>{patp}</Box>
103+ <Row>
104+ <Box className="sigil">
105+ <Sigil
106+ icon
107+ patp={patp}
108+ size={1}
109+ colors={['#000', '#FFF']}
110+ />
111+ </Box>
112+ </Row>
113+ </Row>
114+ );
115+ })}
116+ </Box>
117+ </Dropdown>
118+ </Box>
65− </HeaderPane>
119+ </HeaderPane>
66− <BodyPane>
120+ <BodyPane>
67− <Box className="transaction-container">
121+ <Box className="transaction-container">
68− {txKeys.map(patp => {
122+ {filteredPatps.map(patp => {
69− return (
123+ return (
70− <>
124+ <Box key={patp}>
71− <ShipRow patp={patp} />
125+ <PatpRow patp={patp} />
72− {txnsByPatp[patp]
126+ {txnsByPatp[patp]
73− .sort((a, b) => {
127+ .sort((a, b) => {
74− return a.time - b.time;
128+ return a.time - b.time;
75− })
129+ })
76− .map(tx => (
130+ .map(tx => (
77− <TransactionRow key={tx.time} {...tx} />
131+ <TransactionRow key={tx.time} {...tx} />
132+ ))}
133+ </Box>
134+ );
78− ))}
135+ })}
136+ </Box>
137+ </BodyPane>
79− </>
138+ </>
80− );
81− })}
139+ )}
82− </Box>
83− </BodyPane>
84 </Window>
140 </Window>
85 </View>
141 </View>
86 );
142 );