Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 1 | import actionApi from './common/actionApi'; |
| 2 | import {isoLangs} from './common/consts'; |
| 3 | import Options from './common/options'; |
| 4 | import ExtSessionStorage from './common/sessionStorage'; |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 5 | import URLFactory from './common/urlFactory'; |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 6 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 7 | type NonEmptyArray<T> = [T, ...T[]]; |
| 8 | |
| 9 | // Data types that the extension can translate. |
| 10 | export enum DataType { |
| 11 | DataTypeText, |
| 12 | DataTypeURL, |
Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 13 | } |
| 14 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 15 | // Information about a context menu item which we have added to the browser. |
| 16 | interface MenuItemInfo { |
| 17 | language: string; // Target language displayed in the item. |
| 18 | dataType: DataType; // Data type handled by the context menu item. |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 19 | } |
| 20 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 21 | // Object with the context menu items that have been added by the extension and |
| 22 | // information about them. |
| 23 | interface ContextMenuItems { |
| 24 | [id: string]: MenuItemInfo; |
| 25 | } |
| 26 | |
| 27 | // Definition of the types of context menu items that the extension can inject. |
| 28 | interface MenuItemType { |
| 29 | // Type of data which can be translated with this type of context menu items. |
| 30 | dataType: DataType; |
| 31 | // Contexts in which this type of context menu item will be shown. |
| 32 | contexts: NonEmptyArray<chrome.contextMenus.ContextType>; |
| 33 | // Prefix of the i18n messages for this type of context menu item, and used to |
| 34 | // generate the unique IDs of context menu items. |
| 35 | prefix: string; |
| 36 | } |
| 37 | type MenuItemTypes = MenuItemType[]; |
| 38 | |
| 39 | const MENU_ITEM_TYPES: MenuItemTypes = [ |
| 40 | { |
| 41 | dataType: DataType.DataTypeText, |
| 42 | contexts: ['selection'], |
| 43 | prefix: '', |
| 44 | }, |
| 45 | /* |
| 46 | * @TODO(https://iavm.xyz/b/translateselectedtext/7): Delete this compile-time |
| 47 | * directive after the experimentation phase is done to launch the feature. |
| 48 | * #!if canary || !production |
| 49 | */ |
| 50 | { |
| 51 | dataType: DataType.DataTypeURL, |
| 52 | contexts: ['link'], |
| 53 | prefix: 'link', |
| 54 | }, |
| 55 | // #!endif |
| 56 | ]; |
| 57 | |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 58 | function translationClick( |
| 59 | info: chrome.contextMenus.OnClickData, |
| 60 | initiatorTab: chrome.tabs.Tab): void { |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 61 | const optionsPromise = Options.getOptions(); |
| 62 | const ssPromise = |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 63 | ExtSessionStorage.get(['contextMenuItems', 'translatorTab']); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 64 | Promise.all([optionsPromise, ssPromise]) |
| 65 | .then(returnValues => { |
| 66 | const [options, sessionStorageItems] = returnValues; |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 67 | const contextMenuItems: ContextMenuItems = |
| 68 | sessionStorageItems.contextMenuItems; |
| 69 | const contextMenuItem = contextMenuItems?.[info.menuItemId]; |
| 70 | const translatorTab: number = sessionStorageItems.translatorTab; |
| 71 | |
| 72 | const url = URLFactory.getTranslationURL( |
| 73 | contextMenuItem?.language, info, contextMenuItem?.dataType); |
Adrià Vilanova Martínez | 7ba7ec1 | 2024-05-28 22:17:38 +0200 | [diff] [blame] | 74 | const newTabOptions: Parameters<typeof chrome.tabs.create>[0] = {url}; |
| 75 | |
| 76 | if (initiatorTab.id > 0) newTabOptions.openerTabId = initiatorTab.id; |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 77 | if (initiatorTab.index) newTabOptions.index = initiatorTab.index + 1; |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 78 | |
| 79 | if (contextMenuItem?.dataType !== DataType.DataTypeText) { |
| 80 | // Always create a simple new tab for data types other than text. |
| 81 | // @TODO(https://iavm.xyz/b/translateselectedtext/7): Review this |
| 82 | // behavior in the future. |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 83 | chrome.tabs.create(newTabOptions); |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 84 | } else if (translatorTab && options.uniqueTab == 'yep') { |
| 85 | chrome.tabs.update(translatorTab, {url}, tab => { |
| 86 | chrome.tabs.highlight( |
| 87 | {windowId: tab.windowId, tabs: tab.index}, () => { |
| 88 | chrome.windows.update(tab.windowId, {focused: true}); |
| 89 | }); |
Adrià Vilanova Martínez | ec59934 | 2022-05-31 11:57:35 +0200 | [diff] [blame] | 90 | }); |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 91 | } else if (options.uniqueTab == 'popup') { |
| 92 | chrome.windows.create({type: 'popup', url, width: 1000, height: 382}); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 93 | } else { |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 94 | chrome.tabs.create(newTabOptions, tab => { |
Adrià Vilanova Martínez | 9ff52fe | 2022-09-15 12:27:49 +0200 | [diff] [blame] | 95 | ExtSessionStorage.set({translatorTab: tab?.id}); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 96 | }); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 97 | } |
| 98 | }) |
| 99 | .catch(err => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 100 | console.error('Error handling translation click', err); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 101 | }); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 102 | } |
| 103 | |
Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 104 | function createMenus(options: Options): Promise<void> { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 105 | chrome.contextMenus.removeAll(); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 106 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 107 | const contextMenuItems: ContextMenuItems = {}; |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 108 | const langs = options.targetLangs; |
| 109 | const isSingleEntry = Object.values(langs).length == 1; |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 110 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 111 | for (const type of MENU_ITEM_TYPES) { |
| 112 | let parentEl; |
| 113 | if (!isSingleEntry) { |
| 114 | parentEl = chrome.contextMenus.create({ |
| 115 | 'id': `${type.prefix}parent`, |
| 116 | 'title': chrome.i18n.getMessage(`contextmenu${type.prefix}_title`), |
| 117 | 'contexts': type.contexts, |
| 118 | }); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 119 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 120 | |
| 121 | for (const language of Object.values(langs)) { |
| 122 | const languageDetails = isoLangs[language]; |
| 123 | if (languageDetails === undefined) { |
| 124 | console.error(language + ' doesn\'t exist!'); |
| 125 | continue; |
| 126 | } |
| 127 | let title; |
| 128 | if (isSingleEntry) { |
| 129 | title = chrome.i18n.getMessage( |
| 130 | `contextmenu${type.prefix}_title2`, languageDetails.name); |
| 131 | } else { |
| 132 | title = languageDetails.name + ' (' + languageDetails.nativeName + ')'; |
| 133 | } |
| 134 | const id = chrome.contextMenus.create({ |
| 135 | 'id': `${type.prefix}tr_language_${language}`, |
| 136 | 'title': title, |
| 137 | 'parentId': parentEl, |
| 138 | 'contexts': type.contexts, |
| 139 | }); |
| 140 | contextMenuItems[id] = { |
| 141 | language, |
| 142 | dataType: type.dataType, |
| 143 | }; |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 144 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 145 | |
| 146 | if (!isSingleEntry) { |
| 147 | chrome.contextMenus.create({ |
| 148 | 'id': `${type.prefix}tr_separator`, |
| 149 | 'type': 'separator', |
| 150 | 'parentId': parentEl, |
| 151 | 'contexts': type.contexts, |
| 152 | }); |
| 153 | chrome.contextMenus.create({ |
| 154 | 'id': `${type.prefix}tr_options`, |
| 155 | 'title': chrome.i18n.getMessage('contextmenu_edit'), |
| 156 | 'parentId': parentEl, |
| 157 | 'contexts': type.contexts, |
| 158 | }); |
| 159 | } |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 160 | } |
| 161 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 162 | return ExtSessionStorage.set({contextMenuItems}); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 163 | } |
| 164 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 165 | chrome.storage.onChanged.addListener((_changes, areaName) => { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 166 | if (areaName == 'sync') { |
| 167 | Options.getOptions(/* readOnly = */ false) |
| 168 | .then(options => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 169 | return createMenus(options); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 170 | }) |
| 171 | .catch(err => { |
| 172 | console.error( |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 173 | 'Error setting up the extension after a change ' + |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 174 | 'in the storage area.', |
| 175 | err); |
| 176 | }); |
| 177 | } |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 178 | }); |
| 179 | |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 180 | Options.getOptions(/* readOnly = */ false) |
| 181 | .then(options => { |
| 182 | if (options.isFirstRun) { |
| 183 | chrome.notifications.create('install', { |
| 184 | type: 'basic', |
| 185 | iconUrl: 'icons/translate-128.png', |
| 186 | title: chrome.i18n.getMessage('notification_install_title'), |
| 187 | message: chrome.i18n.getMessage('notification_install_message'), |
| 188 | isClickable: true |
| 189 | }); |
| 190 | } |
| 191 | |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 192 | return createMenus(options); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 193 | }) |
| 194 | .catch(err => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 195 | console.error('Error initializing the extension.', err); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 196 | }); |
| 197 | |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 198 | chrome.notifications.onClicked.addListener(notification_id => { |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 199 | switch (notification_id) { |
| 200 | case 'install': |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 201 | chrome.runtime.openOptionsPage(); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 202 | break; |
| 203 | } |
| 204 | chrome.notifications.clear(notification_id); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 205 | }); |
| 206 | |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 207 | chrome.contextMenus.onClicked.addListener((info, tab) => { |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 208 | for (const type of MENU_ITEM_TYPES) { |
| 209 | if (info.menuItemId == `${type.prefix}tr_options`) { |
| 210 | chrome.runtime.openOptionsPage(); |
| 211 | return; |
| 212 | } |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 213 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame] | 214 | |
Adrià Vilanova Martínez | 2123488 | 2022-06-05 23:11:31 +0200 | [diff] [blame] | 215 | translationClick(info, tab); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 216 | }); |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 217 | |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 218 | chrome.tabs.onRemoved.addListener(tabId => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 219 | ExtSessionStorage.get('translatorTab') |
| 220 | .then(items => { |
| 221 | if (tabId == items.translatorTab) { |
| 222 | ExtSessionStorage.set({translatorTab: null}); |
| 223 | } |
| 224 | }) |
| 225 | .catch(err => console.log(err)); |
Adrià Vilanova Martínez | ec59934 | 2022-05-31 11:57:35 +0200 | [diff] [blame] | 226 | }); |
| 227 | |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 228 | actionApi.onClicked.addListener(() => { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 229 | chrome.runtime.openOptionsPage(); |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 230 | }); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 231 | |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 232 | chrome.runtime.onMessage.addListener(request => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 233 | switch (request.action) { |
| 234 | case 'clearTranslatorTab': |
| 235 | ExtSessionStorage.set({translatorTab: null}); |
| 236 | break; |
| 237 | |
| 238 | default: |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 239 | console.error( |
| 240 | `Unknown action "${request.action}" received as a message.`); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 241 | } |
Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 242 | |
| 243 | return undefined; |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 244 | }); |