Skip to content

Commit fc02640

Browse files
PBnicadclaude
andcommitted
feat: add replyToUserId to track which user is being replied to
- Add replyToUserId field to comments table schema - Update POST endpoint to save the user being replied to - Update GET endpoint to include replyToUser in response - Update frontend to pass replyToUserId when submitting replies This fixes the issue where replying to a reply would show @parentCommentAuthor instead of @replyAuthor. Co-Authored-By: Claude <[email protected]>
1 parent d04ed3f commit fc02640

File tree

3 files changed

+27
-15
lines changed

3 files changed

+27
-15
lines changed

client/src/page/feed.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,13 @@ function CommentInput({
490490
function ReplyInput({
491491
feedId,
492492
parentId,
493+
replyToUserId,
493494
onRefresh,
494495
onCancel,
495496
}: {
496497
feedId: string;
497498
parentId: number;
499+
replyToUserId?: number;
498500
onRefresh: () => void;
499501
onCancel: () => void;
500502
}) {
@@ -519,7 +521,7 @@ function ReplyInput({
519521
client.feed
520522
.comment({ feed: feedId })
521523
.post(
522-
{ content, parentId },
524+
{ content, parentId, replyToUserId },
523525
{
524526
headers: headersWithAuth(),
525527
}
@@ -780,6 +782,7 @@ function CommentItem({
780782
<ReplyInput
781783
feedId={feedId}
782784
parentId={rootId || comment.id}
785+
replyToUserId={comment.user.id}
783786
onRefresh={() => {
784787
onRefresh();
785788
setShowReplyInput(false);
@@ -934,6 +937,7 @@ function ReplyItem({
934937
<ReplyInput
935938
feedId={feedId}
936939
parentId={rootId}
940+
replyToUserId={reply.user.id}
937941
onRefresh={() => {
938942
onRefresh();
939943
setShowReplyInput(false);

server/src/db/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const comments = sqliteTable("comments", {
6969
userId: integer("user_id").references(() => users.id, { onDelete: 'cascade' }).notNull(),
7070
content: text("content").notNull(),
7171
parentId: integer("parent_id").references(() => comments.id, { onDelete: 'cascade' }),
72+
replyToUserId: integer("reply_to_user_id").references(() => users.id, { onDelete: 'set null' }),
7273
createdAt: created_at,
7374
updatedAt: updated_at,
7475
}) as any;
@@ -117,6 +118,10 @@ export const commentsRelations = relations(comments, ({ one, many }) => ({
117118
references: [comments.id],
118119
relationName: 'comment_replies'
119120
}),
121+
replyToUser: one(users, {
122+
fields: [comments.replyToUserId],
123+
references: [users.id],
124+
}),
120125
replies: many(comments, {
121126
relationName: 'comment_replies'
122127
}),

server/src/services/comments.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,14 @@ export function CommentService() {
2525
user: {
2626
columns: { id: true, username: true, avatar: true, permission: true }
2727
},
28-
parent: {
29-
columns: { id: true },
30-
with: {
31-
user: {
32-
columns: { id: true, username: true }
33-
}
34-
}
28+
replyToUser: {
29+
columns: { id: true, username: true }
3530
}
3631
},
3732
orderBy: [desc(comments.createdAt)]
3833
});
3934

40-
// Build nested structure with replyToUser info
35+
// Build nested structure
4136
const rootComments: any[] = [];
4237
const replyMap = new Map();
4338

@@ -47,9 +42,6 @@ export function CommentService() {
4742
if (!replyMap.has(comment.parentId)) {
4843
replyMap.set(comment.parentId, []);
4944
}
50-
// Add replyToUser for replies (the user who wrote the parent comment)
51-
const parentComment = allComments.find(c => c.id === comment.parentId);
52-
(comment as any).replyToUser = parentComment?.user || null;
5345
replyMap.get(comment.parentId).push(comment);
5446
} else {
5547
rootComments.push(comment);
@@ -66,7 +58,7 @@ export function CommentService() {
6658

6759
return rootComments;
6860
})
69-
.post('/:feed', async ({ uid, set, params: { feed }, body: { content, parentId } }) => {
61+
.post('/:feed', async ({ uid, set, params: { feed }, body: { content, parentId, replyToUserId } }) => {
7062
if (!uid) {
7163
set.status = 401;
7264
return 'Unauthorized';
@@ -88,6 +80,8 @@ export function CommentService() {
8880
return 'Feed not found';
8981
}
9082
let finalParentId = parentId || null;
83+
let finalReplyToUserId = replyToUserId ? parseInt(replyToUserId as string) : null;
84+
9185
if (parentId) {
9286
const parentComment = await db.query.comments.findFirst({ where: eq(comments.id, parentId) });
9387
if (!parentComment || parentComment.feedId !== feedId) {
@@ -97,14 +91,22 @@ export function CommentService() {
9791
// Enforce two-level nesting: if parent is already a reply, use its parent (root comment)
9892
if (parentComment.parentId) {
9993
finalParentId = parentComment.parentId;
94+
// If replying to a reply, use the reply's author as replyToUser
95+
if (!finalReplyToUserId) {
96+
finalReplyToUserId = parentComment.userId;
97+
}
98+
} else if (!finalReplyToUserId) {
99+
// If replying to a root comment without explicit replyToUser, use the root comment's author
100+
finalReplyToUserId = parentComment.userId;
100101
}
101102
}
102103

103104
await db.insert(comments).values({
104105
feedId,
105106
userId,
106107
content,
107-
parentId: finalParentId
108+
parentId: finalParentId,
109+
replyToUserId: finalReplyToUserId
108110
});
109111

110112
const webhookUrl = await ServerConfig().get(Config.webhookUrl) || env.WEBHOOK_URL;
@@ -114,7 +116,8 @@ export function CommentService() {
114116
}, {
115117
body: t.Object({
116118
content: t.String(),
117-
parentId: t.Optional(t.Union([t.String(), t.Number()]))
119+
parentId: t.Optional(t.Union([t.String(), t.Number()])),
120+
replyToUserId: t.Optional(t.Union([t.String(), t.Number()]))
118121
})
119122
})
120123
)

0 commit comments

Comments
 (0)