Skip to content

Commit

Permalink
Add first initial implementation of Emoji search in address ba
Browse files Browse the repository at this point in the history
(called omnibar)
  • Loading branch information
rugk committed Apr 5, 2019
1 parent adc21fb commit 085c7b9
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 91 deletions.
4 changes: 4 additions & 0 deletions scripts/manifests/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
]
},

"omnibox": {
"keyword": "emoji"
},

"options_ui": {
"page": "options/options.html",
"browser_style": true
Expand Down
4 changes: 4 additions & 0 deletions scripts/manifests/firefox.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
]
},

"omnibox": {
"keyword": "emoji"
},

"options_ui": {
"page": "options/options.html",
"browser_style": true
Expand Down
30 changes: 30 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,36 @@
"description": "This is text of the link to the LICENSE file of this add-on. Used in the license disclaimer, see optionsLicenseDisclaimer."
},

// emoji-search
"searchTipDescription": {
"message": "Search for emojis via Awesome Emoji Picker…",
"description": "This is the text that is shown when searching for an emoji. It shows a tip to remind the user how to search for an emoji.",
"placeholders": {
"addon": {
"content": "$1",
"example": "The name of the extension. See extensionName."
}
}
},
"searchResultDescription": {
"message": "$NATIVE$ – $NAME$ ($COLON_SYNTAX$)",
"description": "The description for emoji search result.",
"placeholders": {
"native": {
"content": "$1",
"example": "The Unicode symbol for the Emoji."
},
"name": {
"content": "$2",
"example": "The name of the Emoji."
},
"colon_syntax": {
"content": "$3",
"example": "The :colon: syntax of the selected Emoji."
}
}
},

