mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-13 16:14:36 +08:00
sp(composer-emoji): Correct emojis to emoji
This commit is contained in:
parent
38b8d03cf2
commit
e5e474569b
10 changed files with 43 additions and 43 deletions
|
@ -51,7 +51,7 @@ Great starting points for creating your own plugins!
|
||||||
- [Send Later](https://github.com/nylas/N1/tree/master/internal_packages/send-later) — Schedule your emails to be sent at a later time
|
- [Send Later](https://github.com/nylas/N1/tree/master/internal_packages/send-later) — Schedule your emails to be sent at a later time
|
||||||
- [Open Tracking](https://github.com/nylas/N1/tree/master/internal_packages/open-tracking) — See if your emails have been read
|
- [Open Tracking](https://github.com/nylas/N1/tree/master/internal_packages/open-tracking) — See if your emails have been read
|
||||||
- [Link Tracking](https://github.com/nylas/N1/tree/master/internal_packages/link-tracking) — See if your links have been clicked
|
- [Link Tracking](https://github.com/nylas/N1/tree/master/internal_packages/link-tracking) — See if your links have been clicked
|
||||||
- [Emoji Keyboard](https://github.com/nylas/N1/tree/master/internal_packages/composer-emojis) — Insert emojis by typing a colon (:) followed by the name of an emoji symbol
|
- [Emoji Keyboard](https://github.com/nylas/N1/tree/master/internal_packages/composer-emoji) — Insert emoji by typing a colon (:) followed by the name of an emoji symbol
|
||||||
- [GitHub Sidebar Info](https://github.com/nylas/N1/tree/master/internal_packages/github-contact-card)
|
- [GitHub Sidebar Info](https://github.com/nylas/N1/tree/master/internal_packages/github-contact-card)
|
||||||
- [View on GitHub](https://github.com/nylas/N1/tree/master/internal_packages/message-view-on-github)
|
- [View on GitHub](https://github.com/nylas/N1/tree/master/internal_packages/message-view-on-github)
|
||||||
- [Personal Level Indicators](https://github.com/nylas/N1/tree/master/internal_packages/personal-level-indicators)
|
- [Personal Level Indicators](https://github.com/nylas/N1/tree/master/internal_packages/personal-level-indicators)
|
||||||
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -2,13 +2,12 @@ import {DOMUtils, ContenteditableExtension} from 'nylas-exports'
|
||||||
import EmojiActions from './emoji-actions'
|
import EmojiActions from './emoji-actions'
|
||||||
import EmojiPicker from './emoji-picker'
|
import EmojiPicker from './emoji-picker'
|
||||||
const emoji = require('node-emoji');
|
const emoji = require('node-emoji');
|
||||||
const emojis = Object.keys(emoji.emoji).sort();
|
|
||||||
|
|
||||||
class EmojisComposerExtension extends ContenteditableExtension {
|
class EmojiComposerExtension extends ContenteditableExtension {
|
||||||
|
|
||||||
static onContentChanged = ({editor}) => {
|
static onContentChanged = ({editor}) => {
|
||||||
const sel = editor.currentSelection()
|
const sel = editor.currentSelection()
|
||||||
const {emojiOptions, triggerWord} = EmojisComposerExtension._findEmojiOptions(sel);
|
const {emojiOptions, triggerWord} = EmojiComposerExtension._findEmojiOptions(sel);
|
||||||
if (sel.anchorNode && sel.isCollapsed) {
|
if (sel.anchorNode && sel.isCollapsed) {
|
||||||
if (emojiOptions.length > 0) {
|
if (emojiOptions.length > 0) {
|
||||||
const offset = sel.anchorOffset;
|
const offset = sel.anchorOffset;
|
||||||
|
@ -45,7 +44,7 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
static toolbarComponentConfig = ({toolbarState}) => {
|
static toolbarComponentConfig = ({toolbarState}) => {
|
||||||
const sel = toolbarState.selectionSnapshot;
|
const sel = toolbarState.selectionSnapshot;
|
||||||
if (sel) {
|
if (sel) {
|
||||||
const {emojiOptions} = EmojisComposerExtension._findEmojiOptions(sel);
|
const {emojiOptions} = EmojiComposerExtension._findEmojiOptions(sel);
|
||||||
if (emojiOptions.length > 0 && !toolbarState.dragging && !toolbarState.doubleDown) {
|
if (emojiOptions.length > 0 && !toolbarState.dragging && !toolbarState.doubleDown) {
|
||||||
const locationRefNode = DOMUtils.closest(sel.anchorNode,
|
const locationRefNode = DOMUtils.closest(sel.anchorNode,
|
||||||
"n1-emoji-autocomplete");
|
"n1-emoji-autocomplete");
|
||||||
|
@ -56,8 +55,8 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
props: {emojiOptions,
|
props: {emojiOptions,
|
||||||
selectedEmoji},
|
selectedEmoji},
|
||||||
locationRefNode: locationRefNode,
|
locationRefNode: locationRefNode,
|
||||||
width: EmojisComposerExtension._emojiPickerWidth(emojiOptions),
|
width: EmojiComposerExtension._emojiPickerWidth(emojiOptions),
|
||||||
height: EmojisComposerExtension._emojiPickerHeight(emojiOptions),
|
height: EmojiComposerExtension._emojiPickerHeight(emojiOptions),
|
||||||
hidePointer: true,
|
hidePointer: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,13 +67,13 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
static editingActions = () => {
|
static editingActions = () => {
|
||||||
return [{
|
return [{
|
||||||
action: EmojiActions.selectEmoji,
|
action: EmojiActions.selectEmoji,
|
||||||
callback: EmojisComposerExtension._onSelectEmoji,
|
callback: EmojiComposerExtension._onSelectEmoji,
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
static onKeyDown = ({editor, event}) => {
|
static onKeyDown = ({editor, event}) => {
|
||||||
const sel = editor.currentSelection()
|
const sel = editor.currentSelection()
|
||||||
const {emojiOptions} = EmojisComposerExtension._findEmojiOptions(sel);
|
const {emojiOptions} = EmojiComposerExtension._findEmojiOptions(sel);
|
||||||
if (emojiOptions.length > 0) {
|
if (emojiOptions.length > 0) {
|
||||||
if (event.key === "ArrowDown" || event.key === "ArrowRight" ||
|
if (event.key === "ArrowDown" || event.key === "ArrowRight" ||
|
||||||
event.key === "ArrowUp" || event.key === "ArrowLeft") {
|
event.key === "ArrowUp" || event.key === "ArrowLeft") {
|
||||||
|
@ -101,7 +100,7 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
const emojiNameNode = DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete");
|
const emojiNameNode = DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete");
|
||||||
let selectedEmoji = emojiNameNode.getAttribute("selectedEmoji");
|
let selectedEmoji = emojiNameNode.getAttribute("selectedEmoji");
|
||||||
if (!selectedEmoji) selectedEmoji = emojiOptions[0];
|
if (!selectedEmoji) selectedEmoji = emojiOptions[0];
|
||||||
EmojisComposerExtension._onSelectEmoji({editor: editor,
|
EmojiComposerExtension._onSelectEmoji({editor: editor,
|
||||||
actionArg: {emojiChar: emoji.get(selectedEmoji)}});
|
actionArg: {emojiChar: emoji.get(selectedEmoji)}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +117,7 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
if (index !== -1 && words.lastIndexOf(" ") < index) {
|
if (index !== -1 && words.lastIndexOf(" ") < index) {
|
||||||
lastWord = words.substring(index + 1, sel.anchorOffset);
|
lastWord = words.substring(index + 1, sel.anchorOffset);
|
||||||
} else {
|
} else {
|
||||||
const {text} = EmojisComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset);
|
const {text} = EmojiComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset);
|
||||||
index = text.lastIndexOf(":");
|
index = text.lastIndexOf(":");
|
||||||
if (index !== -1 && text.lastIndexOf(" ") < index) {
|
if (index !== -1 && text.lastIndexOf(" ") < index) {
|
||||||
lastWord = text.substring(index + 1);
|
lastWord = text.substring(index + 1);
|
||||||
|
@ -127,7 +126,7 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lastWord.length > 0) {
|
if (lastWord.length > 0) {
|
||||||
return {triggerWord: lastWord, emojiOptions: EmojisComposerExtension._findMatches(lastWord)};
|
return {triggerWord: lastWord, emojiOptions: EmojiComposerExtension._findMatches(lastWord)};
|
||||||
}
|
}
|
||||||
return {triggerWord: lastWord, emojiOptions: []};
|
return {triggerWord: lastWord, emojiOptions: []};
|
||||||
}
|
}
|
||||||
|
@ -151,7 +150,7 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
sel.focusNode,
|
sel.focusNode,
|
||||||
sel.focusOffset);
|
sel.focusOffset);
|
||||||
} else {
|
} else {
|
||||||
const {text, textNode} = EmojisComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset);
|
const {text, textNode} = EmojiComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset);
|
||||||
index = text.lastIndexOf(":");
|
index = text.lastIndexOf(":");
|
||||||
lastWord = text.substring(index + 1);
|
lastWord = text.substring(index + 1);
|
||||||
const offset = textNode.nodeValue.lastIndexOf(":");
|
const offset = textNode.nodeValue.lastIndexOf(":");
|
||||||
|
@ -203,9 +202,10 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
|
|
||||||
static _findMatches(word) {
|
static _findMatches(word) {
|
||||||
const emojiOptions = []
|
const emojiOptions = []
|
||||||
for (const curEmoji of emojis) {
|
const emojiChars = Object.keys(emoji.emoji).sort();
|
||||||
if (word === curEmoji.substring(0, word.length)) {
|
for (const emojiChar of emojiChars) {
|
||||||
emojiOptions.push(curEmoji);
|
if (word === emojiChar.substring(0, word.length)) {
|
||||||
|
emojiOptions.push(emojiChar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emojiOptions;
|
return emojiOptions;
|
||||||
|
@ -213,4 +213,4 @@ class EmojisComposerExtension extends ContenteditableExtension {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmojisComposerExtension;
|
export default EmojiComposerExtension;
|
|
@ -26,20 +26,20 @@ class EmojiPicker extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const emojis = [];
|
const emojiButtons = [];
|
||||||
let emojiIndex = this.props.emojiOptions.indexOf(this.props.selectedEmoji);
|
let emojiIndex = this.props.emojiOptions.indexOf(this.props.selectedEmoji);
|
||||||
if (emojiIndex === -1) emojiIndex = 0;
|
if (emojiIndex === -1) emojiIndex = 0;
|
||||||
if (this.props.emojiOptions) {
|
if (this.props.emojiOptions) {
|
||||||
this.props.emojiOptions.forEach((emojiOption, i) => {
|
this.props.emojiOptions.forEach((emojiOption, i) => {
|
||||||
const emojiChar = emoji.get(emojiOption);
|
const emojiChar = emoji.get(emojiOption);
|
||||||
const emojiClass = emojiIndex === i ? "btn btn-icon emoji-option" : "btn btn-icon";
|
const emojiClass = emojiIndex === i ? "btn btn-icon emoji-option" : "btn btn-icon";
|
||||||
emojis.push(<button key={emojiChar} onMouseDown={() => this.onMouseDown(emojiChar)} className={emojiClass}>{emojiChar} :{emojiOption}:</button>);
|
emojiButtons.push(<button key={emojiChar} onMouseDown={() => this.onMouseDown(emojiChar)} className={emojiClass}>{emojiChar} :{emojiOption}:</button>);
|
||||||
emojis.push(<br key={emojiChar + " br"} />);
|
emojiButtons.push(<br key={emojiChar + " br"} />);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="emoji-picker">
|
<div className="emoji-picker">
|
||||||
{emojis}
|
{emojiButtons}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
11
internal_packages/composer-emoji/lib/main.js
Normal file
11
internal_packages/composer-emoji/lib/main.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/** @babel */
|
||||||
|
import {ExtensionRegistry} from 'nylas-exports';
|
||||||
|
import EmojiComposerExtension from './emoji-composer-extension';
|
||||||
|
|
||||||
|
export function activate() {
|
||||||
|
ExtensionRegistry.Composer.register(EmojiComposerExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deactivate() {
|
||||||
|
ExtensionRegistry.Composer.unregister(EmojiComposerExtension);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "composer-emojis",
|
"name": "composer-emoji",
|
||||||
"main": "./lib/main",
|
"main": "./lib/main",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"repository": {
|
"repository": {
|
|
@ -1,18 +1,18 @@
|
||||||
import React, {addons} from 'react/addons';
|
import React, {addons} from 'react/addons';
|
||||||
import {renderIntoDocument} from '../../../spec/nylas-test-utils';
|
import {renderIntoDocument} from '../../../spec/nylas-test-utils';
|
||||||
import Contenteditable from '../../../src/components/contenteditable/contenteditable';
|
import Contenteditable from '../../../src/components/contenteditable/contenteditable';
|
||||||
import EmojisComposerExtension from '../lib/emojis-composer-extension';
|
import EmojiComposerExtension from '../lib/emoji-composer-extension';
|
||||||
|
|
||||||
const ReactTestUtils = addons.TestUtils;
|
const ReactTestUtils = addons.TestUtils;
|
||||||
|
|
||||||
describe('EmojisComposerExtension', ()=> {
|
describe('EmojiComposerExtension', ()=> {
|
||||||
beforeEach(()=> {
|
beforeEach(()=> {
|
||||||
spyOn(EmojisComposerExtension, 'onContentChanged').andCallThrough()
|
spyOn(EmojiComposerExtension, 'onContentChanged').andCallThrough()
|
||||||
spyOn(EmojisComposerExtension, '_onSelectEmoji').andCallThrough()
|
spyOn(EmojiComposerExtension, '_onSelectEmoji').andCallThrough()
|
||||||
const html = 'Testing!'
|
const html = 'Testing!'
|
||||||
const onChange = jasmine.createSpy('onChange')
|
const onChange = jasmine.createSpy('onChange')
|
||||||
this.component = renderIntoDocument(
|
this.component = renderIntoDocument(
|
||||||
<Contenteditable html={html} onChange={onChange} extensions={[EmojisComposerExtension]}/>
|
<Contenteditable html={html} onChange={onChange} extensions={[EmojiComposerExtension]}/>
|
||||||
)
|
)
|
||||||
this.editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(this.component, 'contentEditable'));
|
this.editableNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithAttr(this.component, 'contentEditable'));
|
||||||
})
|
})
|
||||||
|
@ -59,7 +59,7 @@ describe('EmojisComposerExtension', ()=> {
|
||||||
ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "Enter", keyCode: 13, which: 13});
|
ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "Enter", keyCode: 13, which: 13});
|
||||||
});
|
});
|
||||||
waitsFor(()=> {
|
waitsFor(()=> {
|
||||||
return EmojisComposerExtension._onSelectEmoji.calls.length > 0
|
return EmojiComposerExtension._onSelectEmoji.calls.length > 0
|
||||||
})
|
})
|
||||||
runs(()=> {
|
runs(()=> {
|
||||||
expect(this.editableNode.textContent === "Testing! 💇").toBe(true);
|
expect(this.editableNode.textContent === "Testing! 💇").toBe(true);
|
||||||
|
@ -76,10 +76,10 @@ describe('EmojisComposerExtension', ()=> {
|
||||||
runs(()=> {
|
runs(()=> {
|
||||||
const button = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option'))
|
const button = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option'))
|
||||||
ReactTestUtils.Simulate.mouseDown(button);
|
ReactTestUtils.Simulate.mouseDown(button);
|
||||||
expect(EmojisComposerExtension._onSelectEmoji).toHaveBeenCalled()
|
expect(EmojiComposerExtension._onSelectEmoji).toHaveBeenCalled()
|
||||||
});
|
});
|
||||||
waitsFor(()=> {
|
waitsFor(()=> {
|
||||||
return EmojisComposerExtension._onSelectEmoji.calls.length > 0
|
return EmojiComposerExtension._onSelectEmoji.calls.length > 0
|
||||||
})
|
})
|
||||||
runs(()=> {
|
runs(()=> {
|
||||||
expect(this.editableNode.textContent).toEqual("Testing! 💇");
|
expect(this.editableNode.textContent).toEqual("Testing! 💇");
|
||||||
|
@ -97,14 +97,14 @@ describe('EmojisComposerExtension', ()=> {
|
||||||
ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "ArrowDown", keyCode: 40, which: 40});
|
ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "ArrowDown", keyCode: 40, which: 40});
|
||||||
});
|
});
|
||||||
waitsFor(()=> {
|
waitsFor(()=> {
|
||||||
return EmojisComposerExtension.onContentChanged.calls.length > 1
|
return EmojiComposerExtension.onContentChanged.calls.length > 1
|
||||||
});
|
});
|
||||||
runs(()=> {
|
runs(()=> {
|
||||||
expect(React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option')).textContent).toEqual("🍔 :hamburger:");
|
expect(React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option')).textContent).toEqual("🍔 :hamburger:");
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be able to insert two emojis next to each other', ()=> {
|
it('should be able to insert two emoji next to each other', ()=> {
|
||||||
runs(()=> {
|
runs(()=> {
|
||||||
this._performEdit('Testing! 🍔 :h');
|
this._performEdit('Testing! 🍔 :h');
|
||||||
});
|
});
|
|
@ -1,11 +0,0 @@
|
||||||
/** @babel */
|
|
||||||
import {ExtensionRegistry} from 'nylas-exports';
|
|
||||||
import EmojisComposerExtension from './emojis-composer-extension';
|
|
||||||
|
|
||||||
export function activate() {
|
|
||||||
ExtensionRegistry.Composer.register(EmojisComposerExtension);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deactivate() {
|
|
||||||
ExtensionRegistry.Composer.unregister(EmojisComposerExtension);
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue