From 56c06ec13be0ba4356a5c9715407ee0d84eb4448 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:13:23 +0800 Subject: [PATCH] refactor(timeline): use avatar for activity entries, remove filter, allow nested replies - Activity entries now show ActorAvatar instead of a small dot (consistent with comments) - Remove All/Comments/Activity filter toggle (comments are just a type of activity) - Remove one-level reply restriction in backend (allow nested threading) - Remove unused Circle import Co-Authored-By: Claude Opus 4.6 (1M context) --- .../issues/components/issue-detail.tsx | 21 +++---------------- server/internal/handler/comment.go | 10 --------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/apps/web/features/issues/components/issue-detail.tsx b/apps/web/features/issues/components/issue-detail.tsx index 89b68ed5..a0ba6936 100644 --- a/apps/web/features/issues/components/issue-detail.tsx +++ b/apps/web/features/issues/components/issue-detail.tsx @@ -10,7 +10,6 @@ import { Calendar, ChevronLeft, ChevronRight, - Circle, Link2, MessageSquare, MoreHorizontal, @@ -198,7 +197,6 @@ export function IssueDetail({ issueId, onDelete }: IssueDetailProps) { const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [propertiesOpen, setPropertiesOpen] = useState(true); const [detailsOpen, setDetailsOpen] = useState(true); - const [filter, setFilter] = useState<"all" | "comments" | "activity">("all"); const [replyingTo, setReplyingTo] = useState(null); const [replyEmpty, setReplyEmpty] = useState(true); @@ -750,9 +748,6 @@ export function IssueDetail({ issueId, onDelete }: IssueDetailProps) {

Activity

- - -
@@ -847,21 +842,11 @@ export function IssueDetail({ issueId, onDelete }: IssueDetailProps) { } } - // Apply filter - const filtered = topLevel.filter((e) => { - if (filter === "all") return true; - if (filter === "comments") return e.type === "comment"; - if (filter === "activity") return e.type === "activity"; - return true; - }); - - return filtered.map((entry) => { + return topLevel.map((entry) => { if (entry.type === "activity") { return ( -
-
- -
+
+ {getActorName(entry.actor_type, entry.actor_id)} {formatActivity(entry)} diff --git a/server/internal/handler/comment.go b/server/internal/handler/comment.go index d4f4fc99..99d45113 100644 --- a/server/internal/handler/comment.go +++ b/server/internal/handler/comment.go @@ -94,16 +94,6 @@ func (h *Handler) CreateComment(w http.ResponseWriter, r *http.Request) { var parentID pgtype.UUID if req.ParentID != nil { parentID = parseUUID(*req.ParentID) - // Only allow one level of nesting: parent must be a top-level comment - parent, err := h.Queries.GetComment(r.Context(), parentID) - if err != nil { - writeError(w, http.StatusBadRequest, "parent comment not found") - return - } - if parent.ParentID.Valid { - writeError(w, http.StatusBadRequest, "replies to replies are not supported") - return - } } comment, err := h.Queries.CreateComment(r.Context(), db.CreateCommentParams{