lexical: render native emojis as Twemoji
This commit is contained in:
parent
2a9cb08d08
commit
4b5602a086
2 changed files with 22 additions and 29 deletions
|
@ -1,7 +1,8 @@
|
||||||
import { $applyNodeReplacement, DecoratorNode } from 'lexical';
|
import { $applyNodeReplacement, DecoratorNode } from 'lexical';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Emoji } from 'soapbox/components/ui';
|
import { Emoji as Component } from 'soapbox/components/ui';
|
||||||
|
import { isNativeEmoji, type Emoji } from 'soapbox/features/emoji';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
DOMExportOutput,
|
DOMExportOutput,
|
||||||
|
@ -13,29 +14,26 @@ import type {
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
|
|
||||||
type SerializedEmojiNode = Spread<{
|
type SerializedEmojiNode = Spread<{
|
||||||
name: string
|
data: Emoji
|
||||||
src: string
|
|
||||||
type: 'emoji'
|
type: 'emoji'
|
||||||
version: 1
|
version: 1
|
||||||
}, SerializedLexicalNode>;
|
}, SerializedLexicalNode>;
|
||||||
|
|
||||||
class EmojiNode extends DecoratorNode<JSX.Element> {
|
class EmojiNode extends DecoratorNode<JSX.Element> {
|
||||||
|
|
||||||
__name: string;
|
__emoji: Emoji;
|
||||||
__src: string;
|
|
||||||
|
|
||||||
static getType(): 'emoji' {
|
static getType(): 'emoji' {
|
||||||
return 'emoji';
|
return 'emoji';
|
||||||
}
|
}
|
||||||
|
|
||||||
static clone(node: EmojiNode): EmojiNode {
|
static clone(node: EmojiNode): EmojiNode {
|
||||||
return new EmojiNode(node.__name, node.__src, node.__key);
|
return new EmojiNode(node.__emoji, node.__key);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name: string, src: string, key?: NodeKey) {
|
constructor(emoji: Emoji, key?: NodeKey) {
|
||||||
super(key);
|
super(key);
|
||||||
this.__name = name;
|
this.__emoji = emoji;
|
||||||
this.__src = src;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createDOM(config: EditorConfig): HTMLElement {
|
createDOM(config: EditorConfig): HTMLElement {
|
||||||
|
@ -60,16 +58,13 @@ class EmojiNode extends DecoratorNode<JSX.Element> {
|
||||||
return { element };
|
return { element };
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedEmojiNode): EmojiNode {
|
static importJSON({ data }: SerializedEmojiNode): EmojiNode {
|
||||||
const { name, src } = serializedNode;
|
return $createEmojiNode(data);
|
||||||
const node = $createEmojiNode(name, src);
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedEmojiNode {
|
exportJSON(): SerializedEmojiNode {
|
||||||
return {
|
return {
|
||||||
name: this.__name,
|
data: this.__emoji,
|
||||||
src: this.__src,
|
|
||||||
type: 'emoji',
|
type: 'emoji',
|
||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
|
@ -88,15 +83,19 @@ class EmojiNode extends DecoratorNode<JSX.Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
decorate(): JSX.Element {
|
decorate(): JSX.Element {
|
||||||
return (
|
const emoji = this.__emoji;
|
||||||
<Emoji src={this.__src} alt={this.__name} className='emojione h-4 w-4' />
|
|
||||||
);
|
if (isNativeEmoji(emoji)) {
|
||||||
|
return <Component emoji={emoji.native} alt={emoji.colons} className='emojione h-4 w-4' />;
|
||||||
|
} else {
|
||||||
|
return <Component src={emoji.imageUrl} alt={emoji.colons} className='emojione h-4 w-4' />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function $createEmojiNode (name = '', src: string): EmojiNode {
|
function $createEmojiNode(emoji: Emoji): EmojiNode {
|
||||||
const node = new EmojiNode(name, src);
|
const node = new EmojiNode(emoji);
|
||||||
return $applyNodeReplacement(node);
|
return $applyNodeReplacement(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,8 @@ import React, {
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import { clearComposeSuggestions, fetchComposeSuggestions } from 'soapbox/actions/compose';
|
import { clearComposeSuggestions, fetchComposeSuggestions } from 'soapbox/actions/compose';
|
||||||
import { useEmoji } from 'soapbox/actions/emojis';
|
import { useEmoji as chooseEmoji } from 'soapbox/actions/emojis';
|
||||||
import AutosuggestEmoji from 'soapbox/components/autosuggest-emoji';
|
import AutosuggestEmoji from 'soapbox/components/autosuggest-emoji';
|
||||||
import { isNativeEmoji } from 'soapbox/features/emoji';
|
|
||||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||||
import { selectAccount } from 'soapbox/selectors';
|
import { selectAccount } from 'soapbox/selectors';
|
||||||
import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions';
|
import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions';
|
||||||
|
@ -322,13 +321,8 @@ const AutosuggestPlugin = ({
|
||||||
|
|
||||||
if (typeof suggestion === 'object') {
|
if (typeof suggestion === 'object') {
|
||||||
if (!suggestion.id) return;
|
if (!suggestion.id) return;
|
||||||
dispatch(useEmoji(suggestion)); // eslint-disable-line react-hooks/rules-of-hooks
|
dispatch(chooseEmoji(suggestion));
|
||||||
|
replaceMatch($createEmojiNode(suggestion));
|
||||||
if (isNativeEmoji(suggestion)) {
|
|
||||||
node.spliceText(offset, matchingString.length, `${suggestion.native} `, true);
|
|
||||||
} else {
|
|
||||||
replaceMatch($createEmojiNode(suggestion.colons, suggestion.imageUrl));
|
|
||||||
}
|
|
||||||
} else if (suggestion[0] === '#') {
|
} else if (suggestion[0] === '#') {
|
||||||
node.setTextContent(`${suggestion} `);
|
node.setTextContent(`${suggestion} `);
|
||||||
node.select();
|
node.select();
|
||||||
|
|
Loading…
Reference in a new issue