From 964ba8c758bdb2a1fd271618029f1dfe83a40279 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 13 Oct 2023 17:19:55 -0500 Subject: [PATCH 1/4] Make MentionNode a DecoratorNode instead of a TextNode --- src/features/compose/editor/index.tsx | 2 +- .../compose/editor/nodes/mention-node.ts | 73 ------------- .../compose/editor/nodes/mention-node.tsx | 103 ++++++++++++++++++ .../editor/plugins/autosuggest-plugin.tsx | 2 +- 4 files changed, 105 insertions(+), 75 deletions(-) delete mode 100644 src/features/compose/editor/nodes/mention-node.ts create mode 100644 src/features/compose/editor/nodes/mention-node.tsx 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..5e86bee01 --- /dev/null +++ b/src/features/compose/editor/nodes/mention-node.tsx @@ -0,0 +1,103 @@ +/** + * 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 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; + + 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)); From 5aacfe299a9f67dd3cfd8af591c5841fc181e86d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 13 Oct 2023 17:22:45 -0500 Subject: [PATCH 2/4] MentionNode: show the username, not the full acct --- src/features/compose/editor/nodes/mention-node.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/compose/editor/nodes/mention-node.tsx b/src/features/compose/editor/nodes/mention-node.tsx index 5e86bee01..cb8cbe538 100644 --- a/src/features/compose/editor/nodes/mention-node.tsx +++ b/src/features/compose/editor/nodes/mention-node.tsx @@ -76,6 +76,7 @@ class MentionNode extends DecoratorNode { decorate(): JSX.Element { const acct = this.__acct; + const username = acct.split('@')[0]; return ( ); } From f6d60c243a5f00b149fc3026c8b507919ef86247 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 13 Oct 2023 17:29:52 -0500 Subject: [PATCH 3/4] MentionNode: add Tooltip --- .../compose/editor/nodes/mention-node.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/features/compose/editor/nodes/mention-node.tsx b/src/features/compose/editor/nodes/mention-node.tsx index cb8cbe538..e170550ab 100644 --- a/src/features/compose/editor/nodes/mention-node.tsx +++ b/src/features/compose/editor/nodes/mention-node.tsx @@ -8,6 +8,8 @@ import { addClassNamesToElement } from '@lexical/utils'; import { $applyNodeReplacement, DecoratorNode } from 'lexical'; import React from 'react'; +import { Tooltip } from 'soapbox/components/ui'; + import type { EditorConfig, LexicalNode, @@ -79,14 +81,15 @@ class MentionNode extends DecoratorNode { const username = acct.split('@')[0]; return ( - + + + ); } From 622692fe4d4f1623ae93d0daa3610e3883e6d07b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 13 Oct 2023 17:37:11 -0500 Subject: [PATCH 4/4] MentionNode: truncate Nostr usernames --- src/features/compose/editor/nodes/mention-node.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/compose/editor/nodes/mention-node.tsx b/src/features/compose/editor/nodes/mention-node.tsx index e170550ab..2a4787166 100644 --- a/src/features/compose/editor/nodes/mention-node.tsx +++ b/src/features/compose/editor/nodes/mention-node.tsx @@ -9,6 +9,7 @@ import { $applyNodeReplacement, DecoratorNode } from 'lexical'; import React from 'react'; import { Tooltip } from 'soapbox/components/ui'; +import { isPubkey } from 'soapbox/utils/nostr'; import type { EditorConfig, @@ -87,7 +88,7 @@ class MentionNode extends DecoratorNode { type='button' dir='ltr' > - @{username} + @{isPubkey(username) ? username.slice(0, 8) : username} );