"use client"; import { useState, useRef, useCallback, ReactNode, TouchEvent } from "react"; import { GradientSpinner } from "./GradientSpinner"; import { RefreshCw } from "lucide-react"; interface PullToRefreshProps { children: ReactNode; threshold?: number; } export function PullToRefresh({ children, threshold = 80, }: PullToRefreshProps) { const [pullDistance, setPullDistance] = useState(0); const [isRefreshing, setIsRefreshing] = useState(false); const startY = useRef(0); const isPulling = useRef(false); const containerRef = useRef(null); const handleTouchStart = useCallback((e: TouchEvent) => { // Only allow pull-to-refresh when scrolled to the top const container = containerRef.current; if (!container) return; // Check if the main element inside is scrolled to top const mainElement = container.querySelector("main"); if (mainElement && mainElement.scrollTop === 0) { startY.current = e.touches[0].clientY; isPulling.current = true; } }, []); const handleTouchMove = useCallback( (e: TouchEvent) => { if (!isPulling.current || isRefreshing) return; const currentY = e.touches[0].clientY; const distance = currentY - startY.current; // Only track downward pulls if (distance > 0) { // Apply resistance factor for smoother feel const resistance = 0.5; const adjustedDistance = distance * resistance; setPullDistance(adjustedDistance); // Prevent default scroll behavior when pulling if (adjustedDistance > 10) { e.preventDefault(); } } }, [isRefreshing] ); const handleTouchEnd = useCallback(() => { if (!isPulling.current) return; isPulling.current = false; // Check if we've pulled past the threshold if (pullDistance >= threshold) { setIsRefreshing(true); setPullDistance(threshold); // Lock at threshold during refresh // Trigger full page reload after a brief delay for visual feedback setTimeout(() => { window.location.reload(); }, 300); } else { // Reset if not past threshold setPullDistance(0); } }, [pullDistance, threshold]); // Calculate visual properties based on pull progress const pullProgress = Math.min(pullDistance / threshold, 1); const showIndicator = pullDistance > 0; const shouldRelease = pullDistance >= threshold; return (
{/* Pull-to-refresh indicator */} {showIndicator && (
{isRefreshing ? ( ) : ( )}

{isRefreshing ? "Refreshing..." : shouldRelease ? "Release to refresh" : "Pull to refresh"}

)} {children}
); }