From 42f67464bcc09652531c673ce39399ed8745c6e2 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Thu, 19 Jun 2025 12:37:47 -0700 Subject: [PATCH] feat: Add loading state to all threads page (#260) * feat: Implement a loading and empty state for recent & running threads * cr * feat: Add loading state to all threads page --- .../src/app/(v2)/chat/[thread_id]/page.tsx | 5 ++-- apps/web/src/app/(v2)/chat/page.tsx | 3 +-- apps/web/src/app/(v2)/chat/threads/page.tsx | 24 +++++++++++++++---- apps/web/src/hooks/useThreads.tsx | 4 ++-- 4 files changed, 24 insertions(+), 12 deletions(-) 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 1751df3b..90b6fe3e 100644 --- a/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx +++ b/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx @@ -33,7 +33,7 @@ export default function ThreadPage({ process.env.NEXT_PUBLIC_MANAGER_ASSISTANT_ID, ); // Find the thread by ID - const thread = threads?.find((t) => t.thread_id === thread_id); + const thread = threads.find((t) => t.thread_id === thread_id); const handleBackToHome = () => { router.push("/chat"); @@ -44,8 +44,7 @@ export default function ThreadPage({ } // Convert all threads to display format - const displayThreads: ThreadDisplayInfo[] = - threads?.map(threadToDisplayInfo) ?? []; + const displayThreads: ThreadDisplayInfo[] = threads.map(threadToDisplayInfo); const currentDisplayThread = threadToDisplayInfo(thread); return ( diff --git a/apps/web/src/app/(v2)/chat/page.tsx b/apps/web/src/app/(v2)/chat/page.tsx index fe08a95b..6bb06c6e 100644 --- a/apps/web/src/app/(v2)/chat/page.tsx +++ b/apps/web/src/app/(v2)/chat/page.tsx @@ -14,8 +14,7 @@ export default function ChatPage() { ); // Convert Thread objects to ThreadDisplayInfo for UI - const displayThreads: ThreadDisplayInfo[] = - threads?.map(threadToDisplayInfo) ?? []; + const displayThreads: ThreadDisplayInfo[] = threads.map(threadToDisplayInfo); return (
diff --git a/apps/web/src/app/(v2)/chat/threads/page.tsx b/apps/web/src/app/(v2)/chat/threads/page.tsx index fefbb930..38bfd127 100644 --- a/apps/web/src/app/(v2)/chat/threads/page.tsx +++ b/apps/web/src/app/(v2)/chat/threads/page.tsx @@ -10,22 +10,21 @@ import { useRouter } from "next/navigation"; import { ThreadDisplayInfo, threadToDisplayInfo } from "@/components/v2/types"; import { useThreads } from "@/hooks/useThreads"; import { GraphState } from "@open-swe/shared/open-swe/types"; -import { ThreadCard } from "@/components/v2/thread-card"; +import { ThreadCard, ThreadCardLoading } from "@/components/v2/thread-card"; import { ThemeToggle } from "@/components/theme-toggle"; type FilterStatus = "all" | "running" | "completed" | "failed" | "pending"; export default function AllThreadsPage() { const router = useRouter(); - const { threads } = useThreads( + const { threads, threadsLoading } = useThreads( process.env.NEXT_PUBLIC_MANAGER_ASSISTANT_ID, ); const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); // Convert Thread objects to ThreadDisplayInfo for UI - const displayThreads: ThreadDisplayInfo[] = - threads?.map(threadToDisplayInfo) ?? []; + const displayThreads: ThreadDisplayInfo[] = threads.map(threadToDisplayInfo); // Filter and search threads const filteredThreads = displayThreads.filter((thread) => { @@ -178,7 +177,7 @@ export default function AllThreadsPage() {
)} - {filteredThreads.length === 0 && ( + {filteredThreads.length === 0 && !threadsLoading && (
No threads found
@@ -188,6 +187,21 @@ export default function AllThreadsPage() {
)} + + {threadsLoading && threads.length === 0 && ( +
+
+

+ Loading threads... +

+
+
+ {Array.from({ length: 9 }).map((_, index) => ( + + ))} +
+
+ )} diff --git a/apps/web/src/hooks/useThreads.tsx b/apps/web/src/hooks/useThreads.tsx index cc11ef5d..035c0e95 100644 --- a/apps/web/src/hooks/useThreads.tsx +++ b/apps/web/src/hooks/useThreads.tsx @@ -6,7 +6,7 @@ export function useThreads>( assistantId?: string, ) { const apiUrl: string | undefined = process.env.NEXT_PUBLIC_API_URL ?? ""; - const [threads, setThreads] = useState[] | null>(null); + const [threads, setThreads] = useState[]>([]); const [threadsLoading, setThreadsLoading] = useState(false); const getThread = useCallback( @@ -50,7 +50,7 @@ export function useThreads>( useEffect(() => { getThreads().then((threads) => { - setThreads(threads); + setThreads(threads ?? []); }); }, [getThreads]);