import { Link } from "@tanstack/react-router"; import { formatDistanceToNow } from "date-fns"; import { Tag, GitPullRequestClosed, Pencil, CircleDot } from "lucide-react"; import { useState } from "react"; import { Status, type BugDetailQuery, useBugEditCommentMutation, BugDetailDocument, } from "@/__generated__/graphql"; import { Markdown } from "@/components/content/Markdown"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { useAuth } from "@/lib/auth"; import { LabelBadge } from "./LabelBadge"; type TimelineNode = NonNullable< NonNullable["bug"]>["timeline"]["nodes"][number] >; interface TimelineProps { repo: string | null; 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({ repo, 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, repo, }: { item: CommentItem; bugPrefix: string; repo: string | null; }) { 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; } void 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 */}