Skip to content

Commit

Permalink
feat: add transaction pagination (#1003)
Browse files Browse the repository at this point in the history
* feat: add transaction pagination

* fix: set correct amount

* fix: show txs with balance change less than 0
  • Loading branch information
VmMad authored Jan 23, 2024
1 parent 1496662 commit 4547930
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ export const getTransactionHistoryRecords = (

const transactionLink = getTransactionLink(network, transactionId, isTransactionFromStardustGenesis);

const isSpent = balanceChange < 0;

if (balanceChange === 0) {
return;
}
const isSpent = balanceChange <= 0;

calculatedTransactions.push({
isGenesisByDate: isGenesisByDate,
Expand Down
95 changes: 55 additions & 40 deletions client/src/helpers/hooks/useAddressHistory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { OutputResponse } from "@iota/sdk-wasm/web";
import { useEffect, useState } from "react";
import { useIsMounted } from "./useIsMounted";
import { ServiceFactory } from "~factories/serviceFactory";
Expand All @@ -7,6 +6,19 @@ import { ITransactionHistoryItem, ITransactionHistoryResponse } from "~models/ap
import { STARDUST } from "~models/config/protocolVersion";
import { StardustApiClient } from "~services/stardust/stardustApiClient";
import { groupOutputsByTransactionId } from "~app/components/stardust/history/transactionHistoryUtils";
import { OutputResponse } from "@iota/sdk-wasm/web";

const OUTPUT_PAGE_SIZE = 10;
const TX_PAGE_SIZE = 10;

const SORT = "newest";

interface IHistoryState {
transactionIdToOutputs: Map<string, OutputWithDetails[]>;
outputsWithDetails: OutputWithDetails[];
isAddressHistoryLoading: boolean;
cursor: string | undefined;
}

export type OutputWithDetails = ITransactionHistoryItem & { details: OutputResponse | null; amount?: string };

Expand All @@ -23,13 +35,13 @@ export function useAddressHistory(
setDisabled?: (isDisabled: boolean) => void,
): [Map<string, OutputWithDetails[]>, () => void, boolean, boolean] {
const isMounted = useIsMounted();
const [apiClient] = useState(ServiceFactory.get<StardustApiClient>(`api-client-${STARDUST}`));
const [outputsWithDetails, setOutputsWithDetails] = useState<OutputWithDetails[]>([]);
const [transactionIdToOutputs, setTransactionIdToOutputs] = useState<Map<string, OutputWithDetails[]>>(new Map());
const [isAddressHistoryLoading, setIsAddressHistoryLoading] = useState(true);
const [cursor, setCursor] = useState<string | undefined>();
const PAGE_SIZE: number = 10;
const SORT: string = "newest";
const [apiClient] = useState(() => ServiceFactory.get<StardustApiClient>(`api-client-${STARDUST}`));
const [historyState, setHistoryState] = useState<IHistoryState>({
transactionIdToOutputs: new Map<string, OutputWithDetails[]>(),
outputsWithDetails: [],
isAddressHistoryLoading: true,
cursor: undefined,
});

useEffect(() => {
if (!address || !isMounted) return;
Expand All @@ -38,13 +50,15 @@ export function useAddressHistory(
})();
}, [address, isMounted]);

const requestOutputsList = async () => {
if (!address) return;
const requestOutputsList = async (
cursor: string | undefined,
): Promise<{ outputs: ITransactionHistoryItem[]; cursor: string | undefined }> => {
if (!address) return { outputs: [], cursor: undefined };

const request: ITransactionHistoryRequest = {
network,
address,
pageSize: PAGE_SIZE,
pageSize: OUTPUT_PAGE_SIZE,
sort: SORT,
cursor,
};
Expand All @@ -57,7 +71,7 @@ export function useAddressHistory(
};
};

const requestOutputDetails = async (outputId: string) => {
const requestOutputDetails = async (outputId: string): Promise<OutputResponse | null> => {
if (!outputId) return null;

try {
Expand All @@ -75,32 +89,34 @@ export function useAddressHistory(
};

const loadHistory = async () => {
if (address && isMounted) {
setIsAddressHistoryLoading(true);
let { transactionIdToOutputs, outputsWithDetails, cursor } = historyState;
// Search one more than the desired so the incomplete transaction can be removed with .pop()
const targetSize = transactionIdToOutputs.size + TX_PAGE_SIZE + 1;
let searchMore = true;

try {
const outputList = await requestOutputsList();
setHistoryState((prevState) => ({ ...prevState, isAddressHistoryLoading: true }));

if (!outputList) {
return;
}

const { outputs, cursor } = outputList;
while (transactionIdToOutputs.size < targetSize && searchMore) {
try {
const { outputs, cursor: newCursor } = await requestOutputsList(cursor);

if (!cursor) {
if (!newCursor) {
setDisabled?.(true);
searchMore = false;
}

const fulfilledOutputs: OutputWithDetails[] = [];

for (const output of outputs) {
const outputDetails = await requestOutputDetails(output.outputId);
fulfilledOutputs.push({ ...output, details: outputDetails, amount: outputDetails?.output?.amount });
}

const updatedOutputsWithDetails = [...outputsWithDetails, ...fulfilledOutputs];

updatedOutputsWithDetails.sort((a, b) => {
const fulfilledOutputs: OutputWithDetails[] = await Promise.all(
outputs.map(async (output) => {
const details = await requestOutputDetails(output.outputId);
return {
...output,
details,
amount: details?.output?.amount,
};
}),
);

outputsWithDetails = [...outputsWithDetails, ...fulfilledOutputs].sort((a, b) => {
// Ensure that entries with equal timestamp, but different isSpent,
// have the spending before the depositing
if (a.milestoneTimestamp === b.milestoneTimestamp && a.isSpent !== b.isSpent) {
Expand All @@ -109,16 +125,15 @@ export function useAddressHistory(
return 1;
});

const groupedOutputsByTransactionId = groupOutputsByTransactionId(updatedOutputsWithDetails);

setTransactionIdToOutputs(groupedOutputsByTransactionId);
setOutputsWithDetails([...outputsWithDetails, ...fulfilledOutputs]);
setCursor(cursor);
} finally {
setIsAddressHistoryLoading(false);
transactionIdToOutputs = groupOutputsByTransactionId(outputsWithDetails);
cursor = newCursor;
} catch (error) {
console.error("Failed loading transaction history", error);
searchMore = false;
}
}
setHistoryState({ transactionIdToOutputs, outputsWithDetails, isAddressHistoryLoading: false, cursor });
};

return [transactionIdToOutputs, loadHistory, isAddressHistoryLoading, Boolean(cursor)];
return [historyState.transactionIdToOutputs, loadHistory, historyState.isAddressHistoryLoading, Boolean(historyState.cursor)];
}

0 comments on commit 4547930

Please sign in to comment.