lexical: remove MentionPlugin, only insert mention when selected from autosuggest

environments/review-fix-double-v0jtu2/deployments/4050
Alex Gleason 1 year ago
parent 71bb8cc73e
commit e88b952a6f
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -23,7 +23,6 @@ import { useAppDispatch } from 'soapbox/hooks';
import { useNodes } from './nodes';
import AutosuggestPlugin from './plugins/autosuggest-plugin';
import FocusPlugin from './plugins/focus-plugin';
import MentionPlugin from './plugins/mention-plugin';
import RefPlugin from './plugins/ref-plugin';
import StatePlugin from './plugins/state-plugin';
@ -162,7 +161,6 @@ const ComposeEditor = React.forwardRef<LexicalEditor, IComposeEditor>(({
/>
<HistoryPlugin />
<HashtagPlugin />
<MentionPlugin />
<AutosuggestPlugin composeId={composeId} suggestionsHidden={suggestionsHidden} setSuggestionsHidden={setSuggestionsHidden} />
<AutoLinkPlugin matchers={LINK_MATCHERS} />
<StatePlugin composeId={composeId} handleSubmit={handleSubmit} />

@ -60,7 +60,11 @@ class MentionNode extends TextNode {
}
const $createMentionNode = (text = ''): MentionNode => $applyNodeReplacement(new MentionNode(text));
function $createMentionNode(text: string): MentionNode {
const node = new MentionNode(text);
node.setMode('token').toggleDirectionless();
return $applyNodeReplacement(node);
}
const $isMentionNode = (
node: LexicalNode | null | undefined,

@ -19,6 +19,7 @@ import {
KEY_TAB_COMMAND,
LexicalEditor,
RangeSelection,
TextNode,
} from 'lexical';
import React, {
MutableRefObject,
@ -39,6 +40,7 @@ import { selectAccount } from 'soapbox/selectors';
import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions';
import AutosuggestAccount from '../../components/autosuggest-account';
import { $createMentionNode } from '../nodes/mention-node';
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
@ -303,27 +305,29 @@ const AutosuggestPlugin = ({
dispatch((dispatch, getState) => {
const state = editor.getEditorState();
const node = (state._selection as RangeSelection)?.anchor?.getNode();
const { leadOffset, matchingString } = resolution!.match;
/** Offset for the beginning of the matched text, including the token. */
const offset = leadOffset - 1;
if (typeof suggestion === 'object') {
if (!suggestion.id) return;
dispatch(useEmoji(suggestion)); // eslint-disable-line react-hooks/rules-of-hooks
const { leadOffset, matchingString } = resolution!.match;
if (isNativeEmoji(suggestion)) {
node.spliceText(leadOffset - 1, matchingString.length, `${suggestion.native} `, true);
node.spliceText(offset, matchingString.length, `${suggestion.native} `, true);
} else {
node.spliceText(leadOffset - 1, matchingString.length, `${suggestion.colons} `, true);
node.spliceText(offset, matchingString.length, `${suggestion.colons} `, true);
}
} else if (suggestion[0] === '#') {
node.setTextContent(`${suggestion} `);
node.select();
} else {
const content = selectAccount(getState(), suggestion)!.acct;
node.setTextContent(`@${content} `);
node.select();
const acct = selectAccount(getState(), suggestion)!.acct;
const result = (node as TextNode).splitText(offset, offset + matchingString.length);
const textNode = result[1] ?? result[0];
const mentionNode = textNode?.replace($createMentionNode(`@${acct}`));
mentionNode.insertAfter(new TextNode(' '));
mentionNode.selectNext();
}
dispatch(clearComposeSuggestions(composeId));
@ -337,13 +341,18 @@ const AutosuggestPlugin = ({
if (!node) return null;
if (['mention', 'hashtag'].includes(node.getType())) {
if (['hashtag'].includes(node.getType())) {
const matchingString = node.getTextContent();
return { leadOffset: 0, matchingString };
}
if (node.getType() === 'text') {
const [leadOffset, matchingString] = textAtCursorMatchesToken(node.getTextContent(), (state._selection as RangeSelection)?.anchor?.offset, [':']);
const [leadOffset, matchingString] = textAtCursorMatchesToken(
node.getTextContent(),
(state._selection as RangeSelection)?.anchor?.offset,
[':', '@'],
);
if (!leadOffset || !matchingString) return null;
return { leadOffset, matchingString };
}

@ -1,50 +0,0 @@
/**
* This source code is derived from code from Meta Platforms, Inc.
* and affiliates, licensed under the MIT license located in the
* LICENSE file in the /src/features/compose/editor directory.
*/
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { useLexicalTextEntity } from '@lexical/react/useLexicalTextEntity';
import { useCallback, useEffect } from 'react';
import { $createMentionNode, MentionNode } from '../nodes/mention-node';
import type { TextNode } from 'lexical';
const MENTION_REGEX = /(?:^|\s)@[^\s]+/i;
const MentionPlugin = (): JSX.Element | null => {
const [editor] = useLexicalComposerContext();
useEffect(() => {
if (!editor.hasNodes([MentionNode])) {
throw new Error('MentionPlugin: MentionNode not registered on editor');
}
}, [editor]);
const createNode = useCallback((textNode: TextNode): MentionNode => {
return $createMentionNode(textNode.getTextContent());
}, []);
const getMatch = useCallback((text: string) => {
const match = MENTION_REGEX.exec(text);
if (!match) return null;
const length = match[0].length;
const start = match.index;
const end = start + length;
return { start, end };
}, []);
useLexicalTextEntity<MentionNode>(
getMatch,
MentionNode,
createNode,
);
return null;
};
export default MentionPlugin;
Loading…
Cancel
Save