From 358d30beb3a023a3969dedfebf6f2304d35af2d5 Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Thu, 12 Mar 2026 18:39:49 +0530 Subject: [PATCH] refactor(review): split focusedThreadId into persistent focus + expandRequest pulse - Add DraftPin interface (exported for FeedbackPanel) - Add expandRequest state with seq counter for re-trigger - Add draftPin, openThreadDetailId, expandSeqRef state - Remove old pointerdown pin-click handler with requestAnimationFrame clear --- frontend/src/pages/Editor.tsx | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx index 0faede5..ee9a602 100644 --- a/frontend/src/pages/Editor.tsx +++ b/frontend/src/pages/Editor.tsx @@ -53,6 +53,14 @@ interface OnlineUser { color: string; } +export interface DraftPin { + objectId: string; + pinX: number; // 0-1 relative to object bounds + pinY: number; // 0-1 relative to object bounds + worldX: number; // world-space coords at placement time + worldY: number; // world-space coords at placement time +} + export default function Editor({ isPublicView }: EditorProps) { const { boardId } = useParams<{ boardId?: string }>(); const navigate = useNavigate(); @@ -95,6 +103,11 @@ export default function Editor({ isPublicView }: EditorProps) { const [showExport, setShowExport] = useState(false); const [reviewMode, setReviewMode] = useState(false); const [focusedThreadId, setFocusedThreadId] = useState(null); + // Pulse signal: threadId to open, or null to collapse detail view + const [expandRequest, setExpandRequest] = useState<{ threadId: string | null; seq: number } | null>(null); + const [draftPin, setDraftPin] = useState(null); + const [openThreadDetailId, setOpenThreadDetailId] = useState(null); + const expandSeqRef = useRef(0); const [focusMode, setFocusMode] = useState(false); const [layerList, setLayerList] = useState([]); const [selectedLayerIds, setSelectedLayerIds] = useState([]); @@ -188,27 +201,12 @@ export default function Editor({ isPublicView }: EditorProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [objectCount]); - // Pins are always visible; review mode just controls the panel + click-to-comment + // Pin visibility — will be gated on reviewMode in Task 10 useEffect(() => { if (!pinOverlay) return; pinOverlay.visible = true; pinOverlay.refresh(); - - // Pin click → expand thread in panel (listen on viewport since pins are item children) - const vp = canvasRef.current?.getViewport(); - if (!vp) return; - const onClick = (e: any) => { - const worldPos = vp.toWorld(e.global); - const threadId = pinOverlay.getThreadIdAtPoint(worldPos.x, worldPos.y); - if (threadId) { - if (!reviewMode) setReviewMode(true); - setFocusedThreadId(threadId); - requestAnimationFrame(() => setFocusedThreadId(null)); - } - }; - vp.on('pointerdown', onClick); - return () => { vp.off('pointerdown', onClick); }; - }, [reviewMode, pinOverlay]); + }, [pinOverlay]); // Tool activation useEffect(() => {