{groupedChats.today.length > 0 && (
- <>
+
Today
@@ -307,12 +233,12 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
setOpenMobile={setOpenMobile}
/>
))}
- >
+
)}
{groupedChats.yesterday.length > 0 && (
- <>
-
+
+
Yesterday
{groupedChats.yesterday.map((chat) => (
@@ -327,12 +253,12 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
setOpenMobile={setOpenMobile}
/>
))}
- >
+
)}
{groupedChats.lastWeek.length > 0 && (
- <>
-
+
+
Last 7 days
{groupedChats.lastWeek.map((chat) => (
@@ -347,12 +273,12 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
setOpenMobile={setOpenMobile}
/>
))}
- >
+
)}
{groupedChats.lastMonth.length > 0 && (
- <>
-
+
+
Last 30 days
{groupedChats.lastMonth.map((chat) => (
@@ -367,13 +293,13 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
setOpenMobile={setOpenMobile}
/>
))}
- >
+
)}
{groupedChats.older.length > 0 && (
- <>
-
- Older
+
+
+ Older than last month
{groupedChats.older.map((chat) => (
))}
- >
+
)}
- >
+
);
})()}
+
+
{
+ if (!isValidating && !hasReachedEnd) {
+ setSize((size) => size + 1);
+ }
+ }}
+ />
+
+ {hasReachedEnd ? (
+
+ You have reached the end of your chat history.
+
+ ) : (
+
+
+
+
+
Loading Chats...
+
+ )}
+
diff --git a/hooks/use-chat-visibility.ts b/hooks/use-chat-visibility.ts
index d920785..975d5a8 100644
--- a/hooks/use-chat-visibility.ts
+++ b/hooks/use-chat-visibility.ts
@@ -1,10 +1,14 @@
'use client';
-import { updateChatVisibility } from '@/app/(chat)/actions';
-import { VisibilityType } from '@/components/visibility-selector';
-import { Chat } from '@/lib/db/schema';
import { useMemo } from 'react';
import useSWR, { useSWRConfig } from 'swr';
+import { unstable_serialize } from 'swr/infinite';
+import { updateChatVisibility } from '@/app/(chat)/actions';
+import {
+ getChatHistoryPaginationKey,
+ type ChatHistory,
+} from '@/components/sidebar-history';
+import type { VisibilityType } from '@/components/visibility-selector';
export function useChatVisibility({
chatId,
@@ -14,7 +18,7 @@ export function useChatVisibility({
initialVisibility: VisibilityType;
}) {
const { mutate, cache } = useSWRConfig();
- const history: Array = cache.get('/api/history')?.data;
+ const history: ChatHistory = cache.get('/api/history')?.data;
const { data: localVisibility, mutate: setLocalVisibility } = useSWR(
`${chatId}-visibility`,
@@ -26,31 +30,14 @@ export function useChatVisibility({
const visibilityType = useMemo(() => {
if (!history) return localVisibility;
- const chat = history.find((chat) => chat.id === chatId);
+ const chat = history.chats.find((chat) => chat.id === chatId);
if (!chat) return 'private';
return chat.visibility;
}, [history, chatId, localVisibility]);
const setVisibilityType = (updatedVisibilityType: VisibilityType) => {
setLocalVisibility(updatedVisibilityType);
-
- mutate>(
- '/api/history',
- (history) => {
- return history
- ? history.map((chat) => {
- if (chat.id === chatId) {
- return {
- ...chat,
- visibility: updatedVisibilityType,
- };
- }
- return chat;
- })
- : [];
- },
- { revalidate: false },
- );
+ mutate(unstable_serialize(getChatHistoryPaginationKey));
updateChatVisibility({
chatId: chatId,
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index ebc876a..d51c5ae 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -1,7 +1,7 @@
import 'server-only';
import { genSaltSync, hashSync } from 'bcrypt-ts';
-import { and, asc, desc, eq, gt, gte, inArray } from 'drizzle-orm';
+import { and, asc, desc, eq, gt, gte, inArray, lt, SQL } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
@@ -15,6 +15,7 @@ import {
message,
vote,
type DBMessage,
+ Chat,
} from './schema';
import { ArtifactKind } from '@/components/artifact';
@@ -81,13 +82,68 @@ export async function deleteChatById({ id }: { id: string }) {
}
}
-export async function getChatsByUserId({ id }: { id: string }) {
+export async function getChatsByUserId({
+ id,
+ limit,
+ startingAfter,
+ endingBefore,
+}: {
+ id: string;
+ limit: number;
+ startingAfter: string | null;
+ endingBefore: string | null;
+}) {
try {
- return await db
- .select()
- .from(chat)
- .where(eq(chat.userId, id))
- .orderBy(desc(chat.createdAt));
+ const extendedLimit = limit + 1;
+
+ const query = (whereCondition?: SQL) =>
+ db
+ .select()
+ .from(chat)
+ .where(
+ whereCondition
+ ? and(whereCondition, eq(chat.userId, id))
+ : eq(chat.userId, id),
+ )
+ .orderBy(desc(chat.createdAt))
+ .limit(extendedLimit);
+
+ let filteredChats: Array = [];
+
+ if (startingAfter) {
+ const [selectedChat] = await db
+ .select()
+ .from(chat)
+ .where(eq(chat.id, startingAfter))
+ .limit(1);
+
+ if (!selectedChat) {
+ throw new Error(`Chat with id ${startingAfter} not found`);
+ }
+
+ filteredChats = await query(gt(chat.createdAt, selectedChat.createdAt));
+ } else if (endingBefore) {
+ const [selectedChat] = await db
+ .select()
+ .from(chat)
+ .where(eq(chat.id, endingBefore))
+ .limit(1);
+
+ if (!selectedChat) {
+ throw new Error(`Chat with id ${endingBefore} not found`);
+ }
+
+ filteredChats = await query(lt(chat.createdAt, selectedChat.createdAt));
+ } else {
+ filteredChats = await query();
+ }
+
+ const hasMore = filteredChats.length > limit;
+
+ return {
+ chats: hasMore ? filteredChats.slice(0, limit) : filteredChats,
+ hasMore,
+ };
} catch (error) {
console.error('Failed to get chats by user from database');
throw error;