Skip to content

Commit 7d3b661

Browse files
authored
feat: sort chats by last message usage (via updated_at) (#201)
* feat: add updated_at in chats schema, sort by updated_at * feat: update title, update chat state * feat: add bump (refresh chat state) when message sent
1 parent 1e9eb7e commit 7d3b661

File tree

7 files changed

+46
-8
lines changed

7 files changed

+46
-8
lines changed

INSTALL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ CREATE TABLE chats (
209209
system_prompt TEXT,
210210
agent_id UUID,
211211
created_at TIMESTAMPTZ DEFAULT NOW(),
212+
updated_at: TIMESTAMPTZ DEFAULT NOW(),
212213
public BOOLEAN DEFAULT FALSE NOT NULL, -- Added NOT NULL based on TS type
213214
CONSTRAINT chats_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
214215
CONSTRAINT chats_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL -- Assuming SET NULL, adjust if needed

app/components/chat/chat.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export function Chat() {
5656
createNewChat,
5757
getChatById,
5858
updateChatModel,
59+
bumpChat,
5960
isLoading: isChatsLoading,
6061
} = useChats()
6162

@@ -198,6 +199,9 @@ export function Chat() {
198199
return
199200
}
200201

202+
// refresh the chat history (in sidebar+command/drawer)
203+
bumpChat(currentChatId)
204+
201205
if (input.length > MESSAGE_MAX_LENGTH) {
202206
toast({
203207
title: `The message you submitted was too long, please submit something shorter. (Max ${MESSAGE_MAX_LENGTH} characters)`,

app/components/chat/use-chat-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { toast } from "@/components/ui/toast"
22
import { checkRateLimits } from "@/lib/api"
3+
import type { Chats } from "@/lib/chat-store/types"
34
import { REMAINING_QUERY_ALERT_THRESHOLD } from "@/lib/config"
45
import { Message } from "@ai-sdk/react"
5-
import type { Chats } from "@/lib/chat-store/types"
66

77
type UseChatUtilsProps = {
88
isAuthenticated: boolean

app/components/history/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ export function groupChatsByDate(
2929
const olderChats: Record<number, Chats[]> = {}
3030

3131
chats.forEach((chat) => {
32-
if (!chat.created_at) {
32+
if (!chat.updated_at) {
3333
todayChats.push(chat)
3434
return
3535
}
3636

37-
const chatTimestamp = new Date(chat.created_at).getTime()
37+
const chatTimestamp = new Date(chat.updated_at).getTime()
3838

3939
if (chatTimestamp >= today) {
4040
todayChats.push(chat)
@@ -45,7 +45,7 @@ export function groupChatsByDate(
4545
} else if (chatTimestamp >= yearStart) {
4646
thisYearChats.push(chat)
4747
} else {
48-
const year = new Date(chat.created_at).getFullYear()
48+
const year = new Date(chat.updated_at).getFullYear()
4949
if (!olderChats[year]) {
5050
olderChats[year] = []
5151
}

app/types/database.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export type Database = {
146146
Row: {
147147
agent_id: string | null
148148
created_at: string | null
149+
updated_at: string | null
149150
id: string
150151
model: string | null
151152
title: string | null
@@ -155,6 +156,7 @@ export type Database = {
155156
Insert: {
156157
agent_id?: string | null
157158
created_at?: string | null
159+
updated_at?: string | null
158160
id?: string
159161
model?: string | null
160162
title?: string | null
@@ -164,6 +166,7 @@ export type Database = {
164166
Update: {
165167
agent_id?: string | null
166168
created_at?: string | null
169+
updated_at?: string | null
167170
id?: string
168171
model?: string | null
169172
title?: string | null

lib/chat-store/chats/api.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export async function getChatsForUserInDb(userId: string): Promise<Chats[]> {
1919
.from("chats")
2020
.select("*")
2121
.eq("user_id", userId)
22-
.order("created_at", { ascending: false })
22+
.order("updated_at", { ascending: false })
2323

2424
if (!data || error) {
2525
console.error("Failed to fetch chats:", error)
@@ -33,7 +33,10 @@ export async function updateChatTitleInDb(id: string, title: string) {
3333
const supabase = createClient()
3434
if (!supabase) return
3535

36-
const { error } = await supabase.from("chats").update({ title }).eq("id", id)
36+
const { error } = await supabase
37+
.from("chats")
38+
.update({ title, updated_at: new Date().toISOString() })
39+
.eq("id", id)
3740
if (error) throw error
3841
}
3942

@@ -191,7 +194,13 @@ export async function createNewChat(
191194
): Promise<Chats> {
192195
try {
193196
// Note: Local agent IDs are filtered out at the API level (create-chat route)
194-
const payload: { userId: string; title: string; model: string; isAuthenticated?: boolean; agentId?: string } = {
197+
const payload: {
198+
userId: string
199+
title: string
200+
model: string
201+
isAuthenticated?: boolean
202+
agentId?: string
203+
} = {
195204
userId,
196205
title: title || (agentId ? `Conversation with agent` : "New Chat"),
197206
model: model || MODEL_DEFAULT,
@@ -222,6 +231,7 @@ export async function createNewChat(
222231
agent_id: responseData.chat.agent_id,
223232
user_id: responseData.chat.user_id,
224233
public: responseData.chat.public,
234+
updated_at: responseData.chat.updated_at,
225235
}
226236

227237
await writeToIndexedDB("chats", chat)

lib/chat-store/chats/provider.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ interface ChatsContextType {
4242
agentId: string | null,
4343
isAuthenticated: boolean
4444
) => Promise<void>
45+
bumpChat: (id: string) => Promise<void>
4546
}
4647
const ChatsContext = createContext<ChatsContextType | null>(null)
4748

@@ -89,7 +90,13 @@ export function ChatsProvider({
8990

9091
const updateTitle = async (id: string, title: string) => {
9192
const prev = [...chats]
92-
setChats((prev) => prev.map((c) => (c.id === id ? { ...c, title } : c)))
93+
const updatedChatWithNewTitle = prev.map((c) =>
94+
c.id === id ? { ...c, title, updated_at: new Date().toISOString() } : c
95+
)
96+
const sorted = updatedChatWithNewTitle.sort(
97+
(a, b) => +new Date(b.updated_at || "") - +new Date(a.updated_at || "")
98+
)
99+
setChats(sorted)
93100
try {
94101
await updateChatTitle(id, title)
95102
} catch {
@@ -136,6 +143,7 @@ export function ChatsProvider({
136143
agent_id: agentId || null,
137144
user_id: userId,
138145
public: true,
146+
updated_at: new Date().toISOString(),
139147
}
140148
setChats((prev) => [...prev, optimisticChat])
141149

@@ -191,6 +199,17 @@ export function ChatsProvider({
191199
await updateChatAgentFromDb(userId, chatId, agentId, isAuthenticated)
192200
}
193201

202+
const bumpChat = async (id: string) => {
203+
const prev = [...chats]
204+
const updatedChatWithNewUpdatedAt = prev.map((c) =>
205+
c.id === id ? { ...c, updated_at: new Date().toISOString() } : c
206+
)
207+
const sorted = updatedChatWithNewUpdatedAt.sort(
208+
(a, b) => +new Date(b.updated_at || "") - +new Date(a.updated_at || "")
209+
)
210+
setChats(sorted)
211+
}
212+
194213
return (
195214
<ChatsContext.Provider
196215
value={{
@@ -204,6 +223,7 @@ export function ChatsProvider({
204223
getChatById,
205224
updateChatModel,
206225
updateChatAgent,
226+
bumpChat,
207227
isLoading,
208228
}}
209229
>

0 commit comments

Comments
 (0)