Files
pad/internal/models/comment.go
T
xarmian 998716ae49 feat: unified item timeline with comment-on-update, threading, and reactions (#54)
* feat: unified item timeline with comment-on-update, threading, and reactions (IDEA-115)

Replace the separate Comments section and Version History modal on the
item detail page with a single chronological timeline that interleaves
comments, activities, and content versions.

Key changes:
- Add --comment flag to `pad item update` so agents/users can explain
  status changes inline (creates a comment linked to the activity)
- Add threaded replies (parent_id on comments) with inline reply UI
- Add emoji reactions on comments (new comment_reactions table)
- New /timeline API endpoint merges comments, activities, and versions
  server-side with dedup and collapsing of rapid edits
- New Svelte timeline components: ItemTimeline, TimelineCommentCard,
  TimelineActivityCard, TimelineVersionCard, ReactionPicker
- Remove Implementation Notes and Decision Log inputs from web UI
- Update skill docs to encourage --comment on status changes

* fix: address review findings for PR #54 (iteration 1)

- Use ListItemVersions instead of ListVersions in timeline endpoint
  so item content history renders correctly
- Add workspace validation to reply and reaction handlers to prevent
  cross-workspace comment mutation
- Raise activity cap from 500 to 10000 to avoid silently truncating
  long timelines
- Fix toggleReaction to always POST (idempotent) instead of incorrectly
  matching other users' reactions for DELETE
- Await onReply promise before clearing draft to prevent duplicate
  submissions and lost drafts on failure
- Register reaction_added/reaction_removed in SSE ITEM_EVENTS so
  reactions from other sessions appear in real-time

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address review findings for PR #54 (iteration 2)

- Fix nil pointer dereference in timeline handler when item not found
- Store empty string instead of NULL for reaction user_id so UNIQUE
  constraint works correctly in SQLite
- Add workspace validation to handleDeleteComment (cross-workspace
  deletion was possible)
- Fix SKILL.md duplicate numbering (4. appeared twice)
- Remove || true debug artifacts from reaction conditionals
- Replace SvelteMap with plain Map in non-reactive groupReactions
- Use != null checks for timeline API params to handle offset=0
- Log warning on comment creation failure during item update

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-03 07:25:56 -04:00

48 lines
1.5 KiB
Go

package models
import "time"
// Comment represents a comment on an item.
type Comment struct {
ID string `json:"id"`
ItemID string `json:"item_id"`
WorkspaceID string `json:"workspace_id"`
Author string `json:"author"`
Body string `json:"body"`
CreatedBy string `json:"created_by"`
Source string `json:"source"`
ActivityID string `json:"activity_id,omitempty"`
ParentID string `json:"parent_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Populated by joins (not stored)
ItemTitle string `json:"item_title,omitempty"`
ItemSlug string `json:"item_slug,omitempty"`
// Populated by handlers for threaded views
Replies []Comment `json:"replies,omitempty"`
Reactions []Reaction `json:"reactions,omitempty"`
}
// CommentCreate is the input for creating a new comment.
type CommentCreate struct {
Author string `json:"author,omitempty"`
Body string `json:"body"`
CreatedBy string `json:"created_by,omitempty"`
Source string `json:"source,omitempty"`
ParentID string `json:"parent_id,omitempty"`
ActivityID string `json:"activity_id,omitempty"`
}
// Reaction represents an emoji reaction on a comment.
type Reaction struct {
ID string `json:"id"`
CommentID string `json:"comment_id"`
UserID string `json:"user_id,omitempty"`
Actor string `json:"actor"`
Emoji string `json:"emoji"`
CreatedAt time.Time `json:"created_at"`
ActorName string `json:"actor_name,omitempty"`
}