Tweak data fetching with suspense
This commit is contained in:
parent
f18e90289d
commit
d087b429ee
5 changed files with 68 additions and 41 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
40
components/sidebar-list.tsx
Normal file
40
components/sidebar-list.tsx
Normal 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'
|
||||||
|
|
@ -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 && (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue