From 65f71fe9954c2fc33fea38cc0a0ae99ca7597e87 Mon Sep 17 00:00:00 2001 From: Hermes Date: Thu, 9 Apr 2026 21:04:55 -0500 Subject: [PATCH] Pull-to-refresh on Pulse, usePullToRefresh hook --- app/page.tsx | 7 +++++++ lib/usePullToRefresh.ts | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 lib/usePullToRefresh.ts diff --git a/app/page.tsx b/app/page.tsx index b7d81a4..d419706 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,11 +2,13 @@ import Link from "next/link"; import { Puzzle, Timer, Sparkles, Settings } from "lucide-react"; +import { useQueryClient } from "@tanstack/react-query"; import { StatusCard } from "@/components/StatusCard"; import { StatsCard } from "@/components/StatsCard"; import { SessionCard } from "@/components/SessionCard"; import { MemorySnapshotCard } from "@/components/MemorySnapshotCard"; import { useSessions } from "@/lib/hooks"; +import { usePullToRefresh } from "@/lib/usePullToRefresh"; const QUICK_LINKS = [ { href: "/skills", label: "Skills", icon: Puzzle, color: "#a78bfa" }, @@ -16,8 +18,13 @@ const QUICK_LINKS = [ ] as const; export default function PulsePage() { + const qc = useQueryClient(); const { data: sessionsData, isLoading } = useSessions(8); + usePullToRefresh(() => { + qc.invalidateQueries(); + }); + return (

diff --git a/lib/usePullToRefresh.ts b/lib/usePullToRefresh.ts new file mode 100644 index 0000000..a34103a --- /dev/null +++ b/lib/usePullToRefresh.ts @@ -0,0 +1,36 @@ +"use client"; + +import { useRef, useEffect, useCallback } from "react"; + +export function usePullToRefresh(onRefresh: () => Promise | void) { + const startY = useRef(0); + const pulling = useRef(false); + + const handleTouchStart = useCallback((e: TouchEvent) => { + if (window.scrollY === 0) { + startY.current = e.touches[0].clientY; + pulling.current = true; + } + }, []); + + const handleTouchEnd = useCallback( + (e: TouchEvent) => { + if (!pulling.current) return; + pulling.current = false; + const diff = e.changedTouches[0].clientY - startY.current; + if (diff > 80) { + onRefresh(); + } + }, + [onRefresh] + ); + + useEffect(() => { + document.addEventListener("touchstart", handleTouchStart, { passive: true }); + document.addEventListener("touchend", handleTouchEnd, { passive: true }); + return () => { + document.removeEventListener("touchstart", handleTouchStart); + document.removeEventListener("touchend", handleTouchEnd); + }; + }, [handleTouchStart, handleTouchEnd]); +}