import { query, mutation } from "./_generated/server"; import { v } from "convex/values"; import { paginationOptsValidator } from "convex/server"; export const listByUser = query({ args: { token: v.string() }, handler: async (ctx, args) => { const session = await ctx.db .query("sessions") .withIndex("by_token", (q) => q.eq("token", args.token)) .first(); if (!session) return []; const allChats = await ctx.db.query("chats").order("desc").collect(); const userChats = allChats.filter((chat) => chat.participantIds.includes(session.userId) ); const enriched = await Promise.all( userChats.map(async (chat) => { const otherId = chat.participantIds.find( (id) => id !== session.userId )!; const otherUser = await ctx.db.get(otherId); const lastMessage = await ctx.db .query("messages") .withIndex("by_chat", (q) => q.eq("chatId", chat._id)) .order("desc") .first(); return { _id: chat._id, otherUser: otherUser ? { _id: otherUser._id, name: otherUser.name, avatarId: otherUser.avatarId ?? null, } : null, lastMessage: lastMessage ? { content: lastMessage.content, createdAt: lastMessage.createdAt, senderId: lastMessage.senderId, } : null, lastMessageAt: chat.lastMessageAt, }; }) ); return enriched.sort((a, b) => b.lastMessageAt - a.lastMessageAt); }, }); export const listByUserPaginated = query({ args: { token: v.string(), paginationOpts: paginationOptsValidator }, handler: async (ctx, args) => { const session = await ctx.db .query("sessions") .withIndex("by_token", (q) => q.eq("token", args.token)) .first(); if (!session) { return { page: [], continuationCursor: null, isDone: true }; } const result = await ctx.db .query("chats") .order("desc") .paginate(args.paginationOpts); const filtered = result.page.filter((chat) => chat.participantIds.includes(session.userId) ); const enriched = await Promise.all( filtered.map(async (chat) => { const otherId = chat.participantIds.find( (id) => id !== session.userId )!; const otherUser = await ctx.db.get(otherId); const lastMessage = await ctx.db .query("messages") .withIndex("by_chat", (q) => q.eq("chatId", chat._id)) .order("desc") .first(); return { _id: chat._id, otherUser: otherUser ? { _id: otherUser._id, name: otherUser.name, avatarId: otherUser.avatarId ?? null, } : null, lastMessage: lastMessage ? { content: lastMessage.content, createdAt: lastMessage.createdAt, senderId: lastMessage.senderId, } : null, lastMessageAt: chat.lastMessageAt, }; }) ); return { page: enriched.sort((a, b) => b.lastMessageAt - a.lastMessageAt), continueCursor: result.continueCursor, isDone: result.isDone, }; }, }); export const getOrCreate = mutation({ args: { token: v.string(), partnerId: v.id("users"), }, handler: async (ctx, args) => { const session = await ctx.db .query("sessions") .withIndex("by_token", (q) => q.eq("token", args.token)) .first(); if (!session) throw new Error("Неавторизиран"); const allChats = await ctx.db.query("chats").collect(); const existing = allChats.find( (c) => c.participantIds.includes(session.userId) && c.participantIds.includes(args.partnerId) && c.participantIds.length === 2 ); if (existing) return existing._id; const chatId = await ctx.db.insert("chats", { participantIds: [session.userId, args.partnerId], lastMessageAt: Date.now(), createdBy: session.userId, }); return chatId; }, }); export const getById = query({ args: { token: v.string(), chatId: v.id("chats") }, handler: async (ctx, args) => { const session = await ctx.db .query("sessions") .withIndex("by_token", (q) => q.eq("token", args.token)) .first(); if (!session) throw new Error("Неавторизиран"); const chat = await ctx.db.get(args.chatId); if (!chat) throw new Error("Разговорот не е пронајден"); if (!chat.participantIds.includes(session.userId)) { throw new Error("Немате пристап до овој разговор"); } const otherId = chat.participantIds.find( (id) => id !== session.userId )!; const otherUser = await ctx.db.get(otherId); return { _id: chat._id, participantIds: chat.participantIds, otherUser: otherUser ? { _id: otherUser._id, name: otherUser.name, avatarId: otherUser.avatarId ?? null, } : null, lastMessageAt: chat.lastMessageAt, }; }, });