Skip to content

Commit 8a5c7e7

Browse files
Load repos in server component. Various other nit fixes and improvements
1 parent 9270f29 commit 8a5c7e7

File tree

16 files changed

+284
-237
lines changed

16 files changed

+284
-237
lines changed

packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@ import { useQuery } from '@tanstack/react-query';
66
import { Loader2 } from 'lucide-react';
77
import { useEffect, useState } from 'react';
88
import { useRouter, useSearchParams } from 'next/navigation';
9-
import { LanguageModelInfo, SBChatMessage, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from '@/features/chat/types';
9+
import { SBChatMessage, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from '@/features/chat/types';
1010
import { loadChatMessages } from '@/features/chat/actions';
1111
import { unwrapServiceError } from '@/lib/utils';
1212
import { ResizablePanel } from '@/components/ui/resizable';
1313
import { useChatId } from '../../useChatId';
1414
import { CreateUIMessage } from 'ai';
15+
import { ChatBoxToolbarProps } from '@/features/chat/components/chatBox/chatBoxToolbar';
1516

1617
interface ChatThreadPanelProps {
17-
languageModels: LanguageModelInfo[];
18+
chatBoxToolbarProps: ChatBoxToolbarProps;
1819
order: number;
1920
}
2021

2122
export const ChatThreadPanel = ({
22-
languageModels,
23+
chatBoxToolbarProps,
2324
order,
2425
}: ChatThreadPanelProps) => {
2526
// @note: we are guaranteed to have a chatId because this component will only be
@@ -82,7 +83,7 @@ export const ChatThreadPanel = ({
8283
id={chatId}
8384
initialMessages={messages}
8485
inputMessage={inputMessage}
85-
languageModels={languageModels}
86+
chatBoxToolbarProps={chatBoxToolbarProps}
8687
/>
8788
)}
8889
</div>

packages/web/src/app/[domain]/chat/[id]/page.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
1-
import { ChatThreadPanel } from './components/chatThreadPanel';
1+
import { getRepos } from '@/actions';
22
import { getConfiguredLanguageModelsInfo } from '@/features/chat/actions';
3+
import { ServiceErrorException } from '@/lib/serviceError';
4+
import { isServiceError } from '@/lib/utils';
5+
import { ChatThreadPanel } from './components/chatThreadPanel';
36

4-
export default async function Page() {
7+
interface PageProps {
8+
params: {
9+
domain: string;
10+
};
11+
}
12+
13+
export default async function Page({ params }: PageProps) {
514
const languageModels = await getConfiguredLanguageModelsInfo();
15+
const repos = await getRepos(params.domain);
16+
17+
if (isServiceError(repos)) {
18+
throw new ServiceErrorException(repos);
19+
}
620

721
return (
822
<ChatThreadPanel
9-
languageModels={languageModels}
23+
chatBoxToolbarProps={{
24+
languageModels,
25+
repos,
26+
}}
1027
order={2}
1128
/>
1229
)

packages/web/src/app/[domain]/chat/components/chatName.tsx

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
'use client';
22

3-
import { getChatInfo, updateChatName } from "@/features/chat/actions";
4-
import { useChatId } from "../useChatId";
5-
import { useQuery } from "@tanstack/react-query";
6-
import { useDomain } from "@/hooks/useDomain";
7-
import { isServiceError, unwrapServiceError } from "@/lib/utils";
3+
import { useToast } from "@/components/hooks/use-toast";
84
import { Badge } from "@/components/ui/badge";
9-
import { LockIcon } from "lucide-react";
10-
import { RenameChatDialog } from "./renameChatDialog";
11-
import { useCallback, useMemo, useState } from "react";
125
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
13-
import { useToast } from "@/components/hooks/use-toast";
14-
import { useSession } from "next-auth/react";
6+
import { updateChatName } from "@/features/chat/actions";
7+
import { useDomain } from "@/hooks/useDomain";
8+
import { isServiceError } from "@/lib/utils";
159
import { GlobeIcon } from "@radix-ui/react-icons";
10+
import { LockIcon } from "lucide-react";
11+
import { useSession } from "next-auth/react";
12+
import { useRouter } from "next/navigation";
13+
import { useCallback, useMemo, useState } from "react";
14+
import { useChatId } from "../useChatId";
15+
import { RenameChatDialog } from "./renameChatDialog";
16+
17+
interface ChatNameProps {
18+
chatHistory: {
19+
id: string;
20+
createdAt: Date;
21+
name: string | null;
22+
}[];
23+
}
1624

17-
export const ChatName = () => {
25+
export const ChatName = ({ chatHistory }: ChatNameProps) => {
1826
const chatId = useChatId();
19-
const domain = useDomain();
2027
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
2128
const { toast } = useToast();
29+
const domain = useDomain();
30+
const router = useRouter();
2231

23-
const { data: chatInfo, isPending, isError, refetch } = useQuery({
24-
queryKey: ['chat', 'info', chatId, domain],
25-
queryFn: () => unwrapServiceError(getChatInfo({ chatId: chatId! }, domain)),
26-
enabled: !!chatId,
27-
});
32+
const name = useMemo(() => {
33+
return chatHistory.find((chat) => chat.id === chatId)?.name ?? null;
34+
}, [chatHistory, chatId]);
2835

2936
const onRenameChat = useCallback(async (name: string) => {
3037
if (!chatId) {
@@ -44,9 +51,9 @@ export const ChatName = () => {
4451
toast({
4552
description: `✅ Chat renamed successfully`
4653
});
47-
refetch();
54+
router.refresh();
4855
}
49-
}, [chatId, domain]);
56+
}, [chatId, domain, toast]);
5057

5158
const { status: authStatus } = useSession();
5259

@@ -58,7 +65,7 @@ export const ChatName = () => {
5865
return authStatus === 'authenticated' ? 'private' : 'public';
5966
}, [authStatus]);
6067

61-
if (isPending || isError) {
68+
if (!chatId) {
6269
return null;
6370
}
6471

@@ -73,7 +80,7 @@ export const ChatName = () => {
7380
setIsRenameDialogOpen(true);
7481
}}
7582
>
76-
{chatInfo.name ?? 'Untitled chat'}
83+
{name ?? 'Untitled chat'}
7784
</p>
7885
</TooltipTrigger>
7986
<TooltipContent>
@@ -104,7 +111,7 @@ export const ChatName = () => {
104111
isOpen={isRenameDialogOpen}
105112
onOpenChange={setIsRenameDialogOpen}
106113
onRename={onRenameChat}
107-
currentName={chatInfo.name ?? ""}
114+
currentName={name ?? ""}
108115
/>
109116
</>
110117
)

packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import { ScrollArea } from "@/components/ui/scroll-area";
99
import { Separator } from "@/components/ui/separator";
1010
import { Skeleton } from "@/components/ui/skeleton";
1111
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
12-
import { getRecentChats, updateChatName, deleteChat } from "@/features/chat/actions";
12+
import { deleteChat, updateChatName } from "@/features/chat/actions";
1313
import { useDomain } from "@/hooks/useDomain";
14-
import { cn, isServiceError, unwrapServiceError } from "@/lib/utils";
15-
import { useQuery } from "@tanstack/react-query";
14+
import { cn, isServiceError } from "@/lib/utils";
1615
import { CirclePlusIcon, EllipsisIcon, PencilIcon, TrashIcon } from "lucide-react";
1716
import { useRouter } from "next/navigation";
1817
import { useCallback, useRef, useState } from "react";
@@ -29,10 +28,16 @@ import { useSession } from "next-auth/react";
2928

3029
interface ChatSidePanelProps {
3130
order: number;
31+
chatHistory: {
32+
id: string;
33+
name: string | null;
34+
createdAt: Date;
35+
}[];
3236
}
3337

3438
export const ChatSidePanel = ({
3539
order,
40+
chatHistory,
3641
}: ChatSidePanelProps) => {
3742
const domain = useDomain();
3843
const [isCollapsed, setIsCollapsed] = useState(false);
@@ -58,12 +63,6 @@ export const ChatSidePanel = ({
5863
description: "Toggle side panel",
5964
});
6065

61-
const { data: recentChats, isPending, isError, refetch: refetchRecentChats } = useQuery({
62-
queryKey: ['chat', 'recents', domain],
63-
queryFn: () => unwrapServiceError(getRecentChats(domain)),
64-
enabled: authStatus === 'authenticated',
65-
});
66-
6766
const onRenameChat = useCallback(async (name: string, chatId: string) => {
6867
if (!chatId) {
6968
return;
@@ -82,9 +81,9 @@ export const ChatSidePanel = ({
8281
toast({
8382
description: `✅ Chat renamed successfully`
8483
});
85-
refetchRecentChats();
84+
router.refresh();
8685
}
87-
}, [chatId, refetchRecentChats]);
86+
}, [chatId, router, toast, domain]);
8887

8988
const onDeleteChat = useCallback(async (chatIdToDelete: string) => {
9089
if (!chatIdToDelete) {
@@ -107,9 +106,9 @@ export const ChatSidePanel = ({
107106
router.push(`/${domain}/chat`);
108107
}
109108

110-
refetchRecentChats();
109+
router.refresh();
111110
}
112-
}, [chatId, refetchRecentChats, router, toast, domain]);
111+
}, [chatId, router, toast, domain]);
113112

114113
return (
115114
<>
@@ -154,15 +153,11 @@ export const ChatSidePanel = ({
154153
</Link> to access your chat history.
155154
</p>
156155
</div>
157-
) : isPending ? (
158-
<ChatHistorySkeleton />
159-
) : isError ? (
160-
<p>Error loading recent chats</p>
161-
) : recentChats.length === 0 ? (
156+
) : chatHistory.length === 0 ? (
162157
<div className="mx-auto w-full h-52 border border-dashed border-muted-foreground rounded-md flex items-center justify-center p-6">
163158
<p className="text-sm text-muted-foreground text-center">Recent chats will appear here.</p>
164159
</div>
165-
) : recentChats.map((chat) => (
160+
) : chatHistory.map((chat) => (
166161
<div
167162
key={chat.id}
168163
className={cn("group flex flex-row items-center justify-between hover:bg-muted rounded-md px-2 py-1.5 cursor-pointer",
@@ -254,7 +249,7 @@ export const ChatSidePanel = ({
254249
onRenameChat(name, chatIdToRename);
255250
}
256251
}}
257-
currentName={recentChats?.find((chat) => chat.id === chatIdToRename)?.name ?? ""}
252+
currentName={chatHistory?.find((chat) => chat.id === chatIdToRename)?.name ?? ""}
258253
/>
259254
<DeleteChatDialog
260255
isOpen={isDeleteDialogOpen}

packages/web/src/app/[domain]/chat/components/newChatPanel.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22

33
import { ResizablePanel } from "@/components/ui/resizable";
44
import { ChatBox } from "@/features/chat/components/chatBox";
5-
import { ChatBoxTools } from "@/features/chat/components/chatBox/chatBoxTools";
5+
import { ChatBoxToolbar, ChatBoxToolbarProps } from "@/features/chat/components/chatBox/chatBoxToolbar";
66
import { CustomSlateEditor } from "@/features/chat/customSlateEditor";
7-
import { LanguageModelInfo } from "@/features/chat/types";
87
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
98
import { useCallback } from "react";
109
import { Descendant } from "slate";
1110

1211
interface NewChatPanelProps {
13-
languageModels: LanguageModelInfo[];
12+
chatBoxToolbarProps: ChatBoxToolbarProps;
13+
order: number;
1414
}
1515

1616
export const NewChatPanel = ({
17-
languageModels,
17+
chatBoxToolbarProps,
18+
order,
1819
}: NewChatPanelProps) => {
1920
const { createNewChatThread, isLoading } = useCreateNewChatThread();
2021

@@ -25,7 +26,7 @@ export const NewChatPanel = ({
2526

2627
return (
2728
<ResizablePanel
28-
order={2}
29+
order={order}
2930
id="new-chat-panel"
3031
defaultSize={85}
3132
>
@@ -40,8 +41,8 @@ export const NewChatPanel = ({
4041
isRedirecting={isLoading}
4142
/>
4243
<div className="w-full flex flex-row items-center bg-accent rounded-b-md px-2">
43-
<ChatBoxTools
44-
languageModels={languageModels}
44+
<ChatBoxToolbar
45+
{...chatBoxToolbarProps}
4546
/>
4647
</div>
4748
</CustomSlateEditor>

packages/web/src/app/[domain]/chat/layout.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
'use client';
2-
31
import { AnimatedResizableHandle } from '@/components/ui/animatedResizableHandle';
42
import { ResizablePanelGroup } from '@/components/ui/resizable';
53
import { ChatSidePanel } from './components/chatSidePanel';
64
import { TopBar } from '../components/topBar';
75
import { ChatName } from './components/chatName';
86
import { NavigationGuardProvider } from 'next-navigation-guard';
9-
import { useDomain } from '@/hooks/useDomain';
7+
import { getRecentChats } from '@/features/chat/actions';
8+
import { ServiceErrorException } from '@/lib/serviceError';
9+
import { isServiceError } from '@/lib/utils';
1010

1111
interface LayoutProps {
1212
children: React.ReactNode;
13+
params: {
14+
domain: string;
15+
};
1316
}
1417

15-
export default function Layout({ children }: LayoutProps) {
16-
const domain = useDomain();
18+
export default async function Layout({ children, params: { domain } }: LayoutProps) {
19+
const chatHistory = await getRecentChats(domain);
20+
21+
if (isServiceError(chatHistory)) {
22+
throw new ServiceErrorException(chatHistory);
23+
}
1724

1825
return (
1926
// @note: we use a navigation guard here since we don't support resuming streams yet.
@@ -23,13 +30,20 @@ export default function Layout({ children }: LayoutProps) {
2330
<TopBar
2431
domain={domain}
2532
>
26-
<ChatName />
33+
{/*
34+
@note: since this layout is not scoped to the [id] route,
35+
we cannot get the chat id in this server component. Workaround
36+
here is to pass the chat history to the chat name component
37+
and let it use that to get the chat name.
38+
*/}
39+
<ChatName chatHistory={chatHistory} />
2740
</TopBar>
2841
<ResizablePanelGroup
2942
direction="horizontal"
3043
>
3144
<ChatSidePanel
3245
order={1}
46+
chatHistory={chatHistory}
3347
/>
3448
<AnimatedResizableHandle />
3549
{children}
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1-
import { NewChatPanel } from "./components/newChatPanel";
1+
import { getRepos } from "@/actions";
22
import { getConfiguredLanguageModelsInfo } from "@/features/chat/actions";
3+
import { ServiceErrorException } from "@/lib/serviceError";
4+
import { isServiceError } from "@/lib/utils";
5+
import { NewChatPanel } from "./components/newChatPanel";
36

4-
export default async function Page() {
7+
interface PageProps {
8+
params: {
9+
domain: string;
10+
};
11+
}
512

13+
export default async function Page({ params }: PageProps) {
614
const languageModels = await getConfiguredLanguageModelsInfo();
15+
const repos = await getRepos(params.domain);
16+
17+
if (isServiceError(repos)) {
18+
throw new ServiceErrorException(repos);
19+
}
720

821
return (
922
<NewChatPanel
10-
languageModels={languageModels}
23+
chatBoxToolbarProps={{
24+
languageModels,
25+
repos,
26+
}}
27+
order={2}
1128
/>
1229
)
1330
}

0 commit comments

Comments
 (0)