Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
085c7b9
Add first initial implementation of Emoji search in address ba
rugk Apr 5, 2019
9c20203
Merge branch 'master' into omnibox
rugk May 27, 2019
a761c2f
First round to refactor EmojiInteraction out of EmojiPicker
rugk May 28, 2019
61ba7da
Remove submodule
rugk May 28, 2019
e1895d3
Fix accidentially removed IconHandler
rugk May 28, 2019
5338556
Make emoji selection actually working
rugk May 28, 2019
4688485
Report to Bugzilla, remove TODO
rugk May 28, 2019
4395c21
Fix some null errors
rugk May 28, 2019
1675cf2
Make native return working again
rugk May 30, 2019
80444f5
Really use example strings instead of description
rugk May 31, 2019
b3e5bb8
Refactor to include options to control emoji select behavior
rugk Jun 3, 2019
ba96597
Add option for searching emojis in address bar
rugk Jun 3, 2019
d9f48b2
Add options page for omnibar search
rugk Jun 4, 2019
96bc148
Add fallback when emoji search is disabled
rugk Jun 5, 2019
94bbf12
Add link to report
rugk Jun 5, 2019
ab0f1d2
Update comments with correct links etc.
rugk Jun 5, 2019
297f0e0
Refactor/create new PermissionRequest module
rugk Jun 5, 2019
585c6e9
Add permission request for emoji search
rugk Jun 5, 2019
b62e0d6
Fix edge case where rejecting one message could hide all
rugk Jun 5, 2019
4556c11
Disable by default
rugk Jun 5, 2019
6da5236
[WIP] Add resetting of options when permission is rejected
rugk Jun 6, 2019
4852016
Improve retry implementation
rugk Jul 8, 2019
854b42b
Add retry count instead of simple boolean
rugk Jul 9, 2019
0d1bd89
Add proper parameter validation
rugk Jul 15, 2019
25ec05f
Add revert functionality, and permission check (WIP??)
rugk Sep 6, 2019
27c0d22
Merge branch 'omnibox' of https://github.com/rugk/awesome-emoji-picke…
rugk Sep 6, 2019
59f2e60
Fix wrong parameter validation
rugk Sep 6, 2019
e3e7337
Finally fix retry logic and corner cases
rugk Sep 13, 2019
5b21c0f
Add explanation link
rugk Sep 13, 2019
b06ae00
Merge branch 'omnibox' of https://github.com/rugk/awesome-emoji-picke…
rugk Sep 13, 2019
a63f6b0
Fix with new ID
rugk Sep 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
[submodule "src/popup/lib/awesome-emoji-emoji-mart-embed"]
path = src/popup/lib/emoji-mart-embed
url = https://github.com/rugk/emoji-mart-embed
[submodule "src/common/lib/emoji-mart-embed"]
path = src/common/lib/emoji-mart-embed
url = https://github.com/rugk/emoji-mart-embed
5 changes: 5 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 Expand Up @@ -56,6 +60,7 @@
"optional_permissions": [
"clipboardWrite"
],
// "search" currently not requested though, see https://discourse.mozilla.org/t/why-do-we-need-an-extra-permission-simply-for-starting-a-search/41174?u=rugkx

"applications": {
"gecko": {
Expand Down
8 changes: 7 additions & 1 deletion 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 Expand Up @@ -53,8 +57,10 @@
],

"optional_permissions": [
"clipboardWrite"
"clipboardWrite",
"search"
],
// "search" currently not requested though, see https://discourse.mozilla.org/t/why-do-we-need-an-extra-permission-simply-for-starting-a-search/41174?u=rugkx

"applications": {
"gecko": {
Expand Down
2 changes: 1 addition & 1 deletion src/_locales/de/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"description": "A generic error message shown, if nothing else/more specific can be shown."
},

"emojiCopyOnlyFallbackPermissionInfo": {
"permissionRequiredClipboardWrite": {
"message": "Für dieses Feature wird die Berechtigung, Daten in die Zwischenablage zu kopieren, benötigt.",
"description": "The message shown, when the emojiCopyOnlyFallback option in the settings needs to request permissions to work."
},
Expand Down
42 changes: 41 additions & 1 deletion src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"description": "A generic error message shown, if nothing else/more specific can be shown."
},

"emojiCopyOnlyFallbackPermissionInfo": {
"permissionRequiredClipboardWrite": {
"message": "The permission to copy data into the clipboard is required for this feature.",
"description": "The message shown, when the emojiCopyOnlyFallback option in the settings needs to request permissions to work."
},
Expand Down Expand Up @@ -352,6 +352,46 @@
"description": "The link text linking to the contributors file. See contributorsThanks."
},

// emoji-search
"searchTipDescription": {
"message": "Search for emojis via $ADDON$…",
"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."
}
}
},
"searchTipDescriptionDisabled": {
"message": "Searching for emojis with $ADDON$ is disabled… You can enable it in the options.",
"description": "This is the text that is shown when searching for an emoji, but if the address bar integration is disabled.",
"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": "Monkey Face (The name of the Emoji)"
},
"colon_syntax": {
"content": "$3",
"example": ":monkey_face: (The :colon: syntax of the selected Emoji)"
}
}
},

// emoji-mart
// see https://github.com/missive/emoji-mart#i18n
// for help also see https://github.com/rugk/awesome-emoji-picker/blob/master/CONTRIBUTING.md#translating-emoji-terms-categories-skin-names-etc
Expand Down
4 changes: 2 additions & 2 deletions src/background/background.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as IconHandler from "/common/modules/IconHandler.js";
import * as OmniboxSearch from "./modules/OmniboxSearch.js";

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

