UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

198 lines (197 loc) 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCmsContentList = useCmsContentList; exports.useFeaturedContent = useFeaturedContent; exports.useRecentContent = useRecentContent; const react_1 = require("react"); const utils_1 = require("../utils"); const listCache = new Map(); /** * React hook for fetching lists of CMS content with pagination */ function useCmsContentList(options) { const { contentType, enabled = true, staleTime = 5 * 60 * 1000, // 5 minutes onSuccess, onError, limit = 10, offset = 0, ...fetchOptions } = options; const [content, setContent] = (0, react_1.useState)([]); const [isLoading, setIsLoading] = (0, react_1.useState)(enabled); const [isError, setIsError] = (0, react_1.useState)(false); const [error, setError] = (0, react_1.useState)(null); const [isFetching, setIsFetching] = (0, react_1.useState)(false); const [hasMore, setHasMore] = (0, react_1.useState)(true); const [totalCount, setTotalCount] = (0, react_1.useState)(); const [currentOffset, setCurrentOffset] = (0, react_1.useState)(offset); // Generate cache key const cacheKey = (0, react_1.useMemo)(() => { return `list:${contentType}:${JSON.stringify({ ...fetchOptions, limit, offset: 0, // Always cache from beginning })}`; }, [contentType, fetchOptions, limit]); // Check if cached data is stale const isStale = (0, react_1.useMemo)(() => { const cached = listCache.get(cacheKey); if (!cached) return true; return Date.now() - cached.timestamp > cached.staleTime; }, [cacheKey, content]); // Transform Sanity documents to CmsContent format const transformDocuments = (0, react_1.useCallback)((documents) => { return documents.map(doc => ({ _id: doc._id, _type: doc._type, slug: doc.slug?.current || '', title: doc.title || 'Untitled', content: doc.content || doc.body || null, metadata: doc.seo, publishedAt: doc.publishedAt, author: doc.author, })); }, []); // Fetch content function const fetchContent = (0, react_1.useCallback)(async (isLoadMore = false) => { if (!enabled) return; const effectiveOffset = isLoadMore ? currentOffset : 0; // Check cache for initial load if (!isLoadMore && !isStale && !isFetching) { const cached = listCache.get(cacheKey); if (cached) { setContent(cached.data); setHasMore(cached.hasMore); setTotalCount(cached.totalCount); setIsLoading(false); setIsError(false); setError(null); return; } } try { setIsFetching(true); setIsError(false); setError(null); if (!isLoadMore) { setIsLoading(true); } const result = await (0, utils_1.getDocumentsByType)(contentType, { ...fetchOptions, limit, offset: effectiveOffset, includeTotal: true, }); const transformedContent = transformDocuments(result.documents); const newHasMore = result.documents.length === limit; const newTotalCount = result.total; if (isLoadMore) { // Append to existing content setContent(prev => [...prev, ...transformedContent]); setCurrentOffset(prev => prev + limit); } else { // Replace content setContent(transformedContent); setCurrentOffset(limit); // Cache the initial result listCache.set(cacheKey, { data: transformedContent, timestamp: Date.now(), staleTime, hasMore: newHasMore, totalCount: newTotalCount ?? 0, }); } setHasMore(newHasMore); setTotalCount(newTotalCount); setIsLoading(false); onSuccess?.(isLoadMore ? content.concat(transformedContent) : transformedContent); } catch (err) { const errorObj = err instanceof Error ? err : new Error(String(err)); setError(errorObj); setIsError(true); setIsLoading(false); onError?.(errorObj); } finally { setIsFetching(false); } }, [ enabled, contentType, fetchOptions, limit, currentOffset, cacheKey, isStale, isFetching, staleTime, transformDocuments, onSuccess, onError, content, ]); // Load more function const loadMore = (0, react_1.useCallback)(async () => { if (!hasMore || isFetching) return; await fetchContent(true); }, [hasMore, isFetching, fetchContent]); // Refetch function for manual refresh const refetch = (0, react_1.useCallback)(async () => { listCache.delete(cacheKey); // Clear cache setCurrentOffset(0); await fetchContent(false); }, [cacheKey, fetchContent]); // Reset function to clear state const reset = (0, react_1.useCallback)(() => { setContent([]); setIsLoading(enabled); setIsError(false); setError(null); setIsFetching(false); setHasMore(true); setTotalCount(undefined); setCurrentOffset(0); }, [enabled]); // Initial fetch effect (0, react_1.useEffect)(() => { if (enabled) { fetchContent(false); } }, [enabled, contentType, JSON.stringify(fetchOptions)]); return { content, isLoading, isError, error, isFetching, hasMore, totalCount: totalCount ?? 0, refetch, loadMore, reset, }; } /** * Hook for fetching featured/recent content */ function useFeaturedContent(contentType = 'post', limit = 5, options = {}) { return useCmsContentList({ ...options, contentType, limit, orderBy: 'publishedAt desc', additionalFilter: options.additionalFilter || 'featured == true', }); } /** * Hook for fetching recent content */ function useRecentContent(contentType = 'post', limit = 10, options = {}) { return useCmsContentList({ ...options, contentType, limit, orderBy: 'publishedAt desc', }); }