kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
				
				
				
			lexical: remove MentionPlugin, only insert mention when selected from autosuggest
							rodzic
							
								
									71bb8cc73e
								
							
						
					
					
						commit
						e88b952a6f
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue