@studious-lms/server 1.2.45 → 1.2.47

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 (241) hide show
  1. package/.env.example +45 -0
  2. package/.env.test.example +37 -0
  3. package/README.md +34 -7
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/clover.xml +12110 -0
  7. package/coverage/coverage-final.json +44 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +221 -0
  10. package/coverage/prettify.css +1 -0
  11. package/coverage/prettify.js +2 -0
  12. package/coverage/server/index.html +116 -0
  13. package/coverage/server/src/exportType.ts.html +109 -0
  14. package/coverage/server/src/index.html +161 -0
  15. package/coverage/server/src/index.ts.html +1702 -0
  16. package/coverage/server/src/instrument.ts.html +130 -0
  17. package/coverage/server/src/lib/config/env.ts.html +448 -0
  18. package/coverage/server/src/lib/config/index.html +116 -0
  19. package/coverage/server/src/lib/fileUpload.ts.html +1138 -0
  20. package/coverage/server/src/lib/googleCloudStorage.ts.html +334 -0
  21. package/coverage/server/src/lib/index.html +206 -0
  22. package/coverage/server/src/lib/jsonConversion.ts.html +2323 -0
  23. package/coverage/server/src/lib/jsonStyles.ts.html +193 -0
  24. package/coverage/server/src/lib/notificationHandler.ts.html +193 -0
  25. package/coverage/server/src/lib/pusher.ts.html +121 -0
  26. package/coverage/server/src/lib/thumbnailGenerator.ts.html +592 -0
  27. package/coverage/server/src/middleware/auth.ts.html +646 -0
  28. package/coverage/server/src/middleware/index.html +146 -0
  29. package/coverage/server/src/middleware/logging.ts.html +244 -0
  30. package/coverage/server/src/middleware/security.ts.html +271 -0
  31. package/coverage/server/src/routers/_app.ts.html +232 -0
  32. package/coverage/server/src/routers/agenda.ts.html +319 -0
  33. package/coverage/server/src/routers/announcement.ts.html +3481 -0
  34. package/coverage/server/src/routers/assignment.ts.html +7633 -0
  35. package/coverage/server/src/routers/attendance.ts.html +1030 -0
  36. package/coverage/server/src/routers/auth.ts.html +1081 -0
  37. package/coverage/server/src/routers/class.ts.html +3535 -0
  38. package/coverage/server/src/routers/comment.ts.html +991 -0
  39. package/coverage/server/src/routers/conversation.ts.html +982 -0
  40. package/coverage/server/src/routers/event.ts.html +1609 -0
  41. package/coverage/server/src/routers/file.ts.html +1144 -0
  42. package/coverage/server/src/routers/folder.ts.html +2797 -0
  43. package/coverage/server/src/routers/index.html +386 -0
  44. package/coverage/server/src/routers/labChat.ts.html +3073 -0
  45. package/coverage/server/src/routers/marketing.ts.html +340 -0
  46. package/coverage/server/src/routers/message.ts.html +1912 -0
  47. package/coverage/server/src/routers/notifications.ts.html +364 -0
  48. package/coverage/server/src/routers/section.ts.html +1120 -0
  49. package/coverage/server/src/routers/user.ts.html +862 -0
  50. package/coverage/server/src/routers/worksheet.ts.html +1729 -0
  51. package/coverage/server/src/trpc.ts.html +397 -0
  52. package/coverage/server/src/types/index.html +116 -0
  53. package/coverage/server/src/types/trpc.ts.html +127 -0
  54. package/coverage/server/src/utils/aiUser.ts.html +280 -0
  55. package/coverage/server/src/utils/email.ts.html +121 -0
  56. package/coverage/server/src/utils/generateInviteCode.ts.html +106 -0
  57. package/coverage/server/src/utils/index.html +206 -0
  58. package/coverage/server/src/utils/inference.ts.html +709 -0
  59. package/coverage/server/src/utils/logger.ts.html +664 -0
  60. package/coverage/server/src/utils/prismaErrorHandler.ts.html +907 -0
  61. package/coverage/server/src/utils/prismaWrapper.ts.html +355 -0
  62. package/coverage/server/vitest.config.ts.html +196 -0
  63. package/coverage/sort-arrow-sprite.png +0 -0
  64. package/coverage/sorter.js +210 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +83 -52
  67. package/dist/index.js.map +1 -1
  68. package/dist/instrument.js +15 -8
  69. package/dist/instrument.js.map +1 -1
  70. package/dist/lib/config/env.d.ts +169 -0
  71. package/dist/lib/config/env.d.ts.map +1 -0
  72. package/dist/lib/config/env.js +115 -0
  73. package/dist/lib/config/env.js.map +1 -0
  74. package/dist/lib/fileUpload.d.ts.map +1 -1
  75. package/dist/lib/fileUpload.js +5 -4
  76. package/dist/lib/fileUpload.js.map +1 -1
  77. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  78. package/dist/lib/googleCloudStorage.js +7 -8
  79. package/dist/lib/googleCloudStorage.js.map +1 -1
  80. package/dist/lib/jsonConversion.d.ts.map +1 -1
  81. package/dist/lib/jsonConversion.js +14 -16
  82. package/dist/lib/jsonConversion.js.map +1 -1
  83. package/dist/lib/notificationHandler.d.ts +2 -2
  84. package/dist/lib/prisma.d.ts +2 -2
  85. package/dist/lib/prisma.d.ts.map +1 -1
  86. package/dist/lib/prisma.js +22 -3
  87. package/dist/lib/prisma.js.map +1 -1
  88. package/dist/lib/pusher.d.ts.map +1 -1
  89. package/dist/lib/pusher.js +8 -7
  90. package/dist/lib/pusher.js.map +1 -1
  91. package/dist/middleware/auth.d.ts.map +1 -1
  92. package/dist/middleware/auth.js +7 -5
  93. package/dist/middleware/auth.js.map +1 -1
  94. package/dist/middleware/security.d.ts +5 -0
  95. package/dist/middleware/security.d.ts.map +1 -0
  96. package/dist/middleware/security.js +77 -0
  97. package/dist/middleware/security.js.map +1 -0
  98. package/dist/routers/_app.d.ts +368 -108
  99. package/dist/routers/_app.d.ts.map +1 -1
  100. package/dist/routers/_app.js +4 -2
  101. package/dist/routers/_app.js.map +1 -1
  102. package/dist/routers/agenda.d.ts.map +1 -1
  103. package/dist/routers/agenda.js +12 -9
  104. package/dist/routers/agenda.js.map +1 -1
  105. package/dist/routers/announcement.d.ts +8 -0
  106. package/dist/routers/announcement.d.ts.map +1 -1
  107. package/dist/routers/announcement.js +6 -4
  108. package/dist/routers/announcement.js.map +1 -1
  109. package/dist/routers/assignment.d.ts +17 -4
  110. package/dist/routers/assignment.d.ts.map +1 -1
  111. package/dist/routers/assignment.js +51 -19
  112. package/dist/routers/assignment.js.map +1 -1
  113. package/dist/routers/attendance.d.ts +1 -0
  114. package/dist/routers/attendance.d.ts.map +1 -1
  115. package/dist/routers/attendance.js +4 -4
  116. package/dist/routers/attendance.js.map +1 -1
  117. package/dist/routers/auth.d.ts +20 -0
  118. package/dist/routers/auth.d.ts.map +1 -1
  119. package/dist/routers/auth.js +132 -15
  120. package/dist/routers/auth.js.map +1 -1
  121. package/dist/routers/class.d.ts +10 -0
  122. package/dist/routers/class.d.ts.map +1 -1
  123. package/dist/routers/class.js +49 -5
  124. package/dist/routers/class.js.map +1 -1
  125. package/dist/routers/comment.d.ts +2 -0
  126. package/dist/routers/comment.d.ts.map +1 -1
  127. package/dist/routers/conversation.d.ts +2 -0
  128. package/dist/routers/conversation.d.ts.map +1 -1
  129. package/dist/routers/conversation.js +46 -31
  130. package/dist/routers/conversation.js.map +1 -1
  131. package/dist/routers/file.d.ts.map +1 -1
  132. package/dist/routers/file.js +30 -7
  133. package/dist/routers/file.js.map +1 -1
  134. package/dist/routers/labChat.d.ts +2 -0
  135. package/dist/routers/labChat.d.ts.map +1 -1
  136. package/dist/routers/labChat.js +5 -322
  137. package/dist/routers/labChat.js.map +1 -1
  138. package/dist/routers/marketing.d.ts +1 -1
  139. package/dist/routers/message.d.ts +1 -0
  140. package/dist/routers/message.d.ts.map +1 -1
  141. package/dist/routers/message.js +3 -2
  142. package/dist/routers/message.js.map +1 -1
  143. package/dist/routers/newtonChat.d.ts +55 -0
  144. package/dist/routers/newtonChat.d.ts.map +1 -0
  145. package/dist/routers/newtonChat.js +262 -0
  146. package/dist/routers/newtonChat.js.map +1 -0
  147. package/dist/routers/notifications.d.ts +4 -4
  148. package/dist/routers/section.d.ts +19 -4
  149. package/dist/routers/section.d.ts.map +1 -1
  150. package/dist/routers/section.js +26 -8
  151. package/dist/routers/section.js.map +1 -1
  152. package/dist/routers/user.d.ts.map +1 -1
  153. package/dist/routers/user.js +5 -4
  154. package/dist/routers/user.js.map +1 -1
  155. package/dist/routers/worksheet.d.ts +44 -41
  156. package/dist/routers/worksheet.d.ts.map +1 -1
  157. package/dist/routers/worksheet.js +25 -34
  158. package/dist/routers/worksheet.js.map +1 -1
  159. package/dist/seedDatabase.d.ts +1 -1
  160. package/dist/seedDatabase.js +275 -284
  161. package/dist/seedDatabase.js.map +1 -1
  162. package/dist/server/pipelines/aiLabChat.d.ts +21 -0
  163. package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
  164. package/dist/server/pipelines/aiLabChat.js +456 -0
  165. package/dist/server/pipelines/aiLabChat.js.map +1 -0
  166. package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
  167. package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
  168. package/dist/server/pipelines/aiNewtonChat.js +280 -0
  169. package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
  170. package/dist/server/pipelines/gradeWorksheet.d.ts +15 -0
  171. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
  172. package/dist/server/pipelines/gradeWorksheet.js +139 -0
  173. package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
  174. package/dist/trpc.d.ts.map +1 -1
  175. package/dist/trpc.js +2 -2
  176. package/dist/trpc.js.map +1 -1
  177. package/dist/utils/email.d.ts +9 -1
  178. package/dist/utils/email.d.ts.map +1 -1
  179. package/dist/utils/email.js +20 -5
  180. package/dist/utils/email.js.map +1 -1
  181. package/dist/utils/inference.d.ts +5 -0
  182. package/dist/utils/inference.d.ts.map +1 -1
  183. package/dist/utils/inference.js +71 -7
  184. package/dist/utils/inference.js.map +1 -1
  185. package/dist/utils/logger.d.ts.map +1 -1
  186. package/dist/utils/logger.js +3 -3
  187. package/dist/utils/logger.js.map +1 -1
  188. package/docker-compose.yml +14 -0
  189. package/package.json +13 -4
  190. package/prisma/schema.prisma +34 -5
  191. package/scripts/test-pre-push.ts +14 -0
  192. package/src/index.ts +98 -54
  193. package/src/instrument.ts +13 -6
  194. package/src/lib/config/env.ts +126 -0
  195. package/src/lib/fileUpload.ts +3 -2
  196. package/src/lib/googleCloudStorage.ts +6 -6
  197. package/src/lib/jsonConversion.ts +12 -14
  198. package/src/lib/prisma.ts +23 -2
  199. package/src/lib/pusher.ts +6 -5
  200. package/src/middleware/auth.ts +5 -3
  201. package/src/middleware/security.ts +80 -0
  202. package/src/routers/_app.ts +2 -0
  203. package/src/routers/agenda.ts +10 -7
  204. package/src/routers/announcement.ts +4 -2
  205. package/src/routers/assignment.ts +74 -41
  206. package/src/routers/attendance.ts +2 -2
  207. package/src/routers/auth.ts +143 -14
  208. package/src/routers/class.ts +52 -3
  209. package/src/routers/conversation.ts +49 -29
  210. package/src/routers/file.ts +29 -5
  211. package/src/routers/labChat.ts +3 -367
  212. package/src/routers/message.ts +1 -1
  213. package/src/routers/newtonChat.ts +299 -0
  214. package/src/routers/section.ts +26 -6
  215. package/src/routers/user.ts +3 -2
  216. package/src/routers/worksheet.ts +26 -38
  217. package/src/seedDatabase.ts +290 -283
  218. package/src/server/pipelines/aiLabChat.ts +507 -0
  219. package/src/server/pipelines/aiNewtonChat.ts +338 -0
  220. package/src/server/pipelines/gradeWorksheet.ts +151 -0
  221. package/src/trpc.ts +2 -0
  222. package/src/utils/email.ts +30 -3
  223. package/src/utils/inference.ts +85 -5
  224. package/src/utils/logger.ts +2 -1
  225. package/tests/announcement.test.ts +164 -0
  226. package/tests/assignment.test.ts +296 -0
  227. package/tests/attendance.test.ts +168 -0
  228. package/tests/auth.test.ts +33 -10
  229. package/tests/class.test.ts +34 -9
  230. package/tests/event.test.ts +228 -0
  231. package/tests/section.test.ts +216 -0
  232. package/tests/setup.ts +70 -16
  233. package/tests/user.test.ts +158 -0
  234. package/vitest.config.ts +26 -0
  235. package/API_SPECIFICATION.md +0 -1597
  236. package/BASE64_REMOVAL_SUMMARY.md +0 -164
  237. package/CHAT_API_SPEC.md +0 -579
  238. package/LAB_CHAT_API_SPEC.md +0 -518
  239. package/dist/routers/school.d.ts +0 -208
  240. package/dist/routers/school.d.ts.map +0 -1
  241. package/dist/routers/school.js +0 -483
