mirror of
https://github.com/flarum/framework.git
synced 2024-12-01 14:20:47 +08:00
use twemoji (#16)
* use twemoji * insert emoji instead of shortcode * cleanups * generate emojiMap using emojibase * remove redunant spaces * remove dead codes * use prebuild and prewatch * update dependencies
This commit is contained in:
parent
680c5c3784
commit
ab44c2b629
|
@ -19,18 +19,14 @@ return [
|
||||||
|
|
||||||
(new Extend\Formatter)
|
(new Extend\Formatter)
|
||||||
->configure(function (Configurator $config) {
|
->configure(function (Configurator $config) {
|
||||||
$config->Emoji->useEmojiOne();
|
$config->Emoticons->add(':)', '🙂');
|
||||||
$config->Emoji->omitImageSize();
|
$config->Emoticons->add(':D', '😃');
|
||||||
$config->Emoji->useSVG();
|
$config->Emoticons->add(':P', '😛');
|
||||||
|
$config->Emoticons->add(':(', '🙁');
|
||||||
$config->Emoji->addAlias(':)', '🙂');
|
$config->Emoticons->add(':|', '😐');
|
||||||
$config->Emoji->addAlias(':D', '😃');
|
$config->Emoticons->add(';)', '😉');
|
||||||
$config->Emoji->addAlias(':P', '😛');
|
$config->Emoticons->add(':\'(', '😢');
|
||||||
$config->Emoji->addAlias(':(', '🙁');
|
$config->Emoticons->add(':O', '😮');
|
||||||
$config->Emoji->addAlias(':|', '😐');
|
$config->Emoticons->add('>:(', '😡');
|
||||||
$config->Emoji->addAlias(';)', '😉');
|
|
||||||
$config->Emoji->addAlias(':\'(', '😢');
|
|
||||||
$config->Emoji->addAlias(':O', '😮');
|
|
||||||
$config->Emoji->addAlias('>:(', '😡');
|
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
10
extensions/emoji/js/package-lock.json
generated
10
extensions/emoji/js/package-lock.json
generated
|
@ -1726,6 +1726,11 @@
|
||||||
"minimalistic-crypto-utils": "^1.0.0"
|
"minimalistic-crypto-utils": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"emojibase-data": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojibase-data/-/emojibase-data-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-XRKHADz/s7wTMGThto272slA/yA="
|
||||||
|
},
|
||||||
"emojis-list": {
|
"emojis-list": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||||
|
@ -4296,6 +4301,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
|
||||||
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
|
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
|
||||||
},
|
},
|
||||||
|
"twemoji": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-Z0bRyZ1yO7cqa69oRhLZWmaLM0e9eeTugUXbnNQY3XPgRHJcSF8PKfp/dx3vHWtz9Y6o8fi3Ryjfpq4vBCeXjA=="
|
||||||
|
},
|
||||||
"typedarray": {
|
"typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
|
|
@ -2,13 +2,18 @@
|
||||||
"name": "@flarum/flarum-ext-emoji",
|
"name": "@flarum/flarum-ext-emoji",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"emojibase-data": "^3.1.0",
|
||||||
"flarum-webpack-config": "0.1.0-beta.9",
|
"flarum-webpack-config": "0.1.0-beta.9",
|
||||||
"textarea-caret": "^3.1.0",
|
"textarea-caret": "^3.1.0",
|
||||||
|
"twemoji": "^11.0.1",
|
||||||
"webpack": "^4.19.1",
|
"webpack": "^4.19.1",
|
||||||
"webpack-cli": "^3.1.0"
|
"webpack-cli": "^3.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"watch": "webpack --mode development --watch"
|
"watch": "webpack --mode development --watch",
|
||||||
|
"prebuild": "npm run generate-emoji-map",
|
||||||
|
"prewatch": "npm run generate-emoji-map",
|
||||||
|
"generate-emoji-map": "node ./scripts/generate-emoji-map.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
extensions/emoji/js/scripts/generate-emoji-map.js
Normal file
45
extensions/emoji/js/scripts/generate-emoji-map.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const twemoji = require('twemoji/2/twemoji.npm');
|
||||||
|
|
||||||
|
const outputPath = './src/forum/generated/emojiMap.json';
|
||||||
|
const data = require('emojibase-data/en/data.json');
|
||||||
|
const twemojiFileNames = fs.readdirSync('./node_modules/twemoji/2/svg')
|
||||||
|
.map(name => path.basename(name, '.svg'));
|
||||||
|
|
||||||
|
const alternative = {
|
||||||
|
"👁️🗨️" : "👁🗨",
|
||||||
|
};
|
||||||
|
|
||||||
|
const emojis = {};
|
||||||
|
|
||||||
|
for (let e of data) {
|
||||||
|
const emoji = alternative[e.emoji] || e.emoji;
|
||||||
|
const emojiCode = getEmojiIconCode(emoji);
|
||||||
|
|
||||||
|
if (!checkExistanceInTwemoji(emojiCode)) {
|
||||||
|
console.error('Can not find', emoji, emojiCode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
emojis[emoji] = e.shortcodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputDir = path.dirname(outputPath);
|
||||||
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
fs.mkdirSync(outputDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify(emojis));
|
||||||
|
|
||||||
|
function checkExistanceInTwemoji(code) {
|
||||||
|
return twemojiFileNames.indexOf(code) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEmojiIconCode(emoji) {
|
||||||
|
const U200D = String.fromCharCode(0x200D);
|
||||||
|
return twemoji.convert.toCodePoint(emoji.indexOf(U200D) < 0 ?
|
||||||
|
emoji.replace(/\uFE0F/g, '') :
|
||||||
|
emoji
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,7 +2,8 @@ import getCaretCoordinates from 'textarea-caret';
|
||||||
|
|
||||||
import { extend } from 'flarum/extend';
|
import { extend } from 'flarum/extend';
|
||||||
import ComposerBody from 'flarum/components/ComposerBody';
|
import ComposerBody from 'flarum/components/ComposerBody';
|
||||||
import emojiMap from './helpers/emojiMap';
|
import emojiMap from './generated/emojiMap.json';
|
||||||
|
import getEmojiIconCode from './helpers/getEmojiIconCode';
|
||||||
import KeyboardNavigatable from 'flarum/utils/KeyboardNavigatable';
|
import KeyboardNavigatable from 'flarum/utils/KeyboardNavigatable';
|
||||||
|
|
||||||
import AutocompleteDropdown from './components/AutocompleteDropdown';
|
import AutocompleteDropdown from './components/AutocompleteDropdown';
|
||||||
|
@ -75,25 +76,22 @@ export default function addComposerAutocomplete() {
|
||||||
if (emojiStart) {
|
if (emojiStart) {
|
||||||
typed = value.substring(emojiStart, cursor).toLowerCase();
|
typed = value.substring(emojiStart, cursor).toLowerCase();
|
||||||
|
|
||||||
const makeSuggestion = function(key) {
|
const makeSuggestion = function({emoji, name, code}) {
|
||||||
const code = ':' + key + ':';
|
|
||||||
const imageName = emojiMap[key];
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={key}
|
key={emoji}
|
||||||
onclick={() => applySuggestion(code)}
|
onclick={() => applySuggestion(emoji)}
|
||||||
onmouseenter={function() {
|
onmouseenter={function() {
|
||||||
dropdown.setIndex($(this).parent().index());
|
dropdown.setIndex($(this).parent().index());
|
||||||
}}>
|
}}>
|
||||||
<img alt={code} class="emoji" draggable="false" src={'//cdn.jsdelivr.net/emojione/assets/png/' + imageName + '.png'}/>
|
<img alt={emoji} class="emoji" draggable="false" src={'//twemoji.maxcdn.com/2/72x72/' + code + '.png'}/>
|
||||||
{key}
|
{name}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildSuggestions = () => {
|
const buildSuggestions = () => {
|
||||||
const suggestions = [];
|
const similarEmoji = [];
|
||||||
let similarEmoji = [];
|
|
||||||
|
|
||||||
// Build a regular expression to do a fuzzy match of the given input string
|
// Build a regular expression to do a fuzzy match of the given input string
|
||||||
const fuzzyRegexp = function(str) {
|
const fuzzyRegexp = function(str) {
|
||||||
|
@ -107,9 +105,16 @@ export default function addComposerAutocomplete() {
|
||||||
const findMatchingEmojis = matcher => {
|
const findMatchingEmojis = matcher => {
|
||||||
for (let i = 0; i < emojiKeys.length && maxSuggestions > 0; i++) {
|
for (let i = 0; i < emojiKeys.length && maxSuggestions > 0; i++) {
|
||||||
const curEmoji = emojiKeys[i];
|
const curEmoji = emojiKeys[i];
|
||||||
if (matcher(curEmoji) && similarEmoji.indexOf(curEmoji) === -1) {
|
|
||||||
--maxSuggestions;
|
if (similarEmoji.indexOf(curEmoji) === -1) {
|
||||||
similarEmoji.push(emojiKeys[i]);
|
const names = emojiMap[curEmoji];
|
||||||
|
for (let name of names) {
|
||||||
|
if (matcher(name)) {
|
||||||
|
--maxSuggestions;
|
||||||
|
similarEmoji.push(curEmoji);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -120,13 +125,13 @@ export default function addComposerAutocomplete() {
|
||||||
// If there are still suggestions left, try for some fuzzy matches
|
// If there are still suggestions left, try for some fuzzy matches
|
||||||
findMatchingEmojis(emoji => regTyped.test(emoji));
|
findMatchingEmojis(emoji => regTyped.test(emoji));
|
||||||
|
|
||||||
similarEmoji = similarEmoji.sort((a, b) => {
|
const suggestions = similarEmoji.map(emoji => ({
|
||||||
return a.length - b.length
|
emoji,
|
||||||
});
|
name: emojiMap[emoji][0],
|
||||||
|
code: getEmojiIconCode(emoji),
|
||||||
for (let key of similarEmoji) {
|
})).sort((a, b) => {
|
||||||
suggestions.push(makeSuggestion(key));
|
return a.name.length - b.name.length;
|
||||||
}
|
}).map(makeSuggestion);
|
||||||
|
|
||||||
if (suggestions.length) {
|
if (suggestions.length) {
|
||||||
dropdown.props.items = suggestions;
|
dropdown.props.items = suggestions;
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default class AutocompleteDropdown extends Component {
|
||||||
clearTimeout(this.keyWasJustPressedTimeout);
|
clearTimeout(this.keyWasJustPressedTimeout);
|
||||||
this.keyWasJustPressedTimeout = setTimeout(() => this.keyWasJustPressed = false, 500);
|
this.keyWasJustPressedTimeout = setTimeout(() => this.keyWasJustPressed = false, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
complete() {
|
complete() {
|
||||||
this.$('li').eq(this.index).find('button').click();
|
this.$('li').eq(this.index).find('button').click();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
26
extensions/emoji/js/src/forum/helpers/getEmojiIconCode.js
Normal file
26
extensions/emoji/js/src/forum/helpers/getEmojiIconCode.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*! Copyright Twitter Inc. and other contributors. Licensed under MIT *//*
|
||||||
|
https://github.com/twitter/twemoji/blob/gh-pages/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
import twemoji from 'twemoji/2/twemoji.npm';
|
||||||
|
|
||||||
|
// avoid using a string literal like '\u200D' here because minifiers expand it inline
|
||||||
|
const U200D = String.fromCharCode(0x200D);
|
||||||
|
|
||||||
|
// avoid runtime RegExp creation for not so smart,
|
||||||
|
// not JIT based, and old browsers / engines
|
||||||
|
const UFE0Fg = /\uFE0F/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to both remove the possible variant
|
||||||
|
* and to convert utf16 into code points.
|
||||||
|
* If there is a zero-width-joiner (U+200D), leave the variants in.
|
||||||
|
* @param string the raw text of the emoji match
|
||||||
|
* @return string the code point
|
||||||
|
*/
|
||||||
|
export default function getEmojiIconCode(emoji) {
|
||||||
|
return twemoji.convert.toCodePoint(emoji.indexOf(U200D) < 0 ?
|
||||||
|
emoji.replace(UFE0Fg, '') :
|
||||||
|
emoji
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,9 +3,13 @@ import app from 'flarum/app';
|
||||||
import Post from 'flarum/models/Post';
|
import Post from 'flarum/models/Post';
|
||||||
|
|
||||||
import addComposerAutocomplete from './addComposerAutocomplete';
|
import addComposerAutocomplete from './addComposerAutocomplete';
|
||||||
|
import renderEmoji from './renderEmoji';
|
||||||
|
|
||||||
app.initializers.add('flarum-emoji', () => {
|
app.initializers.add('flarum-emoji', () => {
|
||||||
// After typing ':' in the composer, show a dropdown suggesting a bunch of
|
// After typing ':' in the composer, show a dropdown suggesting a bunch of
|
||||||
// emoji that the user could use.
|
// emoji that the user could use.
|
||||||
addComposerAutocomplete();
|
addComposerAutocomplete();
|
||||||
|
|
||||||
|
// render emoji as image in Posts content and title.
|
||||||
|
renderEmoji();
|
||||||
});
|
});
|
||||||
|
|
25
extensions/emoji/js/src/forum/renderEmoji.js
Normal file
25
extensions/emoji/js/src/forum/renderEmoji.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*global s9e*/
|
||||||
|
|
||||||
|
import twemoji from 'twemoji/2/twemoji.npm';
|
||||||
|
|
||||||
|
import { override } from 'flarum/extend';
|
||||||
|
import Post from 'flarum/models/Post';
|
||||||
|
|
||||||
|
export default function renderEmoji() {
|
||||||
|
override(Post.prototype, 'contentHtml', function(original) {
|
||||||
|
const contentHtml = original();
|
||||||
|
|
||||||
|
if (this.oldContentHtml !== contentHtml) {
|
||||||
|
this.emojifiedContentHtml = twemoji.parse(contentHtml);
|
||||||
|
this.oldContentHtml = contentHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.emojifiedContentHtml;
|
||||||
|
});
|
||||||
|
|
||||||
|
override(s9e.TextFormatter, 'preview', (original, text, element) => {
|
||||||
|
original(text, element);
|
||||||
|
|
||||||
|
twemoji.parse(element);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user