import Header from "@/src/components/layouts/header"; import { Button } from "@/src/components/ui/button"; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/src/components/ui/drawer"; import { CommentList } from "@/src/features/comments/CommentList"; import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess"; import { type CommentObjectType } from "@langfuse/shared"; import { MessageCircleIcon, MessageCircleOff } from "lucide-react"; import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/router"; import { type SelectionData } from "./contexts/InlineCommentSelectionContext"; export function CommentDrawerButton({ projectId, objectId, objectType, count, variant = "secondary", className, size = "default", pendingSelection, onSelectionUsed, isOpen: controlledIsOpen, onOpenChange: controlledOnOpenChange, }: { projectId: string; objectId: string; objectType: CommentObjectType; count?: number; variant?: "secondary" | "outline"; className?: string; size?: "default" | "sm" | "xs" | "lg" | "icon" | "icon-xs" | "icon-sm"; pendingSelection?: SelectionData | null; onSelectionUsed?: () => void; isOpen?: boolean; onOpenChange?: (open: boolean) => void; }) { const router = useRouter(); const [isMentionDropdownOpen, setIsMentionDropdownOpen] = useState(false); const [internalIsDrawerOpen, setInternalIsDrawerOpen] = useState(false); const hasAutoOpenedRef = useRef(false); // Track if we've already auto-opened for current deep link const isDrawerOpen = controlledIsOpen ?? internalIsDrawerOpen; const setIsDrawerOpen = controlledOnOpenChange ?? setInternalIsDrawerOpen; const hasFocusedRef = useRef(false); // Track if we've already focused the drawer const hasReadAccess = useHasProjectAccess({ projectId, scope: "comments:read", }); const hasWriteAccess = useHasProjectAccess({ projectId, scope: "comments:CUD", }); // Auto-open drawer when comments=open query param is present AND matches this drawer's object useEffect(() => { const shouldAutoOpen = router.query.comments === "open" && router.query.commentObjectType === objectType && router.query.commentObjectId === objectId && hasReadAccess && !isDrawerOpen && !hasAutoOpenedRef.current; // Only open if drawer is not already open AND we haven't auto-opened yet for this deep link if (shouldAutoOpen) { hasAutoOpenedRef.current = true; setIsDrawerOpen(true); // Scroll to specific comment if hash is present if (router.asPath.includes("#comment-")) { // Wait for drawer animation to complete before scrolling setTimeout(() => { const hash = router.asPath.split("#")[1]; const element = document.getElementById(hash); if (element) { element.scrollIntoView({ behavior: "smooth", block: "center" }); } }, 300); } } // Reset the flag when query params are cleared (user navigated away from deep link) if (router.query.comments !== "open" && hasAutoOpenedRef.current) { hasAutoOpenedRef.current = false; } }, [ router.query.comments, router.query.commentObjectType, router.query.commentObjectId, router.asPath, hasReadAccess, objectType, objectId, isDrawerOpen, setIsDrawerOpen, ]); if (!hasReadAccess || (!hasWriteAccess && !count)) return ( ); return ( { // Prevent drawer from closing when mention dropdown is open if (!open && isMentionDropdownOpen) { // Keep drawer open return; } setIsDrawerOpen(open); // Reset focus tracking when drawer closes if (!open) { hasFocusedRef.current = false; } // Clear URL parameters and hash when drawer is closed if (!open && router.query.comments === "open") { const { comments, commentObjectType, commentObjectId, ...rest } = router.query; router.replace( { pathname: router.pathname, query: rest, }, undefined, { shallow: true }, ); } }} >
{ // Auto-focus drawer content when it opens (only once) if (el && isDrawerOpen && !hasFocusedRef.current) { hasFocusedRef.current = true; setTimeout(() => el.focus({ preventScroll: true }), 100); } }} >
); }