diff --git a/components/GatewayList/index.tsx b/components/GatewayList/index.tsx index 7eb8dc44..bbf3f1fe 100644 --- a/components/GatewayList/index.tsx +++ b/components/GatewayList/index.tsx @@ -16,10 +16,10 @@ import { } from "@livepeer/design-system"; import { DotsHorizontalIcon } from "@radix-ui/react-icons"; import { GatewaysQuery } from "apollo"; -import { useEnsData } from "hooks"; +import { useEnsData, usePersistedExplorerListState } from "hooks"; import Link from "next/link"; import { useMemo } from "react"; -import { Column } from "react-table"; +import { Column, SortingRule } from "react-table"; type GatewayRow = NonNullable[number] & { depositNumber: number; @@ -28,13 +28,26 @@ type GatewayRow = NonNullable[number] & { totalVolumeNumber: number; }; +const DEFAULT_SORT_BY: SortingRule[] = [ + { id: "ninetyDayVolumeNumber", desc: true }, +]; + const GatewayList = ({ data, + listKey, pageSize = 10, + routePath, }: { + listKey: string; pageSize?: number; + routePath: string; data: GatewaysQuery["gateways"] | undefined; }) => { + const { handleTableStateChange, persistedState, saveCurrentScroll } = + usePersistedExplorerListState({ + listKey, + routePath, + }); const rows: GatewayRow[] = useMemo( () => (data ?? []).map((gateway) => ({ @@ -310,15 +323,22 @@ const GatewayList = ({ } return ( - + +
[]) + : DEFAULT_SORT_BY, + }} + /> + ); }; diff --git a/components/OrchestratorList/index.tsx b/components/OrchestratorList/index.tsx index 8db7ba85..73f302ae 100644 --- a/components/OrchestratorList/index.tsx +++ b/components/OrchestratorList/index.tsx @@ -47,10 +47,16 @@ import { PERCENTAGE_PRECISION_MILLION, } from "@utils/web3"; import { OrchestratorsQueryResult, ProtocolQueryResult } from "apollo"; -import { useEnsData } from "hooks"; +import { + OrchestratorListKey, + OrchestratorListState, + useEnsData, + useExplorerStore, + usePersistedExplorerListState, +} from "hooks"; import { useBondingManagerAddress } from "hooks/useContracts"; import Link from "next/link"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { formatUnits } from "viem"; import { useReadContract } from "wagmi"; @@ -76,11 +82,16 @@ const formatFactors = (factors: ROIFactors) => ? `LPT Only` : `ETH Only`; +const getListPath = (listKey: OrchestratorListKey) => + listKey === "home" ? "/" : "/orchestrators"; + const OrchestratorList = ({ data, + listKey, protocolData, pageSize = 10, }: { + listKey: OrchestratorListKey; pageSize: number; protocolData: | NonNullable["protocol"] @@ -89,6 +100,25 @@ const OrchestratorList = ({ | NonNullable["transcoders"] | undefined; }) => { + const persistedState = useExplorerStore( + (state) => state.orchestratorLists[listKey] + ); + const setOrchestratorListState = useExplorerStore( + (state) => state.setOrchestratorListState + ); + const setPersistedListState = useCallback( + (value: Partial) => { + setOrchestratorListState(listKey, value); + }, + [listKey, setOrchestratorListState] + ); + const { handleTableStateChange, saveCurrentScroll } = + usePersistedExplorerListState({ + listKey: `orchestrator-list:${listKey}`, + routePath: getListPath(listKey), + persistedState, + setPersistedState: setPersistedListState, + }); // Derive protocol inflation data const inflationRate = Number(protocolData?.inflation || 0) / PERCENTAGE_PRECISION_BILLION; @@ -108,16 +138,35 @@ const OrchestratorList = ({ [inflationRate, inflationChangeAmount] ); - const [principle, setPrinciple] = useState(150); - const [inflationChange, setInflationChange] = - useState("none"); - const [factors, setFactors] = useState("lpt+eth"); - const [timeHorizon, setTimeHorizon] = useState("one-year"); + const [principle, setPrinciple] = useState(persistedState.principle); + const [inflationChange, setInflationChange] = useState( + persistedState.inflationChange + ); + const [factors, setFactors] = useState(persistedState.factors); + const [timeHorizon, setTimeHorizon] = useState( + persistedState.timeHorizon + ); const maxSupplyTokens = useMemo( () => Math.floor(Number(protocolData?.totalSupply || 1e7)), [protocolData] ); + useEffect(() => { + setOrchestratorListState(listKey, { + factors, + inflationChange, + principle, + timeHorizon, + }); + }, [ + factors, + inflationChange, + listKey, + principle, + setOrchestratorListState, + timeHorizon, + ]); + const formattedPrinciple = formatLPT(Number(principle) || 150, { precision: 0, }); @@ -1015,215 +1064,38 @@ const OrchestratorList = ({ ); return ( -
- - - - - - {"Forecasted Yield Assumptions"} - - - - - { - e.stopPropagation(); - }} - asChild - > - - - - - - - {"Time horizon:"} - - - {formatTimeHorizon(timeHorizon)} - - - - +
+ + + + + - - setTimeHorizon("half-year")} - > - {"6 months"} - - setTimeHorizon("one-year")} - > - {"1 year"} - - setTimeHorizon("two-years")} - > - {"2 years"} - - setTimeHorizon("three-years")} - > - {"3 years"} - - setTimeHorizon("four-years")} - > - {"4 years"} - - - - - - - { - e.stopPropagation(); - }} - asChild - > - - - - - - {"Delegation:"} - - - {formatLPT(principle, { precision: 1 })} - - - - - - - { - setPrinciple( - Number(e.target.value) > maxSupplyTokens - ? maxSupplyTokens - : Number(e.target.value) - ); - }} - min="1" - max={`${Number( - protocolData?.totalSupply || 1e7 - ).toFixed(0)}`} - /> - - LPT - - - - - - - + {"Forecasted Yield Assumptions"} + + + { @@ -1250,7 +1122,7 @@ const OrchestratorList = ({ marginRight: 3, }} > - {"Factors:"} + {"Time horizon:"} - {formatFactors(factors)} + {formatTimeHorizon(timeHorizon)} @@ -1278,113 +1150,296 @@ const OrchestratorList = ({ css={{ cursor: "pointer", }} - onSelect={() => setFactors("lpt+eth")} + onSelect={() => setTimeHorizon("half-year")} + > + {"6 months"} + + setTimeHorizon("one-year")} > - {formatFactors("lpt+eth")} + {"1 year"} setFactors("lpt")} + onSelect={() => setTimeHorizon("two-years")} > - {formatFactors("lpt")} + {"2 years"} setFactors("eth")} + onSelect={() => setTimeHorizon("three-years")} > - {formatFactors("eth")} + {"3 years"} + + setTimeHorizon("four-years")} + > + {"4 years"} - - - - { - e.stopPropagation(); - }} - asChild - > - + + { + e.stopPropagation(); }} + asChild > - - - - - - {"Inflation change:"} - - - {formatPercentChange(inflationChange)} - - - - - - + + + + {"Delegation:"} + + + {formatLPT(principle, { precision: 1 })} + + + + + setInflationChange("none")} > - {formatPercentChange("none")} - - + { + const nextValue = Number(e.target.value); + setPrinciple( + !Number.isFinite(nextValue) + ? 1 + : nextValue < 1 + ? 1 + : nextValue > maxSupplyTokens + ? maxSupplyTokens + : nextValue + ); + }} + min="1" + max={`${Number( + protocolData?.totalSupply || 1e7 + ).toFixed(0)}`} + /> + + LPT + + + + + + + + + { + e.stopPropagation(); + }} + asChild + > + setInflationChange("positive")} > - {formatPercentChange("positive")} - - + + + + + {"Factors:"} + + + {formatFactors(factors)} + + + + + + setFactors("lpt+eth")} + > + {formatFactors("lpt+eth")} + + setFactors("lpt")} + > + {formatFactors("lpt")} + + setFactors("eth")} + > + {formatFactors("eth")} + + + + + + + + { + e.stopPropagation(); + }} + asChild + > + setInflationChange("negative")} > - {formatPercentChange("negative")} - - - - - - - - } - /> + + + + + + {"Inflation change:"} + + + {formatPercentChange(inflationChange)} + + + + + + setInflationChange("none")} + > + {formatPercentChange("none")} + + setInflationChange("positive")} + > + {formatPercentChange("positive")} + + setInflationChange("negative")} + > + {formatPercentChange("negative")} + + + + + + + + } + /> + ); }; diff --git a/components/PerformanceList/index.tsx b/components/PerformanceList/index.tsx index 4340438d..ec136bfa 100644 --- a/components/PerformanceList/index.tsx +++ b/components/PerformanceList/index.tsx @@ -10,13 +10,20 @@ import { QuestionMarkCircledIcon } from "@radix-ui/react-icons"; import { formatNumber, formatPercent } from "@utils/numberFormatters"; import { formatAddress } from "@utils/web3"; import { OrchestratorsQueryResult } from "apollo"; -import { useEnsData } from "hooks"; +import { useEnsData, usePersistedExplorerListState } from "hooks"; import Link from "next/link"; import { useMemo } from "react"; import { Column } from "react-table"; const EmptyData = () => ; +const DEFAULT_SORT_BY = [ + { + id: "scores", + desc: true, + }, +]; + const PerformanceList = ({ orchestratorIds, pageSize = 20, @@ -41,15 +48,28 @@ const PerformanceList = ({ const scoreAccessor = `scores.${region}`; //total score const successRateAccessor = `successRates.${region}`; //success rate const roundTripScoreAccessor = `roundTripScores.${region}`; //latency score + const listKey = useMemo( + () => + [ + "leaderboard-performance", + region, + pipeline ?? "transcoding", + model ?? "default", + ].join(":"), + [model, pipeline, region] + ); + const { handleTableStateChange, persistedState, saveCurrentScroll } = + usePersistedExplorerListState({ + listKey, + routePath: "/leaderboard", + }); const initialState = { + pageIndex: persistedState.pageIndex, pageSize: pageSize, - sortBy: [ - { - id: "scores", - desc: true, - }, - ], + sortBy: persistedState.sortBy.length + ? persistedState.sortBy + : DEFAULT_SORT_BY, hiddenColumns: [ "activationRound", "deactivationRound", @@ -320,7 +340,17 @@ const PerformanceList = ({ ] ); return ( -
+ +
+ ); }; diff --git a/components/Table/index.tsx b/components/Table/index.tsx index a58a2d7b..42505038 100644 --- a/components/Table/index.tsx +++ b/components/Table/index.tsx @@ -9,33 +9,66 @@ import { Tr, } from "@livepeer/design-system"; import { ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons"; -import { ReactNode } from "react"; +import { ReactNode, useEffect } from "react"; import { Column, HeaderGroup, + SortingRule, TableInstance, + TableOptions, + TableState, usePagination, UsePaginationInstanceProps, + UsePaginationOptions, + UsePaginationState, useSortBy, UseSortByInstanceProps, + UseSortByOptions, + UseSortByState, useTable, } from "react-table"; import Pagination from "./Pagination"; +type TableInitialState = Partial> & + Partial> & + Partial>; + +type PersistedTableState = { + pageIndex: number; + pageSize: number; + sortBy: SortingRule[]; +}; + function DataTable({ heading = null, input = null, data, columns, initialState = {}, + autoResetPage, + autoResetSortBy, + onStateChange, }: { heading?: ReactNode; input?: ReactNode; data: T[]; columns: Column[]; - initialState: object; + initialState: TableInitialState; + autoResetPage?: boolean; + autoResetSortBy?: boolean; + onStateChange?: (state: PersistedTableState) => void; }) { + const tableOptions: TableOptions & + UsePaginationOptions & + UseSortByOptions = { + columns, + data, + initialState, + autoResetPage, + autoResetSortBy, + }; + const { getTableProps, getTableBodyProps, @@ -47,18 +80,16 @@ function DataTable({ pageCount, nextPage, previousPage, - state: { pageIndex }, - } = useTable( - { - columns, - data, - initialState, - }, - useSortBy, - usePagination - ) as TableInstance & + state: { pageIndex, pageSize, sortBy }, + } = useTable(tableOptions, useSortBy, usePagination) as TableInstance & UsePaginationInstanceProps & - UseSortByInstanceProps & { state: { pageIndex: number } }; + UseSortByInstanceProps & { + state: UsePaginationState & UseSortByState; + }; + + useEffect(() => { + onStateChange?.({ pageIndex, pageSize, sortBy }); + }, [onStateChange, pageIndex, pageSize, sortBy]); return ( <> diff --git a/components/TransactionsList/index.tsx b/components/TransactionsList/index.tsx index 89a78df7..d5f6ee19 100644 --- a/components/TransactionsList/index.tsx +++ b/components/TransactionsList/index.tsx @@ -17,6 +17,7 @@ import { } from "@utils/web3"; import { EventsQueryResult, TreasuryProposal } from "apollo"; import { sentenceCase } from "change-case"; +import { usePersistedExplorerListState } from "hooks"; import { useCallback, useMemo } from "react"; export const FILTERED_EVENT_TYPENAMES = [ @@ -67,15 +68,33 @@ const renderEmoji = (emoji: string) => ( ); +const DEFAULT_SORT_BY = [ + { + id: "timestamp", + desc: true, + }, +]; + const TransactionsList = ({ events, + listKey, pageSize = 10, + routePath, }: { + listKey: string; pageSize: number; + routePath: string; events: NonNullable< EventsQueryResult["data"] >["transactions"][number]["events"]; }) => { + const { handleTableStateChange, persistedState, saveCurrentScroll } = + usePersistedExplorerListState({ + listKey, + routePath, + }); + const pageCount = Math.max(1, Math.ceil((events?.length ?? 0) / pageSize)); + const pageIndex = Math.min(persistedState.pageIndex, pageCount - 1); const getAccountForRow = useCallback( ( event: NonNullable< @@ -547,19 +566,22 @@ const TransactionsList = ({ ); return ( -
+ +
+ ); }; diff --git a/hooks/index.tsx b/hooks/index.tsx index 9d37f82c..a22571b6 100644 --- a/hooks/index.tsx +++ b/hooks/index.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; // DO NOT IMPORT useHandleTransaction due to @rainbow-me/rainbowkit issues with SSR export * from "./useExplorerStore"; +export * from "./usePersistedExplorerListState"; export * from "./useSwr"; export * from "./wallet"; diff --git a/hooks/useExplorerStore.tsx b/hooks/useExplorerStore.tsx index 980a1dc2..2d36084a 100644 --- a/hooks/useExplorerStore.tsx +++ b/hooks/useExplorerStore.tsx @@ -1,9 +1,26 @@ import { ProposalExtended } from "@lib/api/treasury"; +import { ROIFactors, ROIInflationChange, ROITimeHorizon } from "@lib/roi"; import { txMessages } from "lib/utils"; +import { SortingRule } from "react-table"; import { Address } from "viem"; import { create } from "zustand"; export type StakingAction = "undelegate" | "delegate" | null; +export type OrchestratorListKey = "home" | "orchestrators"; +export type OrchestratorListState = { + factors: ROIFactors; + inflationChange: ROIInflationChange; + pageIndex: number; + principle: number; + scrollY: number; + sortBy: SortingRule[]; + timeHorizon: ROITimeHorizon; +}; +export type ExplorerListState = { + pageIndex: number; + scrollY: number; + sortBy: SortingRule[]; +}; export type YieldResults = { roiFees: number; roiFeesLpt: number; @@ -48,12 +65,22 @@ export type ExplorerState = { bottomDrawerOpen: boolean; selectedStakingAction: StakingAction; yieldResults: YieldResults; + orchestratorLists: Record; + explorerLists: Record; latestTransaction: TransactionStatus | null; setWalletModalOpen: (v: boolean) => void; setBottomDrawerOpen: (v: boolean) => void; setSelectedStakingAction: (v: StakingAction) => void; setYieldResults: (v: YieldResults) => void; + setOrchestratorListState: ( + key: OrchestratorListKey, + value: Partial + ) => void; + setExplorerListState: ( + key: string, + value: Partial + ) => void; setLatestTransactionDetails: ( hash: string, @@ -66,6 +93,22 @@ export type ExplorerState = { clearLatestTransaction: () => void; }; +const defaultOrchestratorListState: OrchestratorListState = { + factors: "lpt+eth", + inflationChange: "none", + pageIndex: 0, + principle: 150, + scrollY: 0, + sortBy: [{ id: "earnings", desc: true }], + timeHorizon: "one-year", +}; + +export const defaultExplorerListState: ExplorerListState = { + pageIndex: 0, + scrollY: 0, + sortBy: [], +}; + export const useExplorerStore = create()((set) => ({ walletModalOpen: false, bottomDrawerOpen: false, @@ -76,6 +119,11 @@ export const useExplorerStore = create()((set) => ({ roiRewards: 0.0, principle: 0.0, }, + orchestratorLists: { + home: defaultOrchestratorListState, + orchestrators: defaultOrchestratorListState, + }, + explorerLists: {}, latestTransaction: null, setWalletModalOpen: (v: boolean) => set(() => ({ walletModalOpen: v })), @@ -83,6 +131,52 @@ export const useExplorerStore = create()((set) => ({ setSelectedStakingAction: (v: StakingAction) => set(() => ({ selectedStakingAction: v })), setYieldResults: (v: YieldResults) => set(() => ({ yieldResults: v })), + setOrchestratorListState: (key, value) => + set((state) => { + const { orchestratorLists } = state; + const current = orchestratorLists[key]; + const hasChanged = Object.entries(value).some( + ([field, nextValue]) => + current[field as keyof OrchestratorListState] !== nextValue + ); + + if (!hasChanged) { + return state; + } + + return { + orchestratorLists: { + ...orchestratorLists, + [key]: { + ...current, + ...value, + }, + }, + }; + }), + setExplorerListState: (key, value) => + set((state) => { + const { explorerLists } = state; + const current = explorerLists[key] ?? defaultExplorerListState; + const hasChanged = Object.entries(value).some( + ([field, nextValue]) => + current[field as keyof ExplorerListState] !== nextValue + ); + + if (!hasChanged) { + return state; + } + + return { + explorerLists: { + ...explorerLists, + [key]: { + ...current, + ...value, + }, + }, + }; + }), setLatestTransactionDetails: ( hash: string, diff --git a/hooks/usePersistedExplorerListState.ts b/hooks/usePersistedExplorerListState.ts new file mode 100644 index 00000000..82130f40 --- /dev/null +++ b/hooks/usePersistedExplorerListState.ts @@ -0,0 +1,218 @@ +import Router from "next/router"; +import { useCallback, useEffect, useMemo, useRef } from "react"; +import { SortingRule } from "react-table"; + +import { + defaultExplorerListState, + ExplorerListState, + useExplorerStore, +} from "./useExplorerStore"; + +type PersistedListState = { + pageIndex: number; + scrollY: number; + sortBy: SortingRule[]; +}; + +type PersistedTableState = { + pageIndex: number; + sortBy: SortingRule[]; +}; + +type UsePersistedExplorerListStateOptions = { + listKey: string; + routePath: string; + persistedState?: PersistedListState; + setPersistedState?: (value: Partial) => void; +}; + +/** Builds the sessionStorage key used as a same-tab scroll backup for a list. */ +const getScrollStorageKey = (listKey: string) => + `livepeer:explorer-list:${listKey}:scrollY`; + +/** Reads the last saved scroll offset for a list, returning null when unavailable. */ +const readStoredScrollY = (listKey: string) => { + if (typeof window === "undefined") { + return null; + } + + try { + const value = window.sessionStorage.getItem(getScrollStorageKey(listKey)); + const scrollY = value ? Number(value) : NaN; + + return Number.isFinite(scrollY) ? scrollY : null; + } catch { + return null; + } +}; + +/** Writes a non-negative rounded scroll offset for a list into sessionStorage. */ +const writeStoredScrollY = (listKey: string, scrollY: number) => { + if (typeof window === "undefined") { + return; + } + + try { + window.sessionStorage.setItem( + getScrollStorageKey(listKey), + `${Math.max(0, Math.round(scrollY))}` + ); + } catch {} +}; + +/** + * Persists table page/sort state and restores window scroll for a browsable list. + * + * `listKey` scopes stored state per list instance, while `routePath` identifies + * the route that should trigger restoration after Back navigation. Callers can + * provide `persistedState` and `setPersistedState` to reuse an existing store + * slice; otherwise the hook uses the generic explorer list store. + * + * Returns `handleTableStateChange` for the shared Table callback, + * `persistedState` for Table initial state, and `saveCurrentScroll` for click + * capture before navigating from a list item. + */ +export const usePersistedExplorerListState = ({ + listKey, + routePath, + persistedState: externalPersistedState, + setPersistedState: externalSetPersistedState, +}: UsePersistedExplorerListStateOptions) => { + const storedPersistedState = + useExplorerStore((state) => state.explorerLists[listKey]) ?? + defaultExplorerListState; + const setExplorerListState = useExplorerStore( + (state) => state.setExplorerListState + ); + const persistedState = externalPersistedState ?? storedPersistedState; + const setPersistedState = useMemo( + () => + externalSetPersistedState ?? + ((value: Partial) => + setExplorerListState(listKey, value)), + [externalSetPersistedState, listKey, setExplorerListState] + ); + const activeRestoreCleanup = useRef<(() => void) | undefined>(undefined); + + const saveCurrentScroll = useCallback(() => { + writeStoredScrollY(listKey, window.scrollY); + setPersistedState({ scrollY: window.scrollY }); + }, [listKey, setPersistedState]); + + const restoreSavedScroll = useCallback(() => { + const scrollY = readStoredScrollY(listKey) ?? persistedState.scrollY; + + if (scrollY <= 0) { + return; + } + + const getMaxScrollY = () => + Math.max( + document.documentElement.scrollHeight, + document.body.scrollHeight + ) - window.innerHeight; + + const isAtSavedScroll = () => Math.abs(window.scrollY - scrollY) <= 2; + const isTallEnough = () => getMaxScrollY() >= scrollY; + + if (isTallEnough() && isAtSavedScroll()) { + return; + } + + const startedAt = Date.now(); + const maxRestoreMs = 2000; + let frameId: number | undefined; + let cancelled = false; + + const restoreScroll = () => { + if (cancelled) { + return; + } + + if (isTallEnough()) { + window.scrollTo(0, scrollY); + } + + if ( + (!isTallEnough() || !isAtSavedScroll()) && + Date.now() - startedAt < maxRestoreMs + ) { + frameId = requestAnimationFrame(restoreScroll); + } + }; + + frameId = requestAnimationFrame(restoreScroll); + + return () => { + cancelled = true; + if (frameId !== undefined) { + cancelAnimationFrame(frameId); + } + }; + }, [listKey, persistedState.scrollY]); + + const startScrollRestore = useCallback(() => { + activeRestoreCleanup.current?.(); + activeRestoreCleanup.current = restoreSavedScroll(); + }, [restoreSavedScroll]); + + useEffect(() => { + startScrollRestore(); + + return () => { + activeRestoreCleanup.current?.(); + activeRestoreCleanup.current = undefined; + }; + }, [startScrollRestore]); + + useEffect(() => { + let frameId: number | null = null; + const saveScrollToStorage = () => { + if (frameId !== null) { + return; + } + + frameId = requestAnimationFrame(() => { + frameId = null; + writeStoredScrollY(listKey, window.scrollY); + }); + }; + + window.addEventListener("scroll", saveScrollToStorage, { passive: true }); + const restoreAfterRouteChange = (url: string) => { + const path = url.split("?")[0].split("#")[0]; + + if (path === routePath) { + startScrollRestore(); + } + }; + + Router.events.on("routeChangeStart", saveCurrentScroll); + Router.events.on("routeChangeComplete", restoreAfterRouteChange); + + return () => { + if (frameId !== null) { + cancelAnimationFrame(frameId); + } + window.removeEventListener("scroll", saveScrollToStorage); + Router.events.off("routeChangeStart", saveCurrentScroll); + Router.events.off("routeChangeComplete", restoreAfterRouteChange); + }; + }, [listKey, routePath, saveCurrentScroll, startScrollRestore]); + + const handleTableStateChange = useCallback( + ({ pageIndex, sortBy }: PersistedTableState) => { + setPersistedState({ + pageIndex, + sortBy: sortBy as SortingRule[], + }); + }, + [setPersistedState] + ); + + return { + handleTableStateChange, + persistedState, + saveCurrentScroll, + }; +}; diff --git a/pages/gateways.tsx b/pages/gateways.tsx index fefa2b0d..4eeaced4 100644 --- a/pages/gateways.tsx +++ b/pages/gateways.tsx @@ -46,7 +46,12 @@ const GatewaysPage = ({ gateways }: PageProps) => { the last 12 months. - + diff --git a/pages/index.tsx b/pages/index.tsx index 75628cc6..0d244974 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -492,6 +492,7 @@ const Home = ({ {showOrchList ? ( @@ -579,7 +580,12 @@ const Home = ({ ) : ( - + )} @@ -659,7 +665,9 @@ const Home = ({ EventsQueryResult["data"] >["transactions"][number]["events"] } + listKey="home-transactions" pageSize={10} + routePath="/" /> diff --git a/pages/orchestrators.tsx b/pages/orchestrators.tsx index ab291282..7e76aded 100644 --- a/pages/orchestrators.tsx +++ b/pages/orchestrators.tsx @@ -123,6 +123,7 @@ const OrchestratorsPage = ({ {showOrchList ? ( diff --git a/pages/transactions.tsx b/pages/transactions.tsx index d0ededf9..6f81a0e2 100644 --- a/pages/transactions.tsx +++ b/pages/transactions.tsx @@ -72,7 +72,9 @@ const TransactionsPage = ({ hadError, events }: PageProps) => { EventsQueryResult["data"] >["transactions"][number]["events"] } + listKey="transactions" pageSize={TRANSACTIONS_PER_PAGE} + routePath="/transactions" /> )}