diff --git a/public/locales/en.json b/public/locales/en.json
index cf043eb..132cc36 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -369,6 +369,8 @@
"transactions": {
"title": "Network Transactions",
"myTransactionsTitle": "My Transactions",
+ "nameHistoryTitle": "Name History",
+ "nameTransactionsTitle": "Name Transactions",
"columnID": "ID",
"columnType": "Type",
diff --git a/src/global/AppRouter.tsx b/src/global/AppRouter.tsx
index 1211ee0..4d20979 100644
--- a/src/global/AppRouter.tsx
+++ b/src/global/AppRouter.tsx
@@ -43,6 +43,16 @@
name: "transactions",
component:
},
+ {
+ path: "/network/names/:name/history",
+ name: "nameHistory",
+ component:
+ },
+ {
+ path: "/network/names/:name/transactions",
+ name: "nameTransactions",
+ component:
+ },
{ path: "/settings", name: "settings", component: },
{ path: "/settings/debug", name: "settingsDebug" },
diff --git a/src/krist/api/lookup.ts b/src/krist/api/lookup.ts
index 70f6d29..8536e42 100644
--- a/src/krist/api/lookup.ts
+++ b/src/krist/api/lookup.ts
@@ -54,12 +54,20 @@
// =============================================================================
export type SortableTransactionFields = "id" | "from" | "to" | "value" | "time"
| "sent_name" | "sent_metaname";
+
+export enum LookupTransactionType {
+ TRANSACTIONS,
+ NAME_HISTORY,
+ NAME_TRANSACTIONS
+}
+
export interface LookupTransactionsOptions {
includeMined?: boolean;
limit?: number;
offset?: number;
orderBy?: SortableTransactionFields;
order?: "ASC" | "DESC";
+ type?: LookupTransactionType;
}
export interface LookupTransactionsResponse {
@@ -76,12 +84,24 @@
if (opts.orderBy) qs.append("orderBy", opts.orderBy);
if (opts.order) qs.append("order", opts.order);
+ // Map the lookup type to the appropriate route
+ // TODO: this is kinda wack
+ const type = opts.type || LookupTransactionType.TRANSACTIONS;
+ const route = type === LookupTransactionType.TRANSACTIONS
+ ? "transactions" : "names";
+ const routeExtra = type !== LookupTransactionType.TRANSACTIONS
+ ? (type === LookupTransactionType.NAME_HISTORY
+ ? "/history"
+ : "/transactions")
+ : "";
+
return await api.get(
- "lookup/transactions/"
+ `lookup/${route}/`
+ (addresses && addresses.length > 0
? encodeURIComponent(addresses.join(","))
: "")
- + "?" + qs
+ + routeExtra
+ + `?${qs}`
);
}
diff --git a/src/pages/transactions/TransactionsPage.tsx b/src/pages/transactions/TransactionsPage.tsx
index 268424c..ca23866 100644
--- a/src/pages/transactions/TransactionsPage.tsx
+++ b/src/pages/transactions/TransactionsPage.tsx
@@ -12,6 +12,7 @@
import { TransactionsTable } from "./TransactionsTable";
import { useWallets } from "../../krist/wallets/Wallet";
+import { KristNameLink } from "../../components/KristNameLink";
/** The type of transaction listing to search by. */
export enum ListingType {
@@ -21,11 +22,27 @@
/** Transactions across the whole network */
NETWORK_ALL,
/** Network transactions filtered to a particular address */
- NETWORK_ADDRESS
+ NETWORK_ADDRESS,
+
+ /** Name history transactions */
+ NAME_HISTORY,
+ /** Transactions sent to a particular name */
+ NAME_SENT,
}
+const LISTING_TYPE_TITLES: Record = {
+ [ListingType.WALLETS]: "transactions.myTransactionsTitle",
+
+ [ListingType.NETWORK_ALL]: "transactions.title",
+ [ListingType.NETWORK_ADDRESS]: "transactions.title",
+
+ [ListingType.NAME_HISTORY]: "transactions.nameHistoryTitle",
+ [ListingType.NAME_SENT]: "transactions.nameTransactionsTitle"
+};
+
interface ParamTypes {
address?: string;
+ name?: string;
}
interface Props {
@@ -34,30 +51,32 @@
export function TransactionsPage({ listingType }: Props): JSX.Element {
const { t } = useTranslation();
- const { address } = useParams();
+ const { address, name } = useParams();
+ const [includeMined, setIncludeMined] = useState(false);
// If there is an error (e.g. the lookup rejected the address list due to an
// invalid address), the table will bubble it up to here
const [error, setError] = useState();
- const [includeMined, setIncludeMined] = useState(false);
+ const subTitle = name
+ ?
+ : (listingType === ListingType.NETWORK_ADDRESS
+ ? address
+ : undefined);
return
+ extra={!name && <>
@@ -117,7 +140,10 @@
const table = useMemo(() => (
diff --git a/src/pages/transactions/TransactionsTable.tsx b/src/pages/transactions/TransactionsTable.tsx
index 4f1f21c..9ab41c4 100644
--- a/src/pages/transactions/TransactionsTable.tsx
+++ b/src/pages/transactions/TransactionsTable.tsx
@@ -7,7 +7,9 @@
import { useTranslation } from "react-i18next";
import { KristTransaction } from "../../krist/api/types";
-import { convertSorterOrder, lookupTransactions, LookupTransactionsOptions, LookupTransactionsResponse, SortableTransactionFields } from "../../krist/api/lookup";
+import { convertSorterOrder, lookupTransactions, LookupTransactionsOptions, LookupTransactionsResponse, LookupTransactionType, SortableTransactionFields } from "../../krist/api/lookup";
+
+import { ListingType } from "./TransactionsPage";
import { TransactionType } from "../../components/transactions/TransactionType";
import { ContextualAddress } from "../../components/ContextualAddress";
@@ -19,13 +21,27 @@
import Debug from "debug";
const debug = Debug("kristweb:transactions-table");
+// Received 'Cannot access LookupTransactionType before initialization' here,
+// this is a crude workaround
+const LISTING_TYPE_MAP: Record = {
+ [0]: LookupTransactionType.TRANSACTIONS,
+ [1]: LookupTransactionType.TRANSACTIONS,
+ [2]: LookupTransactionType.TRANSACTIONS,
+ [3]: LookupTransactionType.NAME_HISTORY,
+ [4]: LookupTransactionType.NAME_TRANSACTIONS
+};
+
interface Props {
+ listingType: ListingType;
+
addresses?: string[];
+ name?: string;
+
includeMined?: boolean;
setError?: Dispatch>;
}
-export function TransactionsTable({ addresses, includeMined, setError }: Props): JSX.Element {
+export function TransactionsTable({ listingType, addresses, name, includeMined, setError }: Props): JSX.Element {
const { t } = useTranslation();
const [loading, setLoading] = useState(true);
@@ -33,20 +49,24 @@
const [options, setOptions] = useState({
limit: 20,
offset: 0,
- orderBy: "time",
+ orderBy: "time", // Equivalent to sorting by ID
order: "DESC"
});
// Fetch the transactions from the API, mapping the table options
useEffect(() => {
- debug("looking up transactions for %s", addresses ? addresses.join(",") : "network");
+ debug("looking up transactions for %s", name || (addresses ? addresses.join(",") : "network"));
setLoading(true);
- lookupTransactions(addresses, { ...options, includeMined })
+ lookupTransactions(name ? [name] : addresses, {
+ ...options,
+ includeMined,
+ type: LISTING_TYPE_MAP[listingType]
+ })
.then(setRes)
.catch(setError)
.finally(() => setLoading(false));
- }, [addresses, setError, options, includeMined ]);
+ }, [listingType, addresses, name, setError, options, includeMined]);
debug("results? %b res.transactions.length: %d res.count: %d res.total: %d", !!res, res?.transactions?.length, res?.count, res?.total);
@@ -58,8 +78,15 @@
dataSource={res?.transactions || []}
rowKey="id"
+ // Triggered whenever the filter, sorting, or pagination changes
onChange={(pagination, _, sorter) => {
+ // While the pagination should never be undefined, it's important to
+ // ensure that the default pageSize here is equal to the pagination's
+ // default pageSize, otherwise ant-design will print a warning when the
+ // data is first populated.
const pageSize = (pagination?.pageSize) || 20;
+
+ // This will trigger a data re-fetch
setOptions({
...options,
@@ -89,7 +116,10 @@
dataIndex: "id", key: "id",
render: id => <>{id.toLocaleString()}>,
- width: 100,
+ width: 100
+
+ // Don't allow sorting by ID to save a bit of width in the columns;
+ // it's equivalent to sorting by time anyway
},
// Type
{