@hamzasaleemorg/convex-comments 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +201 -0
  3. package/README.md +581 -0
  4. package/dist/client/_generated/_ignore.d.ts +1 -0
  5. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  6. package/dist/client/_generated/_ignore.js +3 -0
  7. package/dist/client/_generated/_ignore.js.map +1 -0
  8. package/dist/client/index.d.ts +745 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client/index.js +579 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/component/_generated/api.d.ts +44 -0
  13. package/dist/component/_generated/api.d.ts.map +1 -0
  14. package/dist/component/_generated/api.js +31 -0
  15. package/dist/component/_generated/api.js.map +1 -0
  16. package/dist/component/_generated/component.d.ts +673 -0
  17. package/dist/component/_generated/component.d.ts.map +1 -0
  18. package/dist/component/_generated/component.js +11 -0
  19. package/dist/component/_generated/component.js.map +1 -0
  20. package/dist/component/_generated/dataModel.d.ts +46 -0
  21. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  22. package/dist/component/_generated/dataModel.js +11 -0
  23. package/dist/component/_generated/dataModel.js.map +1 -0
  24. package/dist/component/_generated/server.d.ts +121 -0
  25. package/dist/component/_generated/server.d.ts.map +1 -0
  26. package/dist/component/_generated/server.js +78 -0
  27. package/dist/component/_generated/server.js.map +1 -0
  28. package/dist/component/convex.config.d.ts +3 -0
  29. package/dist/component/convex.config.d.ts.map +1 -0
  30. package/dist/component/convex.config.js +3 -0
  31. package/dist/component/convex.config.js.map +1 -0
  32. package/dist/component/lib.d.ts +17 -0
  33. package/dist/component/lib.d.ts.map +1 -0
  34. package/dist/component/lib.js +18 -0
  35. package/dist/component/lib.js.map +1 -0
  36. package/dist/component/messages.d.ts +173 -0
  37. package/dist/component/messages.d.ts.map +1 -0
  38. package/dist/component/messages.js +410 -0
  39. package/dist/component/messages.js.map +1 -0
  40. package/dist/component/reactions.d.ts +51 -0
  41. package/dist/component/reactions.d.ts.map +1 -0
  42. package/dist/component/reactions.js +191 -0
  43. package/dist/component/reactions.js.map +1 -0
  44. package/dist/component/schema.d.ts +274 -0
  45. package/dist/component/schema.d.ts.map +1 -0
  46. package/dist/component/schema.js +159 -0
  47. package/dist/component/schema.js.map +1 -0
  48. package/dist/component/threads.d.ts +110 -0
  49. package/dist/component/threads.d.ts.map +1 -0
  50. package/dist/component/threads.js +276 -0
  51. package/dist/component/threads.js.map +1 -0
  52. package/dist/component/typing.d.ts +31 -0
  53. package/dist/component/typing.d.ts.map +1 -0
  54. package/dist/component/typing.js +147 -0
  55. package/dist/component/typing.js.map +1 -0
  56. package/dist/component/zones.d.ts +63 -0
  57. package/dist/component/zones.d.ts.map +1 -0
  58. package/dist/component/zones.js +159 -0
  59. package/dist/component/zones.js.map +1 -0
  60. package/dist/react/AddComment.d.ts +57 -0
  61. package/dist/react/AddComment.d.ts.map +1 -0
  62. package/dist/react/AddComment.js +285 -0
  63. package/dist/react/AddComment.js.map +1 -0
  64. package/dist/react/Comment.d.ts +70 -0
  65. package/dist/react/Comment.d.ts.map +1 -0
  66. package/dist/react/Comment.js +259 -0
  67. package/dist/react/Comment.js.map +1 -0
  68. package/dist/react/Comments.d.ts +74 -0
  69. package/dist/react/Comments.d.ts.map +1 -0
  70. package/dist/react/Comments.js +108 -0
  71. package/dist/react/Comments.js.map +1 -0
  72. package/dist/react/CommentsProvider.d.ts +104 -0
  73. package/dist/react/CommentsProvider.d.ts.map +1 -0
  74. package/dist/react/CommentsProvider.js +98 -0
  75. package/dist/react/CommentsProvider.js.map +1 -0
  76. package/dist/react/ReactionPicker.d.ts +28 -0
  77. package/dist/react/ReactionPicker.d.ts.map +1 -0
  78. package/dist/react/ReactionPicker.js +56 -0
  79. package/dist/react/ReactionPicker.js.map +1 -0
  80. package/dist/react/Thread.d.ts +84 -0
  81. package/dist/react/Thread.d.ts.map +1 -0
  82. package/dist/react/Thread.js +124 -0
  83. package/dist/react/Thread.js.map +1 -0
  84. package/dist/react/TypingIndicator.d.ts +25 -0
  85. package/dist/react/TypingIndicator.d.ts.map +1 -0
  86. package/dist/react/TypingIndicator.js +99 -0
  87. package/dist/react/TypingIndicator.js.map +1 -0
  88. package/dist/react/index.d.ts +15 -0
  89. package/dist/react/index.d.ts.map +1 -0
  90. package/dist/react/index.js +15 -0
  91. package/dist/react/index.js.map +1 -0
  92. package/package.json +106 -0
  93. package/src/client/_generated/_ignore.ts +1 -0
  94. package/src/client/index.ts +813 -0
  95. package/src/component/_generated/api.ts +60 -0
  96. package/src/component/_generated/component.ts +784 -0
  97. package/src/component/_generated/dataModel.ts +60 -0
  98. package/src/component/_generated/server.ts +156 -0
  99. package/src/component/convex.config.ts +3 -0
  100. package/src/component/lib.ts +57 -0
  101. package/src/component/messages.ts +476 -0
  102. package/src/component/reactions.ts +222 -0
  103. package/src/component/schema.ts +169 -0
  104. package/src/component/threads.ts +319 -0
  105. package/src/component/typing.ts +168 -0
  106. package/src/component/zones.ts +180 -0
  107. package/src/react/AddComment.tsx +463 -0
  108. package/src/react/Comment.tsx +519 -0
  109. package/src/react/Comments.tsx +276 -0
  110. package/src/react/CommentsProvider.tsx +197 -0
  111. package/src/react/ReactionPicker.tsx +95 -0
  112. package/src/react/Thread.tsx +336 -0
  113. package/src/react/TypingIndicator.tsx +144 -0
  114. package/src/react/index.ts +45 -0
