Tweak data fetching with suspense

This commit is contained in:
Jared Palmer 2023-06-11 11:14:39 -04:00
parent f18e90289d
commit d087b429ee
5 changed files with 68 additions and 41 deletions

View file

@ -5,7 +5,11 @@ import { kv } from '@vercel/kv'
import { type Chat } from '@/lib/types' import { type Chat } from '@/lib/types'
export async function getChats(userId: string) { export async function getChats(userId?: string | null) {
if (!userId) {
return []
}
try { try {
const pipeline = kv.pipeline() const pipeline = kv.pipeline()
const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1) const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1)

View file

@ -32,7 +32,7 @@ export function ChatPanel({
onClick={() => stop()} onClick={() => stop()}
className="bg-background" className="bg-background"
> >
<StopCircle className="mr-2 h-4 w-4" /> <StopCircle className="w-4 h-4 mr-2" />
Stop generating Stop generating
</Button> </Button>
) : ( ) : (
@ -42,13 +42,13 @@ export function ChatPanel({
onClick={() => reload()} onClick={() => reload()}
className="bg-background" className="bg-background"
> >
<RefreshCcw className="mr-2 h-4 w-4" /> <RefreshCcw className="w-4 h-4 mr-2" />
Regenerate response Regenerate response
</Button> </Button>
) )
)} )}
</div> </div>
<div className="space-y-4 border-t bg-background shadow-lg sm:rounded-t-xl sm:border sm:p-4"> <div className="p-4 space-y-4 border-t shadow-lg bg-background sm:rounded-t-xl sm:border">
<PromptForm <PromptForm
onSubmit={value => { onSubmit={value => {
append({ append({
@ -58,7 +58,7 @@ export function ChatPanel({
}} }}
isLoading={isLoading} isLoading={isLoading}
/> />
<p className="hidden px-2 text-center text-xs leading-normal text-muted-foreground sm:block"> <p className="hidden px-2 text-xs leading-normal text-center text-muted-foreground sm:block">
Open source AI chatbot app built with{' '} Open source AI chatbot app built with{' '}
<ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{' '} <ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{' '}
<ExternalLink href="https://vercel.com/storage/kv"> <ExternalLink href="https://vercel.com/storage/kv">

View file

@ -5,19 +5,24 @@ import { buttonVariants } from '@/components/ui/button'
import { GitHub, Separator, Vercel } from '@/components/icons' import { GitHub, Separator, Vercel } from '@/components/icons'
import { Sidebar } from '@/components/sidebar' import { Sidebar } from '@/components/sidebar'
import { UserMenu } from '@/components/user-menu' import { UserMenu } from '@/components/user-menu'
import { getChats } from '@/app/actions' import { SidebarList } from './sidebar-list'
import { Suspense } from 'react'
export async function Header() { export async function Header() {
const session = await auth() const session = await auth()
const chats = session?.user?.email ? await getChats(session.user.email) : []
return ( return (
<header className="sticky top-0 z-50 flex h-16 w-full shrink-0 items-center justify-between border-b bg-gradient-to-b from-background/10 via-background/50 to-background/80 px-4 backdrop-blur-xl"> <header className="sticky top-0 z-50 flex items-center justify-between w-full h-16 px-4 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
<div className="flex items-center"> <div className="flex items-center">
{/* @ts-ignore */} {/* @ts-ignore */}
<Sidebar chats={chats} session={session} /> <Sidebar session={session}>
<div className="hidden items-center md:flex"> <Suspense fallback={<div className="flex-1 overflow-auto" />}>
<Separator className="h-6 w-6 text-muted-foreground/50" /> {/* @ts-ignore */}
<SidebarList session={session} />
</Suspense>
</Sidebar>
<div className="items-center hidden md:flex">
<Separator className="w-6 h-6 text-muted-foreground/50" />
<UserMenu session={session} /> <UserMenu session={session} />
</div> </div>
</div> </div>
@ -28,7 +33,7 @@ export async function Header() {
rel="noopener noreferrer" rel="noopener noreferrer"
className={cn(buttonVariants({ variant: 'outline' }))} className={cn(buttonVariants({ variant: 'outline' }))}
> >
<GitHub className="mr-2 h-4 w-4" /> <GitHub className="w-4 h-4 mr-2" />
<span>GitHub</span> <span>GitHub</span>
</a> </a>
<a <a
@ -36,9 +41,9 @@ export async function Header() {
target="_blank" target="_blank"
className={cn(buttonVariants())} className={cn(buttonVariants())}
> >
<Vercel className="mr-2 h-4 w-4" /> <Vercel className="w-4 h-4 mr-2" />
<span className="hidden sm:block">Deploy to Vercel</span> <span className="hidden sm:block">Deploy to Vercel</span>
<span className="sm:hidden">Deploy</span> <span className="sm:hidden">Vercel</span>
</a> </a>
</div> </div>
</header> </header>

View file

@ -0,0 +1,40 @@
import { getChats } from '@/app/actions'
import { Session } from '@auth/core/types'
import { SidebarItem } from './sidebar-item'
export interface SidebarListProps {
session?: Session
}
export async function SidebarList(props: SidebarListProps) {
const chats = await getChats(props.session?.user?.email)
return (
<div className="flex-1 overflow-auto">
{chats?.length ? (
<div className="px-2 space-y-2">
{chats.map(chat => (
<SidebarItem
key={chat.id}
title={chat.title}
userId={props?.session?.user?.email ?? ''}
href={`/chat/${chat.id}`}
id={chat.id}
/>
))}
</div>
) : (
<div className="p-8 text-center">
<p className="text-sm text-muted-foreground">
{props?.session?.user ? (
<>No chat history</>
) : (
<>Login for history</>
)}
</p>
</div>
)}
</div>
)
}
SidebarList.displayName = 'SidebarList'

View file

@ -5,7 +5,6 @@ import { signOut } from '@auth/nextjs/client'
import { type Session } from '@auth/nextjs/types' import { type Session } from '@auth/nextjs/types'
import { Sidebar as SidebarIcon } from 'lucide-react' import { Sidebar as SidebarIcon } from 'lucide-react'
import { type Chat } from '@/lib/types'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { import {
Sheet, Sheet,
@ -14,20 +13,19 @@ import {
SheetTitle, SheetTitle,
SheetTrigger SheetTrigger
} from '@/components/ui/sheet' } from '@/components/ui/sheet'
import { SidebarItem } from '@/components/sidebar-item'
import { ThemeToggle } from '@/components/theme-toggle' import { ThemeToggle } from '@/components/theme-toggle'
export interface SidebarProps { export interface SidebarProps {
session?: Session session?: Session
chats: Chat[] children?: React.ReactNode
} }
export async function Sidebar({ session, chats }: SidebarProps) { export function Sidebar({ session, children }: SidebarProps) {
return ( return (
<Sheet> <Sheet>
<SheetTrigger asChild> <SheetTrigger asChild>
<Button variant="ghost" className="-ml-2 h-9 w-9 p-0"> <Button variant="ghost" className="p-0 -ml-2 h-9 w-9">
<SidebarIcon className="h-6 w-6" /> <SidebarIcon className="w-6 h-6" />
<span className="sr-only">Toggle Sidebar</span> <span className="sr-only">Toggle Sidebar</span>
</Button> </Button>
</SheetTrigger> </SheetTrigger>
@ -38,27 +36,7 @@ export async function Sidebar({ session, chats }: SidebarProps) {
<SheetHeader className="p-4"> <SheetHeader className="p-4">
<SheetTitle className="text-sm">Chat History</SheetTitle> <SheetTitle className="text-sm">Chat History</SheetTitle>
</SheetHeader> </SheetHeader>
<div className="flex-1 overflow-auto"> {children}
{chats?.length ? (
<div className="space-y-2 px-2">
{chats.map(chat => (
<SidebarItem
key={chat.id}
title={chat.title}
userId={session?.user?.email ?? ''}
href={`/chat/${chat.id}`}
id={chat.id}
/>
))}
</div>
) : (
<div className="p-8 text-center">
<p className="text-sm text-muted-foreground">
{session?.user ? <>No chat history</> : <>Login for history</>}
</p>
</div>
)}
</div>
<div className="flex items-center p-4"> <div className="flex items-center p-4">
<ThemeToggle /> <ThemeToggle />
{session?.user && ( {session?.user && (