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 | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 58 | function translationClick(info: chrome.contextMenus.OnClickData): void { |
| 59 | const optionsPromise = Options.getOptions(); |
| 60 | const ssPromise = |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 61 | ExtSessionStorage.get(['contextMenuItems', 'translatorTab']); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 62 | Promise.all([optionsPromise, ssPromise]) |
| 63 | .then(returnValues => { |
| 64 | const [options, sessionStorageItems] = returnValues; |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 65 | const contextMenuItems: ContextMenuItems = |
| 66 | sessionStorageItems.contextMenuItems; |
| 67 | const contextMenuItem = contextMenuItems?.[info.menuItemId]; |
| 68 | const translatorTab: number = sessionStorageItems.translatorTab; |
| 69 | |
| 70 | const url = URLFactory.getTranslationURL( |
| 71 | contextMenuItem?.language, info, contextMenuItem?.dataType); |
| 72 | |
| 73 | if (contextMenuItem?.dataType !== DataType.DataTypeText) { |
| 74 | // Always create a simple new tab for data types other than text. |
| 75 | // @TODO(https://iavm.xyz/b/translateselectedtext/7): Review this |
| 76 | // behavior in the future. |
| 77 | chrome.tabs.create({url}); |
| 78 | } else if (translatorTab && options.uniqueTab == 'yep') { |
| 79 | chrome.tabs.update(translatorTab, {url}, tab => { |
| 80 | chrome.tabs.highlight( |
| 81 | {windowId: tab.windowId, tabs: tab.index}, () => { |
| 82 | chrome.windows.update(tab.windowId, {focused: true}); |
| 83 | }); |
Adrià Vilanova Martínez | ec59934 | 2022-05-31 11:57:35 +0200 | [diff] [blame] | 84 | }); |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 85 | } else if (options.uniqueTab == 'popup') { |
| 86 | chrome.windows.create({type: 'popup', url, width: 1000, height: 382}); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 87 | } else { |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 88 | chrome.tabs.create({url}, tab => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 89 | ExtSessionStorage.set({translatorTab: tab.id}); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 90 | }); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 91 | } |
| 92 | }) |
| 93 | .catch(err => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 94 | console.error('Error handling translation click', err); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 95 | }); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 96 | } |
| 97 | |
Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 98 | function createMenus(options: Options): Promise<void> { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 99 | chrome.contextMenus.removeAll(); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 100 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 101 | const contextMenuItems: ContextMenuItems = {}; |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 102 | const langs = options.targetLangs; |
| 103 | const isSingleEntry = Object.values(langs).length == 1; |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 104 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 105 | for (const type of MENU_ITEM_TYPES) { |
| 106 | let parentEl; |
| 107 | if (!isSingleEntry) { |
| 108 | parentEl = chrome.contextMenus.create({ |
| 109 | 'id': `${type.prefix}parent`, |
| 110 | 'title': chrome.i18n.getMessage(`contextmenu${type.prefix}_title`), |
| 111 | 'contexts': type.contexts, |
| 112 | }); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 113 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 114 | |
| 115 | for (const language of Object.values(langs)) { |
| 116 | const languageDetails = isoLangs[language]; |
| 117 | if (languageDetails === undefined) { |
| 118 | console.error(language + ' doesn\'t exist!'); |
| 119 | continue; |
| 120 | } |
| 121 | let title; |
| 122 | if (isSingleEntry) { |
| 123 | title = chrome.i18n.getMessage( |
| 124 | `contextmenu${type.prefix}_title2`, languageDetails.name); |
| 125 | } else { |
| 126 | title = languageDetails.name + ' (' + languageDetails.nativeName + ')'; |
| 127 | } |
| 128 | const id = chrome.contextMenus.create({ |
| 129 | 'id': `${type.prefix}tr_language_${language}`, |
| 130 | 'title': title, |
| 131 | 'parentId': parentEl, |
| 132 | 'contexts': type.contexts, |
| 133 | }); |
| 134 | contextMenuItems[id] = { |
| 135 | language, |
| 136 | dataType: type.dataType, |
| 137 | }; |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 138 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 139 | |
| 140 | if (!isSingleEntry) { |
| 141 | chrome.contextMenus.create({ |
| 142 | 'id': `${type.prefix}tr_separator`, |
| 143 | 'type': 'separator', |
| 144 | 'parentId': parentEl, |
| 145 | 'contexts': type.contexts, |
| 146 | }); |
| 147 | chrome.contextMenus.create({ |
| 148 | 'id': `${type.prefix}tr_options`, |
| 149 | 'title': chrome.i18n.getMessage('contextmenu_edit'), |
| 150 | 'parentId': parentEl, |
| 151 | 'contexts': type.contexts, |
| 152 | }); |
| 153 | } |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 154 | } |
| 155 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 156 | return ExtSessionStorage.set({contextMenuItems}); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 157 | } |
| 158 | |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 159 | chrome.storage.onChanged.addListener((_changes, areaName) => { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 160 | if (areaName == 'sync') { |
| 161 | Options.getOptions(/* readOnly = */ false) |
| 162 | .then(options => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 163 | return createMenus(options); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 164 | }) |
| 165 | .catch(err => { |
| 166 | console.error( |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 167 | 'Error setting up the extension after a change ' + |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 168 | 'in the storage area.', |
| 169 | err); |
| 170 | }); |
| 171 | } |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 172 | }); |
| 173 | |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 174 | Options.getOptions(/* readOnly = */ false) |
| 175 | .then(options => { |
| 176 | if (options.isFirstRun) { |
| 177 | chrome.notifications.create('install', { |
| 178 | type: 'basic', |
| 179 | iconUrl: 'icons/translate-128.png', |
| 180 | title: chrome.i18n.getMessage('notification_install_title'), |
| 181 | message: chrome.i18n.getMessage('notification_install_message'), |
| 182 | isClickable: true |
| 183 | }); |
| 184 | } |
| 185 | |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 186 | return createMenus(options); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 187 | }) |
| 188 | .catch(err => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 189 | console.error('Error initializing the extension.', err); |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 190 | }); |
| 191 | |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 192 | chrome.notifications.onClicked.addListener(notification_id => { |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 193 | switch (notification_id) { |
| 194 | case 'install': |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 195 | chrome.runtime.openOptionsPage(); |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 196 | break; |
| 197 | } |
| 198 | chrome.notifications.clear(notification_id); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 199 | }); |
| 200 | |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 201 | chrome.contextMenus.onClicked.addListener(info => { |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 202 | for (const type of MENU_ITEM_TYPES) { |
| 203 | if (info.menuItemId == `${type.prefix}tr_options`) { |
| 204 | chrome.runtime.openOptionsPage(); |
| 205 | return; |
| 206 | } |
avm99963 | 5a57c41 | 2020-12-27 00:26:45 +0100 | [diff] [blame] | 207 | } |
Adrià Vilanova Martínez | 9f03abd | 2022-06-02 00:51:00 +0200 | [diff] [blame^] | 208 | |
| 209 | translationClick(info); |
avm99963 | 4a2a5d5 | 2016-06-04 16:17:29 +0200 | [diff] [blame] | 210 | }); |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 211 | |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 212 | chrome.tabs.onRemoved.addListener(tabId => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 213 | ExtSessionStorage.get('translatorTab') |
| 214 | .then(items => { |
| 215 | if (tabId == items.translatorTab) { |
| 216 | ExtSessionStorage.set({translatorTab: null}); |
| 217 | } |
| 218 | }) |
| 219 | .catch(err => console.log(err)); |
Adrià Vilanova Martínez | ec59934 | 2022-05-31 11:57:35 +0200 | [diff] [blame] | 220 | }); |
| 221 | |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 222 | actionApi.onClicked.addListener(() => { |
Adrià Vilanova Martínez | 53f7a7f | 2022-05-30 13:59:59 +0200 | [diff] [blame] | 223 | chrome.runtime.openOptionsPage(); |
avm99963 | ce257a9 | 2020-12-27 00:07:13 +0100 | [diff] [blame] | 224 | }); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 225 | |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 226 | chrome.runtime.onMessage.addListener(request => { |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 227 | switch (request.action) { |
| 228 | case 'clearTranslatorTab': |
| 229 | ExtSessionStorage.set({translatorTab: null}); |
| 230 | break; |
| 231 | |
| 232 | default: |
Adrià Vilanova Martínez | 51f151f | 2022-06-01 23:17:38 +0200 | [diff] [blame] | 233 | console.error( |
| 234 | `Unknown action "${request.action}" received as a message.`); |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 235 | } |
Adrià Vilanova Martínez | 5bdc473 | 2022-05-31 20:12:21 +0200 | [diff] [blame] | 236 | |
| 237 | return undefined; |
Adrià Vilanova Martínez | 86fda49 | 2022-05-31 15:05:21 +0200 | [diff] [blame] | 238 | }); |