feat: add scroll-to-top button and improve pagination handling

- Implemented a scroll-to-top button in the viewer UI for better navigation.
- Added styles for the scroll-to-top button in viewer.html and viewer-template.html.
- Created a new ScrollToTop component to manage visibility and scrolling behavior.
- Updated Feed component to include the ScrollToTop component.
- Enhanced pagination logic in usePagination hook to prevent stale closures and improve performance.
- Modified SDKAgent to include additional observation fields for better data handling.
This commit is contained in:
Alex Newman
2025-11-07 17:57:54 -05:00
parent d6f1237283
commit 30a42036aa
9 changed files with 183 additions and 17 deletions
+18 -6
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { Observation, Summary, UserPrompt } from '../types';
import { UI } from '../constants/ui';
import { API_ENDPOINTS } from '../constants/api';
@@ -21,6 +21,18 @@ function usePaginationFor(endpoint: string, dataType: DataType, currentFilter: s
});
const [offset, setOffset] = useState(0);
// Use refs to avoid stale closures and prevent infinite loops
const stateRef = useRef(state);
const offsetRef = useRef(offset);
useEffect(() => {
stateRef.current = state;
}, [state]);
useEffect(() => {
offsetRef.current = offset;
}, [offset]);
// Reset pagination when filter changes
useEffect(() => {
setOffset(0);
@@ -34,17 +46,17 @@ function usePaginationFor(endpoint: string, dataType: DataType, currentFilter: s
* Load more items from the API
*/
const loadMore = useCallback(async (): Promise<DataItem[]> => {
// Prevent concurrent requests using state
if (state.isLoading || !state.hasMore) {
// Prevent concurrent requests using ref (always current)
if (stateRef.current.isLoading || !stateRef.current.hasMore) {
return [];
}
setState(prev => ({ ...prev, isLoading: true }));
try {
// Build query params
// Build query params using ref (always current)
const params = new URLSearchParams({
offset: offset.toString(),
offset: offsetRef.current.toString(),
limit: UI.PAGINATION_PAGE_SIZE.toString()
});
@@ -74,7 +86,7 @@ function usePaginationFor(endpoint: string, dataType: DataType, currentFilter: s
setState(prev => ({ ...prev, isLoading: false }));
return [];
}
}, [offset, state.hasMore, state.isLoading, currentFilter, endpoint, dataType]);
}, [currentFilter, endpoint, dataType]); // Only stable values - no state/offset deps
return {
...state,