diff --git a/static/app/views/seerExplorer/blockComponents.tsx b/static/app/views/seerExplorer/blockComponents.tsx index 8f0c7a09846a8d..55443aa911cc64 100644 --- a/static/app/views/seerExplorer/blockComponents.tsx +++ b/static/app/views/seerExplorer/blockComponents.tsx @@ -26,6 +26,7 @@ import { interface BlockProps { block: Block; blockIndex: number; + editEnabled?: boolean; isAwaitingFileApproval?: boolean; isAwaitingQuestion?: boolean; isFocused?: boolean; @@ -136,6 +137,7 @@ function BlockComponent({ isLatestTodoBlock, isFocused, isPolling, + editEnabled = true, onClick, onDelete, onMouseEnter, @@ -275,7 +277,11 @@ function BlockComponent({ }; const showActions = - isFocused && !block.loading && !isAwaitingFileApproval && !isAwaitingQuestion; + isFocused && + !block.loading && + !isAwaitingFileApproval && + !isAwaitingQuestion && + editEnabled; // Update when there are more actions than restart return ( sessionData?.blocks || [], [sessionData]); + // Check owner id to determine edit permission + const userId = useUser().id; + const ownerUserId = sessionData?.owner_user_id; + const canEdit = ownerUserId === undefined || ownerUserId.toString() === userId; + // Get PR widget data for menu const {menuItems: prWidgetItems, menuFooter: prWidgetFooter} = usePRWidgetData({ blocks, @@ -203,6 +209,9 @@ function ExplorerPanel({isVisible = false}: ExplorerPanelProps) { }, [focusedBlockIndex]); const handleInputKeyDown = (e: React.KeyboardEvent) => { + if (!canEdit) { + return; + } if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (inputValue.trim() && !isPolling) { @@ -398,22 +407,26 @@ function ExplorerPanel({isVisible = false}: ExplorerPanelProps) { const handleKeyDown = (e: KeyboardEvent) => { const isPrintableChar = e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey; - if ( - e.key === 'Escape' && - isPolling && - !interruptRequested && - !isFileApprovalPending - ) { - e.preventDefault(); - interruptRun(); - } else if (e.key === 'Escape' && !isFileApprovalPending) { - // Don't minimize if file approval is pending (Escape is used to reject) - e.preventDefault(); - setIsMinimized(true); - } else if (isPrintableChar) { - // Don't auto-type if file approval or question is pending (textarea isn't visible) - if (focusedBlockIndex !== -1 && !isFileApprovalPending && !isQuestionPending) { - // If a block is focused, auto-focus input when user starts typing. + if (e.key === 'Escape') { + if (isPolling && canEdit && !interruptRequested && !isFileApprovalPending) { + e.preventDefault(); + interruptRun(); + } else if (!isFileApprovalPending) { + // Don't minimize if file approval is pending (Escape is used to reject) + e.preventDefault(); + setIsMinimized(true); + } + } + + if (isPrintableChar) { + // If a block is focused, auto-focus input when user starts typing. + // Don't do this if file approval or question is pending (textarea isn't visible) + if ( + canEdit && + focusedBlockIndex !== -1 && + !isFileApprovalPending && + !isQuestionPending + ) { e.preventDefault(); setFocusedBlockIndex(-1); textareaRef.current?.focus(); @@ -437,6 +450,7 @@ function ExplorerPanel({isVisible = false}: ExplorerPanelProps) { isVisible, isMenuOpen, isPolling, + canEdit, focusedBlockIndex, interruptRun, interruptRequested, @@ -533,6 +547,7 @@ function ExplorerPanel({isVisible = false}: ExplorerPanelProps) { } isFocused={focusedBlockIndex === index} isPolling={isPolling} + editEnabled={canEdit} onClick={() => handleBlockClick(index)} onMouseEnter={() => { // Don't change focus while menu is open, if already on this block, or if hover is disabled @@ -586,6 +601,7 @@ function ExplorerPanel({isVisible = false}: ExplorerPanelProps) { )} ; run_id?: number; diff --git a/static/app/views/seerExplorer/inputSection.tsx b/static/app/views/seerExplorer/inputSection.tsx index be7e0520b48521..24723f9da87cbb 100644 --- a/static/app/views/seerExplorer/inputSection.tsx +++ b/static/app/views/seerExplorer/inputSection.tsx @@ -30,6 +30,7 @@ interface QuestionActions { } interface InputSectionProps { + enabled: boolean; focusedBlockIndex: number; inputValue: string; interruptRequested: boolean; @@ -47,6 +48,7 @@ interface InputSectionProps { } function InputSection({ + enabled, inputValue, focusedBlockIndex, isMinimized = false, @@ -62,6 +64,9 @@ function InputSection({ questionActions, }: InputSectionProps) { const getPlaceholder = () => { + if (!enabled) { + return 'This conversation is owned by another user and is read-only'; + } if (focusedBlockIndex !== -1) { return 'Press Tab ⇥ to return here'; } @@ -70,7 +75,7 @@ function InputSection({ // Handle keyboard shortcuts for file approval useEffect(() => { - if (!fileApprovalActions || !isVisible || isMinimized) { + if (!fileApprovalActions || !isVisible || isMinimized || !enabled) { return undefined; } @@ -94,11 +99,11 @@ function InputSection({ document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); - }, [fileApprovalActions, isVisible, isMinimized]); + }, [fileApprovalActions, isVisible, isMinimized, enabled]); // Handle keyboard shortcuts for questions useEffect(() => { - if (!questionActions || !isVisible || isMinimized) { + if (!questionActions || !isVisible || isMinimized || !enabled) { return undefined; } @@ -134,7 +139,27 @@ function InputSection({ document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); - }, [questionActions, isVisible, isMinimized]); + }, [questionActions, isVisible, isMinimized, enabled]); + + if (!enabled) { + return ( + + + + + + ); + } // Render file approval action bar instead of entire input section if (fileApprovalActions) {