// emoji-mart
// see https://github.com/missive/emoji-mart#i18n
"emojiMartSearch": {
Expand Down
9 changes: 7 additions & 2 deletions src/background/background.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import * as IconHandler from "/common/modules/IconHandler.js";
import * as AddonSettings from "/common/modules/AddonSettings/AddonSettings.js";

import * as OmniboxSearch from "./modules/OmniboxSearch.js";

// init modules
IconHandler.init();
const emojiSearch = AddonSettings.get("emojiSearch");
if (emojiSearch) {
OmniboxSearch.init();
}
Empty file removed src/background/modules/.gitkeep
Empty file.
94 changes: 94 additions & 0 deletions src/background/modules/OmniboxSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as EmojiInteraction from "/common/modules/EmojiInteraction.js";

/**
* Lazy-load the emoji-mart library, .
*
* This consumes some memory (RAM), up-to 10MB, as remount and other things are loaded.
*
* @public
* @returns {void}
*/
function loadEmojiMart() {
const emojiMartLoader = document.createElement("script");
emojiMartLoader.setAttribute("async", true);
emojiMartLoader.setAttribute("src", "/common/lib/emoji-mart-embed/dist/emoji-mart.js");
document.querySelector("head").appendChild(emojiMartLoader);
}

/**
* Trigger the evaluation for the search for emojis.
*
* @public
* @param {string} text the string the user entered
* @param {function} suggest function to call to add suggestions
* @returns {void}
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox/onInputChanged}
*/
export function triggerOmnixboxSuggestion(text, suggest) {
const suggestions = window.emojiMart.emojiIndex.search(text).map((emoji) => {
return {
description: browser.i18n.getMessage("searchResultDescription", [
emoji.native,
emoji.name,
emoji.colons
]),
content: emoji.native
};
});

suggest(suggestions);
}

/**
* Triggered when the search is actually executed,.
*
* @public
* @param {string} text the string the user entered or selected
* @param {string} disposition how the result should be possible
* @returns {void}
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox/onInputEntered}
*/
export function triggerOmnixboxSearch(text) {
const searchResult = window.emojiMart.emojiIndex.search(text);

// if a single emoji is selected or searched for, detect this and return
// emoji data
try {
searchResult.push(
window.emojiMart.getEmojiDataFromNative(text)
);
} catch (e) {
// ignore errors, as we usually expect text strings there and these are
// totally fine, too; search may find something here
}

// emoji itself copied or found
if (searchResult.length === 1) {
// if result is only one emoji, also instantly copy it
EmojiInteraction.copyEmoji(searchResult[0]);
} else {
// otherwise open popup to show all emoji choices
browser.browserAction.openPopup();
}

}

/**
* Init omnibox search.
*
* @public
* @returns {void}
*/
export function init() {
// lazy-load emoji-mart
loadEmojiMart();

browser.omnibox.onInputChanged.addListener(triggerOmnixboxSuggestion);
browser.omnibox.onInputEntered.addListener(triggerOmnixboxSearch);

browser.omnibox.setDefaultSuggestion({
description: browser.i18n.getMessage("searchTipDescription", [
browser.i18n.getMessage("extensionName")
])
});
}
Submodule emoji-mart-embed updated from 000000 to eb65bd
72 changes: 72 additions & 0 deletions src/common/modules/EmojiInteraction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as AddonSettings from "/common/modules/AddonSettings/AddonSettings.js";

const POPUP_ICON_OPTION = "popupIconColored";

/**
* Sets a popup icon variant.
*
* @private
* @param {string} icon version or "null"/"undefined" to reset to default
* @returns {Promise}
*/
function setPopupIcon(icon) {
// verify parameter
switch (icon) {
case "dark": // fall through
case "light":
case "colored":
case null:
// ok
break;
default:
throw new TypeError(`invalid parameter: ${icon}`);
}

// ignore request if API is not available
if (browser.browserAction.setIcon === undefined) {
return Promise.resolve();
}

if (icon === null || icon === undefined) {
return browser.browserAction.setIcon({path: null});
}

// set colored icon
if (icon === "colored") {
// WTF: For whatever reason, these paths need to be absolute...
return browser.browserAction.setIcon({path: {
"16": "/icons/icon_32.png",
"32": "/icons/icon_32.png",
"64": "/icons/icon_64.png",
"128": "/icons/icon_128.png"
}});
}

return browser.browserAction.setIcon({path: `/icons/fa-grin-${icon}.svg`});
}

/**
* Set icon depending on whether it should be colored, or not.
*
* @public
* @param {boolean} popupIconColored if popupIconColored is colored or not
* @returns {Promise}
*/
export function changeIconIfColored(popupIconColored) {
if (popupIconColored === true) {
return setPopupIcon("colored");
} else {
// reset icon
return setPopupIcon(null);
}
}

/**
* Init icon module.
*
* @public
* @returns {void}
*/
export function init() {
return AddonSettings.get(POPUP_ICON_OPTION).then((popupIconColored) => changeIconIfColored(popupIconColored));
}
74 changes: 12 additions & 62 deletions src/common/modules/IconHandler.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
import * as AddonSettings from "/common/modules/AddonSettings/AddonSettings.js";

const POPUP_ICON_OPTION = "popupIconColored";

/**
* Sets a popup icon variant.
* Copy the Emoji to clipboard, once it has been selected.
*
* @private
* @param {string} icon version or "null"/"undefined" to reset to default
* @returns {Promise}
* @param {Object} emoji
* @returns {void}
*/
function setPopupIcon(icon) {
// verify parameter
switch (icon) {
case "dark": // fall through
case "light":
case "colored":
case null:
// ok
export async function copyEmoji(emoji) {
const emojiCopyOption = await AddonSettings.get("copyEmoji");
switch (emojiCopyOption) {
case "native":
navigator.clipboard.writeText(emoji.native);
break;
case "colons":
navigator.clipboard.writeText(emoji.colons);
break;
default:
throw new TypeError(`invalid parameter: ${icon}`);
}

// ignore request if API is not available
if (browser.browserAction.setIcon === undefined) {
return Promise.resolve();
}

if (icon === null || icon === undefined) {
return browser.browserAction.setIcon({path: null});
}

// set colored icon
if (icon === "colored") {
// WTF: For whatever reason, these paths need to be absolute...
return browser.browserAction.setIcon({path: {
"16": "/icons/icon_32.png",
"32": "/icons/icon_32.png",
"64": "/icons/icon_64.png",
"128": "/icons/icon_128.png"
}});
}

return browser.browserAction.setIcon({path: `/icons/fa-grin-${icon}.svg`});
}

/**
* Set icon depending on whether it should be colored, or not.
*
* @public
* @param {boolean} popupIconColored if popupIconColored is colored or not
* @returns {Promise}
*/
export function changeIconIfColored(popupIconColored) {
if (popupIconColored === true) {
return setPopupIcon("colored");
} else {
// reset icon
return setPopupIcon(null);
throw new Error("invalid option:", "copyEmoji", emojiCopyOption);
}
}

/**
* Init icon module.
*
* @public
* @returns {void}
*/
export function init() {
return AddonSettings.get(POPUP_ICON_OPTION).then((popupIconColored) => changeIconIfColored(popupIconColored));
}
3 changes: 2 additions & 1 deletion src/common/modules/data/DefaultSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export const DEFAULT_SETTINGS = Object.freeze({
emojiSize: 24
},
copyEmoji: "native",
emojiMart: {}
emojiMart: {},
emojiSearch: true
});
7 changes: 6 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
]
},

"omnibox": {
"keyword": "emoji"
},

"options_ui": {
"page": "options/options.html",
"browser_style": true
Expand All @@ -41,7 +45,8 @@
"default_locale": "en",

"permissions": [
"storage"
"storage",
"clipboardWrite"
],

"applications": {
Expand Down
4 changes: 2 additions & 2 deletions src/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../common/common.css">
<link rel="stylesheet" href="index.css">
<link rel="stylesheet" href="./lib/emoji-mart-embed/dist/emoji-mart.css">
<link rel="stylesheet" href="/common/lib/emoji-mart-embed/dist/emoji-mart.css">

<script defer src="../common/common.js" type="module" charset="utf-8"></script>
<script defer src="index.js" type="module" charset="utf-8"></script>
<!-- we need to load emoji-mart last, so autoFocus works -->
<script defer src="./lib/emoji-mart-embed/dist/emoji-mart.js"></script>
<script defer src="/common/lib/emoji-mart-embed/dist/emoji-mart.js"></script>
</head>
<body>
<head>
Expand Down
Loading

0 comments on commit 085c7b9

Please sign in to comment.