convex-feedback 0.1.1 → 0.1.2
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.
- package/README.md +71 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/client/api.d.ts +21 -17
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/index.d.ts +146 -174
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +71 -10
- package/dist/client/index.js.map +1 -1
- package/dist/component/comments.d.ts +12 -12
- package/dist/component/comments.d.ts.map +1 -1
- package/dist/component/comments.js +43 -60
- package/dist/component/comments.js.map +1 -1
- package/dist/component/entries.d.ts +29 -29
- package/dist/component/entries.d.ts.map +1 -1
- package/dist/component/entries.js +18 -25
- package/dist/component/entries.js.map +1 -1
- package/dist/react/index.d.ts +16 -10
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/api.ts +21 -16
- package/src/client/index.ts +407 -14
- package/src/component/comments.ts +43 -61
- package/src/component/entries.ts +18 -22
- package/src/react/index.ts +11 -3
|
@@ -22,28 +22,16 @@ import schema from "./schema.js";
|
|
|
22
22
|
export const list = query({
|
|
23
23
|
args: {
|
|
24
24
|
paginationOpts: paginationOptsValidator,
|
|
25
|
-
entryId: v.
|
|
26
|
-
parentCommentId: v.optional(v.
|
|
25
|
+
entryId: v.id("entries"),
|
|
26
|
+
parentCommentId: v.optional(v.id("comments")),
|
|
27
27
|
sort: commentSortValidator,
|
|
28
28
|
viewerActorId: v.optional(v.string()),
|
|
29
29
|
},
|
|
30
30
|
returns: paginationResultValidator(publicCommentValidator),
|
|
31
31
|
handler: async (ctx, args) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const parentCommentId =
|
|
36
|
-
args.parentCommentId === undefined
|
|
37
|
-
? undefined
|
|
38
|
-
: ctx.db.normalizeId("comments", args.parentCommentId);
|
|
39
|
-
|
|
40
|
-
if (parentCommentId === null) {
|
|
41
|
-
throw new ConvexError("Parent comment not found.");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (parentCommentId !== undefined && parentCommentId !== null) {
|
|
45
|
-
const parent = await ctx.db.get("comments", parentCommentId);
|
|
46
|
-
if (parent === null || parent.entryId !== entryId) {
|
|
32
|
+
if (args.parentCommentId !== undefined) {
|
|
33
|
+
const parent = await ctx.db.get("comments", args.parentCommentId);
|
|
34
|
+
if (parent === null || parent.entryId !== args.entryId) {
|
|
47
35
|
throw new ConvexError("Parent comment not found on this entry.");
|
|
48
36
|
}
|
|
49
37
|
}
|
|
@@ -54,14 +42,18 @@ export const list = query({
|
|
|
54
42
|
? await db
|
|
55
43
|
.query("comments")
|
|
56
44
|
.withIndex("by_entry_parent_likes", (q) =>
|
|
57
|
-
q
|
|
45
|
+
q
|
|
46
|
+
.eq("entryId", args.entryId)
|
|
47
|
+
.eq("parentCommentId", args.parentCommentId),
|
|
58
48
|
)
|
|
59
49
|
.order("desc")
|
|
60
50
|
.paginate(args.paginationOpts)
|
|
61
51
|
: await db
|
|
62
52
|
.query("comments")
|
|
63
53
|
.withIndex("by_entry_parent", (q) =>
|
|
64
|
-
q
|
|
54
|
+
q
|
|
55
|
+
.eq("entryId", args.entryId)
|
|
56
|
+
.eq("parentCommentId", args.parentCommentId),
|
|
65
57
|
)
|
|
66
58
|
.order(args.sort === "newest" ? "desc" : "asc")
|
|
67
59
|
.paginate(args.paginationOpts);
|
|
@@ -80,21 +72,19 @@ export const list = query({
|
|
|
80
72
|
export const create = mutation({
|
|
81
73
|
args: {
|
|
82
74
|
actorId: v.string(),
|
|
83
|
-
entryId: v.
|
|
84
|
-
parentCommentId: v.optional(v.
|
|
75
|
+
entryId: v.id("entries"),
|
|
76
|
+
parentCommentId: v.optional(v.id("comments")),
|
|
85
77
|
body: v.string(),
|
|
86
78
|
maxDepth: v.number(),
|
|
87
79
|
maxCommentLength: v.number(),
|
|
88
80
|
},
|
|
89
|
-
returns: v.
|
|
81
|
+
returns: v.id("comments"),
|
|
90
82
|
handler: async (ctx, args) => {
|
|
91
83
|
assertActorId(args.actorId);
|
|
92
84
|
assertPositiveInteger(args.maxDepth, "Maximum comment depth");
|
|
93
85
|
assertPositiveInteger(args.maxCommentLength, "Maximum comment length");
|
|
94
86
|
|
|
95
|
-
const
|
|
96
|
-
if (entryId === null) throw new ConvexError("Entry not found.");
|
|
97
|
-
const entry = await ctx.db.get("entries", entryId);
|
|
87
|
+
const entry = await ctx.db.get("entries", args.entryId);
|
|
98
88
|
if (entry === null) throw new ConvexError("Entry not found.");
|
|
99
89
|
|
|
100
90
|
const body = normalizeRequiredText(
|
|
@@ -103,18 +93,10 @@ export const create = mutation({
|
|
|
103
93
|
args.maxCommentLength,
|
|
104
94
|
);
|
|
105
95
|
|
|
106
|
-
const parentCommentId =
|
|
107
|
-
args.parentCommentId === undefined
|
|
108
|
-
? undefined
|
|
109
|
-
: ctx.db.normalizeId("comments", args.parentCommentId);
|
|
110
|
-
if (args.parentCommentId !== undefined && parentCommentId === null) {
|
|
111
|
-
throw new ConvexError("Parent comment not found.");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
96
|
let depth = 0;
|
|
115
|
-
if (parentCommentId !== undefined
|
|
116
|
-
const parent = await ctx.db.get("comments", parentCommentId);
|
|
117
|
-
if (parent === null || parent.entryId !== entryId) {
|
|
97
|
+
if (args.parentCommentId !== undefined) {
|
|
98
|
+
const parent = await ctx.db.get("comments", args.parentCommentId);
|
|
99
|
+
if (parent === null || parent.entryId !== args.entryId) {
|
|
118
100
|
throw new ConvexError("Parent comment not found on this entry.");
|
|
119
101
|
}
|
|
120
102
|
depth = parent.depth + 1;
|
|
@@ -126,10 +108,10 @@ export const create = mutation({
|
|
|
126
108
|
}
|
|
127
109
|
|
|
128
110
|
const commentId = await ctx.db.insert("comments", {
|
|
129
|
-
entryId,
|
|
130
|
-
...(parentCommentId === undefined
|
|
111
|
+
entryId: args.entryId,
|
|
112
|
+
...(args.parentCommentId === undefined
|
|
131
113
|
? {}
|
|
132
|
-
: { parentCommentId }),
|
|
114
|
+
: { parentCommentId: args.parentCommentId }),
|
|
133
115
|
actorId: args.actorId,
|
|
134
116
|
depth,
|
|
135
117
|
body,
|
|
@@ -137,14 +119,14 @@ export const create = mutation({
|
|
|
137
119
|
replyCount: 0,
|
|
138
120
|
});
|
|
139
121
|
|
|
140
|
-
await ctx.db.patch("entries", entryId, {
|
|
122
|
+
await ctx.db.patch("entries", args.entryId, {
|
|
141
123
|
commentCount: entry.commentCount + 1,
|
|
142
124
|
});
|
|
143
125
|
|
|
144
|
-
if (parentCommentId !== undefined
|
|
145
|
-
const parent = await ctx.db.get("comments", parentCommentId);
|
|
126
|
+
if (args.parentCommentId !== undefined) {
|
|
127
|
+
const parent = await ctx.db.get("comments", args.parentCommentId);
|
|
146
128
|
if (parent !== null) {
|
|
147
|
-
await ctx.db.patch("comments", parentCommentId, {
|
|
129
|
+
await ctx.db.patch("comments", args.parentCommentId, {
|
|
148
130
|
replyCount: parent.replyCount + 1,
|
|
149
131
|
});
|
|
150
132
|
}
|
|
@@ -157,7 +139,7 @@ export const create = mutation({
|
|
|
157
139
|
export const update = mutation({
|
|
158
140
|
args: {
|
|
159
141
|
actor: actorValidator,
|
|
160
|
-
commentId: v.
|
|
142
|
+
commentId: v.id("comments"),
|
|
161
143
|
body: v.string(),
|
|
162
144
|
editableByAuthor: v.boolean(),
|
|
163
145
|
maxCommentLength: v.number(),
|
|
@@ -165,9 +147,7 @@ export const update = mutation({
|
|
|
165
147
|
returns: v.null(),
|
|
166
148
|
handler: async (ctx, args) => {
|
|
167
149
|
assertActorId(args.actor.id);
|
|
168
|
-
const
|
|
169
|
-
if (commentId === null) throw new ConvexError("Comment not found.");
|
|
170
|
-
const comment = await ctx.db.get("comments", commentId);
|
|
150
|
+
const comment = await ctx.db.get("comments", args.commentId);
|
|
171
151
|
if (comment === null) throw new ConvexError("Comment not found.");
|
|
172
152
|
if (comment.deletedAt !== undefined) {
|
|
173
153
|
throw new ConvexError("Deleted comments cannot be edited.");
|
|
@@ -183,7 +163,10 @@ export const update = mutation({
|
|
|
183
163
|
"Comment",
|
|
184
164
|
args.maxCommentLength,
|
|
185
165
|
);
|
|
186
|
-
await ctx.db.patch("comments", commentId, {
|
|
166
|
+
await ctx.db.patch("comments", args.commentId, {
|
|
167
|
+
body,
|
|
168
|
+
updatedAt: Date.now(),
|
|
169
|
+
});
|
|
187
170
|
return null;
|
|
188
171
|
},
|
|
189
172
|
});
|
|
@@ -191,15 +174,13 @@ export const update = mutation({
|
|
|
191
174
|
export const remove = mutation({
|
|
192
175
|
args: {
|
|
193
176
|
actor: actorValidator,
|
|
194
|
-
commentId: v.
|
|
177
|
+
commentId: v.id("comments"),
|
|
195
178
|
deletableByAuthor: v.boolean(),
|
|
196
179
|
},
|
|
197
180
|
returns: v.null(),
|
|
198
181
|
handler: async (ctx, args) => {
|
|
199
182
|
assertActorId(args.actor.id);
|
|
200
|
-
const
|
|
201
|
-
if (commentId === null) throw new ConvexError("Comment not found.");
|
|
202
|
-
const comment = await ctx.db.get("comments", commentId);
|
|
183
|
+
const comment = await ctx.db.get("comments", args.commentId);
|
|
203
184
|
if (comment === null) throw new ConvexError("Comment not found.");
|
|
204
185
|
if (comment.deletedAt !== undefined) return null;
|
|
205
186
|
|
|
@@ -210,7 +191,7 @@ export const remove = mutation({
|
|
|
210
191
|
throw new ConvexError("Not authorized to delete this comment.");
|
|
211
192
|
}
|
|
212
193
|
|
|
213
|
-
await ctx.db.patch("comments", commentId, { deletedAt: Date.now() });
|
|
194
|
+
await ctx.db.patch("comments", args.commentId, { deletedAt: Date.now() });
|
|
214
195
|
return null;
|
|
215
196
|
},
|
|
216
197
|
});
|
|
@@ -218,15 +199,13 @@ export const remove = mutation({
|
|
|
218
199
|
export const setLike = mutation({
|
|
219
200
|
args: {
|
|
220
201
|
actorId: v.string(),
|
|
221
|
-
commentId: v.
|
|
202
|
+
commentId: v.id("comments"),
|
|
222
203
|
desiredState: v.boolean(),
|
|
223
204
|
},
|
|
224
205
|
returns: v.object({ active: v.boolean(), likeCount: v.number() }),
|
|
225
206
|
handler: async (ctx, args) => {
|
|
226
207
|
assertActorId(args.actorId);
|
|
227
|
-
const
|
|
228
|
-
if (commentId === null) throw new ConvexError("Comment not found.");
|
|
229
|
-
const comment = await ctx.db.get("comments", commentId);
|
|
208
|
+
const comment = await ctx.db.get("comments", args.commentId);
|
|
230
209
|
if (comment === null) throw new ConvexError("Comment not found.");
|
|
231
210
|
if (comment.deletedAt !== undefined) {
|
|
232
211
|
throw new ConvexError("Deleted comments cannot receive likes.");
|
|
@@ -235,21 +214,24 @@ export const setLike = mutation({
|
|
|
235
214
|
const existing = await ctx.db
|
|
236
215
|
.query("reactions")
|
|
237
216
|
.withIndex("by_comment_actor", (q) =>
|
|
238
|
-
q.eq("commentId", commentId).eq("actorId", args.actorId),
|
|
217
|
+
q.eq("commentId", args.commentId).eq("actorId", args.actorId),
|
|
239
218
|
)
|
|
240
219
|
.unique();
|
|
241
220
|
|
|
242
221
|
if (args.desiredState && existing === null) {
|
|
243
|
-
await ctx.db.insert("reactions", {
|
|
222
|
+
await ctx.db.insert("reactions", {
|
|
223
|
+
actorId: args.actorId,
|
|
224
|
+
commentId: args.commentId,
|
|
225
|
+
});
|
|
244
226
|
const likeCount = comment.likeCount + 1;
|
|
245
|
-
await ctx.db.patch("comments", commentId, { likeCount });
|
|
227
|
+
await ctx.db.patch("comments", args.commentId, { likeCount });
|
|
246
228
|
return { active: true, likeCount };
|
|
247
229
|
}
|
|
248
230
|
|
|
249
231
|
if (!args.desiredState && existing !== null) {
|
|
250
232
|
await ctx.db.delete("reactions", existing._id);
|
|
251
233
|
const likeCount = Math.max(0, comment.likeCount - 1);
|
|
252
|
-
await ctx.db.patch("comments", commentId, { likeCount });
|
|
234
|
+
await ctx.db.patch("comments", args.commentId, { likeCount });
|
|
253
235
|
return { active: false, likeCount };
|
|
254
236
|
}
|
|
255
237
|
|
package/src/component/entries.ts
CHANGED
|
@@ -198,14 +198,12 @@ export const list = query({
|
|
|
198
198
|
|
|
199
199
|
export const get = query({
|
|
200
200
|
args: {
|
|
201
|
-
entryId: v.
|
|
201
|
+
entryId: v.id("entries"),
|
|
202
202
|
viewerActorId: v.optional(v.string()),
|
|
203
203
|
},
|
|
204
204
|
returns: v.union(publicEntryValidator, v.null()),
|
|
205
205
|
handler: async (ctx, args) => {
|
|
206
|
-
const
|
|
207
|
-
if (entryId === null) return null;
|
|
208
|
-
const entry = await ctx.db.get("entries", entryId);
|
|
206
|
+
const entry = await ctx.db.get("entries", args.entryId);
|
|
209
207
|
return entry === null
|
|
210
208
|
? null
|
|
211
209
|
: serializeEntry(ctx, entry, args.viewerActorId);
|
|
@@ -427,7 +425,7 @@ export const create = mutation({
|
|
|
427
425
|
maxTitleLength: v.number(),
|
|
428
426
|
maxBodyLength: v.number(),
|
|
429
427
|
},
|
|
430
|
-
returns: v.
|
|
428
|
+
returns: v.id("entries"),
|
|
431
429
|
handler: async (ctx, args) => {
|
|
432
430
|
assertActorId(args.actorId);
|
|
433
431
|
if (!args.enabledKinds.includes(args.kind)) {
|
|
@@ -465,7 +463,7 @@ export const create = mutation({
|
|
|
465
463
|
export const update = mutation({
|
|
466
464
|
args: {
|
|
467
465
|
actor: actorValidator,
|
|
468
|
-
entryId: v.
|
|
466
|
+
entryId: v.id("entries"),
|
|
469
467
|
title: v.string(),
|
|
470
468
|
body: v.string(),
|
|
471
469
|
editableByAuthor: v.boolean(),
|
|
@@ -475,9 +473,7 @@ export const update = mutation({
|
|
|
475
473
|
returns: v.null(),
|
|
476
474
|
handler: async (ctx, args) => {
|
|
477
475
|
assertActorId(args.actor.id);
|
|
478
|
-
const
|
|
479
|
-
if (entryId === null) throw new ConvexError("Entry not found.");
|
|
480
|
-
const entry = await ctx.db.get("entries", entryId);
|
|
476
|
+
const entry = await ctx.db.get("entries", args.entryId);
|
|
481
477
|
if (entry === null) throw new ConvexError("Entry not found.");
|
|
482
478
|
|
|
483
479
|
const canEdit =
|
|
@@ -492,7 +488,7 @@ export const update = mutation({
|
|
|
492
488
|
);
|
|
493
489
|
const body = normalizeRequiredText(args.body, "Body", args.maxBodyLength);
|
|
494
490
|
|
|
495
|
-
await ctx.db.patch("entries", entryId, {
|
|
491
|
+
await ctx.db.patch("entries", args.entryId, {
|
|
496
492
|
title,
|
|
497
493
|
body,
|
|
498
494
|
normalizedTitle: normalizeTitle(title),
|
|
@@ -506,7 +502,7 @@ export const update = mutation({
|
|
|
506
502
|
export const setStatus = mutation({
|
|
507
503
|
args: {
|
|
508
504
|
actor: actorValidator,
|
|
509
|
-
entryId: v.
|
|
505
|
+
entryId: v.id("entries"),
|
|
510
506
|
status: entryStatusValidator,
|
|
511
507
|
},
|
|
512
508
|
returns: v.null(),
|
|
@@ -514,11 +510,10 @@ export const setStatus = mutation({
|
|
|
514
510
|
if (!args.actor.isModerator) {
|
|
515
511
|
throw new ConvexError("Moderator access is required to change status.");
|
|
516
512
|
}
|
|
517
|
-
|
|
518
|
-
if (entryId === null || (await ctx.db.get("entries", entryId)) === null) {
|
|
513
|
+
if ((await ctx.db.get("entries", args.entryId)) === null) {
|
|
519
514
|
throw new ConvexError("Entry not found.");
|
|
520
515
|
}
|
|
521
|
-
await ctx.db.patch("entries", entryId, {
|
|
516
|
+
await ctx.db.patch("entries", args.entryId, {
|
|
522
517
|
status: args.status,
|
|
523
518
|
updatedAt: Date.now(),
|
|
524
519
|
});
|
|
@@ -529,35 +524,36 @@ export const setStatus = mutation({
|
|
|
529
524
|
export const setUpvote = mutation({
|
|
530
525
|
args: {
|
|
531
526
|
actorId: v.string(),
|
|
532
|
-
entryId: v.
|
|
527
|
+
entryId: v.id("entries"),
|
|
533
528
|
desiredState: v.boolean(),
|
|
534
529
|
},
|
|
535
530
|
returns: v.object({ active: v.boolean(), upvoteCount: v.number() }),
|
|
536
531
|
handler: async (ctx, args) => {
|
|
537
532
|
assertActorId(args.actorId);
|
|
538
|
-
const
|
|
539
|
-
if (entryId === null) throw new ConvexError("Entry not found.");
|
|
540
|
-
const entry = await ctx.db.get("entries", entryId);
|
|
533
|
+
const entry = await ctx.db.get("entries", args.entryId);
|
|
541
534
|
if (entry === null) throw new ConvexError("Entry not found.");
|
|
542
535
|
|
|
543
536
|
const existing = await ctx.db
|
|
544
537
|
.query("reactions")
|
|
545
538
|
.withIndex("by_entry_actor", (q) =>
|
|
546
|
-
q.eq("entryId", entryId).eq("actorId", args.actorId),
|
|
539
|
+
q.eq("entryId", args.entryId).eq("actorId", args.actorId),
|
|
547
540
|
)
|
|
548
541
|
.unique();
|
|
549
542
|
|
|
550
543
|
if (args.desiredState && existing === null) {
|
|
551
|
-
await ctx.db.insert("reactions", {
|
|
544
|
+
await ctx.db.insert("reactions", {
|
|
545
|
+
actorId: args.actorId,
|
|
546
|
+
entryId: args.entryId,
|
|
547
|
+
});
|
|
552
548
|
const upvoteCount = entry.upvoteCount + 1;
|
|
553
|
-
await ctx.db.patch("entries", entryId, { upvoteCount });
|
|
549
|
+
await ctx.db.patch("entries", args.entryId, { upvoteCount });
|
|
554
550
|
return { active: true, upvoteCount };
|
|
555
551
|
}
|
|
556
552
|
|
|
557
553
|
if (!args.desiredState && existing !== null) {
|
|
558
554
|
await ctx.db.delete("reactions", existing._id);
|
|
559
555
|
const upvoteCount = Math.max(0, entry.upvoteCount - 1);
|
|
560
|
-
await ctx.db.patch("entries", entryId, { upvoteCount });
|
|
556
|
+
await ctx.db.patch("entries", args.entryId, { upvoteCount });
|
|
561
557
|
return { active: false, upvoteCount };
|
|
562
558
|
}
|
|
563
559
|
|
package/src/react/index.ts
CHANGED
|
@@ -182,9 +182,12 @@ function positivePageSize(value: number | undefined, fallback: number): number {
|
|
|
182
182
|
*
|
|
183
183
|
* Create this once in the consuming application and reuse the returned hook
|
|
184
184
|
* collection.
|
|
185
|
+
*
|
|
186
|
+
* @typeParam RateLimitResult The validated non-throwing rate-limit rejection
|
|
187
|
+
* type returned by mutation hooks. It is inferred from the supplied API.
|
|
185
188
|
*/
|
|
186
|
-
export function createFeedbackHooks(
|
|
187
|
-
api: FeedbackPublicApi,
|
|
189
|
+
export function createFeedbackHooks<RateLimitResult = never>(
|
|
190
|
+
api: FeedbackPublicApi<string | undefined, RateLimitResult>,
|
|
188
191
|
options: FeedbackHooksOptions = {},
|
|
189
192
|
) {
|
|
190
193
|
const entryPageSize = positivePageSize(options.entryPageSize, 20);
|
|
@@ -336,5 +339,10 @@ export function createFeedbackHooks(
|
|
|
336
339
|
*
|
|
337
340
|
* This type is intended to be passed to `FeedbackScreen` and other
|
|
338
341
|
* `convex-feedback-ui` integrations.
|
|
342
|
+
*
|
|
343
|
+
* @typeParam RateLimitResult The validated non-throwing rate-limit rejection
|
|
344
|
+
* type returned by mutation hooks.
|
|
339
345
|
*/
|
|
340
|
-
export type FeedbackHooks = ReturnType<
|
|
346
|
+
export type FeedbackHooks<RateLimitResult = never> = ReturnType<
|
|
347
|
+
typeof createFeedbackHooks<RateLimitResult>
|
|
348
|
+
>;
|