convex-feedback 0.2.0-beta.9 → 0.2.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.
- package/README.md +398 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/client/api.d.ts +49 -9
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/config.d.ts +1 -3
- package/dist/client/config.d.ts.map +1 -1
- package/dist/client/config.js.map +1 -1
- package/dist/client/index.d.ts +707 -15
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +243 -12
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +222 -4
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/admin.d.ts +5 -90
- package/dist/component/admin.d.ts.map +1 -1
- package/dist/component/admin.js +59 -29
- package/dist/component/admin.js.map +1 -1
- package/dist/component/comments.d.ts +107 -15
- package/dist/component/comments.d.ts.map +1 -1
- package/dist/component/comments.js +342 -28
- package/dist/component/comments.js.map +1 -1
- package/dist/component/crons.d.ts.map +1 -1
- package/dist/component/crons.js +4 -0
- package/dist/component/crons.js.map +1 -1
- package/dist/component/entries.d.ts +119 -91
- package/dist/component/entries.d.ts.map +1 -1
- package/dist/component/entries.js +348 -29
- package/dist/component/entries.js.map +1 -1
- package/dist/component/helpers.d.ts +32 -1
- package/dist/component/helpers.d.ts.map +1 -1
- package/dist/component/helpers.js +121 -4
- package/dist/component/helpers.js.map +1 -1
- package/dist/component/migrations.d.ts +24 -0
- package/dist/component/migrations.d.ts.map +1 -1
- package/dist/component/migrations.js +318 -0
- package/dist/component/migrations.js.map +1 -1
- package/dist/component/model.d.ts +455 -120
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/model.js +70 -2
- package/dist/component/model.js.map +1 -1
- package/dist/component/reactions.d.ts +37 -0
- package/dist/component/reactions.d.ts.map +1 -0
- package/dist/component/reactions.js +52 -0
- package/dist/component/reactions.js.map +1 -0
- package/dist/component/roadmap.d.ts +10 -46
- package/dist/component/roadmap.d.ts.map +1 -1
- package/dist/component/roadmap.js +239 -21
- package/dist/component/roadmap.js.map +1 -1
- package/dist/component/schema.d.ts +33 -5
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +40 -4
- package/dist/component/schema.js.map +1 -1
- package/dist/react/index.d.ts +26 -238
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +65 -2
- package/dist/react/index.js.map +1 -1
- package/dist/test.d.ts +23 -5
- package/dist/test.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/README.md +6 -1
- package/src/client/api.ts +89 -7
- package/src/client/config.ts +1 -3
- package/src/client/index.ts +1237 -34
- package/src/component/README.md +32 -4
- package/src/component/_generated/api.ts +2 -0
- package/src/component/_generated/component.ts +290 -8
- package/src/component/admin.ts +88 -30
- package/src/component/comments.ts +405 -26
- package/src/component/crons.ts +10 -0
- package/src/component/entries.ts +388 -27
- package/src/component/helpers.ts +163 -4
- package/src/component/migrations.ts +415 -0
- package/src/component/model.ts +381 -117
- package/src/component/reactions.ts +60 -0
- package/src/component/roadmap.ts +357 -35
- package/src/component/schema.ts +40 -4
- package/src/react/README.md +5 -0
- package/src/react/index.ts +90 -3
package/src/component/model.ts
CHANGED
|
@@ -116,14 +116,91 @@ export const publicCommentValidator = v.object({
|
|
|
116
116
|
parentCommentId: v.optional(v.string()),
|
|
117
117
|
actorId: v.string(),
|
|
118
118
|
depth: v.number(),
|
|
119
|
-
body: v.
|
|
119
|
+
body: v.string(),
|
|
120
120
|
likeCount: v.number(),
|
|
121
121
|
replyCount: v.number(),
|
|
122
122
|
updatedAt: v.optional(v.number()),
|
|
123
|
-
deletedAt: v.optional(v.number()),
|
|
124
123
|
viewerHasLiked: v.boolean(),
|
|
125
124
|
});
|
|
126
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Entry activity returned by actor-scoped queries.
|
|
128
|
+
*
|
|
129
|
+
* Unlike the ordinary public entry shape, activity entries do not contain
|
|
130
|
+
* viewer-relative reaction state. The actor is already the subject of the
|
|
131
|
+
* query.
|
|
132
|
+
*/
|
|
133
|
+
export const activityEntryValidator = v.object({
|
|
134
|
+
id: v.string(),
|
|
135
|
+
creationTime: v.number(),
|
|
136
|
+
actorId: v.string(),
|
|
137
|
+
kind: entryKindValidator,
|
|
138
|
+
status: entryStatusValidator,
|
|
139
|
+
title: v.string(),
|
|
140
|
+
body: v.string(),
|
|
141
|
+
upvoteCount: v.number(),
|
|
142
|
+
commentCount: v.number(),
|
|
143
|
+
updatedAt: v.optional(v.number()),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Component-level activity entry shape with retained diagnostic and triage
|
|
148
|
+
* context. The host wrapper deliberately strips these optional fields from
|
|
149
|
+
* its normal actor-facing API.
|
|
150
|
+
*/
|
|
151
|
+
export const activityEntryWithContextValidator = activityEntryValidator.extend({
|
|
152
|
+
metadata: v.optional(feedbackMetadataValidator),
|
|
153
|
+
priority: v.optional(entryPriorityValidator),
|
|
154
|
+
roadmap: v.optional(roadmapItemValidator),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** Comment activity with the basic entry context needed by an activity UI. */
|
|
158
|
+
export const activityCommentValidator = v.object({
|
|
159
|
+
id: v.string(),
|
|
160
|
+
creationTime: v.number(),
|
|
161
|
+
entryId: v.string(),
|
|
162
|
+
entryTitle: v.string(),
|
|
163
|
+
parentCommentId: v.optional(v.string()),
|
|
164
|
+
actorId: v.string(),
|
|
165
|
+
depth: v.number(),
|
|
166
|
+
body: v.string(),
|
|
167
|
+
likeCount: v.number(),
|
|
168
|
+
replyCount: v.number(),
|
|
169
|
+
updatedAt: v.optional(v.number()),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/** Resolved target context for an entry-upvote activity record. */
|
|
173
|
+
export const entryReactionTargetValidator = v.object({
|
|
174
|
+
id: v.string(),
|
|
175
|
+
title: v.string(),
|
|
176
|
+
kind: entryKindValidator,
|
|
177
|
+
status: entryStatusValidator,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/** Resolved target context for a comment-like activity record. */
|
|
181
|
+
export const commentReactionTargetValidator = v.object({
|
|
182
|
+
id: v.string(),
|
|
183
|
+
body: v.string(),
|
|
184
|
+
entryId: v.string(),
|
|
185
|
+
entryTitle: v.string(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/** A reaction activity record with a live, resolved target. */
|
|
189
|
+
export const feedbackReactionValidator = v.union(
|
|
190
|
+
v.object({
|
|
191
|
+
type: v.literal("entry_upvote"),
|
|
192
|
+
id: v.string(),
|
|
193
|
+
creationTime: v.number(),
|
|
194
|
+
entry: entryReactionTargetValidator,
|
|
195
|
+
}),
|
|
196
|
+
v.object({
|
|
197
|
+
type: v.literal("comment_like"),
|
|
198
|
+
id: v.string(),
|
|
199
|
+
creationTime: v.number(),
|
|
200
|
+
comment: commentReactionTargetValidator,
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
|
|
127
204
|
export const similarEntriesValidator = v.object({
|
|
128
205
|
exact: v.array(publicEntryValidator),
|
|
129
206
|
similar: v.array(publicEntryValidator),
|
|
@@ -135,6 +212,20 @@ export const adminEntryValidator = publicEntryValidator.extend({
|
|
|
135
212
|
metadata: v.optional(feedbackMetadataValidator),
|
|
136
213
|
});
|
|
137
214
|
|
|
215
|
+
type InferredFeedbackActor = Infer<typeof actorValidator>;
|
|
216
|
+
type InferredRoadmapItem = Infer<typeof roadmapItemValidator>;
|
|
217
|
+
type InferredFeedbackMetadata = Infer<typeof feedbackMetadataValidator>;
|
|
218
|
+
type InferredFeedbackEntry = Infer<typeof publicEntryValidator>;
|
|
219
|
+
type InferredAdminFeedbackEntry = Infer<typeof adminEntryValidator>;
|
|
220
|
+
type InferredFeedbackComment = Infer<typeof publicCommentValidator>;
|
|
221
|
+
type InferredActivityEntry = Infer<typeof activityEntryValidator>;
|
|
222
|
+
type InferredActivityEntryWithContext = Infer<
|
|
223
|
+
typeof activityEntryWithContextValidator
|
|
224
|
+
>;
|
|
225
|
+
type InferredActivityComment = Infer<typeof activityCommentValidator>;
|
|
226
|
+
type InferredFeedbackReaction = Infer<typeof feedbackReactionValidator>;
|
|
227
|
+
type InferredSimilarEntriesResult = Infer<typeof similarEntriesValidator>;
|
|
228
|
+
|
|
138
229
|
/**
|
|
139
230
|
* Category of feedback represented by an entry.
|
|
140
231
|
*
|
|
@@ -177,7 +268,34 @@ export type EntryPriority = Infer<typeof entryPriorityValidator>;
|
|
|
177
268
|
export type RoadmapStatus = Infer<typeof roadmapStatusValidator>;
|
|
178
269
|
|
|
179
270
|
/** Admin-managed roadmap item and its current attached-feedback count. */
|
|
180
|
-
export
|
|
271
|
+
export interface RoadmapItem {
|
|
272
|
+
/** Public component document identifier. */
|
|
273
|
+
id: InferredRoadmapItem["id"];
|
|
274
|
+
|
|
275
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
276
|
+
creationTime: InferredRoadmapItem["creationTime"];
|
|
277
|
+
|
|
278
|
+
/** Roadmap item title. */
|
|
279
|
+
title: InferredRoadmapItem["title"];
|
|
280
|
+
|
|
281
|
+
/** Optional roadmap item description. */
|
|
282
|
+
description?: InferredRoadmapItem["description"];
|
|
283
|
+
|
|
284
|
+
/** Current roadmap workflow stage. */
|
|
285
|
+
status: InferredRoadmapItem["status"];
|
|
286
|
+
|
|
287
|
+
/** Relative ordering position among roadmap items. */
|
|
288
|
+
position: InferredRoadmapItem["position"];
|
|
289
|
+
|
|
290
|
+
/** Millisecond timestamp at which the roadmap item was created. */
|
|
291
|
+
createdAt: InferredRoadmapItem["createdAt"];
|
|
292
|
+
|
|
293
|
+
/** Millisecond timestamp of the latest roadmap item update. */
|
|
294
|
+
updatedAt: InferredRoadmapItem["updatedAt"];
|
|
295
|
+
|
|
296
|
+
/** Number of feedback entries currently attached to the roadmap item. */
|
|
297
|
+
feedbackCount: InferredRoadmapItem["feedbackCount"];
|
|
298
|
+
}
|
|
181
299
|
|
|
182
300
|
/**
|
|
183
301
|
* Server-side ordering strategy for comments and replies.
|
|
@@ -194,21 +312,25 @@ export type CommentSort = Infer<typeof commentSortValidator>;
|
|
|
194
312
|
*
|
|
195
313
|
* The component does not access the host application's authentication system
|
|
196
314
|
* directly. The host resolves its current user/session into this shape.
|
|
197
|
-
*
|
|
198
|
-
* @property id
|
|
199
|
-
* Stable identifier for the actor. This can be a Clerk ID, Convex Auth ID,
|
|
200
|
-
* application user ID, anonymous installation ID, or another stable
|
|
201
|
-
* host-controlled identifier.
|
|
202
|
-
*
|
|
203
|
-
* @property isAdmin
|
|
204
|
-
* Whether the actor can perform admin-only actions such as changing entry
|
|
205
|
-
* status or modifying content they do not own. When omitted, the deprecated
|
|
206
|
-
* `isModerator` field is used.
|
|
207
|
-
*
|
|
208
|
-
* @property isModerator
|
|
209
|
-
* @deprecated Use `isAdmin`.
|
|
210
315
|
*/
|
|
211
|
-
export
|
|
316
|
+
export interface FeedbackActor {
|
|
317
|
+
/**
|
|
318
|
+
* Stable identifier for the actor. This can be a Clerk ID, Convex Auth ID,
|
|
319
|
+
* application user ID, anonymous installation ID, or another stable
|
|
320
|
+
* host-controlled identifier.
|
|
321
|
+
*/
|
|
322
|
+
id: InferredFeedbackActor["id"];
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Whether the actor can perform admin-only actions such as changing entry
|
|
326
|
+
* status or modifying content they do not own. When omitted, the deprecated
|
|
327
|
+
* `isModerator` field is used.
|
|
328
|
+
*/
|
|
329
|
+
isAdmin?: InferredFeedbackActor["isAdmin"];
|
|
330
|
+
|
|
331
|
+
/** @deprecated Use `isAdmin`. */
|
|
332
|
+
isModerator?: InferredFeedbackActor["isModerator"];
|
|
333
|
+
}
|
|
212
334
|
|
|
213
335
|
/** Scalar value accepted in entry diagnostic metadata. */
|
|
214
336
|
export type FeedbackMetadataValue = Infer<
|
|
@@ -216,61 +338,111 @@ export type FeedbackMetadataValue = Infer<
|
|
|
216
338
|
>;
|
|
217
339
|
|
|
218
340
|
/** Flat metadata values grouped by their source. */
|
|
219
|
-
export
|
|
341
|
+
export interface FeedbackMetadata {
|
|
342
|
+
/** Standard diagnostic metadata collected by the component. */
|
|
343
|
+
standard?: InferredFeedbackMetadata["standard"];
|
|
220
344
|
|
|
221
|
-
/**
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
345
|
+
/** Additional host-provided diagnostic metadata. */
|
|
346
|
+
additional?: InferredFeedbackMetadata["additional"];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Public representation of a feedback, feature-request, or bug-report entry. */
|
|
350
|
+
export interface FeedbackEntry {
|
|
351
|
+
/** Public component document identifier. */
|
|
352
|
+
id: InferredFeedbackEntry["id"];
|
|
353
|
+
|
|
354
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
355
|
+
creationTime: InferredFeedbackEntry["creationTime"];
|
|
356
|
+
|
|
357
|
+
/** Stable identifier of the actor who created the entry. */
|
|
358
|
+
actorId: InferredFeedbackEntry["actorId"];
|
|
359
|
+
|
|
360
|
+
/** Entry category. */
|
|
361
|
+
kind: InferredFeedbackEntry["kind"];
|
|
362
|
+
|
|
363
|
+
/** Current workflow status. */
|
|
364
|
+
status: InferredFeedbackEntry["status"];
|
|
365
|
+
|
|
366
|
+
/** User-provided entry title. */
|
|
367
|
+
title: InferredFeedbackEntry["title"];
|
|
368
|
+
|
|
369
|
+
/** User-provided entry description. */
|
|
370
|
+
body: InferredFeedbackEntry["body"];
|
|
371
|
+
|
|
372
|
+
/** Denormalized number of actors currently upvoting this entry. */
|
|
373
|
+
upvoteCount: InferredFeedbackEntry["upvoteCount"];
|
|
374
|
+
|
|
375
|
+
/** Denormalized total number of comments belonging to the entry, including nested replies. */
|
|
376
|
+
commentCount: InferredFeedbackEntry["commentCount"];
|
|
377
|
+
|
|
378
|
+
/** Millisecond timestamp of the most recent content update. Absent when the entry has never been edited. */
|
|
379
|
+
updatedAt?: InferredFeedbackEntry["updatedAt"];
|
|
380
|
+
|
|
381
|
+
/** Whether the actor associated with the current query has upvoted the entry. `false` when no viewer actor is available. */
|
|
382
|
+
viewerHasUpvoted: InferredFeedbackEntry["viewerHasUpvoted"];
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Whether the actor associated with the current query created the entry.
|
|
386
|
+
* `false` when no viewer actor is available. This is optional for compatibility
|
|
387
|
+
* with hosts that have not yet redeployed the updated wrapper.
|
|
388
|
+
*/
|
|
389
|
+
viewerIsAuthor?: InferredFeedbackEntry["viewerIsAuthor"];
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Creation-time diagnostic metadata. Present only when `getEntry` is queried
|
|
393
|
+
* by an admin. Ordinary entry lists, searches, and non-admin reads omit this
|
|
394
|
+
* property.
|
|
395
|
+
*/
|
|
396
|
+
metadata?: InferredFeedbackEntry["metadata"];
|
|
397
|
+
}
|
|
271
398
|
|
|
272
399
|
/** Feedback entry enriched with private triage metadata for admin clients. */
|
|
273
|
-
export
|
|
400
|
+
export interface AdminFeedbackEntry {
|
|
401
|
+
/** Public component document identifier. */
|
|
402
|
+
id: InferredAdminFeedbackEntry["id"];
|
|
403
|
+
|
|
404
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
405
|
+
creationTime: InferredAdminFeedbackEntry["creationTime"];
|
|
406
|
+
|
|
407
|
+
/** Stable identifier of the actor who created the entry. */
|
|
408
|
+
actorId: InferredAdminFeedbackEntry["actorId"];
|
|
409
|
+
|
|
410
|
+
/** Entry category. */
|
|
411
|
+
kind: InferredAdminFeedbackEntry["kind"];
|
|
412
|
+
|
|
413
|
+
/** Current workflow status. */
|
|
414
|
+
status: InferredAdminFeedbackEntry["status"];
|
|
415
|
+
|
|
416
|
+
/** User-provided entry title. */
|
|
417
|
+
title: InferredAdminFeedbackEntry["title"];
|
|
418
|
+
|
|
419
|
+
/** User-provided entry description. */
|
|
420
|
+
body: InferredAdminFeedbackEntry["body"];
|
|
421
|
+
|
|
422
|
+
/** Denormalized number of actors currently upvoting this entry. */
|
|
423
|
+
upvoteCount: InferredAdminFeedbackEntry["upvoteCount"];
|
|
424
|
+
|
|
425
|
+
/** Denormalized total number of comments belonging to the entry, including nested replies. */
|
|
426
|
+
commentCount: InferredAdminFeedbackEntry["commentCount"];
|
|
427
|
+
|
|
428
|
+
/** Millisecond timestamp of the most recent content update. Absent when the entry has never been edited. */
|
|
429
|
+
updatedAt?: InferredAdminFeedbackEntry["updatedAt"];
|
|
430
|
+
|
|
431
|
+
/** Whether the actor associated with the current query has upvoted the entry. `false` when no viewer actor is available. */
|
|
432
|
+
viewerHasUpvoted: InferredAdminFeedbackEntry["viewerHasUpvoted"];
|
|
433
|
+
|
|
434
|
+
/** Whether the actor associated with the current query created the entry. */
|
|
435
|
+
viewerIsAuthor?: InferredAdminFeedbackEntry["viewerIsAuthor"];
|
|
436
|
+
|
|
437
|
+
/** Creation-time diagnostic metadata returned to admin clients. */
|
|
438
|
+
metadata?: InferredAdminFeedbackEntry["metadata"];
|
|
439
|
+
|
|
440
|
+
/** Internal triage priority. */
|
|
441
|
+
priority?: InferredAdminFeedbackEntry["priority"];
|
|
442
|
+
|
|
443
|
+
/** Roadmap item attached to the entry, when one exists. */
|
|
444
|
+
roadmap?: InferredAdminFeedbackEntry["roadmap"];
|
|
445
|
+
}
|
|
274
446
|
|
|
275
447
|
/**
|
|
276
448
|
* Public representation of a comment or reply.
|
|
@@ -278,47 +450,140 @@ export type AdminFeedbackEntry = Infer<typeof adminEntryValidator>;
|
|
|
278
450
|
* Comments are returned one level at a time. Child comments are not included
|
|
279
451
|
* automatically; query them separately using the comment's `id` as
|
|
280
452
|
* `parentCommentId`.
|
|
281
|
-
*
|
|
282
|
-
* @property id
|
|
283
|
-
* Public component document identifier.
|
|
284
|
-
*
|
|
285
|
-
* @property creationTime
|
|
286
|
-
* Convex document creation timestamp in milliseconds since the Unix epoch.
|
|
287
|
-
*
|
|
288
|
-
* @property entryId
|
|
289
|
-
* Entry this comment belongs to.
|
|
290
|
-
*
|
|
291
|
-
* @property parentCommentId
|
|
292
|
-
* Direct parent comment. Absent for top-level comments.
|
|
293
|
-
*
|
|
294
|
-
* @property actorId
|
|
295
|
-
* Stable identifier of the actor who created the comment.
|
|
296
|
-
*
|
|
297
|
-
* @property depth
|
|
298
|
-
* Zero-based nesting depth. Top-level comments have depth `0`.
|
|
299
|
-
*
|
|
300
|
-
* @property body
|
|
301
|
-
* User-provided comment text. `null` when the comment was soft-deleted so
|
|
302
|
-
* nested replies can retain their place in the conversation.
|
|
303
|
-
*
|
|
304
|
-
* @property likeCount
|
|
305
|
-
* Denormalized number of actors currently liking this comment.
|
|
306
|
-
*
|
|
307
|
-
* @property replyCount
|
|
308
|
-
* Number of direct child replies. Descendants below those direct children are
|
|
309
|
-
* not included in this count.
|
|
310
|
-
*
|
|
311
|
-
* @property updatedAt
|
|
312
|
-
* Millisecond timestamp of the latest edit, when the comment has been edited.
|
|
313
|
-
*
|
|
314
|
-
* @property deletedAt
|
|
315
|
-
* Millisecond timestamp at which the comment was soft-deleted.
|
|
316
|
-
*
|
|
317
|
-
* @property viewerHasLiked
|
|
318
|
-
* Whether the actor associated with the current query likes this comment.
|
|
319
|
-
* `false` when no viewer actor is available.
|
|
320
453
|
*/
|
|
321
|
-
export
|
|
454
|
+
export interface FeedbackComment {
|
|
455
|
+
/** Public component document identifier. */
|
|
456
|
+
id: InferredFeedbackComment["id"];
|
|
457
|
+
|
|
458
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
459
|
+
creationTime: InferredFeedbackComment["creationTime"];
|
|
460
|
+
|
|
461
|
+
/** Entry this comment belongs to. */
|
|
462
|
+
entryId: InferredFeedbackComment["entryId"];
|
|
463
|
+
|
|
464
|
+
/** Direct parent comment. Absent for top-level comments. */
|
|
465
|
+
parentCommentId?: InferredFeedbackComment["parentCommentId"];
|
|
466
|
+
|
|
467
|
+
/** Stable identifier of the actor who created the comment. */
|
|
468
|
+
actorId: InferredFeedbackComment["actorId"];
|
|
469
|
+
|
|
470
|
+
/** Zero-based nesting depth. Top-level comments have depth `0`. */
|
|
471
|
+
depth: InferredFeedbackComment["depth"];
|
|
472
|
+
|
|
473
|
+
/** User-provided comment text. */
|
|
474
|
+
body: InferredFeedbackComment["body"];
|
|
475
|
+
|
|
476
|
+
/** Denormalized number of actors currently liking this comment. */
|
|
477
|
+
likeCount: InferredFeedbackComment["likeCount"];
|
|
478
|
+
|
|
479
|
+
/** Number of direct child replies. Descendants below those direct children are not included in this count. */
|
|
480
|
+
replyCount: InferredFeedbackComment["replyCount"];
|
|
481
|
+
|
|
482
|
+
/** Millisecond timestamp of the latest edit, when the comment has been edited. */
|
|
483
|
+
updatedAt?: InferredFeedbackComment["updatedAt"];
|
|
484
|
+
|
|
485
|
+
/** Whether the actor associated with the current query likes this comment. `false` when no viewer actor is available. */
|
|
486
|
+
viewerHasLiked: InferredFeedbackComment["viewerHasLiked"];
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Entry created by the actor used for an actor-scoped activity query. */
|
|
490
|
+
export interface FeedbackActivityEntry {
|
|
491
|
+
/** Public component document identifier. */
|
|
492
|
+
id: InferredActivityEntry["id"];
|
|
493
|
+
|
|
494
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
495
|
+
creationTime: InferredActivityEntry["creationTime"];
|
|
496
|
+
|
|
497
|
+
/** Stable identifier of the actor who created the entry. */
|
|
498
|
+
actorId: InferredActivityEntry["actorId"];
|
|
499
|
+
|
|
500
|
+
/** Entry category. */
|
|
501
|
+
kind: InferredActivityEntry["kind"];
|
|
502
|
+
|
|
503
|
+
/** Current workflow status. */
|
|
504
|
+
status: InferredActivityEntry["status"];
|
|
505
|
+
|
|
506
|
+
/** User-provided entry title. */
|
|
507
|
+
title: InferredActivityEntry["title"];
|
|
508
|
+
|
|
509
|
+
/** User-provided entry description. */
|
|
510
|
+
body: InferredActivityEntry["body"];
|
|
511
|
+
|
|
512
|
+
/** Denormalized number of actors currently upvoting this entry. */
|
|
513
|
+
upvoteCount: InferredActivityEntry["upvoteCount"];
|
|
514
|
+
|
|
515
|
+
/** Denormalized total number of comments belonging to the entry. */
|
|
516
|
+
commentCount: InferredActivityEntry["commentCount"];
|
|
517
|
+
|
|
518
|
+
/** Millisecond timestamp of the most recent content update. */
|
|
519
|
+
updatedAt?: InferredActivityEntry["updatedAt"];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Component-only activity entry shape with retained diagnostic and triage
|
|
524
|
+
* context. It is used by trusted server consumers, not the normal `listUser`
|
|
525
|
+
* wrappers.
|
|
526
|
+
*/
|
|
527
|
+
export interface FeedbackActivityEntryWithContext extends FeedbackActivityEntry {
|
|
528
|
+
metadata?: InferredActivityEntryWithContext["metadata"];
|
|
529
|
+
priority?: InferredActivityEntryWithContext["priority"];
|
|
530
|
+
roadmap?: InferredActivityEntryWithContext["roadmap"];
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** Comment created by the actor used for an actor-scoped activity query. */
|
|
534
|
+
export interface FeedbackActivityComment {
|
|
535
|
+
/** Public component document identifier. */
|
|
536
|
+
id: InferredActivityComment["id"];
|
|
537
|
+
|
|
538
|
+
/** Convex document creation timestamp in milliseconds since the Unix epoch. */
|
|
539
|
+
creationTime: InferredActivityComment["creationTime"];
|
|
540
|
+
|
|
541
|
+
/** Entry this comment belongs to. */
|
|
542
|
+
entryId: InferredActivityComment["entryId"];
|
|
543
|
+
|
|
544
|
+
/** Current entry title. Activity rows with missing entries are removed. */
|
|
545
|
+
entryTitle: InferredActivityComment["entryTitle"];
|
|
546
|
+
|
|
547
|
+
/** Direct parent comment. Absent for top-level comments. */
|
|
548
|
+
parentCommentId?: InferredActivityComment["parentCommentId"];
|
|
549
|
+
|
|
550
|
+
/** Stable identifier of the actor who created the comment. */
|
|
551
|
+
actorId: InferredActivityComment["actorId"];
|
|
552
|
+
|
|
553
|
+
/** Zero-based nesting depth. */
|
|
554
|
+
depth: InferredActivityComment["depth"];
|
|
555
|
+
|
|
556
|
+
/** User-provided comment text. */
|
|
557
|
+
body: InferredActivityComment["body"];
|
|
558
|
+
|
|
559
|
+
/** Denormalized number of actors currently liking this comment. */
|
|
560
|
+
likeCount: InferredActivityComment["likeCount"];
|
|
561
|
+
|
|
562
|
+
/** Number of direct child replies. */
|
|
563
|
+
replyCount: InferredActivityComment["replyCount"];
|
|
564
|
+
|
|
565
|
+
/** Millisecond timestamp of the latest edit, when edited. */
|
|
566
|
+
updatedAt?: InferredActivityComment["updatedAt"];
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** Resolved entry context for an entry-upvote activity record. */
|
|
570
|
+
export interface FeedbackEntryReactionTarget {
|
|
571
|
+
id: InferredActivityEntry["id"];
|
|
572
|
+
title: string;
|
|
573
|
+
kind: EntryKind;
|
|
574
|
+
status: EntryStatus;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/** Resolved comment context for a comment-like activity record. */
|
|
578
|
+
export interface FeedbackCommentReactionTarget {
|
|
579
|
+
id: InferredActivityComment["id"];
|
|
580
|
+
body: string;
|
|
581
|
+
entryId: InferredActivityComment["entryId"];
|
|
582
|
+
entryTitle: string;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** A reaction created by the actor used for an actor-scoped activity query. */
|
|
586
|
+
export type FeedbackReaction = InferredFeedbackReaction;
|
|
322
587
|
|
|
323
588
|
/**
|
|
324
589
|
* Duplicate-detection result for a proposed entry.
|
|
@@ -335,12 +600,11 @@ export type FeedbackComment = Infer<typeof publicCommentValidator>;
|
|
|
335
600
|
* - 0 exact matches → `0 exact + at most 3 similar`
|
|
336
601
|
*
|
|
337
602
|
* An entry returned in `exact` is never repeated in `similar`.
|
|
338
|
-
*
|
|
339
|
-
* @property exact
|
|
340
|
-
* Entries whose normalized title exactly matches the proposed title.
|
|
341
|
-
*
|
|
342
|
-
* @property similar
|
|
343
|
-
* Full-text matches ordered by search relevance after exact matches have been
|
|
344
|
-
* removed.
|
|
345
603
|
*/
|
|
346
|
-
export
|
|
604
|
+
export interface SimilarEntriesResult {
|
|
605
|
+
/** Entries whose normalized title exactly matches the proposed title. */
|
|
606
|
+
exact: InferredSimilarEntriesResult["exact"];
|
|
607
|
+
|
|
608
|
+
/** Full-text matches ordered by search relevance after exact matches have been removed. */
|
|
609
|
+
similar: InferredSimilarEntriesResult["similar"];
|
|
610
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { stream } from "convex-helpers/server/stream";
|
|
2
|
+
import {
|
|
3
|
+
paginationOptsValidator,
|
|
4
|
+
paginationResultValidator,
|
|
5
|
+
} from "convex/server";
|
|
6
|
+
import { v } from "convex/values";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
assertActorId,
|
|
10
|
+
commentIsLive,
|
|
11
|
+
serializeActivityReaction,
|
|
12
|
+
} from "./helpers.js";
|
|
13
|
+
import { feedbackReactionValidator } from "./model.js";
|
|
14
|
+
import { query } from "./_generated/server.js";
|
|
15
|
+
import schema from "./schema.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* List reactions created by a known actor. Target documents are loaded once
|
|
19
|
+
* by the stream mapper and reused by serialization. Reactions whose target or
|
|
20
|
+
* owning entry is missing or pending deletion are omitted.
|
|
21
|
+
*/
|
|
22
|
+
export const listByActor = query({
|
|
23
|
+
args: {
|
|
24
|
+
paginationOpts: paginationOptsValidator,
|
|
25
|
+
actorId: v.string(),
|
|
26
|
+
},
|
|
27
|
+
returns: paginationResultValidator(feedbackReactionValidator),
|
|
28
|
+
handler: async (ctx, args) => {
|
|
29
|
+
assertActorId(args.actorId);
|
|
30
|
+
|
|
31
|
+
const result = await stream(ctx.db, schema)
|
|
32
|
+
.query("reactions")
|
|
33
|
+
.withIndex("by_actor", (q) => q.eq("actorId", args.actorId))
|
|
34
|
+
.order("desc")
|
|
35
|
+
.map(async (reaction) => {
|
|
36
|
+
if (reaction.entryId !== undefined) {
|
|
37
|
+
const entry = await ctx.db.get("entries", reaction.entryId);
|
|
38
|
+
if (entry === null || entry.deletingAt !== undefined) return null;
|
|
39
|
+
return serializeActivityReaction(reaction, { type: "entry", entry });
|
|
40
|
+
}
|
|
41
|
+
if (reaction.commentId !== undefined) {
|
|
42
|
+
const comment = await ctx.db.get("comments", reaction.commentId);
|
|
43
|
+
if (comment === null || !(await commentIsLive(ctx, comment))) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const entry = await ctx.db.get("entries", comment.entryId);
|
|
47
|
+
if (entry === null || entry.deletingAt !== undefined) return null;
|
|
48
|
+
return serializeActivityReaction(reaction, {
|
|
49
|
+
type: "comment",
|
|
50
|
+
comment,
|
|
51
|
+
entry,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
})
|
|
56
|
+
.paginate(args.paginationOpts);
|
|
57
|
+
|
|
58
|
+
return result;
|
|
59
|
+
},
|
|
60
|
+
});
|