diff --git a/src/features/compose/editor/index.tsx b/src/features/compose/editor/index.tsx index 0bff80d23..a73081b89 100644 --- a/src/features/compose/editor/index.tsx +++ b/src/features/compose/editor/index.tsx @@ -52,7 +52,7 @@ interface IComposeEditor { const theme: InitialConfigType['theme'] = { emoji: 'select-none', hashtag: 'hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue', - mention: 'hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue', + mention: 'hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue select-none', link: 'hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue', text: { bold: 'font-bold', diff --git a/src/features/compose/editor/nodes/mention-node.ts b/src/features/compose/editor/nodes/mention-node.ts deleted file mode 100644 index 343ea4af5..000000000 --- a/src/features/compose/editor/nodes/mention-node.ts +++ /dev/null @@ -1,73 +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 { addClassNamesToElement } from '@lexical/utils'; -import { $applyNodeReplacement, TextNode } from 'lexical'; - -import type { - EditorConfig, - LexicalNode, - NodeKey, - SerializedTextNode, -} from 'lexical'; - -class MentionNode extends TextNode { - - static getType(): string { - return 'mention'; - } - - static clone(node: MentionNode): MentionNode { - return new MentionNode(node.__text, node.__key); - } - - constructor(text: string, key?: NodeKey) { - super(text, key); - } - - createDOM(config: EditorConfig): HTMLElement { - const element = super.createDOM(config); - addClassNamesToElement(element, config.theme.mention); - return element; - } - - static importJSON(serializedNode: SerializedTextNode): MentionNode { - const node = $createMentionNode(serializedNode.text); - node.setFormat(serializedNode.format); - node.setDetail(serializedNode.detail); - node.setMode(serializedNode.mode); - node.setStyle(serializedNode.style); - return node; - } - - exportJSON(): SerializedTextNode { - return { - ...super.exportJSON(), - type: 'mention', - }; - } - - canInsertTextBefore(): boolean { - return false; - } - - isTextEntity(): true { - return true; - } - -} - -function $createMentionNode(text: string): MentionNode { - const node = new MentionNode(text); - node.setMode('segmented').toggleDirectionless(); - return $applyNodeReplacement(node); -} - -const $isMentionNode = ( - node: LexicalNode | null | undefined, -): node is MentionNode => node instanceof MentionNode; - -export { MentionNode, $createMentionNode, $isMentionNode }; diff --git a/src/features/compose/editor/nodes/mention-node.tsx b/src/features/compose/editor/nodes/mention-node.tsx new file mode 100644 index 000000000..2a4787166 --- /dev/null +++ b/src/features/compose/editor/nodes/mention-node.tsx @@ -0,0 +1,108 @@ +/** + * 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 { addClassNamesToElement } from '@lexical/utils'; +import { $applyNodeReplacement, DecoratorNode } from 'lexical'; +import React from 'react'; + +import { Tooltip } from 'soapbox/components/ui'; +import { isPubkey } from 'soapbox/utils/nostr'; + +import type { + EditorConfig, + LexicalNode, + NodeKey, + SerializedLexicalNode, + Spread, +} from 'lexical'; + +type SerializedMentionNode = Spread<{ + acct: string; + type: 'mention'; + version: 1; +}, SerializedLexicalNode>; + +class MentionNode extends DecoratorNode { + + __acct: string; + + static getType(): string { + return 'mention'; + } + + static clone(node: MentionNode): MentionNode { + return new MentionNode(node.__acct, node.__key); + } + + constructor(acct: string, key?: NodeKey) { + super(key); + this.__acct = acct; + } + + createDOM(config: EditorConfig): HTMLElement { + const span = document.createElement('span'); + addClassNamesToElement(span, config.theme.mention); + return span; + } + + updateDOM(): false { + return false; + } + + static importJSON(serializedNode: SerializedMentionNode): MentionNode { + const node = $createMentionNode(serializedNode.acct); + return node; + } + + exportJSON(): SerializedMentionNode { + return { + type: 'mention', + acct: this.__acct, + version: 1, + }; + } + + getTextContent(): string { + return `@${this.__acct}`; + } + + canInsertTextBefore(): boolean { + return false; + } + + isTextEntity(): true { + return true; + } + + decorate(): JSX.Element { + const acct = this.__acct; + const username = acct.split('@')[0]; + + return ( + + + + ); + } + +} + +function $createMentionNode(acct: string): MentionNode { + const node = new MentionNode(acct); + return $applyNodeReplacement(node); +} + +const $isMentionNode = ( + node: LexicalNode | null | undefined, +): node is MentionNode => node instanceof MentionNode; + +export { MentionNode, $createMentionNode, $isMentionNode }; diff --git a/src/features/compose/editor/plugins/autosuggest-plugin.tsx b/src/features/compose/editor/plugins/autosuggest-plugin.tsx index 33ab39b0f..93d032b42 100644 --- a/src/features/compose/editor/plugins/autosuggest-plugin.tsx +++ b/src/features/compose/editor/plugins/autosuggest-plugin.tsx @@ -328,7 +328,7 @@ const AutosuggestPlugin = ({ node.select(); } else { const acct = selectAccount(getState(), suggestion)!.acct; - replaceMatch($createMentionNode(`@${acct}`)); + replaceMatch($createMentionNode(acct)); } dispatch(clearComposeSuggestions(composeId));