package/CHAT_API_SPEC.md DELETED
@@ -1,579 +0,0 @@
1
- # Chat API Specification - Studious LMS
2
-
3
- ## Overview
4
-
5
- Real-time chat system with support for direct messages (DMs) and group conversations, featuring mentions and separate unread tracking.
6
-
7
- ### Stack
8
- - **Backend**: tRPC + Prisma + PostgreSQL
9
- - **Real-time**: Pusher
10
- - **Authentication**: Bearer token in `Authorization` header
11
-
12
- ---
13
-
14
- ## 🔐 Authentication
15
-
16
- All chat endpoints require authentication via Bearer token:
17
- ```typescript
18
- headers: {
19
- 'Authorization': 'Bearer <your-session-token>'
20
- }
21
- ```
22
-
23
- ---
24
-
25
- ## 📝 Data Models
26
-
27
- ### Conversation Types
28
- ```typescript
29
- type ConversationType = 'DM' | 'GROUP';
30
- type ConversationRole = 'ADMIN' | 'MEMBER';
31
- ```
32
-
33
- ### User Object
34
- ```typescript
35
- interface User {
36
- id: string;
37
- username: string;
38
- profile?: {
39
- displayName?: string;
40
- profilePicture?: string;
41
- };
42
- }
43
- ```
44
-
45
- ### Conversation Object
46
- ```typescript
47
- interface Conversation {
48
- id: string;
49
- type: ConversationType;
50
- name?: string; // Required for GROUP, null for DM
51
- createdAt: Date;
52
- updatedAt: Date;
53
- members: ConversationMember[];
54
- lastMessage?: Message;
55
- unreadCount: number;
56
- unreadMentionCount: number;
57
- }
58
-
59
- interface ConversationMember {
60
- id: string;
61
- userId: string;
62
- conversationId: string;
63
- role: ConversationRole;
64
- joinedAt: Date;
65
- lastViewedAt?: Date;
66
- lastViewedMentionAt?: Date;
67
- user: User;
68
- }
69
- ```
70
-
71
- ### Message Object
72
- ```typescript
73
- interface Message {
74
- id: string;
75
- content: string;
76
- senderId: string;
77
- conversationId: string;
78
- createdAt: Date;
79
- sender: User;
80
- mentions: Array<{ user: User }>;
81
- mentionsMe: boolean; // True if current user is mentioned
82
- }
83
- ```
84
-
85
- ---
86
-
87
- ## 💬 Conversation Endpoints
88
-
89
- ### `conversation.list`
90
- **Type**: Query
91
- **Description**: Get all conversations for current user with unread counts
92
-
93
- **Input**: None
94
-
95
- **Output**:
96
- ```typescript
97
- Array<{
98
- id: string;
99
- type: 'DM' | 'GROUP';
100
- name?: string;
101
- createdAt: Date;
102
- updatedAt: Date;
103
- members: ConversationMember[];
104
- lastMessage?: Message;
105
- unreadCount: number; // Regular unread messages
106
- unreadMentionCount: number; // Unread mentions
107
- }>
108
- ```
109
-
110
- **Example**:
111
- ```typescript
112
- const conversations = await trpc.conversation.list.query();
113
- ```
114
-
115
- ---
116
-
117
- ### `conversation.create`
118
- **Type**: Mutation
119
- **Description**: Create a new conversation (DM or Group)
120
-
121
- **Input**:
122
- ```typescript
123
- {
124
- type: 'DM' | 'GROUP';
125
- name?: string; // Required for GROUP
126
- memberIds: string[]; // User IDs to add (1 for DM, multiple for GROUP)
127
- }
128
- ```
129
-
130
- **Output**: `Conversation`
131
-
132
- **Examples**:
133
- ```typescript
134
- // Create DM
135
- const dmConversation = await trpc.conversation.create.mutate({
136
- type: 'DM',
137
- memberIds: ['user-123']
138
- });
139
-
140
- // Create Group
141
- const groupConversation = await trpc.conversation.create.mutate({
142
- type: 'GROUP',
143
- name: 'Project Team',
144
- memberIds: ['user-123', 'user-456', 'user-789']
145
- });
146
- ```
147
-
148
- **Notes**:
149
- - For DMs: Automatically detects and returns existing DM if it exists
150
- - For Groups: Creator becomes ADMIN, others become MEMBER
151
- - All memberIds must be valid user IDs
152
-
153
- ---
154
-
155
- ### `conversation.get`
156
- **Type**: Query
157
- **Description**: Get specific conversation details
158
-
159
- **Input**:
160
- ```typescript
161
- {
162
- conversationId: string;
163
- }
164
- ```
165
-
166
- **Output**: `Conversation`
167
-
168
- **Example**:
169
- ```typescript
170
- const conversation = await trpc.conversation.get.query({
171
- conversationId: 'conv-123'
172
- });
173
- ```
174
-
175
- ---
176
-
177
- ## 💬 Message Endpoints
178
-
179
- ### `message.list`
180
- **Type**: Query
181
- **Description**: Get paginated messages for a conversation (lazy loading)
182
-
183
- **Input**:
184
- ```typescript
185
- {
186
- conversationId: string;
187
- cursor?: string; // ISO date string for pagination
188
- limit?: number; // Default: 50, Max: 100
189
- }
190
- ```
191
-
192
- **Output**:
193
- ```typescript
194
- {
195
- messages: Message[];
196
- nextCursor?: string; // Use for next page
197
- }
198
- ```
199
-
200
- **Example**:
201
- ```typescript
202
- // Get first page
203
- const firstPage = await trpc.message.list.query({
204
- conversationId: 'conv-123',
205
- limit: 20
206
- });
207
-
208
- // Get next page
209
- const nextPage = await trpc.message.list.query({
210
- conversationId: 'conv-123',
211
- cursor: firstPage.nextCursor,
212
- limit: 20
213
- });
214
- ```
215
-
216
- **Notes**:
217
- - Messages are returned in chronological order (oldest first)
218
- - Use `cursor` for infinite scroll pagination
219
- - Each message includes mention info and `mentionsMe` flag
220
-
221
- ---
222
-
223
- ### `message.send`
224
- **Type**: Mutation
225
- **Description**: Send a new message with optional mentions
226
-
227
- **Input**:
228
- ```typescript
229
- {
230
- conversationId: string;
231
- content: string; // 1-4000 characters
232
- mentionedUserIds?: string[]; // Optional user IDs to mention
233
- }
234
- ```
235
-
236
- **Output**:
237
- ```typescript
238
- {
239
- id: string;
240
- content: string;
241
- senderId: string;
242
- conversationId: string;
243
- createdAt: Date;
244
- sender: User;
245
- mentionedUserIds: string[];
246
- }
247
- ```
248
-
249
- **Example**:
250
- ```typescript
251
- // Regular message
252
- const message = await trpc.message.send.mutate({
253
- conversationId: 'conv-123',
254
- content: 'Hello everyone!'
255
- });
256
-
257
- // Message with mentions
258
- const mentionMessage = await trpc.message.send.mutate({
259
- conversationId: 'conv-123',
260
- content: 'Hey @john, can you review this?',
261
- mentionedUserIds: ['user-john-id']
262
- });
263
- ```
264
-
265
- **Notes**:
266
- - Mentioned users must be members of the conversation
267
- - Broadcasts real-time event to all conversation members
268
- - Updates conversation's `updatedAt` timestamp
269
-
270
- ---
271
-
272
- ### `message.markAsRead`
273
- **Type**: Mutation
274
- **Description**: Mark all messages in conversation as read
275
-
276
- **Input**:
277
- ```typescript
278
- {
279
- conversationId: string;
280
- }
281
- ```
282
-
283
- **Output**:
284
- ```typescript
285
- {
286
- success: boolean;
287
- }
288
- ```
289
-
290
- **Example**:
291
- ```typescript
292
- await trpc.message.markAsRead.mutate({
293
- conversationId: 'conv-123'
294
- });
295
- ```
296
-
297
- **Notes**:
298
- - Updates user's `lastViewedAt` timestamp for the conversation
299
- - Affects `unreadCount` but NOT `unreadMentionCount`
300
- - Broadcasts `conversation-viewed` event
301
-
302
- ---
303
-
304
- ### `message.markMentionsAsRead`
305
- **Type**: Mutation
306
- **Description**: Mark all mentions in conversation as read (separate from regular messages)
307
-
308
- **Input**:
309
- ```typescript
310
- {
311
- conversationId: string;
312
- }
313
- ```
314
-
315
- **Output**:
316
- ```typescript
317
- {
318
- success: boolean;
319
- }
320
- ```
321
-
322
- **Example**:
323
- ```typescript
324
- await trpc.message.markMentionsAsRead.mutate({
325
- conversationId: 'conv-123'
326
- });
327
- ```
328
-
329
- **Notes**:
330
- - Updates user's `lastViewedMentionAt` timestamp
331
- - Affects `unreadMentionCount` but NOT `unreadCount`
332
- - Broadcasts `mentions-viewed` event
333
-
334
- ---
335
-
336
- ### `message.getUnreadCount`
337
- **Type**: Query
338
- **Description**: Get unread counts for a conversation
339
-
340
- **Input**:
341
- ```typescript
342
- {
343
- conversationId: string;
344
- }
345
- ```
346
-
347
- **Output**:
348
- ```typescript
349
- {
350
- unreadCount: number; // Regular unread messages
351
- unreadMentionCount: number; // Unread mentions
352
- }
353
- ```
354
-
355
- **Example**:
356
- ```typescript
357
- const counts = await trpc.message.getUnreadCount.query({
358
- conversationId: 'conv-123'
359
- });
360
-
361
- // Use for UI badges
362
- if (counts.unreadMentionCount > 0) {
363
- // Show red mention badge
364
- } else if (counts.unreadCount > 0) {
365
- // Show regular unread badge
366
- }
367
- ```
368
-
369
- ---
370
-
371
- ## 🔴 Real-time Events (Pusher)
372
-
373
- ### Channel Pattern
374
- Subscribe to: `conversation-{conversationId}`
375
-
376
- ### Events
377
-
378
- #### `new-message`
379
- **Triggered**: When someone sends a message
380
- **Payload**:
381
- ```typescript
382
- {
383
- id: string;
384
- content: string;
385
- senderId: string;
386
- conversationId: string;
387
- createdAt: Date;
388
- sender: User;
389
- mentionedUserIds: string[];
390
- }
391
- ```
392
-
393
- #### `conversation-viewed`
394
- **Triggered**: When someone marks messages as read
395
- **Payload**:
396
- ```typescript
397
- {
398
- userId: string;
399
- viewedAt: Date;
400
- }
401
- ```
402
-
403
- #### `mentions-viewed`
404
- **Triggered**: When someone marks mentions as read
405
- **Payload**:
406
- ```typescript
407
- {
408
- userId: string;
409
- viewedAt: Date;
410
- }
411
- ```
412
-
413
- ### Frontend Integration Example
414
- ```typescript
415
- import Pusher from 'pusher-js';
416
-
417
- const pusher = new Pusher('your-pusher-key', {
418
- cluster: 'your-cluster'
419
- });
420
-
421
- // Subscribe to conversation
422
- const channel = pusher.subscribe(`conversation-${conversationId}`);
423
-
424
- // Listen for new messages
425
- channel.bind('new-message', (data) => {
426
- // Add message to UI
427
- addMessageToUI(data);
428
-
429
- // Check if user is mentioned
430
- if (data.mentionedUserIds.includes(currentUserId)) {
431
- // Show mention notification
432
- showMentionNotification(data);
433
- }
434
- });
435
-
436
- // Listen for read receipts
437
- channel.bind('conversation-viewed', (data) => {
438
- // Update read indicators
439
- updateReadIndicators(data.userId, data.viewedAt);
440
- });
441
- ```
442
-
443
- ---
444
-
445
- ## 🎨 UI/UX Guidelines
446
-
447
- ### Unread Indicators
448
- ```typescript
449
- // Conversation list item
450
- function ConversationItem({ conversation }) {
451
- const { unreadCount, unreadMentionCount } = conversation;
452
-
453
- return (
454
- <div className="conversation-item">
455
- <div className="conversation-info">
456
- {/* Conversation details */}
457
- </div>
458
-
459
- <div className="badges">
460
- {unreadMentionCount > 0 && (
461
- <Badge variant="mention" count={unreadMentionCount} />
462
- )}
463
- {unreadCount > 0 && (
464
- <Badge variant="regular" count={unreadCount} />
465
- )}
466
- </div>
467
- </div>
468
- );
469
- }
470
- ```
471
-
472
- ### Message Display
473
- ```typescript
474
- function MessageItem({ message, currentUserId }) {
475
- const isMentioned = message.mentionsMe;
476
- const isOwnMessage = message.senderId === currentUserId;
477
-
478
- return (
479
- <div className={`message ${isMentioned ? 'mentioned' : ''} ${isOwnMessage ? 'own' : ''}`}>
480
- <div className="message-header">
481
- <span className="sender">{message.sender.profile?.displayName || message.sender.username}</span>
482
- <span className="timestamp">{formatTime(message.createdAt)}</span>
483
- </div>
484
-
485
- <div className="message-content">
486
- {renderMessageWithMentions(message.content, message.mentions)}
487
- </div>
488
- </div>
489
- );
490
- }
491
- ```
492
-
493
- ### Mention Input
494
- ```typescript
495
- // When user types @username, suggest users from conversation members
496
- function MessageInput({ conversationId, members }) {
497
- const [content, setContent] = useState('');
498
- const [mentionedUsers, setMentionedUsers] = useState<string[]>([]);
499
-
500
- const handleSend = async () => {
501
- await trpc.message.send.mutate({
502
- conversationId,
503
- content,
504
- mentionedUserIds: mentionedUsers
505
- });
506
-
507
- setContent('');
508
- setMentionedUsers([]);
509
- };
510
-
511
- // Implement @mention autocomplete logic
512
- }
513
- ```
514
-
515
- ---
516
-
517
- ## ⚠️ Error Handling
518
-
519
- ### Common Error Codes
520
- - `UNAUTHORIZED`: Invalid or missing authentication
521
- - `FORBIDDEN`: Not a member of the conversation
522
- - `NOT_FOUND`: Conversation doesn't exist
523
- - `BAD_REQUEST`: Invalid input (e.g., mentioned user not in conversation)
524
-
525
- ### Error Response Format
526
- ```typescript
527
- {
528
- error: {
529
- code: string;
530
- message: string;
531
- data?: any;
532
- }
533
- }
534
- ```
535
-
536
- ---
537
-
538
- ## 🚀 Implementation Checklist
539
-
540
- ### Basic Chat
541
- - [ ] Display conversation list with unread counts
542
- - [ ] Create DM conversations
543
- - [ ] Send and receive messages
544
- - [ ] Real-time message updates
545
- - [ ] Mark conversations as read
546
-
547
- ### Advanced Features
548
- - [ ] Create group conversations
549
- - [ ] @mention autocomplete
550
- - [ ] Separate mention/regular unread tracking
551
- - [ ] Mention notifications
552
- - [ ] Message pagination/infinite scroll
553
- - [ ] Typing indicators (optional)
554
- - [ ] Message search (optional)
555
-
556
- ### UI Components Needed
557
- - [ ] ConversationList
558
- - [ ] ConversationItem
559
- - [ ] MessageList
560
- - [ ] MessageItem
561
- - [ ] MessageInput
562
- - [ ] MentionAutocomplete
563
- - [ ] UnreadBadge
564
- - [ ] UserAvatar
565
-
566
- ---
567
-
568
- ## 📱 Mobile Considerations
569
-
570
- - Use appropriate notification permissions
571
- - Handle app backgrounding/foregrounding
572
- - Implement proper connection management for Pusher
573
- - Consider offline message queuing
574
-
575
- ---
576
-
577
- **Generated**: September 22, 2025
578
- **Version**: 1.0
579
- **Backend Version**: Compatible with Studious LMS Server v1.1.8+