import { useState } from 'react' import { formatDistanceToNow } from 'date-fns' import { Link } from 'react-router-dom' import { Tag, GitPullRequestClosed, Pencil, CircleDot } from 'lucide-react' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Markdown } from '@/components/content/Markdown' import { LabelBadge } from './LabelBadge' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Status, type BugDetailQuery, useBugEditCommentMutation, BugDetailDocument, } from '@/__generated__/graphql' import { useAuth } from '@/lib/auth' type TimelineNode = NonNullable< NonNullable['bug']>['timeline']['nodes'][number] > interface TimelineProps { bugPrefix: string items: TimelineNode[] } // Ordered sequence of events on a bug: comments (create and add-comment) and // inline events (label changes, status changes, title edits). Comment items // support inline editing for the logged-in user. export function Timeline({ bugPrefix, items }: TimelineProps) { return (
{items.map((item) => { switch (item.__typename) { case 'BugCreateTimelineItem': case 'BugAddCommentTimelineItem': return case 'BugLabelChangeTimelineItem': return case 'BugSetStatusTimelineItem': return case 'BugSetTitleTimelineItem': return default: return null } })}
) } // ── Comment (create or add-comment) ────────────────────────────────────────── type CommentItem = Extract< TimelineNode, { __typename: 'BugCreateTimelineItem' | 'BugAddCommentTimelineItem' } > function CommentItem({ item, bugPrefix }: { item: CommentItem; bugPrefix: string }) { const { user } = useAuth() const [editing, setEditing] = useState(false) const [editValue, setEditValue] = useState(item.message ?? '') const [editComment, { loading }] = useBugEditCommentMutation({ refetchQueries: [{ query: BugDetailDocument, variables: { prefix: bugPrefix } }], }) function handleSave() { if (editValue.trim() === (item.message ?? '').trim()) { setEditing(false) return } editComment({ variables: { input: { targetPrefix: item.id, message: editValue } }, }).then(() => setEditing(false)) } function handleCancel() { setEditValue(item.message ?? '') setEditing(false) } const canEdit = user !== null && user.id === item.author.id return (
{item.author.displayName.slice(0, 2).toUpperCase()}
{item.author.displayName} {formatDistanceToNow(new Date(item.createdAt), { addSuffix: true })} {item.edited && !editing && ( edited )} {canEdit && !editing && ( )}
{editing ? (
{/* Ctrl/Cmd+Enter saves; Escape cancels — standard editor shortcuts */}