From b752b2326e87f65dfd967698fa1322d9e5fbd59f Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 18 Jun 2025 15:15:11 -0700 Subject: [PATCH] feat: Add loading state to threads page (#234) * feat: Add loading state to threads page * format * cr --- .../src/app/(v2)/chat/[thread_id]/loading.tsx | 5 + .../src/app/(v2)/chat/[thread_id]/page.tsx | 18 +-- apps/web/src/app/(v2)/chat/page.tsx | 2 +- .../src/components/v2/thread-view-loading.tsx | 150 ++++++++++++++++++ apps/web/src/hooks/useThreads.tsx | 6 +- 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/app/(v2)/chat/[thread_id]/loading.tsx create mode 100644 apps/web/src/components/v2/thread-view-loading.tsx diff --git a/apps/web/src/app/(v2)/chat/[thread_id]/loading.tsx b/apps/web/src/app/(v2)/chat/[thread_id]/loading.tsx new file mode 100644 index 00000000..128fd49a --- /dev/null +++ b/apps/web/src/app/(v2)/chat/[thread_id]/loading.tsx @@ -0,0 +1,5 @@ +import { ThreadViewLoading } from "@/components/v2/thread-view-loading"; + +export default function Loading() { + return ; +} diff --git a/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx b/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx index 21d36b3c..64cbfc9e 100644 --- a/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx +++ b/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx @@ -1,6 +1,7 @@ "use client"; import { ThreadView } from "@/components/v2/thread-view"; +import { ThreadViewLoading } from "@/components/v2/thread-view-loading"; import { ThreadDisplayInfo, threadToDisplayInfo } from "@/components/v2/types"; import { useThreads } from "@/hooks/useThreads"; import { useStream } from "@langchain/langgraph-sdk/react"; @@ -28,13 +29,16 @@ export default function ThreadPage({ reconnectOnMount: true, }); - const { threads } = useThreads(); - + const { threads, threadsLoading } = useThreads(); // Find the thread by ID const thread = threads?.find((t) => t.thread_id === thread_id); - // If thread not found, show 404 - if (!thread) { - return <>Loading...; + + const handleBackToHome = () => { + router.push("/chat"); + }; + + if (!thread || threadsLoading || !threadsLoading) { + return ; } // Convert all threads to display format @@ -46,10 +50,6 @@ export default function ThreadPage({ router.push(`/chat/${selectedThread.id}`); }; - const handleBackToHome = () => { - router.push("/chat"); - }; - return (
- Loading...
}> + diff --git a/apps/web/src/components/v2/thread-view-loading.tsx b/apps/web/src/components/v2/thread-view-loading.tsx new file mode 100644 index 00000000..23345ea3 --- /dev/null +++ b/apps/web/src/components/v2/thread-view-loading.tsx @@ -0,0 +1,150 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { ArrowLeft, Loader2 } from "lucide-react"; + +interface ThreadViewLoadingProps { + onBackToHome?: () => void; +} + +const isEven = (num: number) => num % 2 === 0; + +export function LoadingActionsSection() { + return ( + + +
+
+
+ + Loading actions... +
+
+
+ + {Array.from({ length: 8 }, (_, i) => ( +
+ {isEven(i) ? ( +
+
+
+
+ ) : ( +
+
+
+
+
+
+
+
+
+
+ )} +
+ ))} +
+
+
+
+ +
+
+
+
+
+
+ ); +} + +export function LoadingChatSection() { + return ( +
+
+
+ + Loading conversation... +
+
+ + {Array.from({ length: 3 }, (_, i) => ( +
+
+
+
+
+
+
+
+
+
+
+
+ {i === 1 && ( +
+ )} +
+
+
+ ))} +
+ ); +} + +export function ThreadViewLoading({ onBackToHome }: ThreadViewLoadingProps) { + return ( +
+
+
+ +
+
+
+ • +
+
+
+
+
+ +
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ ); +} diff --git a/apps/web/src/hooks/useThreads.tsx b/apps/web/src/hooks/useThreads.tsx index 1052e2fe..cc11ef5d 100644 --- a/apps/web/src/hooks/useThreads.tsx +++ b/apps/web/src/hooks/useThreads.tsx @@ -7,6 +7,7 @@ export function useThreads>( ) { const apiUrl: string | undefined = process.env.NEXT_PUBLIC_API_URL ?? ""; const [threads, setThreads] = useState[] | null>(null); + const [threadsLoading, setThreadsLoading] = useState(false); const getThread = useCallback( async (threadId: string): Promise | null> => { @@ -26,6 +27,7 @@ export function useThreads>( const getThreads = useCallback(async (): Promise[] | null> => { if (!apiUrl) return null; + setThreadsLoading(true); const client = createClient(apiUrl); try { @@ -41,6 +43,8 @@ export function useThreads>( } catch (error) { console.error("Failed to fetch threads:", error); return null; + } finally { + setThreadsLoading(false); } }, [apiUrl]); @@ -50,5 +54,5 @@ export function useThreads>( }); }, [getThreads]); - return { threads, setThreads, getThread, getThreads }; + return { threads, setThreads, getThread, getThreads, threadsLoading }; }