Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions frontend/components/swap-transfer-tracker/swaps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ const TABLE_GRID = 'grid grid-cols-6 gap-6 px-4'
const ROW_HEIGHT = 45

interface SwapRowData {
dataRef: React.RefObject<SwapData[]>
data: SwapData[]
gridClass: string
}

// Cell component defined outside to maintain stable reference
function SwapCell({
index,
style,
dataRef,
data,
gridClass,
}: RowComponentProps<SwapRowData>): ReactElement {
const swap = dataRef.current?.[index]
const swap = data[index]

return (
<div style={style}>
Expand Down Expand Up @@ -81,7 +81,6 @@ export function Swaps({ data, isLoading, isFollowingData }: SwapsProps) {
</div>
) : (
<List
key={displayedData.length > 0 ? displayedData[0]?.id : 'empty'}
listRef={listRef}
rowComponent={SwapCell}
rowCount={displayedData.length}
Expand Down
7 changes: 3 additions & 4 deletions frontend/components/swap-transfer-tracker/transfers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ const TABLE_GRID = 'grid grid-cols-6 gap-6 px-4'
const ROW_HEIGHT = 45

interface TransferRowData {
dataRef: React.RefObject<TransferData[]>
data: TransferData[]
gridClass: string
}

// Cell component defined outside to maintain stable reference
function TransferCell({
index,
style,
dataRef,
data,
gridClass,
}: RowComponentProps<TransferRowData>): ReactElement {
const transfer = dataRef.current?.[index]
const transfer = data[index]

return (
<div style={style}>
Expand Down Expand Up @@ -94,7 +94,6 @@ export function Transfers({
</div>
) : (
<List
key={displayedData.length > 0 ? displayedData[0]?.id : 'empty'}
listRef={listRef}
rowComponent={TransferCell}
rowCount={displayedData.length}
Expand Down
2 changes: 1 addition & 1 deletion frontend/contexts/events-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface EventsProviderProps {
const EventsContext = createContext<EventsContextValue | null>(null)

const RECONNECT_DELAY = 3000
const MAX_EVENTS_STORED = 10000
const MAX_EVENTS_STORED = 25000

/**
* A context provider for the events context.
Expand Down
2 changes: 1 addition & 1 deletion frontend/hooks/use-execution-event-blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const BLOCK_EVENTS = [
'BlockReject',
] as const

const MAX_BLOCKS = 5000
const MAX_BLOCKS = 15000

interface UseExecutionEventBlocksReturn {
blocks: Block[]
Expand Down
60 changes: 26 additions & 34 deletions frontend/hooks/use-virtualized-list.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useEffect, useRef, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import type { ListImperativeAPI } from 'react-window'

interface UseVirtualizedListOptions<T> {
Expand All @@ -9,14 +9,18 @@ interface UseVirtualizedListOptions<T> {
gridClass: string
}

interface VirtualizedRowProps<T> {
data: T[]
gridClass: string
}

interface UseVirtualizedListReturn<T> {
scrollContainerRef: React.RefObject<HTMLDivElement | null>
containerRef: React.RefObject<HTMLDivElement | null>
listRef: React.RefObject<ListImperativeAPI | null>
containerHeight: number
displayedData: T[]
dataRef: React.RefObject<T[]>
rowProps: { dataRef: React.RefObject<T[]>; gridClass: string }
rowProps: VirtualizedRowProps<T>
}

export function useVirtualizedList<T>({
Expand All @@ -31,41 +35,27 @@ export function useVirtualizedList<T>({

// Freeze data when paused - this prevents re-renders while paused
const [displayedData, setDisplayedData] = useState<T[]>(data)
const [dataVersion, setDataVersion] = useState(0)

// Update displayed data only when following
useEffect(() => {
if (isFollowing) {
setDisplayedData(data)
setDataVersion((prev) => prev + 1)
}
}, [data, isFollowing])
if (!isFollowing) return

// Store displayed data in ref for stable rowProps
const dataRef = useRef<T[]>(displayedData)
dataRef.current = displayedData
setDisplayedData((prev) => (Object.is(prev, data) ? prev : data))
}, [data, isFollowing])

// Auto-scroll to top when following and new data arrives
useEffect(() => {
if (
isFollowing &&
displayedData.length > 0 &&
listRef.current &&
dataVersion > 0
) {
// Use requestAnimationFrame to ensure the List has updated before scrolling
requestAnimationFrame(() => {
if (listRef.current) {
// Force scroll to top to show newest data
listRef.current.scrollToRow({
index: 0,
align: 'start',
behavior: 'auto', // Use 'auto' for immediate scroll when following
})
}
if (!isFollowing || displayedData.length === 0 || !listRef.current) return

// Use requestAnimationFrame to ensure the List has updated before scrolling.
requestAnimationFrame(() => {
listRef.current?.scrollToRow({
index: 0,
align: 'start',
behavior: 'smooth',
})
}
}, [dataVersion, isFollowing])
})
}, [displayedData, isFollowing])

// Reset horizontal scroll when resuming (isFollowing becomes true)
useEffect(() => {
Expand All @@ -74,9 +64,12 @@ export function useVirtualizedList<T>({
}
}, [isFollowing])

// Stable rowProps - dataRef never changes reference, only its .current
const rowPropsRef = useRef({ dataRef, gridClass })
const rowProps = rowPropsRef.current
// Memoize rowProps so react-window re-renders rows when data changes,
// even if the list length stays constant (e.g. when capped at max items).
const rowProps = useMemo(
() => ({ data: displayedData, gridClass }),
[displayedData, gridClass],
)

// Update container height on resize
useEffect(() => {
Expand Down Expand Up @@ -112,7 +105,6 @@ export function useVirtualizedList<T>({
listRef,
containerHeight,
displayedData,
dataRef,
rowProps,
}
}