From ea557f476376081456d31f657a18efef7c546041 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Sat, 16 Nov 2024 11:14:32 -0500 Subject: [PATCH 1/2] Start on dynamic IO --- app/(chat)/layout.tsx | 46 +++++++++++++++++++- app/(chat)/sidebar-data-wrapper.tsx | 17 ++++++++ components/app-sidebar.tsx | 10 ++--- components/chat.tsx | 4 +- components/multimodal-input.tsx | 3 +- components/sidebar-history.tsx | 65 +++++++---------------------- next.config.ts | 1 + 7 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 app/(chat)/sidebar-data-wrapper.tsx diff --git a/app/(chat)/layout.tsx b/app/(chat)/layout.tsx index 981aa505b..a2838a5e5 100644 --- a/app/(chat)/layout.tsx +++ b/app/(chat)/layout.tsx @@ -1,9 +1,17 @@ import { cookies } from 'next/headers'; import { AppSidebar } from '@/components/app-sidebar'; -import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'; +import { + SidebarGroup, + SidebarGroupContent, + SidebarInset, + SidebarProvider, +} from '@/components/ui/sidebar'; import { auth } from '../(auth)/auth'; +import { Suspense } from 'react'; +import { notFound } from 'next/navigation'; +import { SidebarDataWrapper } from './sidebar-data-wrapper'; export const experimental_ppr = true; @@ -14,11 +22,45 @@ export default async function Layout({ }) { const [session, cookieStore] = await Promise.all([auth(), cookies()]); const isCollapsed = cookieStore.get('sidebar:state')?.value !== 'true'; + if (!session?.user) { + return notFound(); + } return ( - + + }> + + + {children} ); } + +function SidebarLoading() { + return ( + +
Today
+ +
+ {[44, 32, 28, 64, 52].map((item) => ( +
+
+
+ ))} +
+ + + ); +} diff --git a/app/(chat)/sidebar-data-wrapper.tsx b/app/(chat)/sidebar-data-wrapper.tsx new file mode 100644 index 000000000..230099932 --- /dev/null +++ b/app/(chat)/sidebar-data-wrapper.tsx @@ -0,0 +1,17 @@ +import type { User } from 'next-auth'; +import { getChatsByUserId } from '@/lib/db/queries'; +import { unstable_cacheTag as cacheTag } from 'next/cache'; +import { SidebarHistory } from '@/components/sidebar-history'; + +export async function getChats(id: string) { + 'use cache'; + cacheTag('history'); + const chats = await getChatsByUserId({ id }); + return chats; +} + +export async function SidebarDataWrapper({ user }: { user: User }) { + // biome-ignore lint: Forbidden non-null assertion. + const chats = await getChats(user?.id!); + return ; +} diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index 0eebc35b0..209d5c9f6 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -4,7 +4,6 @@ import type { User } from 'next-auth'; import { useRouter } from 'next/navigation'; import { PlusIcon } from '@/components/icons'; -import { SidebarHistory } from '@/components/sidebar-history'; import { SidebarUserNav } from '@/components/sidebar-user-nav'; import { Button } from '@/components/ui/button'; import { @@ -20,7 +19,10 @@ import { import { BetterTooltip } from '@/components/ui/tooltip'; import Link from 'next/link'; -export function AppSidebar({ user }: { user: User | undefined }) { +export function AppSidebar({ + user, + children, +}: { user: User | undefined; children: React.ReactNode }) { const router = useRouter(); const { setOpenMobile } = useSidebar(); @@ -58,9 +60,7 @@ export function AppSidebar({ user }: { user: User | undefined }) { - - - + {children} {user && ( diff --git a/components/chat.tsx b/components/chat.tsx index 425ea1513..4a2f18963 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -17,6 +17,7 @@ import { Block, type UIBlock } from './block'; import { BlockStreamHandler } from './block-stream-handler'; import { MultimodalInput } from './multimodal-input'; import { Overview } from './overview'; +import { revalidateTag } from 'next/cache'; export function Chat({ id, @@ -43,7 +44,8 @@ export function Chat({ body: { id, modelId: selectedModelId }, initialMessages, onFinish: () => { - mutate('/api/history'); + revalidateTag('history'); + // mutate('/api/history'); }, }); diff --git a/components/multimodal-input.tsx b/components/multimodal-input.tsx index 152e9792f..cd93bfc26 100644 --- a/components/multimodal-input.tsx +++ b/components/multimodal-input.tsx @@ -27,6 +27,7 @@ import { ArrowUpIcon, PaperclipIcon, StopIcon } from './icons'; import { PreviewAttachment } from './preview-attachment'; import { Button } from './ui/button'; import { Textarea } from './ui/textarea'; +import { revalidateTag } from 'next/cache'; const suggestedActions = [ { @@ -130,7 +131,7 @@ export function MultimodalInput({ setAttachments([]); setLocalStorageInput(''); - + revalidateTag('history'); if (width && width > 768) { textareaRef.current?.focus(); } diff --git a/components/sidebar-history.tsx b/components/sidebar-history.tsx index 3147041d5..b159d0c47 100644 --- a/components/sidebar-history.tsx +++ b/components/sidebar-history.tsx @@ -4,9 +4,7 @@ import { isToday, isYesterday, subMonths, subWeeks } from 'date-fns'; import Link from 'next/link'; import { useParams, usePathname, useRouter } from 'next/navigation'; import type { User } from 'next-auth'; -import { useEffect, useState } from 'react'; -import { toast } from 'sonner'; -import useSWR from 'swr'; +import { useState } from 'react'; import { MoreHorizontalIcon, TrashIcon } from '@/components/icons'; import { @@ -35,7 +33,7 @@ import { useSidebar, } from '@/components/ui/sidebar'; import type { Chat } from '@/lib/db/schema'; -import { fetcher } from '@/lib/utils'; +import { revalidateTag } from 'next/cache'; type GroupedChats = { today: Chat[]; @@ -85,21 +83,21 @@ const ChatItem = ({ ); -export function SidebarHistory({ user }: { user: User | undefined }) { +export function SidebarHistory({ + user, + history, +}: { + user: User | undefined; + history: { + id: string; + createdAt: Date; + title: string; + userId: string; + }[]; +}) { const { setOpenMobile } = useSidebar(); const { id } = useParams(); const pathname = usePathname(); - const { - data: history, - isLoading, - mutate, - } = useSWR>(user ? '/api/history' : null, fetcher, { - fallbackData: [], - }); - - useEffect(() => { - mutate(); - }, [pathname, mutate]); const [deleteId, setDeleteId] = useState(null); const [showDeleteDialog, setShowDeleteDialog] = useState(false); @@ -112,11 +110,7 @@ export function SidebarHistory({ user }: { user: User | undefined }) { toast.promise(deletePromise, { loading: 'Deleting chat...', success: () => { - mutate((history) => { - if (history) { - return history.filter((h) => h.id !== id); - } - }); + revalidateTag('history'); return 'Chat deleted successfully'; }, error: 'Failed to delete chat', @@ -141,35 +135,6 @@ export function SidebarHistory({ user }: { user: User | undefined }) { ); } - if (isLoading) { - return ( - -
- Today -
- -
- {[44, 32, 28, 64, 52].map((item) => ( -
-
-
- ))} -
- - - ); - } - if (history?.length === 0) { return ( diff --git a/next.config.ts b/next.config.ts index b875b006f..56aa08cae 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,6 +4,7 @@ const nextConfig: NextConfig = { /* config options here */ experimental: { ppr: true, + dynamicIO: true, }, images: { remotePatterns: [ From 239f5d52aa8b104e0cedba24892a6734d2e07cb9 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Mon, 18 Nov 2024 18:56:02 -0800 Subject: [PATCH 2/2] Keep migrating --- app/(chat)/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx index c837e5894..9cfc38ebe 100644 --- a/app/(chat)/page.tsx +++ b/app/(chat)/page.tsx @@ -1,10 +1,12 @@ import { cookies } from 'next/headers'; +import { connection } from 'next/server'; import { Chat } from '@/components/chat'; import { DEFAULT_MODEL_NAME, models } from '@/lib/ai/models'; import { generateUUID } from '@/lib/utils'; export default async function Page() { + await connection(); const id = generateUUID(); const cookieStore = await cookies();