Refactor background page to use Typescript
Also, this CL adds clean-terminal-webpack-plugin to make it easier to
debug Typescript errors while developing the extension.
Bug: translateselectedtext:15
Change-Id: If9b97cb7508859e2e05f5dc82940808fd935bf1a
diff --git a/src/background.ts b/src/background.ts
new file mode 100644
index 0000000..699109d
--- /dev/null
+++ b/src/background.ts
@@ -0,0 +1,194 @@
+import actionApi from './common/actionApi';
+import {isoLangs} from './common/consts';
+import Options from './common/options';
+import ExtSessionStorage from './common/sessionStorage';
+
+interface ContextMenuLangs {
+ [id: string]: string;
+}
+
+function getTranslationUrl(lang: string, text: string): string {
+ let params = new URLSearchParams({
+ sl: 'auto',
+ tl: lang,
+ text: text,
+ op: 'translate',
+ });
+ return 'https://translate.google.com/?' + params.toString();
+}
+
+function translationClick(
+ info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab): void {
+ let optionsPromise = Options.getOptions();
+ let ssPromise = ExtSessionStorage.get(['contextMenuLangs', 'translatorTab']);
+ Promise.all([optionsPromise, ssPromise])
+ .then(returnValues => {
+ const [options, sessionStorageItems] = returnValues;
+ let url = getTranslationUrl(
+ sessionStorageItems.contextMenuLangs?.[info.menuItemId],
+ info.selectionText);
+ let settings_tab = {url};
+ if (sessionStorageItems.translatorTab && options.uniqueTab == 'yep') {
+ chrome.tabs.update(
+ sessionStorageItems.translatorTab, settings_tab, tab => {
+ chrome.tabs.highlight(
+ {
+ windowId: tab.windowId,
+ tabs: tab.index,
+ },
+ () => {
+ chrome.windows.update(tab.windowId, {
+ focused: true,
+ });
+ });
+ });
+ } else if (options.uniqueTab == 'popup') {
+ chrome.windows.create({
+ type: 'popup',
+ url,
+ width: 1000,
+ height: 382,
+ });
+ } else {
+ chrome.tabs.create(settings_tab, function(tab) {
+ ExtSessionStorage.set({translatorTab: tab.id});
+ });
+ }
+ })
+ .catch(err => {
+ console.error('Error handling translation click', err);
+ });
+}
+
+function createMenus(options: Options): Promise<void> {
+ chrome.contextMenus.removeAll();
+
+ let contextMenuLangs: ContextMenuLangs = {};
+ let langs = options.targetLangs;
+ let isSingleEntry = Object.values(langs).length == 1;
+
+ let parentEl;
+ if (!isSingleEntry) {
+ parentEl = chrome.contextMenus.create({
+ 'id': 'parent',
+ 'title': chrome.i18n.getMessage('contextmenu_title'),
+ 'contexts': ['selection']
+ });
+ }
+
+ for (let [index, language] of Object.entries(langs)) {
+ let languageDetails = isoLangs[language];
+ if (languageDetails === undefined) {
+ console.error(language + ' doesn\'t exist!');
+ continue;
+ }
+ let title;
+ if (isSingleEntry) {
+ title =
+ chrome.i18n.getMessage('contextmenu_title2', languageDetails.name);
+ } else {
+ title = languageDetails.name + ' (' + languageDetails.nativeName + ')';
+ }
+ let id = chrome.contextMenus.create({
+ 'id': 'tr_language_' + language,
+ 'title': title,
+ 'parentId': parentEl,
+ 'contexts': ['selection']
+ });
+ contextMenuLangs[id] = language;
+ }
+
+ if (!isSingleEntry) {
+ chrome.contextMenus.create({
+ 'id': 'tr_separator',
+ 'type': 'separator',
+ 'parentId': parentEl,
+ 'contexts': ['selection']
+ });
+ chrome.contextMenus.create({
+ 'id': 'tr_options',
+ 'title': chrome.i18n.getMessage('contextmenu_edit'),
+ 'parentId': parentEl,
+ 'contexts': ['selection']
+ });
+ }
+
+ return ExtSessionStorage.set({contextMenuLangs});
+}
+
+chrome.storage.onChanged.addListener((changes, areaName) => {
+ if (areaName == 'sync') {
+ Options.getOptions(/* readOnly = */ false)
+ .then(options => {
+ return createMenus(options);
+ })
+ .catch(err => {
+ console.error(
+ 'Error setting up the extension after a change ' +
+ 'in the storage area.',
+ err);
+ });
+ }
+});
+
+Options.getOptions(/* readOnly = */ false)
+ .then(options => {
+ if (options.isFirstRun) {
+ chrome.notifications.create('install', {
+ type: 'basic',
+ iconUrl: 'icons/translate-128.png',
+ title: chrome.i18n.getMessage('notification_install_title'),
+ message: chrome.i18n.getMessage('notification_install_message'),
+ isClickable: true
+ });
+ }
+
+ return createMenus(options);
+ })
+ .catch(err => {
+ console.error('Error initializing the extension.', err);
+ });
+
+chrome.notifications.onClicked.addListener(notification_id => {
+ switch (notification_id) {
+ case 'install':
+ chrome.runtime.openOptionsPage();
+ break;
+ }
+ chrome.notifications.clear(notification_id);
+});
+
+chrome.contextMenus.onClicked.addListener((info, tab) => {
+ if (info.menuItemId == 'tr_options') {
+ chrome.runtime.openOptionsPage();
+ } else {
+ translationClick(info, tab);
+ }
+});
+
+chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
+ ExtSessionStorage.get('translatorTab')
+ .then(items => {
+ if (tabId == items.translatorTab) {
+ ExtSessionStorage.set({translatorTab: null});
+ }
+ })
+ .catch(err => console.log(err));
+});
+
+actionApi.onClicked.addListener(() => {
+ chrome.runtime.openOptionsPage();
+});
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ switch (request.action) {
+ case 'clearTranslatorTab':
+ ExtSessionStorage.set({translatorTab: null});
+ break;
+
+ default:
+ console.error(`Unknown action "${request.action}" received as a message.`);
+ }
+
+ return undefined;
+});