[web] remove mentions from chat message editing (#35761)

GitOrigin-RevId: 6cd27490485196b82a9c4d9ffa19ba5d8e10922d
This commit is contained in:
Kristina
2026-09-17 11:36:38 +00:00
committed by Copybot
parent 94cad14caf
commit 4e2840e247
7 changed files with 56 additions and 240 deletions
@@ -583,7 +583,6 @@
"edit_dictionary_empty": "",
"edit_dictionary_remove": "",
"edit_figure": "",
"edit_message": "",
"edit_reference": "",
"edit_sso_configuration": "",
"edit_tag": "",
@@ -6,10 +6,7 @@ import { debugConsole } from '@/utils/debugging'
import { Message, useChatContext } from '@/features/chat/context/chat-context'
import OLButton from '@/shared/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
import { mentionsFeatureEnabled } from '@/shared/utils/mentions'
import { MentionsInput } from '@/shared/components/mentions-input'
import { parseMentions } from '@/shared/utils/parse-mentions'
import { MentionBadge } from '@/shared/components/mention-badge'
import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
const MessageContent: FC<{
content: Message['content']
@@ -50,51 +47,64 @@ const MessageContent: FC<{
}
}, [content, mounted])
const completeEdit = useCallback(
(value: string) => {
editMessage(messageId, value)
const completeEdit = useCallback(() => {
editMessage(messageId, editedContent)
}, [editMessage, editedContent, messageId])
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault()
completeEdit()
} else if (e.key === 'Escape') {
e.preventDefault()
cancelMessageEdit()
setEditedContent(content)
}
},
[editMessage, messageId]
[cancelMessageEdit, completeEdit, content]
)
const handleCancel = useCallback(() => {
cancelMessageEdit()
setEditedContent(content)
}, [cancelMessageEdit, content])
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setEditedContent(e.target.value)
},
[]
)
const mentionsEnabled = mentionsFeatureEnabled()
const handleAutoFocus = useCallback(
(textarea: HTMLTextAreaElement) => textarea.select(),
[]
)
return editing ? (
<>
<MentionsInput
className="chat-message-edit-input"
label={t('edit_message')}
initialValue={content}
onChange={setEditedContent}
onSubmit={completeEdit}
onCancel={handleCancel}
<AutoExpandingTextArea
value={editedContent}
style={{ width: '100%' }}
onKeyDown={handleKeyDown}
onChange={handleChange}
autoFocus // eslint-disable-line jsx-a11y/no-autofocus
selectOnFocus
onAutoFocus={handleAutoFocus}
/>
<br />
<OLButton size="sm" variant="secondary" onClick={handleCancel}>
{t('cancel')}
</OLButton>
<OLButton
size="sm"
variant="secondary"
onClick={() => completeEdit(editedContent)}
onClick={() => {
cancelMessageEdit()
setEditedContent(content)
}}
>
{t('cancel')}
</OLButton>
<OLButton size="sm" variant="secondary" onClick={() => completeEdit()}>
{t('save')}
</OLButton>
</>
) : (
<p ref={root} translate="no">
{mentionsEnabled ? (
renderMentionedContent(content)
) : (
<Linkify>{content}</Linkify>
)}
<Linkify>{content}</Linkify>
{edited ? (
<>
{' '}
@@ -105,14 +115,4 @@ const MessageContent: FC<{
)
}
function renderMentionedContent(content: string) {
return parseMentions(content).map((segment, index) =>
segment.type === 'mention' ? (
<MentionBadge key={index} userId={segment.userId} />
) : (
<Linkify key={index}>{segment.value}</Linkify>
)
)
}
export default MessageContent
@@ -6,7 +6,7 @@ import {
drawSelection,
placeholder as placeholderExt,
} from '@codemirror/view'
import { Compartment, EditorSelection } from '@codemirror/state'
import { Compartment } from '@codemirror/state'
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
import { autocompletion, completionStatus } from '@codemirror/autocomplete'
import classNames from 'classnames'
@@ -27,7 +27,7 @@ export type MentionsInputHandle = {
// Returns true if the editor was focused, false if the view isn't ready yet.
focus: () => boolean
// Empties the editor. Used by inputs that stay mounted between submissions
// (the chat composer and the review-panel reply box).
// (the review-panel reply box).
clear: () => void
}
@@ -35,16 +35,11 @@ type MentionsInputProps = {
onSubmit: (value: string) => void
onChange?: (value: string) => void
onBlur?: (value: string) => void
// Called on Escape (when the autocomplete popup is not open). Used by the
// edit flows to cancel without saving.
onCancel?: () => void
onFocus?: () => void
placeholder?: string
label?: string
disabled?: boolean
initialValue?: string
autoFocus?: boolean
selectOnFocus?: boolean
className?: string
}
@@ -73,8 +68,8 @@ const mentionsInputTheme = EditorView.theme({
})
// A small CodeMirror 6 input that supports `@mention` autocompletion. Shared by
// the chat composer/edit field and the review panel's add-comment, reply, and
// edit fields so all of them get the same mention behaviour.
// the review panel's add-comment, reply, and edit fields so all of them get the
// same mention behaviour.
export const MentionsInput = forwardRef<
MentionsInputHandle,
MentionsInputProps
@@ -83,14 +78,11 @@ export const MentionsInput = forwardRef<
onChange,
onSubmit,
onBlur,
onCancel,
onFocus,
placeholder = '',
label,
disabled = false,
initialValue = '',
autoFocus = false,
selectOnFocus = false,
className,
},
ref
@@ -108,14 +100,10 @@ export const MentionsInput = forwardRef<
const onChangeRef = useRef(onChange)
const onSubmitRef = useRef(onSubmit)
const onBlurRef = useRef(onBlur)
const onCancelRef = useRef(onCancel)
const onFocusRef = useRef(onFocus)
const disabledRef = useRef(disabled)
onChangeRef.current = onChange
onSubmitRef.current = onSubmit
onBlurRef.current = onBlur
onCancelRef.current = onCancel
onFocusRef.current = onFocus
disabledRef.current = disabled
useImperativeHandle(
@@ -182,10 +170,6 @@ export const MentionsInput = forwardRef<
}
}),
EditorView.domEventHandlers({
focus: () => {
onFocusRef.current?.()
return false
},
blur: (_event, view) => {
if (completionStatus(view.state) === 'active') {
return false
@@ -205,20 +189,6 @@ export const MentionsInput = forwardRef<
return true
},
},
{
key: 'Escape',
run: view => {
// Let the autocomplete popup handle Escape when it's open.
if (completionStatus(view.state) === 'active') {
return false
}
if (onCancelRef.current) {
onCancelRef.current()
return true
}
return false
},
},
...defaultKeymap,
...historyKeymap,
]),
@@ -228,11 +198,6 @@ export const MentionsInput = forwardRef<
viewRef.current = view
if (autoFocus) {
view.focus()
if (selectOnFocus) {
view.dispatch({
selection: EditorSelection.range(0, view.state.doc.length),
})
}
}
return () => {
@@ -90,35 +90,6 @@
padding: calc(var(--line-height-03) / 4);
background-color: var(--chat-new-message-textarea-bg);
}
.chat-message-input {
height: 100%;
width: 100%;
.cm-editor {
height: 100%;
border-radius: var(--border-radius-base);
border: 1px solid var(--border-divider-themed);
background-color: var(--chat-new-message-textarea-bg);
&.cm-focused {
// suppress CM6's default focus outline; the border conveys focus
outline: none;
}
}
.cm-content {
color: var(--chat-new-message-textarea-color);
font-size: var(--font-size-02);
line-height: var(--line-height-02);
padding: calc(var(--line-height-03) / 4);
}
.cm-cursor,
.cm-cursor-primary {
border-left-color: var(--chat-new-message-textarea-color);
}
}
}
}
@@ -250,36 +221,6 @@
font-weight: normal;
}
.mention-badge {
color: var(--content-info);
}
.chat-message-edit-input {
width: 100%;
.cm-editor {
border-radius: var(--border-radius-base);
border: 1px solid var(--border-divider-themed);
background-color: var(--chat-new-message-textarea-bg);
&.cm-focused {
outline: none;
}
}
.cm-content {
color: var(--chat-new-message-textarea-color);
font-size: var(--font-size-02);
line-height: var(--line-height-02);
padding: calc(var(--line-height-03) / 4);
}
.cm-cursor,
.cm-cursor-primary {
border-left-color: var(--chat-new-message-textarea-color);
}
}
}
.message-container.message-from-self {