@@ -0,0 +1,410 @@
1
+ import { v } from "convex/values";
2
+ import { mutation, query } from "./_generated/server.js";
3
+ import { attachmentValidator, mentionValidator, linkValidator } from "./schema.js";
4
+ // ============================================================================
5
+ // Message Validators
6
+ // ============================================================================
7
+ const messageValidator = v.object({
8
+ _id: v.id("messages"),
9
+ _creationTime: v.number(),
10
+ threadId: v.id("threads"),
11
+ authorId: v.string(),
12
+ body: v.string(),
13
+ mentions: v.array(mentionValidator),
14
+ links: v.array(linkValidator),
15
+ attachments: v.array(attachmentValidator),
16
+ isEdited: v.boolean(),
17
+ isDeleted: v.boolean(),
18
+ resolved: v.optional(v.boolean()),
19
+ resolvedBy: v.optional(v.string()),
20
+ resolvedAt: v.optional(v.number()),
21
+ createdAt: v.number(),
22
+ editedAt: v.optional(v.number()),
23
+ });
24
+ const reactionSummaryValidator = v.object({
25
+ emoji: v.string(),
26
+ count: v.number(),
27
+ users: v.array(v.string()),
28
+ includesMe: v.boolean(),
29
+ });
30
+ const messageWithReactionsValidator = v.object({
31
+ message: messageValidator,
32
+ reactions: v.array(reactionSummaryValidator),
33
+ });
34
+ // ============================================================================
35
+ // Mention & Link Parsing
36
+ // ============================================================================
37
+ /**
38
+ * Parse mentions from message body.
39
+ * Supports @userId format where userId can contain alphanumeric, underscore, hyphen.
40
+ */
41
+ function parseMentions(body) {
42
+ const mentions = [];
43
+ // Match @mentions - supports various ID formats
44
+ const mentionRegex = /@([a-zA-Z0-9_\-:]+)/g;
45
+ let match;
46
+ while ((match = mentionRegex.exec(body)) !== null) {
47
+ mentions.push({
48
+ userId: match[1],
49
+ start: match.index,
50
+ end: match.index + match[0].length,
51
+ });
52
+ }
53
+ return mentions;
54
+ }
55
+ /**
56
+ * Parse links from message body.
57
+ */
58
+ function parseLinks(body) {
59
+ const links = [];
60
+ // Simple URL regex - matches http(s) URLs
61
+ const urlRegex = /(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/g;
62
+ let match;
63
+ while ((match = urlRegex.exec(body)) !== null) {
64
+ links.push({
65
+ url: match[1],
66
+ start: match.index,
67
+ end: match.index + match[0].length,
68
+ });
69
+ }
70
+ return links;
71
+ }
72
+ // ============================================================================
73
+ // Message Functions
74
+ // ============================================================================
75
+ /**
76
+ * Add a new comment/message to a thread.
77
+ * Parses mentions and links from the body.
78
+ */
79
+ export const addComment = mutation({
80
+ args: {
81
+ threadId: v.id("threads"),
82
+ authorId: v.string(),
83
+ body: v.string(),
84
+ attachments: v.optional(v.array(attachmentValidator)),
85
+ },
86
+ returns: v.object({
87
+ messageId: v.id("messages"),
88
+ mentions: v.array(mentionValidator),
89
+ links: v.array(linkValidator),
90
+ }),
91
+ handler: async (ctx, args) => {
92
+ // Verify thread exists
93
+ const thread = await ctx.db.get(args.threadId);
94
+ if (!thread) {
95
+ throw new Error(`Thread ${args.threadId} not found`);
96
+ }
97
+ // Parse mentions and links
98
+ const mentions = parseMentions(args.body);
99
+ const links = parseLinks(args.body);
100
+ const now = Date.now();
101
+ // Insert message
102
+ const messageId = await ctx.db.insert("messages", {
103
+ threadId: args.threadId,
104
+ authorId: args.authorId,
105
+ body: args.body,
106
+ mentions,
107
+ links,
108
+ attachments: args.attachments ?? [],
109
+ isEdited: false,
110
+ isDeleted: false,
111
+ createdAt: now,
112
+ });
113
+ // Update thread's last activity
114
+ await ctx.db.patch(args.threadId, {
115
+ lastActivityAt: now,
116
+ });
117
+ // Clear typing indicator for this user
118
+ const typingIndicator = await ctx.db
119
+ .query("typingIndicators")
120
+ .withIndex("threadId_userId", (q) => q.eq("threadId", args.threadId).eq("userId", args.authorId))
121
+ .first();
122
+ if (typingIndicator) {
123
+ await ctx.db.delete(typingIndicator._id);
124
+ }
125
+ return { messageId, mentions, links };
126
+ },
127
+ });
128
+ /**
129
+ * Get a single message by ID.
130
+ */
131
+ export const getMessage = query({
132
+ args: {
133
+ messageId: v.id("messages"),
134
+ currentUserId: v.optional(v.string()),
135
+ },
136
+ returns: v.union(v.null(), messageWithReactionsValidator),
137
+ handler: async (ctx, args) => {
138
+ const message = await ctx.db.get(args.messageId);
139
+ if (!message) {
140
+ return null;
141
+ }
142
+ // Get reactions grouped by emoji
143
+ const reactions = await ctx.db
144
+ .query("reactions")
145
+ .withIndex("messageId", (q) => q.eq("messageId", args.messageId))
146
+ .collect();
147
+ const reactionMap = new Map();
148
+ for (const reaction of reactions) {
149
+ const existing = reactionMap.get(reaction.emoji);
150
+ if (existing) {
151
+ existing.count++;
152
+ existing.users.push(reaction.userId);
153
+ }
154
+ else {
155
+ reactionMap.set(reaction.emoji, { count: 1, users: [reaction.userId] });
156
+ }
157
+ }
158
+ const reactionSummaries = Array.from(reactionMap.entries()).map(([emoji, data]) => ({
159
+ emoji,
160
+ count: data.count,
161
+ users: data.users,
162
+ includesMe: args.currentUserId
163
+ ? data.users.includes(args.currentUserId)
164
+ : false,
165
+ }));
166
+ return {
167
+ message,
168
+ reactions: reactionSummaries,
169
+ };
170
+ },
171
+ });
172
+ /**
173
+ * Get messages in a thread with pagination.
174
+ * Uses cursor-based pagination for efficiency.
175
+ */
176
+ export const getMessages = query({
177
+ args: {
178
+ threadId: v.id("threads"),
179
+ limit: v.optional(v.number()),
180
+ cursor: v.optional(v.string()),
181
+ order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
182
+ currentUserId: v.optional(v.string()),
183
+ includeDeleted: v.optional(v.boolean()),
184
+ },
185
+ returns: v.object({
186
+ messages: v.array(messageWithReactionsValidator),
187
+ nextCursor: v.optional(v.string()),
188
+ hasMore: v.boolean(),
189
+ }),
190
+ handler: async (ctx, args) => {
191
+ const limit = args.limit ?? 50;
192
+ const order = args.order ?? "asc";
193
+ const includeDeleted = args.includeDeleted ?? false;
194
+ // Build query
195
+ let query;
196
+ if (args.cursor) {
197
+ const cursorTime = parseInt(args.cursor, 10);
198
+ if (order === "asc") {
199
+ query = ctx.db
200
+ .query("messages")
201
+ .withIndex("threadId_createdAt", (q) => q.eq("threadId", args.threadId).gt("createdAt", cursorTime))
202
+ .order("asc");
203
+ }
204
+ else {
205
+ query = ctx.db
206
+ .query("messages")
207
+ .withIndex("threadId_createdAt", (q) => q.eq("threadId", args.threadId).lt("createdAt", cursorTime))
208
+ .order("desc");
209
+ }
210
+ }
211
+ else {
212
+ query = ctx.db
213
+ .query("messages")
214
+ .withIndex("threadId_createdAt", (q) => q.eq("threadId", args.threadId))
215
+ .order(order);
216
+ }
217
+ // Fetch one extra to check if there are more
218
+ const messages = await query.take(limit + 1);
219
+ // Filter deleted if needed
220
+ const filteredMessages = includeDeleted
221
+ ? messages
222
+ : messages.filter((m) => !m.isDeleted);
223
+ const hasMore = filteredMessages.length > limit;
224
+ const resultMessages = filteredMessages.slice(0, limit);
225
+ // Get reactions for each message
226
+ const messagesWithReactions = await Promise.all(resultMessages.map(async (message) => {
227
+ const reactions = await ctx.db
228
+ .query("reactions")
229
+ .withIndex("messageId", (q) => q.eq("messageId", message._id))
230
+ .collect();
231
+ const reactionMap = new Map();
232
+ for (const reaction of reactions) {
233
+ const existing = reactionMap.get(reaction.emoji);
234
+ if (existing) {
235
+ existing.count++;
236
+ existing.users.push(reaction.userId);
237
+ }
238
+ else {
239
+ reactionMap.set(reaction.emoji, { count: 1, users: [reaction.userId] });
240
+ }
241
+ }
242
+ const reactionSummaries = Array.from(reactionMap.entries()).map(([emoji, data]) => ({
243
+ emoji,
244
+ count: data.count,
245
+ users: data.users,
246
+ includesMe: args.currentUserId
247
+ ? data.users.includes(args.currentUserId)
248
+ : false,
249
+ }));
250
+ return {
251
+ message,
252
+ reactions: reactionSummaries,
253
+ };
254
+ }));
255
+ // Generate next cursor
256
+ const lastMessage = resultMessages[resultMessages.length - 1];
257
+ const nextCursor = hasMore && lastMessage
258
+ ? lastMessage.createdAt.toString()
259
+ : undefined;
260
+ return {
261
+ messages: messagesWithReactions,
262
+ nextCursor,
263
+ hasMore,
264
+ };
265
+ },
266
+ });
267
+ /**
268
+ * Edit a message.
269
+ */
270
+ export const editMessage = mutation({
271
+ args: {
272
+ messageId: v.id("messages"),
273
+ body: v.string(),
274
+ /** Optional: User ID to verify ownership */
275
+ authorId: v.optional(v.string()),
276
+ },
277
+ returns: v.object({
278
+ mentions: v.array(mentionValidator),
279
+ links: v.array(linkValidator),
280
+ }),
281
+ handler: async (ctx, args) => {
282
+ const message = await ctx.db.get(args.messageId);
283
+ if (!message) {
284
+ throw new Error(`Message ${args.messageId} not found`);
285
+ }
286
+ if (message.isDeleted) {
287
+ throw new Error("Cannot edit a deleted message");
288
+ }
289
+ // Optionally verify ownership
290
+ if (args.authorId && message.authorId !== args.authorId) {
291
+ throw new Error("You can only edit your own messages");
292
+ }
293
+ // Re-parse mentions and links
294
+ const mentions = parseMentions(args.body);
295
+ const links = parseLinks(args.body);
296
+ await ctx.db.patch(args.messageId, {
297
+ body: args.body,
298
+ mentions,
299
+ links,
300
+ isEdited: true,
301
+ editedAt: Date.now(),
302
+ });
303
+ // Update thread activity
304
+ const thread = await ctx.db.get(message.threadId);
305
+ if (thread) {
306
+ await ctx.db.patch(message.threadId, {
307
+ lastActivityAt: Date.now(),
308
+ });
309
+ }
310
+ return { mentions, links };
311
+ },
312
+ });
313
+ /**
314
+ * Soft delete a message.
315
+ */
316
+ export const deleteMessage = mutation({
317
+ args: {
318
+ messageId: v.id("messages"),
319
+ /** Optional: User ID to verify ownership */
320
+ authorId: v.optional(v.string()),
321
+ },
322
+ returns: v.null(),
323
+ handler: async (ctx, args) => {
324
+ const message = await ctx.db.get(args.messageId);
325
+ if (!message) {
326
+ throw new Error(`Message ${args.messageId} not found`);
327
+ }
328
+ // Optionally verify ownership
329
+ if (args.authorId && message.authorId !== args.authorId) {
330
+ throw new Error("You can only delete your own messages");
331
+ }
332
+ await ctx.db.patch(args.messageId, {
333
+ isDeleted: true,
334
+ body: "[deleted]",
335
+ mentions: [],
336
+ links: [],
337
+ attachments: [],
338
+ });
339
+ return null;
340
+ },
341
+ });
342
+ /**
343
+ * Hard delete a message (permanently remove).
344
+ */
345
+ export const permanentlyDeleteMessage = mutation({
346
+ args: {
347
+ messageId: v.id("messages"),
348
+ },
349
+ returns: v.null(),
350
+ handler: async (ctx, args) => {
351
+ // Delete all reactions on this message
352
+ const reactions = await ctx.db
353
+ .query("reactions")
354
+ .withIndex("messageId", (q) => q.eq("messageId", args.messageId))
355
+ .collect();
356
+ for (const reaction of reactions) {
357
+ await ctx.db.delete(reaction._id);
358
+ }
359
+ // Delete the message
360
+ await ctx.db.delete(args.messageId);
361
+ return null;
362
+ },
363
+ });
364
+ /**
365
+ * Resolve a message (mark as addressed/completed).
366
+ */
367
+ export const resolveMessage = mutation({
368
+ args: {
369
+ messageId: v.id("messages"),
370
+ userId: v.string(),
371
+ },
372
+ returns: v.null(),
373
+ handler: async (ctx, args) => {
374
+ const message = await ctx.db.get(args.messageId);
375
+ if (!message) {
376
+ throw new Error(`Message ${args.messageId} not found`);
377
+ }
378
+ if (message.isDeleted) {
379
+ throw new Error("Cannot resolve a deleted message");
380
+ }
381
+ await ctx.db.patch(args.messageId, {
382
+ resolved: true,
383
+ resolvedBy: args.userId,
384
+ resolvedAt: Date.now(),
385
+ });
386
+ return null;
387
+ },
388
+ });
389
+ /**
390
+ * Unresolve a message (mark as not addressed).
391
+ */
392
+ export const unresolveMessage = mutation({
393
+ args: {
394
+ messageId: v.id("messages"),
395
+ },
396
+ returns: v.null(),
397
+ handler: async (ctx, args) => {
398
+ const message = await ctx.db.get(args.messageId);
399
+ if (!message) {
400
+ throw new Error(`Message ${args.messageId} not found`);
401
+ }
402
+ await ctx.db.patch(args.messageId, {
403
+ resolved: undefined,
404
+ resolvedBy: undefined,
405
+ resolvedAt: undefined,
406
+ });
407
+ return null;
408
+ },
409
+ });
410
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/component/messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Add a reaction to a message.
3
+ * If the user already has the same reaction, this is a no-op.
4
+ */
5
+ export declare const addReaction: import("convex/server").RegisteredMutation<"public", {
6
+ emoji: string;
7
+ messageId: import("convex/values").GenericId<"messages">;
8
+ userId: string;
9
+ }, Promise<import("convex/values").GenericId<"reactions"> | null>>;
10
+ /**
11
+ * Remove a reaction from a message.
12
+ */
13
+ export declare const removeReaction: import("convex/server").RegisteredMutation<"public", {
14
+ emoji: string;
15
+ messageId: import("convex/values").GenericId<"messages">;
16
+ userId: string;
17
+ }, Promise<boolean>>;
18
+ /**
19
+ * Toggle a reaction (add if not present, remove if present).
20
+ */
21
+ export declare const toggleReaction: import("convex/server").RegisteredMutation<"public", {
22
+ emoji: string;
23
+ messageId: import("convex/values").GenericId<"messages">;
24
+ userId: string;
25
+ }, Promise<{
26
+ added: boolean;
27
+ reactionId: undefined;
28
+ } | {
29
+ added: boolean;
30
+ reactionId: import("convex/values").GenericId<"reactions">;
31
+ }>>;
32
+ /**
33
+ * Get all reactions for a message.
34
+ */
35
+ export declare const getReactions: import("convex/server").RegisteredQuery<"public", {
36
+ currentUserId?: string | undefined;
37
+ messageId: import("convex/values").GenericId<"messages">;
38
+ }, Promise<{
39
+ emoji: string;
40
+ count: number;
41
+ users: string[];
42
+ includesMe: boolean;
43
+ }[]>>;
44
+ /**
45
+ * Get users who reacted with a specific emoji.
46
+ */
47
+ export declare const getReactionUsers: import("convex/server").RegisteredQuery<"public", {
48
+ emoji: string;
49
+ messageId: import("convex/values").GenericId<"messages">;
50
+ }, Promise<string[]>>;
51
+ //# sourceMappingURL=reactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.d.ts","sourceRoot":"","sources":["../../src/component/reactions.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;kEAoDtB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;oBA0BzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;GAwDzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;KAwCvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;qBAgB3B,CAAC"}
@@ -0,0 +1,191 @@
1
+ import { v } from "convex/values";
2
+ import { mutation, query } from "./_generated/server.js";
3
+ // ============================================================================
4
+ // Reaction Functions
5
+ // ============================================================================
6
+ /**
7
+ * Add a reaction to a message.
8
+ * If the user already has the same reaction, this is a no-op.
9
+ */
10
+ export const addReaction = mutation({
11
+ args: {
12
+ messageId: v.id("messages"),
13
+ userId: v.string(),
14
+ emoji: v.string(),
15
+ },
16
+ returns: v.union(v.id("reactions"), v.null()),
17
+ handler: async (ctx, args) => {
18
+ // Verify message exists
19
+ const message = await ctx.db.get(args.messageId);
20
+ if (!message) {
21
+ throw new Error(`Message ${args.messageId} not found`);
22
+ }
23
+ if (message.isDeleted) {
24
+ throw new Error("Cannot react to a deleted message");
25
+ }
26
+ // Check if reaction already exists
27
+ const existing = await ctx.db
28
+ .query("reactions")
29
+ .withIndex("messageId_emoji_userId", (q) => q
30
+ .eq("messageId", args.messageId)
31
+ .eq("emoji", args.emoji)
32
+ .eq("userId", args.userId))
33
+ .first();
34
+ if (existing) {
35
+ // Already reacted with this emoji
36
+ return null;
37
+ }
38
+ // Add reaction
39
+ const reactionId = await ctx.db.insert("reactions", {
40
+ messageId: args.messageId,
41
+ userId: args.userId,
42
+ emoji: args.emoji,
43
+ createdAt: Date.now(),
44
+ });
45
+ // Update thread activity
46
+ const thread = await ctx.db.get(message.threadId);
47
+ if (thread) {
48
+ await ctx.db.patch(message.threadId, {
49
+ lastActivityAt: Date.now(),
50
+ });
51
+ }
52
+ return reactionId;
53
+ },
54
+ });
55
+ /**
56
+ * Remove a reaction from a message.
57
+ */
58
+ export const removeReaction = mutation({
59
+ args: {
60
+ messageId: v.id("messages"),
61
+ userId: v.string(),
62
+ emoji: v.string(),
63
+ },
64
+ returns: v.boolean(),
65
+ handler: async (ctx, args) => {
66
+ // Find the reaction
67
+ const reaction = await ctx.db
68
+ .query("reactions")
69
+ .withIndex("messageId_emoji_userId", (q) => q
70
+ .eq("messageId", args.messageId)
71
+ .eq("emoji", args.emoji)
72
+ .eq("userId", args.userId))
73
+ .first();
74
+ if (!reaction) {
75
+ return false;
76
+ }
77
+ await ctx.db.delete(reaction._id);
78
+ return true;
79
+ },
80
+ });
81
+ /**
82
+ * Toggle a reaction (add if not present, remove if present).
83
+ */
84
+ export const toggleReaction = mutation({
85
+ args: {
86
+ messageId: v.id("messages"),
87
+ userId: v.string(),
88
+ emoji: v.string(),
89
+ },
90
+ returns: v.object({
91
+ added: v.boolean(),
92
+ reactionId: v.optional(v.id("reactions")),
93
+ }),
94
+ handler: async (ctx, args) => {
95
+ // Verify message exists
96
+ const message = await ctx.db.get(args.messageId);
97
+ if (!message) {
98
+ throw new Error(`Message ${args.messageId} not found`);
99
+ }
100
+ if (message.isDeleted) {
101
+ throw new Error("Cannot react to a deleted message");
102
+ }
103
+ // Check if reaction already exists
104
+ const existing = await ctx.db
105
+ .query("reactions")
106
+ .withIndex("messageId_emoji_userId", (q) => q
107
+ .eq("messageId", args.messageId)
108
+ .eq("emoji", args.emoji)
109
+ .eq("userId", args.userId))
110
+ .first();
111
+ if (existing) {
112
+ // Remove existing reaction
113
+ await ctx.db.delete(existing._id);
114
+ return { added: false, reactionId: undefined };
115
+ }
116
+ // Add new reaction
117
+ const reactionId = await ctx.db.insert("reactions", {
118
+ messageId: args.messageId,
119
+ userId: args.userId,
120
+ emoji: args.emoji,
121
+ createdAt: Date.now(),
122
+ });
123
+ // Update thread activity
124
+ const thread = await ctx.db.get(message.threadId);
125
+ if (thread) {
126
+ await ctx.db.patch(message.threadId, {
127
+ lastActivityAt: Date.now(),
128
+ });
129
+ }
130
+ return { added: true, reactionId };
131
+ },
132
+ });
133
+ /**
134
+ * Get all reactions for a message.
135
+ */
136
+ export const getReactions = query({
137
+ args: {
138
+ messageId: v.id("messages"),
139
+ currentUserId: v.optional(v.string()),
140
+ },
141
+ returns: v.array(v.object({
142
+ emoji: v.string(),
143
+ count: v.number(),
144
+ users: v.array(v.string()),
145
+ includesMe: v.boolean(),
146
+ })),
147
+ handler: async (ctx, args) => {
148
+ const reactions = await ctx.db
149
+ .query("reactions")
150
+ .withIndex("messageId", (q) => q.eq("messageId", args.messageId))
151
+ .collect();
152
+ // Group by emoji
153
+ const reactionMap = new Map();
154
+ for (const reaction of reactions) {
155
+ const existing = reactionMap.get(reaction.emoji);
156
+ if (existing) {
157
+ existing.count++;
158
+ existing.users.push(reaction.userId);
159
+ }
160
+ else {
161
+ reactionMap.set(reaction.emoji, { count: 1, users: [reaction.userId] });
162
+ }
163
+ }
164
+ return Array.from(reactionMap.entries()).map(([emoji, data]) => ({
165
+ emoji,
166
+ count: data.count,
167
+ users: data.users,
168
+ includesMe: args.currentUserId
169
+ ? data.users.includes(args.currentUserId)
170
+ : false,
171
+ }));
172
+ },
173
+ });
174
+ /**
175
+ * Get users who reacted with a specific emoji.
176
+ */
177
+ export const getReactionUsers = query({
178
+ args: {
179
+ messageId: v.id("messages"),
180
+ emoji: v.string(),
181
+ },
182
+ returns: v.array(v.string()),
183
+ handler: async (ctx, args) => {
184
+ const reactions = await ctx.db
185
+ .query("reactions")
186
+ .withIndex("messageId_emoji", (q) => q.eq("messageId", args.messageId).eq("emoji", args.emoji))
187
+ .collect();
188
+ return reactions.map((r) => r.userId);
189
+ },
190
+ });
191
+ //# sourceMappingURL=reactions.js.map