import { COMMUNICATION_MESSAGE_TYPE } from "/common/modules/data/BrowserCommunicationTypes.js";

const CLIPBOARD_WRITE_PERMISSION = {
permissions: ["clipboardWrite"]
};

let emojiMartIsLoaded = false;

/**
* Lazy-load the emoji-mart library, .
*
* This consumes some memory (RAM), up-to 10MB, as remount and other things are loaded.
*
* @private
* @returns {void}
*/
function loadEmojiMart() {
// prevent that it is loaded twice
if (emojiMartIsLoaded) {
return;
}

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);

emojiMartIsLoaded = true;
}

/**
* Navigates to the URL in this tab or a new tab.
*
* @private
* @param {string} url the URL that should be opened
* @param {string} disposition as per {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox/onInputEntered}
* @returns {void}
*/
function openTabUrl(url, disposition) {
switch (disposition) {
case "currentTab":
browser.tabs.update({
url
});
break;
case "newForegroundTab":
browser.tabs.create({
active: true,
url: url
});
break;
case "newBackgroundTab":
browser.tabs.create({
active: false,
url: url
});
break;
}
}

/**
* 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 searchResult = window.emojiMart.emojiIndex.search(text);

// if none are found, return…
if (!searchResult) {
return;
}

const suggestions = searchResult.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, but the omnibox feature is disabled.
*
* @public
* @param {string} text the string the user entered or selected
* @param {string} disposition how the result should be possible
* @returns {Promise}
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox/onInputEntered}
*/
export async function triggerOmnixboxDisabledSearch(text, disposition) {
// if search API is allowed, we just fall-back to default search
if (browser.search) {
let tabId = undefined;

switch (disposition) {
case "currentTab": {
const currentTab = await browser.tabs.query({
active: true,
currentWindow: true
});

if (currentTab.length >= 1) {
tabId = currentTab[0].id;
}

// deliberately fall-through
}
default: // eslint-disable-line no-fallthrough
return browser.search.search({
query: text,
tabId: tabId
});
}
}

// otherwise we just open the options page
return browser.runtime.openOptionsPage();
}

/**
* 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 async function triggerOmnixboxSearch(text, disposition) {
const searchResult = window.emojiMart.emojiIndex.search(text);

const emojiSearch = await AddonSettings.get("emojiSearch");

// if a single emoji is selected or searched for, detect this and return
// emoji data
try {
const foundEmoji = window.emojiMart.getEmojiDataFromNative(text);

if (foundEmoji) {
searchResult.push(foundEmoji);
}
} 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) {
const emojiText = searchResult[0][emojiSearch.resultType];

if (emojiSearch.action === "copy") {
// if result is only one emoji, also instantly copy it
EmojiInteraction.insertOrCopy(emojiText, {
insertIntoPage: false,
copyOnlyOnFallback: false,
copyToClipboard: true
});
} else if (emojiSearch.action === "emojipedia") {
const resultUrl = `https://emojipedia.org/search/?q=${emojiText}`;

// navigate to URL in current or new tab
openTabUrl(resultUrl, disposition);
} else {
throw new Error(`invalid emojiSearch.resultType setting: ${emojiSearch.resultType}`);
}
} else {
// fallback when we have either too many or too few emoji results

// otherwise open popup to show all emoji choices
// does not work, because we have no permission
// see https://bugzilla.mozilla.org/show_bug.cgi?id=1542358
// browser.browserAction.openPopup();

// search for result in emojipedia
const resultUrl = `https://emojipedia.org/search/?q=${text}`;
openTabUrl(resultUrl, disposition);
}
}

/**
* Enables or disables the search in the omnibar.
*
* @private
* @param {boolean} toEnable
* @returns {void}
* @throws TypeError
*/
async function toggleEnabledStatus(toEnable) {
// if we do not have the permission for clipboard, and need it for settings, force-disable feature
if (!(await browser.permissions.contains(CLIPBOARD_WRITE_PERMISSION))) {
const emojiSearch = await AddonSettings.get("emojiSearch");

if (emojiSearch.action === "copy") {
toEnable = false;
}
}

// enable it
if (toEnable) {
// lazy-load emoji-mart
loadEmojiMart();

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

browser.omnibox.onInputEntered.removeListener(triggerOmnixboxDisabledSearch);

browser.omnibox.setDefaultSuggestion({
description: browser.i18n.getMessage("searchTipDescription", [
browser.i18n.getMessage("extensionName")
])
});
} else if (!toEnable) {
// disable it
browser.omnibox.onInputChanged.removeListener(triggerOmnixboxSuggestion);
browser.omnibox.onInputEntered.removeListener(triggerOmnixboxSearch);

browser.omnibox.onInputEntered.addListener(triggerOmnixboxDisabledSearch);

browser.omnibox.setDefaultSuggestion({
description: browser.i18n.getMessage("searchTipDescriptionDisabled", [
browser.i18n.getMessage("extensionName")
])
});
} else {
throw new TypeError("isEnabled must be boolean!");
}
}


/**
* Init omnibox search.
*
* @public
* @returns {Promise}
*/
export async function init() {
// load whether it is enabled
const emojiSearch = await AddonSettings.get("emojiSearch");

toggleEnabledStatus(emojiSearch.enabled);
}

BrowserCommunication.addListener(COMMUNICATION_MESSAGE_TYPE.OMNIBAR_TOGGLE, async (request) => {
// clear cache by reloading all options
await AddonSettings.loadOptions();

return toggleEnabledStatus(request.toEnable);
});
1 change: 1 addition & 0 deletions src/common/lib/emoji-mart-embed
Submodule emoji-mart-embed added at 0ac47b
Loading