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 }; }