@studious-lms/server 1.1.7 → 1.1.9

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.
@@ -0,0 +1,134 @@
1
+ import { z } from 'zod';
2
+ export declare const conversationRouter: import("@trpc/server").TRPCBuiltRouter<{
3
+ ctx: import("../trpc.js").Context;
4
+ meta: object;
5
+ errorShape: {
6
+ data: {
7
+ zodError: z.typeToFlattenedError<any, string> | null;
8
+ prismaError: import("../utils/prismaErrorHandler.js").PrismaErrorInfo | null;
9
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
10
+ httpStatus: number;
11
+ path?: string;
12
+ stack?: string;
13
+ };
14
+ message: string;
15
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
16
+ };
17
+ transformer: false;
18
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
19
+ list: import("@trpc/server").TRPCQueryProcedure<{
20
+ input: void;
21
+ output: {
22
+ id: string;
23
+ type: import(".prisma/client").$Enums.ConversationType;
24
+ name: string | null;
25
+ createdAt: Date;
26
+ updatedAt: Date;
27
+ members: ({
28
+ user: {
29
+ id: string;
30
+ username: string;
31
+ profile: {
32
+ displayName: string | null;
33
+ profilePicture: string | null;
34
+ } | null;
35
+ };
36
+ } & {
37
+ id: string;
38
+ role: import(".prisma/client").$Enums.ConversationRole;
39
+ userId: string;
40
+ conversationId: string;
41
+ joinedAt: Date;
42
+ lastViewedAt: Date | null;
43
+ lastViewedMentionAt: Date | null;
44
+ })[];
45
+ lastMessage: {
46
+ sender: {
47
+ id: string;
48
+ username: string;
49
+ profile: {
50
+ displayName: string | null;
51
+ } | null;
52
+ };
53
+ } & {
54
+ id: string;
55
+ content: string;
56
+ createdAt: Date;
57
+ senderId: string;
58
+ conversationId: string;
59
+ };
60
+ unreadCount: number;
61
+ unreadMentionCount: number;
62
+ }[];
63
+ meta: object;
64
+ }>;
65
+ create: import("@trpc/server").TRPCMutationProcedure<{
66
+ input: {
67
+ type: "DM" | "GROUP";
68
+ memberIds: string[];
69
+ name?: string | undefined;
70
+ };
71
+ output: {
72
+ members: ({
73
+ user: {
74
+ id: string;
75
+ username: string;
76
+ profile: {
77
+ displayName: string | null;
78
+ profilePicture: string | null;
79
+ } | null;
80
+ };
81
+ } & {
82
+ id: string;
83
+ role: import(".prisma/client").$Enums.ConversationRole;
84
+ userId: string;
85
+ conversationId: string;
86
+ joinedAt: Date;
87
+ lastViewedAt: Date | null;
88
+ lastViewedMentionAt: Date | null;
89
+ })[];
90
+ } & {
91
+ type: import(".prisma/client").$Enums.ConversationType;
92
+ id: string;
93
+ name: string | null;
94
+ createdAt: Date;
95
+ updatedAt: Date;
96
+ displayInChat: boolean;
97
+ };
98
+ meta: object;
99
+ }>;
100
+ get: import("@trpc/server").TRPCQueryProcedure<{
101
+ input: {
102
+ conversationId: string;
103
+ };
104
+ output: {
105
+ members: ({
106
+ user: {
107
+ id: string;
108
+ username: string;
109
+ profile: {
110
+ displayName: string | null;
111
+ profilePicture: string | null;
112
+ } | null;
113
+ };
114
+ } & {
115
+ id: string;
116
+ role: import(".prisma/client").$Enums.ConversationRole;
117
+ userId: string;
118
+ conversationId: string;
119
+ joinedAt: Date;
120
+ lastViewedAt: Date | null;
121
+ lastViewedMentionAt: Date | null;
122
+ })[];
123
+ } & {
124
+ type: import(".prisma/client").$Enums.ConversationType;
125
+ id: string;
126
+ name: string | null;
127
+ createdAt: Date;
128
+ updatedAt: Date;
129
+ displayInChat: boolean;
130
+ };
131
+ meta: object;
132
+ }>;
133
+ }>>;
134
+ //# sourceMappingURL=conversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/routers/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuR7B,CAAC"}
@@ -0,0 +1,261 @@
1
+ import { z } from 'zod';
2
+ import { createTRPCRouter, protectedProcedure } from '../trpc.js';
3
+ import { prisma } from '../lib/prisma.js';
4
+ import { TRPCError } from '@trpc/server';
5
+ export const conversationRouter = createTRPCRouter({
6
+ list: protectedProcedure.query(async ({ ctx }) => {
7
+ const userId = ctx.user.id;
8
+ const conversations = await prisma.conversation.findMany({
9
+ where: {
10
+ members: {
11
+ some: {
12
+ userId,
13
+ },
14
+ },
15
+ },
16
+ include: {
17
+ members: {
18
+ include: {
19
+ user: {
20
+ select: {
21
+ id: true,
22
+ username: true,
23
+ profile: {
24
+ select: {
25
+ displayName: true,
26
+ profilePicture: true,
27
+ },
28
+ },
29
+ },
30
+ },
31
+ },
32
+ },
33
+ messages: {
34
+ orderBy: {
35
+ createdAt: 'desc',
36
+ },
37
+ take: 1,
38
+ include: {
39
+ sender: {
40
+ select: {
41
+ id: true,
42
+ username: true,
43
+ profile: {
44
+ select: {
45
+ displayName: true,
46
+ },
47
+ },
48
+ },
49
+ },
50
+ },
51
+ },
52
+ },
53
+ orderBy: {
54
+ updatedAt: 'desc',
55
+ },
56
+ });
57
+ // Calculate unread counts for each conversation
58
+ const conversationsWithUnread = await Promise.all(conversations.map(async (conversation) => {
59
+ const userMembership = conversation.members.find(m => m.userId === userId);
60
+ const lastViewedAt = userMembership?.lastViewedAt;
61
+ const lastViewedMentionAt = userMembership?.lastViewedMentionAt;
62
+ // Count regular unread messages
63
+ const unreadCount = await prisma.message.count({
64
+ where: {
65
+ conversationId: conversation.id,
66
+ senderId: { not: userId },
67
+ ...(lastViewedAt && {
68
+ createdAt: { gt: lastViewedAt }
69
+ }),
70
+ },
71
+ });
72
+ // Count unread mentions
73
+ const unreadMentionCount = await prisma.mention.count({
74
+ where: {
75
+ userId,
76
+ message: {
77
+ conversationId: conversation.id,
78
+ senderId: { not: userId },
79
+ ...(lastViewedMentionAt && {
80
+ createdAt: { gt: lastViewedMentionAt }
81
+ }),
82
+ },
83
+ },
84
+ });
85
+ return {
86
+ id: conversation.id,
87
+ type: conversation.type,
88
+ name: conversation.name,
89
+ createdAt: conversation.createdAt,
90
+ updatedAt: conversation.updatedAt,
91
+ members: conversation.members,
92
+ lastMessage: conversation.messages[0] || null,
93
+ unreadCount,
94
+ unreadMentionCount,
95
+ };
96
+ }));
97
+ return conversationsWithUnread;
98
+ }),
99
+ create: protectedProcedure
100
+ .input(z.object({
101
+ type: z.enum(['DM', 'GROUP']),
102
+ name: z.string().optional(),
103
+ memberIds: z.array(z.string()),
104
+ }))
105
+ .mutation(async ({ input, ctx }) => {
106
+ const userId = ctx.user.id;
107
+ const { type, name, memberIds } = input;
108
+ // Validate input
109
+ if (type === 'GROUP' && !name) {
110
+ throw new TRPCError({
111
+ code: 'BAD_REQUEST',
112
+ message: 'Group conversations must have a name',
113
+ });
114
+ }
115
+ if (type === 'DM' && memberIds.length !== 1) {
116
+ throw new TRPCError({
117
+ code: 'BAD_REQUEST',
118
+ message: 'DM conversations must have exactly one other member',
119
+ });
120
+ }
121
+ // For DMs, check if conversation already exists
122
+ if (type === 'DM') {
123
+ const existingDM = await prisma.conversation.findFirst({
124
+ where: {
125
+ type: 'DM',
126
+ members: {
127
+ every: {
128
+ userId: {
129
+ in: [userId, memberIds[0]],
130
+ },
131
+ },
132
+ },
133
+ AND: {
134
+ members: {
135
+ some: {
136
+ userId,
137
+ },
138
+ },
139
+ },
140
+ },
141
+ include: {
142
+ members: {
143
+ include: {
144
+ user: {
145
+ select: {
146
+ id: true,
147
+ username: true,
148
+ profile: {
149
+ select: {
150
+ displayName: true,
151
+ profilePicture: true,
152
+ },
153
+ },
154
+ },
155
+ },
156
+ },
157
+ },
158
+ },
159
+ });
160
+ if (existingDM) {
161
+ return existingDM;
162
+ }
163
+ }
164
+ // Verify all members exist
165
+ const members = await prisma.user.findMany({
166
+ where: {
167
+ id: {
168
+ in: memberIds,
169
+ },
170
+ },
171
+ select: {
172
+ id: true,
173
+ },
174
+ });
175
+ if (members.length !== memberIds.length) {
176
+ throw new TRPCError({
177
+ code: 'BAD_REQUEST',
178
+ message: 'One or more members not found',
179
+ });
180
+ }
181
+ // Create conversation with members
182
+ const conversation = await prisma.conversation.create({
183
+ data: {
184
+ type,
185
+ name,
186
+ members: {
187
+ create: [
188
+ {
189
+ userId,
190
+ role: type === 'GROUP' ? 'ADMIN' : 'MEMBER',
191
+ },
192
+ ...memberIds.map((memberId) => ({
193
+ userId: memberId,
194
+ role: 'MEMBER',
195
+ })),
196
+ ],
197
+ },
198
+ },
199
+ include: {
200
+ members: {
201
+ include: {
202
+ user: {
203
+ select: {
204
+ id: true,
205
+ username: true,
206
+ profile: {
207
+ select: {
208
+ displayName: true,
209
+ profilePicture: true,
210
+ },
211
+ },
212
+ },
213
+ },
214
+ },
215
+ },
216
+ },
217
+ });
218
+ return conversation;
219
+ }),
220
+ get: protectedProcedure
221
+ .input(z.object({ conversationId: z.string() }))
222
+ .query(async ({ input, ctx }) => {
223
+ const userId = ctx.user.id;
224
+ const { conversationId } = input;
225
+ const conversation = await prisma.conversation.findFirst({
226
+ where: {
227
+ id: conversationId,
228
+ members: {
229
+ some: {
230
+ userId,
231
+ },
232
+ },
233
+ },
234
+ include: {
235
+ members: {
236
+ include: {
237
+ user: {
238
+ select: {
239
+ id: true,
240
+ username: true,
241
+ profile: {
242
+ select: {
243
+ displayName: true,
244
+ profilePicture: true,
245
+ },
246
+ },
247
+ },
248
+ },
249
+ },
250
+ },
251
+ },
252
+ });
253
+ if (!conversation) {
254
+ throw new TRPCError({
255
+ code: 'NOT_FOUND',
256
+ message: 'Conversation not found or access denied',
257
+ });
258
+ }
259
+ return conversation;
260
+ }),
261
+ });
@@ -0,0 +1,108 @@
1
+ import { z } from 'zod';
2
+ export declare const messageRouter: import("@trpc/server").TRPCBuiltRouter<{
3
+ ctx: import("../trpc.js").Context;
4
+ meta: object;
5
+ errorShape: {
6
+ data: {
7
+ zodError: z.typeToFlattenedError<any, string> | null;
8
+ prismaError: import("../utils/prismaErrorHandler.js").PrismaErrorInfo | null;
9
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
10
+ httpStatus: number;
11
+ path?: string;
12
+ stack?: string;
13
+ };
14
+ message: string;
15
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
16
+ };
17
+ transformer: false;
18
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
19
+ list: import("@trpc/server").TRPCQueryProcedure<{
20
+ input: {
21
+ conversationId: string;
22
+ cursor?: string | undefined;
23
+ limit?: number | undefined;
24
+ };
25
+ output: {
26
+ messages: {
27
+ id: string;
28
+ content: string;
29
+ senderId: string;
30
+ conversationId: string;
31
+ createdAt: Date;
32
+ sender: {
33
+ id: string;
34
+ username: string;
35
+ profile: {
36
+ displayName: string | null;
37
+ profilePicture: string | null;
38
+ } | null;
39
+ };
40
+ mentions: {
41
+ user: {
42
+ id: string;
43
+ username: string;
44
+ profile: {
45
+ displayName: string | null;
46
+ } | null;
47
+ };
48
+ }[];
49
+ mentionsMe: boolean;
50
+ }[];
51
+ nextCursor: string | undefined;
52
+ };
53
+ meta: object;
54
+ }>;
55
+ send: import("@trpc/server").TRPCMutationProcedure<{
56
+ input: {
57
+ content: string;
58
+ conversationId: string;
59
+ mentionedUserIds?: string[] | undefined;
60
+ };
61
+ output: {
62
+ id: string;
63
+ content: string;
64
+ senderId: string;
65
+ conversationId: string;
66
+ createdAt: Date;
67
+ sender: {
68
+ id: string;
69
+ username: string;
70
+ profile: {
71
+ displayName: string | null;
72
+ profilePicture: string | null;
73
+ } | null;
74
+ };
75
+ mentionedUserIds: string[];
76
+ };
77
+ meta: object;
78
+ }>;
79
+ markAsRead: import("@trpc/server").TRPCMutationProcedure<{
80
+ input: {
81
+ conversationId: string;
82
+ };
83
+ output: {
84
+ success: boolean;
85
+ };
86
+ meta: object;
87
+ }>;
88
+ markMentionsAsRead: import("@trpc/server").TRPCMutationProcedure<{
89
+ input: {
90
+ conversationId: string;
91
+ };
92
+ output: {
93
+ success: boolean;
94
+ };
95
+ meta: object;
96
+ }>;
97
+ getUnreadCount: import("@trpc/server").TRPCQueryProcedure<{
98
+ input: {
99
+ conversationId: string;
100
+ };
101
+ output: {
102
+ unreadCount: number;
103
+ unreadMentionCount: number;
104
+ };
105
+ meta: object;
106
+ }>;
107
+ }>>;
108
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/routers/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsWxB,CAAC"}