lexical: remove MentionPlugin, only insert mention when selected from autosuggest
This commit is contained in:
parent
71bb8cc73e
commit
e88b952a6f
4 changed files with 25 additions and 64 deletions
|
@ -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…
Reference in a new issue