mirror of
https://github.com/logos-storage/logos-storage-marketplace-ui.git
synced 2026-04-26 21:03:30 +00:00
130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import {
|
|
Cell,
|
|
Row,
|
|
Spinner,
|
|
Table,
|
|
TabSortState,
|
|
} from "@codex-storage/marketplace-ui-components";
|
|
import { Times } from "../../utils/times";
|
|
import { useState } from "react";
|
|
import { FileCell } from "../../components/FileCellRender/FileCell";
|
|
import { useData } from "../../hooks/useData";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { CodexSdk } from "../../sdk/codex";
|
|
import { Promises } from "../../utils/promises";
|
|
import { CodexPurchase } from "@codex-storage/sdk-js";
|
|
import { TruncateCell } from "../TruncateCell/TruncateCell";
|
|
import { CustomStateCellRender } from "../CustomStateCellRender/CustomStateCellRender";
|
|
import { PurchaseUtils } from "./purchase.utils";
|
|
|
|
type SortFn = (a: CodexPurchase, b: CodexPurchase) => number;
|
|
|
|
export function PurchasesTable() {
|
|
const content = useData();
|
|
const { data, isPending } = useQuery({
|
|
queryFn: () =>
|
|
CodexSdk.client()
|
|
.marketplace.purchases()
|
|
.then((s) => Promises.rejectOnError(s)),
|
|
queryKey: ["purchases"],
|
|
|
|
// No need to retry because if the connection to the node
|
|
// is back again, all the queries will be invalidated.
|
|
retry: false,
|
|
|
|
// The client node should be local, so display the cache value while
|
|
// making a background request looks good.
|
|
staleTime: 0,
|
|
|
|
// Refreshing when focus returns can be useful if a user comes back
|
|
// to the UI after performing an operation in the terminal.
|
|
refetchOnWindowFocus: true,
|
|
|
|
initialData: [],
|
|
|
|
// Throw the error to the error boundary
|
|
throwOnError: true,
|
|
});
|
|
|
|
// const onMetadata = (
|
|
// requestId: string,
|
|
// { uploadedAt }: { uploadedAt: number }
|
|
// ) => {
|
|
// setMetadata((m) => ({ ...m, [requestId]: uploadedAt }));
|
|
// setSortFn(() =>
|
|
// PurchaseUtils.sortByUploadedAt("desc", {
|
|
// ...metadata,
|
|
// [requestId]: uploadedAt,
|
|
// })
|
|
// );
|
|
// };
|
|
|
|
const [sortFn, setSortFn] = useState<SortFn>(() =>
|
|
PurchaseUtils.sortByDuration("desc")
|
|
);
|
|
|
|
const onSortByDuration = (state: TabSortState) =>
|
|
setSortFn(() => PurchaseUtils.sortByDuration(state));
|
|
|
|
const onSortByReward = (state: TabSortState) =>
|
|
setSortFn(() => PurchaseUtils.sortByReward(state));
|
|
|
|
const onSortByState = (state: TabSortState) =>
|
|
setSortFn(() => PurchaseUtils.sortByState(state));
|
|
|
|
// const onSortByUploadedAt = (state: TabSortState) =>
|
|
// setSortFn(() => PurchaseUtils.sortByUploadedAt(state, metadata));
|
|
|
|
const headers = [
|
|
["file"],
|
|
["request id"],
|
|
["duration", onSortByDuration],
|
|
["slots"],
|
|
["price per byte", onSortByReward],
|
|
["proof probability"],
|
|
["state", onSortByState],
|
|
] satisfies [string, ((state: TabSortState) => void)?][];
|
|
|
|
const sorted = sortFn ? [...data].sort(sortFn) : data;
|
|
|
|
const rows = sorted
|
|
.filter((p) => !!p.request)
|
|
.map((p, index) => {
|
|
const r = p.request!;
|
|
const ask = r.ask;
|
|
const duration = parseInt(ask.duration, 10);
|
|
const pf = ask.proofProbability;
|
|
return (
|
|
<Row
|
|
cells={[
|
|
<FileCell
|
|
requestId={r.id}
|
|
purchaseCid={r.content.cid}
|
|
index={index}
|
|
data={content}
|
|
/>,
|
|
<TruncateCell value={r.id} />,
|
|
<Cell>{Times.pretty(duration)}</Cell>,
|
|
<Cell>{ask.slots.toString()}</Cell>,
|
|
<Cell>{r.ask.pricePerBytePerSecond + " CDX"}</Cell>,
|
|
<Cell>{pf.toString()}</Cell>,
|
|
<CustomStateCellRender state={p.state} message={p.error || ""} />,
|
|
]}></Row>
|
|
);
|
|
});
|
|
|
|
if (isPending) {
|
|
return (
|
|
<div>
|
|
<Spinner width="3rem" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Table headers={headers} rows={rows} defaultSortIndex={2} />
|
|
</>
|
|
);
|
|
}
|