import React, {useState} from "react"; import {useParams, useNavigate} from "react-router-dom"; import {useCurrentUserQuery} from "@/hooks/useAuth"; import { useCreateCommentMutation, useDeleteCommentMutation, useIssueCommentsQuery, useIssueDetailQuery, useUpdateCommentMutation, } from "@/hooks/useIssueDetailQuery"; import { useCloseIssueMutation, useReopenIssueMutation, useUpdateIssueMutation, useDeleteIssueMutation, } from "@/hooks/useIssueExtraQuery"; import {LoadingState} from "@/components/ui/LoadingState"; import {ErrorState} from "@/components/ui/ErrorState"; import {IrRenderer} from "@/lib/ir/renderer"; import {extractIrNodes} from "@/lib/ir/parser"; import {Button} from "@/components/ui/button"; import {Textarea} from "@/components/ui/textarea"; import {Input} from "@/components/ui/input"; import {Loader2, MessageSquare, Pencil, Trash2, Lock, Unlock} from "lucide-react"; import type {IssueCommentResponse} from "@/client/model"; import {IssueSidebar} from "./IssueSidebar"; import {ReactionBar} from "./ReactionBar"; export function IssueDetailPage() { const {projectName, issueNumber} = useParams<{ projectName: string; issueNumber: string; }>(); const number = issueNumber ? Number(issueNumber) : 0; const {data: currentUser} = useCurrentUserQuery(); const navigate = useNavigate(); const { data: issueDetail, isLoading: detailLoading, error: detailError, refetch: refetchDetail, } = useIssueDetailQuery({projectName: projectName!, issueNumber: number}); const { data: comments = [], } = useIssueCommentsQuery({projectName: projectName!, issueNumber: number}); const closeIssue = useCloseIssueMutation(); const reopenIssue = useReopenIssueMutation(); const updateIssue = useUpdateIssueMutation(); const deleteIssue = useDeleteIssueMutation(); const createComment = useCreateCommentMutation(); const updateComment = useUpdateCommentMutation(); const deleteComment = useDeleteCommentMutation(); const [newCommentBody, setNewCommentBody] = useState(""); const [editingId, setEditingId] = useState(null); const [editBody, setEditBody] = useState(""); // Issue edit state const [isEditingIssue, setIsEditingIssue] = useState(false); const [editTitle, setEditTitle] = useState(""); const [editIssueBody, setEditIssueBody] = useState(""); const handleRetry = () => { refetchDetail(); }; const handleClose = () => { if (!projectName || !number) return; closeIssue.mutate({ projectName, issueNumber: number }, { onSuccess: () => refetchDetail() }); }; const handleReopen = () => { if (!projectName || !number) return; reopenIssue.mutate({ projectName, issueNumber: number }, { onSuccess: () => refetchDetail() }); }; const handleDelete = () => { if (!projectName || !number) return; if (!window.confirm("Are you sure you want to delete this issue?")) return; deleteIssue.mutate({ projectName, issueNumber: number }, { onSuccess: () => navigate(`/${projectName}/issues`) }); }; const startEditIssue = () => { if (!issueDetail) return; setEditTitle(issueDetail.title); setEditIssueBody(issueDetail.body ?? ""); setIsEditingIssue(true); }; const saveEditIssue = () => { if (!projectName || !number) return; updateIssue.mutate( { projectName, issueNumber: number, req: { title: editTitle, body: editIssueBody } }, { onSuccess: () => { setIsEditingIssue(false); refetchDetail(); } } ); }; const cancelEditIssue = () => { setIsEditingIssue(false); setEditTitle(""); setEditIssueBody(""); }; const handleCreateComment = () => { if (!projectName || !number || !newCommentBody.trim()) return; createComment.mutate( {projectName, issueNumber: number, body: newCommentBody.trim()}, { onSuccess: () => { setNewCommentBody(""); }, } ); }; const handleStartEdit = (comment: IssueCommentResponse) => { setEditingId(comment.id); setEditBody(comment.body); }; const handleCancelEdit = () => { setEditingId(null); setEditBody(""); }; const handleSaveEdit = () => { if (!projectName || !number || editingId === null || !editBody.trim()) return; updateComment.mutate( {projectName, issueNumber: number, commentId: editingId, body: editBody.trim()}, { onSuccess: () => { setEditingId(null); setEditBody(""); }, } ); }; const handleDeleteComment = (commentId: number) => { if (!projectName || !number) return; if (!window.confirm("Are you sure you want to delete this comment?")) return; deleteComment.mutate( { projectName, issueNumber: number, commentId }, { onSuccess: () => refetchComments() } ); }; const isOwnComment = (comment: IssueCommentResponse) => { if (!currentUser) return false; return currentUser.uid === comment.author || currentUser.username === comment.author_username; }; const isMutating = createComment.isPending || updateComment.isPending || deleteComment.isPending; if (detailLoading) { return (
); } if (detailError || !issueDetail) { return (
); } return (
{isEditingIssue ? (
setEditTitle(e.target.value)} className="text-xl font-bold border-none focus-visible:ring-1" style={{ backgroundColor: "var(--surface-elevated)", "--tw-ring-color": "var(--accent)" } as React.CSSProperties} />