1import React, { useCallback, useEffect, useMemo, useState } from 'react';
3import * as need from 'lib/need';
4import * as ob from 'urbit-ob';
5import { useWallet } from 'store/wallet';
6import useRoller from 'lib/useRoller';
7import { useLocalRouter } from 'lib/LocalRouter';
9import View from 'components/View';
10import L2BackHeader from 'components/L2/Headers/L2BackHeader';
11import Window from 'components/L2/Window/Window';
12import HeaderPane from 'components/L2/Window/HeaderPane';
13import BodyPane from 'components/L2/Window/BodyPane';
14import { RollerTransaction } from '@urbit/roller-api';
16import './TransactionHistory.scss';
17import { TransactionRow } from './TransactionRow';
18import { Box, LoadingSpinner, Row } from '@tlon/indigo-react';
19import { PatpRow } from './PatpRow';
20import Dropdown from 'components/L2/Dropdowns/Dropdown';
21import Sigil from 'components/Sigil';
22import { AddressRow } from './AddressRow';
24interface GroupedTransactions {
25 [ship: string]: RollerTransaction[];
28const TransactionHistory = () => {
29 const { pop, data }: any = useLocalRouter();
30 const { api } = useRoller();
31 const { wallet }: any = useWallet();
32 const address = need.addressFromWallet(wallet);
33 const [loading, setLoading] = useState(true);
34 const [transactions, setTransactions] = useState<RollerTransaction[]>([]);
35 const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);
36 const [selectedPatp, setSelectedPatp] = useState<string | null>();
38 // Pre-filter the results if navigating from Point view
39 const filterByPoint = useMemo(() => {
40 return data?.filterByPoint.matchWith({
42 Just: (p: any) => ob.patp(p.value),
46 const shortAddress = useMemo(() => {
47 return address.slice(0, 6);
50 const fetchTransactions = useCallback(async () => {
51 const txns = await api.getHistory(address);
52 setTransactions(txns);
54 setSelectedPatp(filterByPoint.slice(1));
57 }, [address, api, filterByPoint]);
59 const selectPatp = useCallback(patp => {
60 setSelectedPatp(patp);
61 setDropdownOpen(false);
64 const txnsByPatp = useMemo(() => {
65 return transactions.reduce((memo, tx) => {
67 if (patp && Object.keys(memo).includes(patp as string)) {
70 memo[patp || '~'] = [tx];
73 }, {} as GroupedTransactions);
76 const txPatps = useMemo(() => {
77 return Object.keys(txnsByPatp).sort((a, b) => {
78 return ob.patp2dec(`~${a}`) - ob.patp2dec(`~${b}`);
82 const filteredPatps = useMemo(() => {
87 return txPatps.filter(p => p === selectedPatp);
88 }, [selectedPatp, txPatps]);
92 // Load the TX history only once
93 }, []); // eslint-disable-line react-hooks/exhaustive-deps
100 className="transaction-history"
101 header={<L2BackHeader />}>
105 <Box className="transaction-header">
106 <h5>Transactions</h5>
108 className="transaction-dropdown"
110 value={selectedPatp ? selectedPatp : shortAddress}
111 toggleOpen={() => setDropdownOpen(!dropdownOpen)}>
112 <Box className="divider" />
113 <Box className="points">
114 <Row className="entry mono" onClick={() => selectPatp(null)}>
115 <Box>{shortAddress}</Box>
124 {txPatps.map(patp => {
127 className="entry mono"
128 onClick={() => selectPatp(patp)}
132 <Box className="sigil">
137 colors={['#000', '#FFF']}
150 <Box className={'loading'}>
152 foreground="rgba(0,0,0,0.3)"
157 <Box className="transaction-container">
158 {!selectedPatp && <AddressRow address={address} />}
159 {filteredPatps && filteredPatps.length > 0 ? (
160 filteredPatps.map(patp => {
163 <PatpRow patp={patp} />
166 return (b?.time || 0) - (a?.time || 0);
169 <TransactionRow key={tx.time} {...tx} />
175 <Box className="no-results">No transactions yet</Box>
186export default TransactionHistory;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list