feat: improve mentioned document prefetching and add skeletons in new chat interface

This commit is contained in:
Anish Sarkar 2026-02-03 20:45:52 +05:30
parent f730df7c9d
commit c4cf773bb0
2 changed files with 86 additions and 54 deletions

View file

@ -9,7 +9,6 @@ import {
import { useQueryClient } from "@tanstack/react-query";
import { useAtomValue, useSetAtom } from "jotai";
import { useParams, useSearchParams } from "next/navigation";
import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { z } from "zod";
@ -39,7 +38,7 @@ import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast";
import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview";
import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage";
import { RecallMemoryToolUI, SaveMemoryToolUI } from "@/components/tool-ui/user-memory";
import { Spinner } from "@/components/ui/spinner";
import { Skeleton } from "@/components/ui/skeleton";
import { useChatSessionStateSync } from "@/hooks/use-chat-session-state";
import { useMessagesElectric } from "@/hooks/use-messages-electric";
// import { WriteTodosToolUI } from "@/components/tool-ui/write-todos";
@ -53,12 +52,10 @@ import {
} from "@/lib/chat/podcast-state";
import {
appendMessage,
type ChatVisibility,
createThread,
getRegenerateUrl,
getThreadFull,
getThreadMessages,
type MessageRecord,
type ThreadRecord,
} from "@/lib/chat/thread-persistence";
import {
@ -67,6 +64,7 @@ import {
trackChatMessageSent,
trackChatResponseReceived,
} from "@/lib/posthog/events";
import { documentsApiService } from "@/lib/apis/documents-api.service";
/**
* Extract thinking steps from message content
@ -137,7 +135,6 @@ interface ThinkingStepData {
}
export default function NewChatPage() {
const t = useTranslations("dashboard");
const params = useParams();
const queryClient = useQueryClient();
const [isInitializing, setIsInitializing] = useState(true);
@ -329,6 +326,33 @@ export default function NewChatPage() {
initializeThread();
}, [initializeThread]);
// Prefetch document titles for @ mention picker
// Runs when user lands on page so data is ready when they type @
useEffect(() => {
if (!searchSpaceId) return;
const prefetchParams = {
search_space_id: searchSpaceId,
page: 0,
page_size: 20,
};
queryClient.prefetchQuery({
queryKey: ["document-titles", prefetchParams],
queryFn: () => documentsApiService.searchDocumentTitles({ queryParams: prefetchParams }),
staleTime: 60 * 1000,
});
queryClient.prefetchQuery({
queryKey: ["surfsense-docs-mention", "", false],
queryFn: () =>
documentsApiService.getSurfsenseDocs({
queryParams: { page: 0, page_size: 20 },
}),
staleTime: 3 * 60 * 1000,
});
}, [searchSpaceId, queryClient]);
// Handle scroll to comment from URL query params (e.g., from inbox item click)
const searchParams = useSearchParams();
const targetCommentIdParam = searchParams.get("commentId");
@ -366,20 +390,7 @@ export default function NewChatPage() {
}
setIsRunning(false);
}, []);
// Handle visibility change from ChatShareButton
const handleVisibilityChange = useCallback(
(newVisibility: ChatVisibility) => {
setCurrentThread((prev) => (prev ? { ...prev, visibility: newVisibility } : null));
// Refetch all thread queries so sidebar reflects the change immediately
// Use predicate to match any query that starts with "threads"
queryClient.refetchQueries({
predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "threads",
});
},
[queryClient]
);
// Handle new message from user
const onNew = useCallback(
async (message: AppendMessage) => {
@ -1347,7 +1358,7 @@ export default function NewChatPage() {
// Handle reloading/refreshing the last AI response
const onReload = useCallback(
async (parentId: string | null) => {
async () => {
// parentId is the ID of the message to reload from (the user message)
// We call regenerate without a query to use the same query
await handleRegenerate(null);
@ -1372,9 +1383,39 @@ export default function NewChatPage() {
// Show loading state only when loading an existing thread
if (isInitializing) {
return (
<div className="flex h-[calc(100vh-64px)] flex-col items-center justify-center gap-4">
<Spinner size="lg" />
<div className="text-sm text-muted-foreground">{t("loading_chat")}</div>
<div className="flex h-[calc(100vh-64px)] flex-col bg-background px-4">
<div className="mx-auto w-full max-w-[44rem] flex flex-1 flex-col gap-6 py-8">
{/* User message */}
<div className="flex justify-end">
<Skeleton className="h-12 w-56 rounded-2xl" />
</div>
{/* Assistant message */}
<div className="flex flex-col gap-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-[85%]" />
<Skeleton className="h-4 w-[70%]" />
</div>
{/* User message */}
<div className="flex justify-end">
<Skeleton className="h-12 w-40 rounded-2xl" />
</div>
{/* Assistant message */}
<div className="flex flex-col gap-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-[90%]" />
<Skeleton className="h-4 w-[60%]" />
</div>
</div>
{/* Input bar */}
<div className="sticky bottom-0 pb-6 bg-background">
<div className="mx-auto w-full max-w-[44rem]">
<Skeleton className="h-24 w-full rounded-2xl" />
</div>
</div>
</div>
);
}