Refactor emoji code (#720)

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
mrdrogdrog 2020-11-04 21:33:52 +01:00 committed by GitHub
parent 0e058e16e2
commit e73661d406
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 93 deletions

View file

@ -40,7 +40,7 @@
"copy-webpack-plugin": "6.2.1",
"d3-graphviz": "3.1.0",
"diff": "4.0.2",
"emoji-picker-element": "1.2.1",
"emoji-picker-element": "1.2.2",
"emojibase-data": "5.1.1",
"eslint-config-react-app": "6.0.0",
"eslint-config-standard": "16.0.1",
@ -127,6 +127,7 @@
},
"rules": {
"no-use-before-define": "off",
"no-debugger": "warn",
"default-param-last": "off"
},
"plugins": [

View file

@ -1,60 +1,57 @@
import { Editor, Hint, Hints, Pos } from 'codemirror'
import Database from 'emoji-picker-element/database'
import { Emoji, EmojiClickEventDetail, NativeEmoji } from 'emoji-picker-element/shared'
import { customEmojis } from '../tool-bar/emoji-picker/emoji-picker'
import { emojiPickerConfig } from '../tool-bar/emoji-picker/emoji-picker'
import { getEmojiIcon, getEmojiShortCode } from '../tool-bar/utils/emojiUtils'
import { findWordAtCursor, Hinter } from './index'
const emojiIndex = new Database({
customEmoji: customEmojis,
dataSource: '/static/js/emoji-data.json'
})
const emojiIndex = new Database(emojiPickerConfig)
const emojiWordRegex = /^:([\w-_+]*)$/
const generateEmojiHints = (editor: Editor): Promise< Hints| null > => {
return new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
const searchResult = emojiWordRegex.exec(searchTerm.text)
if (searchResult === null) {
resolve(null)
return
const findEmojiInDatabase = async (emojiIndex: Database, term: string): Promise<Emoji[]> => {
try {
if (term === '') {
return await emojiIndex.getTopFavoriteEmoji(7)
}
const term = searchResult[1]
let suggestionList: Emoji[]
emojiIndex.getEmojiBySearchQuery(term)
.then(async (result) => {
suggestionList = result
if (result.length === 0) {
suggestionList = await emojiIndex.getTopFavoriteEmoji(7)
}
const cursor = editor.getCursor()
const skinTone = await emojiIndex.getPreferredSkinTone()
const emojiEventDetails: EmojiClickEventDetail[] = suggestionList.map((emoji) => {
return {
emoji,
skinTone: skinTone,
unicode: ((emoji as NativeEmoji).unicode ? (emoji as NativeEmoji).unicode : undefined),
name: emoji.name
}
})
resolve({
list: emojiEventDetails.map((emojiData): Hint => ({
text: getEmojiShortCode(emojiData),
render: (parent: HTMLLIElement) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = `${getEmojiIcon(emojiData)} ${getEmojiShortCode(emojiData)}`
parent.appendChild(wrapper)
}
})),
from: Pos(cursor.line, searchTerm.start),
to: Pos(cursor.line, searchTerm.end)
})
})
.catch(error => {
console.error(error)
resolve(null)
})
})
const queryResult = await emojiIndex.getEmojiBySearchQuery(term)
if (queryResult.length === 0) {
return await emojiIndex.getTopFavoriteEmoji(7)
} else {
return queryResult
}
} catch (error) {
console.error(error)
return []
}
}
const generateEmojiHints = async (editor: Editor): Promise<Hints | null> => {
const searchTerm = findWordAtCursor(editor)
const searchResult = emojiWordRegex.exec(searchTerm.text)
if (searchResult === null) {
return null
}
const suggestionList: Emoji[] = await findEmojiInDatabase(emojiIndex, searchResult[1])
const cursor = editor.getCursor()
const skinTone = await emojiIndex.getPreferredSkinTone()
const emojiEventDetails: EmojiClickEventDetail[] = suggestionList.map((emoji) => ({
emoji,
skinTone: skinTone,
unicode: ((emoji as NativeEmoji).unicode ? (emoji as NativeEmoji).unicode : undefined),
name: emoji.name
}))
return {
list: emojiEventDetails.map((emojiData): Hint => ({
text: getEmojiShortCode(emojiData),
render: (parent: HTMLLIElement) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = `${getEmojiIcon(emojiData)} ${getEmojiShortCode(emojiData)}`
parent.appendChild(wrapper)
}
})),
from: Pos(cursor.line, searchTerm.start),
to: Pos(cursor.line, searchTerm.end)
}
}
export const EmojiHinter: Hinter = {

View file

@ -1,6 +1,6 @@
import { Picker } from 'emoji-picker-element'
import { CustomEmoji, EmojiClickEvent, EmojiClickEventDetail } from 'emoji-picker-element/shared'
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
import React, { useEffect, useRef } from 'react'
import { useSelector } from 'react-redux'
import { useClickAway } from 'react-use'
import { ApplicationState } from '../../../../../redux'
@ -21,61 +21,72 @@ export const customEmojis: CustomEmoji[] = Object.keys(ForkAwesomeIcons).map((na
category: 'ForkAwesome'
}))
export const EMOJI_DATA_PATH = '/static/js/emoji-data.json'
export const emojiPickerConfig = {
customEmoji: customEmojis,
dataSource: EMOJI_DATA_PATH
}
const twemojiStyle = (): HTMLStyleElement => {
const style = document.createElement('style')
style.textContent = 'section.picker { --font-family: "Twemoji Mozilla" !important; }'
return style
}
export const EmojiPicker: React.FC<EmojiPickerProps> = ({ show, onEmojiSelected, onDismiss }) => {
const darkModeEnabled = useSelector((state: ApplicationState) => state.darkMode.darkMode)
const pickerContainerRef = useRef<HTMLDivElement>(null)
const firstOpened = useRef(false)
const pickerRef = useRef<Picker>()
useClickAway(pickerContainerRef, () => {
onDismiss()
})
const emojiClickListener = useCallback((event) => {
onEmojiSelected((event as EmojiClickEvent).detail)
}, [onEmojiSelected])
const twemojiStyle = useMemo(() => {
const style = document.createElement('style')
style.textContent = 'section.picker { --font-family: "Twemoji Mozilla" !important; }'
return style
}, [])
useEffect(() => {
if (!pickerContainerRef.current || firstOpened.current) {
return
}
const picker = new Picker({
customEmoji: customEmojis,
dataSource: '/static/js/emoji-data.json'
})
const container = pickerContainerRef.current
picker.addEventListener('emoji-click', emojiClickListener)
if (picker.shadowRoot) {
picker.shadowRoot.appendChild(twemojiStyle)
}
container.appendChild(picker)
firstOpened.current = true
}, [pickerContainerRef, emojiClickListener, darkModeEnabled, twemojiStyle])
useEffect(() => {
if (!pickerContainerRef.current) {
return
}
const pickerDomList = pickerContainerRef.current.getElementsByTagName('emoji-picker')
if (pickerDomList.length === 0) {
const picker = new Picker(emojiPickerConfig)
if (picker.shadowRoot) {
picker.shadowRoot.appendChild(twemojiStyle())
}
pickerContainerRef.current.appendChild(picker)
pickerRef.current = picker
return () => {
picker.remove()
pickerRef.current = undefined
}
}, [])
useEffect(() => {
if (!pickerRef.current) {
return
}
const picker = pickerDomList[0]
picker.setAttribute('class', darkModeEnabled ? 'dark' : 'light')
if (darkModeEnabled) {
picker.removeAttribute('style')
} else {
picker.setAttribute('style', '--background: #f8f9fa')
const emojiClick = (event: EmojiClickEvent): void => {
onEmojiSelected(event.detail)
}
}, [darkModeEnabled, pickerContainerRef, firstOpened])
const picker = pickerRef.current
picker.addEventListener('emoji-click', emojiClick, true)
return () => {
picker.removeEventListener('emoji-click', emojiClick, true)
}
}, [onEmojiSelected])
useEffect(() => {
if (!pickerRef.current) {
return
}
pickerRef.current.setAttribute('class', darkModeEnabled ? 'dark' : 'light')
if (darkModeEnabled) {
pickerRef.current.removeAttribute('style')
} else {
pickerRef.current.setAttribute('style', '--background: #f8f9fa')
}
}, [darkModeEnabled])
// noinspection CheckTagEmptyBody
return (
<div className={`position-absolute emoji-picker-container ${!show ? 'd-none' : ''}`} ref={pickerContainerRef}></div>
<div className={`position-absolute emoji-picker-container ${!show ? 'd-none' : ''}`} ref={pickerContainerRef}/>
)
}

View file

@ -5780,10 +5780,10 @@ emittery@^0.7.1:
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82"
integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==
emoji-picker-element@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/emoji-picker-element/-/emoji-picker-element-1.2.1.tgz#a8eb99035e07f970c16e202a6e0588dce15dda02"
integrity sha512-gk0NBg7G/S6ClfIUjRKchXLrl4o1dcvKmEamFT9GERHfCeAyi+afUeMhwVY168I65RiqjGCJkGpoTV2CVa2QNA==
emoji-picker-element@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/emoji-picker-element/-/emoji-picker-element-1.2.2.tgz#821b2dcdb89183a098dcaa6df10f3dae798b3acb"
integrity sha512-iQDMY+7lGYKQRL5tgC51PKWqf1V5uGNDQgP+tR1ga//4BUP+HVs/A70oo53Q4iuzkd1IVaUbJBL3YY/6ky9KQA==
emoji-regex@^7.0.1:
version "7.0.3"