/** * useMemories Hook * Fetch and manage a list of memories with auto-refresh */ import { useState, useEffect, useCallback, useRef } from 'react'; import { useMemoryStackClient } from './useMemoryStack'; import type { Memory, ListMemoriesRequest, UseMemoriesOptions, UseMemoriesResult } from '../types'; /** * Hook to fetch and manage memories list * * @example * ```tsx * function MemoriesList() { * const { memories, isLoading, error, refetch, hasMore, loadMore } = useMemories({ * userId: 'user_123', * limit: 20, * }); * * if (isLoading && memories.length === 0) { * return ; * } * * if (error) { * return Error: {error.message}; * } * * return ( * } * onEndReached={() => hasMore && loadMore()} * refreshing={isLoading} * onRefresh={refetch} * /> * ); * } * ``` */ export function useMemories(options: UseMemoriesOptions = {}): UseMemoriesResult { const client = useMemoryStackClient(); const [memories, setMemories] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [hasMore, setHasMore] = useState(true); const cursorRef = useRef(null); const isMountedRef = useRef(true); const { userId, limit = 20, autoRefresh = false, refreshInterval = 30000, } = options; // Fetch memories const fetchMemories = useCallback(async (isRefresh: boolean = false) => { if (!client) return; try { setIsLoading(true); setError(null); const request: ListMemoriesRequest = { limit, ...(userId && { user_id: userId }), ...(isRefresh ? {} : { cursor: cursorRef.current ?? undefined }), }; const response = await client.listMemories(request); if (!isMountedRef.current) return; if (isRefresh) { setMemories(response.results); } else { setMemories(prev => [...prev, ...response.results]); } cursorRef.current = response.next_cursor; setHasMore(response.next_cursor !== null); } catch (err) { if (!isMountedRef.current) return; setError(err instanceof Error ? err : new Error('Failed to fetch memories')); } finally { if (isMountedRef.current) { setIsLoading(false); } } }, [client, userId, limit]); // Initial fetch useEffect(() => { isMountedRef.current = true; cursorRef.current = null; setMemories([]); fetchMemories(true); return () => { isMountedRef.current = false; }; }, [userId, limit]); // Auto-refresh useEffect(() => { if (!autoRefresh || refreshInterval <= 0) return; const interval = setInterval(() => { cursorRef.current = null; fetchMemories(true); }, refreshInterval); return () => clearInterval(interval); }, [autoRefresh, refreshInterval, fetchMemories]); // Refetch from beginning const refetch = useCallback(async () => { cursorRef.current = null; setMemories([]); await fetchMemories(true); }, [fetchMemories]); // Load more (pagination) const loadMore = useCallback(async () => { if (!hasMore || isLoading) return; await fetchMemories(false); }, [fetchMemories, hasMore, isLoading]); return { memories, isLoading, error, refetch, hasMore, loadMore, }; } export default useMemories;