diff options
-rw-r--r-- | src/stores/lib/Reaction.ts (renamed from src/stores/lib/Reaction.js) | 6 | ||||
-rw-r--r-- | src/webview/contextMenu.js | 20 | ||||
-rw-r--r-- | src/webview/contextMenu.ts | 29 | ||||
-rw-r--r-- | src/webview/contextMenuBuilder.ts (renamed from src/webview/contextMenuBuilder.js) | 217 | ||||
-rw-r--r-- | src/webview/darkmode/custom.ts (renamed from src/webview/darkmode/custom.js) | 0 | ||||
-rw-r--r-- | src/webview/darkmode/ignore.js | 3 | ||||
-rw-r--r-- | src/webview/darkmode/ignore.ts | 1 | ||||
-rw-r--r-- | src/webview/find.ts (renamed from src/webview/find.js) | 10 | ||||
-rw-r--r-- | src/webview/notifications.ts (renamed from src/webview/notifications.js) | 7 | ||||
-rw-r--r-- | src/webview/screenshare.ts (renamed from src/webview/screenshare.js) | 8 |
10 files changed, 205 insertions, 96 deletions
diff --git a/src/stores/lib/Reaction.js b/src/stores/lib/Reaction.ts index 7e1bc685e..0ca24a6fa 100644 --- a/src/stores/lib/Reaction.js +++ b/src/stores/lib/Reaction.ts | |||
@@ -20,12 +20,10 @@ export default class Reaction { | |||
20 | 20 | ||
21 | stop() { | 21 | stop() { |
22 | if (this.isRunning) { | 22 | if (this.isRunning) { |
23 | this.dispose(); | 23 | this.dispose?.(); |
24 | this.isRunning = false; | 24 | this.isRunning = false; |
25 | } | 25 | } |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | export const createReactions = (reactions) => ( | 29 | export const createReactions = reactions => reactions.map(r => new Reaction(r)); |
30 | reactions.map((r) => new Reaction(r)) | ||
31 | ); | ||
diff --git a/src/webview/contextMenu.js b/src/webview/contextMenu.js deleted file mode 100644 index 567a2d470..000000000 --- a/src/webview/contextMenu.js +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | import { getCurrentWebContents } from '@electron/remote'; | ||
2 | import ContextMenuBuilder from './contextMenuBuilder'; | ||
3 | |||
4 | const webContents = getCurrentWebContents(); | ||
5 | |||
6 | export default async function setupContextMenu(isSpellcheckEnabled, getDefaultSpellcheckerLanguage, getSpellcheckerLanguage, getSearchEngine, getClipboardNotifications) { | ||
7 | const contextMenuBuilder = new ContextMenuBuilder( | ||
8 | webContents, | ||
9 | ); | ||
10 | |||
11 | webContents.on('context-menu', (e, props) => { | ||
12 | // TODO?: e.preventDefault(); | ||
13 | contextMenuBuilder.showPopupMenu( | ||
14 | { ...props, searchEngine: getSearchEngine(), clipboardNotifications: getClipboardNotifications() }, | ||
15 | isSpellcheckEnabled(), | ||
16 | getDefaultSpellcheckerLanguage(), | ||
17 | getSpellcheckerLanguage(), | ||
18 | ); | ||
19 | }); | ||
20 | } | ||
diff --git a/src/webview/contextMenu.ts b/src/webview/contextMenu.ts new file mode 100644 index 000000000..72f927ef4 --- /dev/null +++ b/src/webview/contextMenu.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import { getCurrentWebContents } from '@electron/remote'; | ||
2 | import { ContextMenuBuilder } from './contextMenuBuilder'; | ||
3 | |||
4 | const webContents = getCurrentWebContents(); | ||
5 | |||
6 | export default async function setupContextMenu( | ||
7 | isSpellcheckEnabled: () => void, | ||
8 | getDefaultSpellcheckerLanguage: () => void, | ||
9 | getSpellcheckerLanguage: () => void, | ||
10 | getSearchEngine: () => void, | ||
11 | getClipboardNotifications: () => void, | ||
12 | ) { | ||
13 | const contextMenuBuilder = new ContextMenuBuilder(webContents); | ||
14 | |||
15 | webContents.on('context-menu', (_e, props) => { | ||
16 | // TODO?: e.preventDefault(); | ||
17 | contextMenuBuilder.showPopupMenu( | ||
18 | { | ||
19 | ...props, | ||
20 | searchEngine: getSearchEngine(), | ||
21 | clipboardNotifications: getClipboardNotifications(), | ||
22 | }, | ||
23 | // @ts-expect-error Expected 1 arguments, but got 4. | ||
24 | isSpellcheckEnabled(), | ||
25 | getDefaultSpellcheckerLanguage(), | ||
26 | getSpellcheckerLanguage(), | ||
27 | ); | ||
28 | }); | ||
29 | } | ||
diff --git a/src/webview/contextMenuBuilder.js b/src/webview/contextMenuBuilder.ts index 938eade1e..fda5fa8b8 100644 --- a/src/webview/contextMenuBuilder.js +++ b/src/webview/contextMenuBuilder.ts | |||
@@ -14,17 +14,38 @@ import { cmdOrCtrlShortcutKey, isMac } from '../environment'; | |||
14 | import { SEARCH_ENGINE_NAMES, SEARCH_ENGINE_URLS } from '../config'; | 14 | import { SEARCH_ENGINE_NAMES, SEARCH_ENGINE_URLS } from '../config'; |
15 | import { openExternalUrl } from '../helpers/url-helpers'; | 15 | import { openExternalUrl } from '../helpers/url-helpers'; |
16 | 16 | ||
17 | const { URL } = require('url'); | 17 | function matchesWord(string: string) { |
18 | |||
19 | function matchesWord(string) { | ||
20 | const regex = | 18 | const regex = |
21 | /[A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g; | 19 | /[A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g; |
22 | 20 | ||
23 | return string.match(regex); | 21 | return string.match(regex); |
24 | } | 22 | } |
25 | 23 | ||
24 | interface ContextMenuStringTable { | ||
25 | lookUpDefinition: ({ word }: { word: string }) => string; | ||
26 | cut: () => string; | ||
27 | copy: () => string; | ||
28 | paste: () => string; | ||
29 | pasteAndMatchStyle: () => string; | ||
30 | searchWith: ({ searchEngine }: { searchEngine: string }) => string; | ||
31 | openLinkUrl: () => string; | ||
32 | openLinkInFerdiUrl: () => string; | ||
33 | openInBrowser: () => string; | ||
34 | copyLinkUrl: () => string; | ||
35 | copyImageUrl: () => string; | ||
36 | copyImage: () => string; | ||
37 | downloadImage: () => string; | ||
38 | addToDictionary: () => string; | ||
39 | goBack: () => string; | ||
40 | goForward: () => string; | ||
41 | copyPageUrl: () => string; | ||
42 | goToHomePage: () => string; | ||
43 | copyMail: () => string; | ||
44 | inspectElement: () => string; | ||
45 | } | ||
46 | |||
26 | // TODO: Need to externalize for i18n | 47 | // TODO: Need to externalize for i18n |
27 | const contextMenuStringTable = { | 48 | const contextMenuStringTable: ContextMenuStringTable = { |
28 | lookUpDefinition: ({ word }) => `Look Up "${word}"`, | 49 | lookUpDefinition: ({ word }) => `Look Up "${word}"`, |
29 | cut: () => 'Cut', | 50 | cut: () => 'Cut', |
30 | copy: () => 'Copy', | 51 | copy: () => 'Copy', |
@@ -54,16 +75,31 @@ const contextMenuStringTable = { | |||
54 | * which we use to generate the menu. We also use the spell-check information to | 75 | * which we use to generate the menu. We also use the spell-check information to |
55 | * generate suggestions. | 76 | * generate suggestions. |
56 | */ | 77 | */ |
57 | module.exports = class ContextMenuBuilder { | 78 | export class ContextMenuBuilder { |
79 | debugMode: boolean; | ||
80 | |||
81 | processMenu: ( | ||
82 | menu: Electron.CrossProcessExports.Menu, | ||
83 | ) => Electron.CrossProcessExports.Menu; | ||
84 | |||
85 | menu: Electron.CrossProcessExports.Menu | null; | ||
86 | |||
87 | stringTable: ContextMenuStringTable; | ||
88 | |||
89 | getWebContents: () => Electron.WebContents; | ||
90 | |||
58 | /** | 91 | /** |
59 | * Creates an instance of ContextMenuBuilder | 92 | * Creates an instance of ContextMenuBuilder |
60 | * | 93 | * |
61 | * @param {WebContents} webContents Current webContents | 94 | * @param webContents Current webContents |
62 | * @param {Boolean} debugMode If true, display the "Inspect Element" menu item. | 95 | * @param debugMode If true, display the "Inspect Element" menu item. |
63 | * @param {function} processMenu If passed, this method will be passed the menu to change | 96 | * @param processMenu If passed, this method will be passed the menu to change it prior to display. Signature: (menu, info) => menu |
64 | * it prior to display. Signature: (menu, info) => menu | ||
65 | */ | 97 | */ |
66 | constructor(webContents, debugMode = false, processMenu = m => m) { | 98 | constructor( |
99 | webContents: WebContents, | ||
100 | debugMode: boolean = false, | ||
101 | processMenu = (menu: Electron.CrossProcessExports.Menu) => menu, | ||
102 | ) { | ||
67 | this.debugMode = debugMode; | 103 | this.debugMode = debugMode; |
68 | this.processMenu = processMenu; | 104 | this.processMenu = processMenu; |
69 | this.menu = null; | 105 | this.menu = null; |
@@ -77,12 +113,11 @@ module.exports = class ContextMenuBuilder { | |||
77 | * allows to change string in runtime. All formatters are simply typeof () => string, except | 113 | * allows to change string in runtime. All formatters are simply typeof () => string, except |
78 | * lookUpDefinition provides word, ({word}) => string. | 114 | * lookUpDefinition provides word, ({word}) => string. |
79 | * | 115 | * |
80 | * @param {Object} stringTable The object contains string foramtter function for context menu. | 116 | * @param stringTable The object contains string formatter function for context menu. |
81 | * It is allowed to specify only certain menu string as necessary, which will makes other string | 117 | * It is allowed to specify only certain menu string as necessary, which will makes other string |
82 | * fall backs to default. | 118 | * fall backs to default. |
83 | * | ||
84 | */ | 119 | */ |
85 | setAlternateStringFormatter(stringTable) { | 120 | setAlternateStringFormatter(stringTable: ContextMenuStringTable) { |
86 | this.stringTable = Object.assign(this.stringTable, stringTable); | 121 | this.stringTable = Object.assign(this.stringTable, stringTable); |
87 | } | 122 | } |
88 | 123 | ||
@@ -90,12 +125,9 @@ module.exports = class ContextMenuBuilder { | |||
90 | * Shows a popup menu given the information returned from the context-menu | 125 | * Shows a popup menu given the information returned from the context-menu |
91 | * event. This is probably the only method you need to call in this class. | 126 | * event. This is probably the only method you need to call in this class. |
92 | * | 127 | * |
93 | * @param {Object} contextInfo The object returned from the 'context-menu' | 128 | * @param contextInfo The object returned from the 'context-menu' Electron event. |
94 | * Electron event. | ||
95 | * | ||
96 | * @return {Promise} Completion | ||
97 | */ | 129 | */ |
98 | async showPopupMenu(contextInfo) { | 130 | async showPopupMenu(contextInfo: Electron.ContextMenuParams): Promise<void> { |
99 | const menu = await this.buildMenuForElement(contextInfo); | 131 | const menu = await this.buildMenuForElement(contextInfo); |
100 | if (!menu) return; | 132 | if (!menu) return; |
101 | menu.popup(); | 133 | menu.popup(); |
@@ -105,10 +137,10 @@ module.exports = class ContextMenuBuilder { | |||
105 | * Builds a context menu specific to the given info that _would_ be shown | 137 | * Builds a context menu specific to the given info that _would_ be shown |
106 | * immediately by {{showPopupMenu}}. Use this to add your own menu items to | 138 | * immediately by {{showPopupMenu}}. Use this to add your own menu items to |
107 | * the list but use most of the default behavior. | 139 | * the list but use most of the default behavior. |
108 | * | ||
109 | * @return {Promise<Menu>} The newly created `Menu` | ||
110 | */ | 140 | */ |
111 | async buildMenuForElement(info) { | 141 | async buildMenuForElement( |
142 | info: Electron.ContextMenuParams, | ||
143 | ): Promise<Electron.CrossProcessExports.Menu> { | ||
112 | if (info.linkURL && info.linkURL.length > 0) { | 144 | if (info.linkURL && info.linkURL.length > 0) { |
113 | return this.buildMenuForLink(info); | 145 | return this.buildMenuForLink(info); |
114 | } | 146 | } |
@@ -132,7 +164,9 @@ module.exports = class ContextMenuBuilder { | |||
132 | * | 164 | * |
133 | * @return {Menu} The `Menu` | 165 | * @return {Menu} The `Menu` |
134 | */ | 166 | */ |
135 | buildMenuForTextInput(menuInfo) { | 167 | buildMenuForTextInput( |
168 | menuInfo: Electron.ContextMenuParams, | ||
169 | ): Electron.CrossProcessExports.Menu { | ||
136 | const menu = new Menu(); | 170 | const menu = new Menu(); |
137 | 171 | ||
138 | this.addSpellingItems(menu, menuInfo); | 172 | this.addSpellingItems(menu, menuInfo); |
@@ -143,8 +177,10 @@ module.exports = class ContextMenuBuilder { | |||
143 | this.addPaste(menu, menuInfo); | 177 | this.addPaste(menu, menuInfo); |
144 | this.addPastePlain(menu, menuInfo); | 178 | this.addPastePlain(menu, menuInfo); |
145 | this.addInspectElement(menu, menuInfo); | 179 | this.addInspectElement(menu, menuInfo); |
180 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
146 | this.processMenu(menu, menuInfo); | 181 | this.processMenu(menu, menuInfo); |
147 | 182 | ||
183 | // @ts-expect-error Expected 2 arguments, but got 1. | ||
148 | this.copyPageUrl(menu); | 184 | this.copyPageUrl(menu); |
149 | this.goToHomePage(menu, menuInfo); | 185 | this.goToHomePage(menu, menuInfo); |
150 | this.openInBrowser(menu, menuInfo); | 186 | this.openInBrowser(menu, menuInfo); |
@@ -157,7 +193,9 @@ module.exports = class ContextMenuBuilder { | |||
157 | * | 193 | * |
158 | * @return {Menu} The `Menu` | 194 | * @return {Menu} The `Menu` |
159 | */ | 195 | */ |
160 | buildMenuForLink(menuInfo) { | 196 | buildMenuForLink( |
197 | menuInfo: Electron.ContextMenuParams, | ||
198 | ): Electron.CrossProcessExports.Menu { | ||
161 | const menu = new Menu(); | 199 | const menu = new Menu(); |
162 | const isEmailAddress = menuInfo.linkURL.startsWith('mailto:'); | 200 | const isEmailAddress = menuInfo.linkURL.startsWith('mailto:'); |
163 | 201 | ||
@@ -170,6 +208,7 @@ module.exports = class ContextMenuBuilder { | |||
170 | const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL; | 208 | const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL; |
171 | clipboard.writeText(url); | 209 | clipboard.writeText(url); |
172 | this._sendNotificationOnClipboardEvent( | 210 | this._sendNotificationOnClipboardEvent( |
211 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
173 | menuInfo.clipboardNotifications, | 212 | menuInfo.clipboardNotifications, |
174 | () => `Link URL copied: ${url}`, | 213 | () => `Link URL copied: ${url}`, |
175 | ); | 214 | ); |
@@ -200,11 +239,13 @@ module.exports = class ContextMenuBuilder { | |||
200 | } | 239 | } |
201 | 240 | ||
202 | this.addInspectElement(menu, menuInfo); | 241 | this.addInspectElement(menu, menuInfo); |
242 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
203 | this.processMenu(menu, menuInfo); | 243 | this.processMenu(menu, menuInfo); |
204 | 244 | ||
205 | this.addSeparator(menu); | 245 | this.addSeparator(menu); |
206 | this.goBack(menu); | 246 | this.goBack(menu); |
207 | this.goForward(menu); | 247 | this.goForward(menu); |
248 | // @ts-expect-error Expected 2 arguments, but got 1. | ||
208 | this.copyPageUrl(menu); | 249 | this.copyPageUrl(menu); |
209 | this.goToHomePage(menu, menuInfo); | 250 | this.goToHomePage(menu, menuInfo); |
210 | this.openInBrowser(menu, menuInfo); | 251 | this.openInBrowser(menu, menuInfo); |
@@ -214,15 +255,16 @@ module.exports = class ContextMenuBuilder { | |||
214 | 255 | ||
215 | /** | 256 | /** |
216 | * Builds a menu applicable to a text field. | 257 | * Builds a menu applicable to a text field. |
217 | * | ||
218 | * @return {Menu} The `Menu` | ||
219 | */ | 258 | */ |
220 | buildMenuForText(menuInfo) { | 259 | buildMenuForText( |
260 | menuInfo: Electron.ContextMenuParams, | ||
261 | ): Electron.CrossProcessExports.Menu { | ||
221 | const menu = new Menu(); | 262 | const menu = new Menu(); |
222 | 263 | ||
223 | this.addSearchItems(menu, menuInfo); | 264 | this.addSearchItems(menu, menuInfo); |
224 | this.addCopy(menu, menuInfo); | 265 | this.addCopy(menu, menuInfo); |
225 | this.addInspectElement(menu, menuInfo); | 266 | this.addInspectElement(menu, menuInfo); |
267 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
226 | this.processMenu(menu, menuInfo); | 268 | this.processMenu(menu, menuInfo); |
227 | 269 | ||
228 | this.addSeparator(menu); | 270 | this.addSeparator(menu); |
@@ -240,13 +282,16 @@ module.exports = class ContextMenuBuilder { | |||
240 | * | 282 | * |
241 | * @return {Menu} The `Menu` | 283 | * @return {Menu} The `Menu` |
242 | */ | 284 | */ |
243 | buildMenuForImage(menuInfo) { | 285 | buildMenuForImage( |
286 | menuInfo: Electron.ContextMenuParams, | ||
287 | ): Electron.CrossProcessExports.Menu { | ||
244 | const menu = new Menu(); | 288 | const menu = new Menu(); |
245 | 289 | ||
246 | if (this.isSrcUrlValid(menuInfo)) { | 290 | if (this.isSrcUrlValid(menuInfo)) { |
247 | this.addImageItems(menu, menuInfo); | 291 | this.addImageItems(menu, menuInfo); |
248 | } | 292 | } |
249 | this.addInspectElement(menu, menuInfo); | 293 | this.addInspectElement(menu, menuInfo); |
294 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
250 | this.processMenu(menu, menuInfo); | 295 | this.processMenu(menu, menuInfo); |
251 | 296 | ||
252 | return menu; | 297 | return menu; |
@@ -256,7 +301,10 @@ module.exports = class ContextMenuBuilder { | |||
256 | * Checks if the current text selection contains a single misspelled word and | 301 | * Checks if the current text selection contains a single misspelled word and |
257 | * if so, adds suggested spellings as individual menu items. | 302 | * if so, adds suggested spellings as individual menu items. |
258 | */ | 303 | */ |
259 | addSpellingItems(menu, menuInfo) { | 304 | addSpellingItems( |
305 | menu: Electron.CrossProcessExports.Menu, | ||
306 | menuInfo: Electron.ContextMenuParams, | ||
307 | ) { | ||
260 | const webContents = this.getWebContents(); | 308 | const webContents = this.getWebContents(); |
261 | // Add each spelling suggestion | 309 | // Add each spelling suggestion |
262 | for (const suggestion of menuInfo.dictionarySuggestions) { | 310 | for (const suggestion of menuInfo.dictionarySuggestions) { |
@@ -288,7 +336,10 @@ module.exports = class ContextMenuBuilder { | |||
288 | /** | 336 | /** |
289 | * Adds search-related menu items. | 337 | * Adds search-related menu items. |
290 | */ | 338 | */ |
291 | addSearchItems(menu, menuInfo) { | 339 | addSearchItems( |
340 | menu: Electron.CrossProcessExports.Menu, | ||
341 | menuInfo: Electron.ContextMenuParams, | ||
342 | ) { | ||
292 | if (!menuInfo.selectionText || menuInfo.selectionText.length === 0) { | 343 | if (!menuInfo.selectionText || menuInfo.selectionText.length === 0) { |
293 | return menu; | 344 | return menu; |
294 | } | 345 | } |
@@ -313,9 +364,11 @@ module.exports = class ContextMenuBuilder { | |||
313 | 364 | ||
314 | const search = new MenuItem({ | 365 | const search = new MenuItem({ |
315 | label: this.stringTable.searchWith({ | 366 | label: this.stringTable.searchWith({ |
367 | // @ts-expect-error Property 'searchEngine' does not exist on type 'ContextMenuParams'. | ||
316 | searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine], | 368 | searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine], |
317 | }), | 369 | }), |
318 | click: () => { | 370 | click: () => { |
371 | // @ts-expect-error Property 'searchEngine' does not exist on type 'ContextMenuParams'. | ||
319 | const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({ | 372 | const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({ |
320 | searchTerm: encodeURIComponent(menuInfo.selectionText), | 373 | searchTerm: encodeURIComponent(menuInfo.selectionText), |
321 | }); | 374 | }); |
@@ -329,22 +382,28 @@ module.exports = class ContextMenuBuilder { | |||
329 | return menu; | 382 | return menu; |
330 | } | 383 | } |
331 | 384 | ||
332 | isSrcUrlValid(menuInfo) { | 385 | isSrcUrlValid(menuInfo: Electron.ContextMenuParams) { |
333 | return menuInfo.srcURL && menuInfo.srcURL.length > 0; | 386 | return menuInfo.srcURL && menuInfo.srcURL.length > 0; |
334 | } | 387 | } |
335 | 388 | ||
336 | /** | 389 | /** |
337 | * Adds "Copy Image" and "Copy Image URL" items when `src` is valid. | 390 | * Adds "Copy Image" and "Copy Image URL" items when `src` is valid. |
338 | */ | 391 | */ |
339 | addImageItems(menu, menuInfo) { | 392 | addImageItems( |
393 | menu: Electron.CrossProcessExports.Menu, | ||
394 | menuInfo: Electron.ContextMenuParams, | ||
395 | ) { | ||
340 | const copyImage = new MenuItem({ | 396 | const copyImage = new MenuItem({ |
341 | label: this.stringTable.copyImage(), | 397 | label: this.stringTable.copyImage(), |
342 | click: () => { | 398 | click: () => { |
343 | const result = this.convertImageToBase64(menuInfo.srcURL, dataURL => | 399 | const result = this.convertImageToBase64( |
344 | clipboard.writeImage(nativeImage.createFromDataURL(dataURL)), | 400 | menuInfo.srcURL, |
401 | (dataURL: string) => | ||
402 | clipboard.writeImage(nativeImage.createFromDataURL(dataURL)), | ||
345 | ); | 403 | ); |
346 | 404 | ||
347 | this._sendNotificationOnClipboardEvent( | 405 | this._sendNotificationOnClipboardEvent( |
406 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
348 | menuInfo.clipboardNotifications, | 407 | menuInfo.clipboardNotifications, |
349 | () => `Image copied from URL: ${menuInfo.srcURL}`, | 408 | () => `Image copied from URL: ${menuInfo.srcURL}`, |
350 | ); | 409 | ); |
@@ -359,6 +418,7 @@ module.exports = class ContextMenuBuilder { | |||
359 | click: () => { | 418 | click: () => { |
360 | const result = clipboard.writeText(menuInfo.srcURL); | 419 | const result = clipboard.writeText(menuInfo.srcURL); |
361 | this._sendNotificationOnClipboardEvent( | 420 | this._sendNotificationOnClipboardEvent( |
421 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
362 | menuInfo.clipboardNotifications, | 422 | menuInfo.clipboardNotifications, |
363 | () => `Image URL copied: ${menuInfo.srcURL}`, | 423 | () => `Image URL copied: ${menuInfo.srcURL}`, |
364 | ); | 424 | ); |
@@ -374,7 +434,7 @@ module.exports = class ContextMenuBuilder { | |||
374 | label: this.stringTable.downloadImage(), | 434 | label: this.stringTable.downloadImage(), |
375 | click: () => { | 435 | click: () => { |
376 | const urlWithoutBlob = menuInfo.srcURL.slice(5); | 436 | const urlWithoutBlob = menuInfo.srcURL.slice(5); |
377 | this.convertImageToBase64(menuInfo.srcURL, dataURL => { | 437 | this.convertImageToBase64(menuInfo.srcURL, (dataURL: any) => { |
378 | const url = new window.URL(urlWithoutBlob); | 438 | const url = new window.URL(urlWithoutBlob); |
379 | const fileName = url.pathname.slice(1); | 439 | const fileName = url.pathname.slice(1); |
380 | ipcRenderer.send('download-file', { | 440 | ipcRenderer.send('download-file', { |
@@ -386,6 +446,7 @@ module.exports = class ContextMenuBuilder { | |||
386 | }); | 446 | }); |
387 | }); | 447 | }); |
388 | this._sendNotificationOnClipboardEvent( | 448 | this._sendNotificationOnClipboardEvent( |
449 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
389 | menuInfo.clipboardNotifications, | 450 | menuInfo.clipboardNotifications, |
390 | () => `Image downloaded: ${urlWithoutBlob}`, | 451 | () => `Image downloaded: ${urlWithoutBlob}`, |
391 | ); | 452 | ); |
@@ -401,7 +462,10 @@ module.exports = class ContextMenuBuilder { | |||
401 | /** | 462 | /** |
402 | * Adds the Cut menu item | 463 | * Adds the Cut menu item |
403 | */ | 464 | */ |
404 | addCut(menu, menuInfo) { | 465 | addCut( |
466 | menu: Electron.CrossProcessExports.Menu, | ||
467 | menuInfo: Electron.ContextMenuParams, | ||
468 | ) { | ||
405 | const webContents = this.getWebContents(); | 469 | const webContents = this.getWebContents(); |
406 | menu.append( | 470 | menu.append( |
407 | new MenuItem({ | 471 | new MenuItem({ |
@@ -418,7 +482,10 @@ module.exports = class ContextMenuBuilder { | |||
418 | /** | 482 | /** |
419 | * Adds the Copy menu item. | 483 | * Adds the Copy menu item. |
420 | */ | 484 | */ |
421 | addCopy(menu, menuInfo) { | 485 | addCopy( |
486 | menu: Electron.CrossProcessExports.Menu, | ||
487 | menuInfo: Electron.ContextMenuParams, | ||
488 | ) { | ||
422 | const webContents = this.getWebContents(); | 489 | const webContents = this.getWebContents(); |
423 | menu.append( | 490 | menu.append( |
424 | new MenuItem({ | 491 | new MenuItem({ |
@@ -435,7 +502,10 @@ module.exports = class ContextMenuBuilder { | |||
435 | /** | 502 | /** |
436 | * Adds the Paste menu item. | 503 | * Adds the Paste menu item. |
437 | */ | 504 | */ |
438 | addPaste(menu, menuInfo) { | 505 | addPaste( |
506 | menu: Electron.CrossProcessExports.Menu, | ||
507 | menuInfo: Electron.ContextMenuParams, | ||
508 | ) { | ||
439 | const webContents = this.getWebContents(); | 509 | const webContents = this.getWebContents(); |
440 | menu.append( | 510 | menu.append( |
441 | new MenuItem({ | 511 | new MenuItem({ |
@@ -449,7 +519,10 @@ module.exports = class ContextMenuBuilder { | |||
449 | return menu; | 519 | return menu; |
450 | } | 520 | } |
451 | 521 | ||
452 | addPastePlain(menu, menuInfo) { | 522 | addPastePlain( |
523 | menu: Electron.CrossProcessExports.Menu, | ||
524 | menuInfo: Electron.ContextMenuParams, | ||
525 | ) { | ||
453 | if ( | 526 | if ( |
454 | menuInfo.editFlags.canPaste && | 527 | menuInfo.editFlags.canPaste && |
455 | !menuInfo.linkText && | 528 | !menuInfo.linkText && |
@@ -469,7 +542,7 @@ module.exports = class ContextMenuBuilder { | |||
469 | /** | 542 | /** |
470 | * Adds a separator item. | 543 | * Adds a separator item. |
471 | */ | 544 | */ |
472 | addSeparator(menu) { | 545 | addSeparator(menu: Electron.CrossProcessExports.Menu) { |
473 | menu.append(new MenuItem({ type: 'separator' })); | 546 | menu.append(new MenuItem({ type: 'separator' })); |
474 | return menu; | 547 | return menu; |
475 | } | 548 | } |
@@ -477,7 +550,11 @@ module.exports = class ContextMenuBuilder { | |||
477 | /** | 550 | /** |
478 | * Adds the "Inspect Element" menu item. | 551 | * Adds the "Inspect Element" menu item. |
479 | */ | 552 | */ |
480 | addInspectElement(menu, menuInfo, needsSeparator = true) { | 553 | addInspectElement( |
554 | menu: Electron.CrossProcessExports.Menu, | ||
555 | menuInfo: Electron.ContextMenuParams, | ||
556 | needsSeparator = true, | ||
557 | ) { | ||
481 | const webContents = this.getWebContents(); | 558 | const webContents = this.getWebContents(); |
482 | if (!this.debugMode) return menu; | 559 | if (!this.debugMode) return menu; |
483 | if (needsSeparator) this.addSeparator(menu); | 560 | if (needsSeparator) this.addSeparator(menu); |
@@ -498,21 +575,30 @@ module.exports = class ContextMenuBuilder { | |||
498 | * @param {Function} callback A callback that will be invoked with the result | 575 | * @param {Function} callback A callback that will be invoked with the result |
499 | * @param {String} outputFormat The image format to use, defaults to 'image/png' | 576 | * @param {String} outputFormat The image format to use, defaults to 'image/png' |
500 | */ | 577 | */ |
501 | convertImageToBase64(url, callback, outputFormat = 'image/png') { | 578 | convertImageToBase64( |
502 | let canvas = document.createElement('canvas'); | 579 | url: string, |
580 | callback: { | ||
581 | (dataURL: any): void; | ||
582 | (dataURL: any): void; | ||
583 | (arg0: string): void; | ||
584 | }, | ||
585 | outputFormat: string = 'image/png', | ||
586 | ) { | ||
587 | let canvas: HTMLCanvasElement | null = document.createElement('canvas'); | ||
503 | const ctx = canvas.getContext('2d'); | 588 | const ctx = canvas.getContext('2d'); |
504 | // eslint-disable-next-line no-undef | ||
505 | const img = new Image(); | 589 | const img = new Image(); |
506 | img.crossOrigin = 'Anonymous'; | 590 | img.crossOrigin = 'Anonymous'; |
507 | 591 | ||
508 | img.addEventListener('load', () => { | 592 | img.addEventListener('load', () => { |
509 | canvas.height = img.height; | 593 | if (canvas) { |
510 | canvas.width = img.width; | 594 | canvas.height = img.height; |
511 | ctx?.drawImage(img, 0, 0); | 595 | canvas.width = img.width; |
512 | 596 | ctx?.drawImage(img, 0, 0); | |
513 | const dataURL = canvas.toDataURL(outputFormat); | 597 | |
514 | canvas = null; | 598 | const dataURL = canvas.toDataURL(outputFormat); |
515 | callback(dataURL); | 599 | canvas = null; |
600 | callback(dataURL); | ||
601 | } | ||
516 | }); | 602 | }); |
517 | 603 | ||
518 | img.src = url; | 604 | img.src = url; |
@@ -521,7 +607,7 @@ module.exports = class ContextMenuBuilder { | |||
521 | /** | 607 | /** |
522 | * Adds the 'go back' menu item | 608 | * Adds the 'go back' menu item |
523 | */ | 609 | */ |
524 | goBack(menu) { | 610 | goBack(menu: Electron.CrossProcessExports.Menu) { |
525 | const webContents = this.getWebContents(); | 611 | const webContents = this.getWebContents(); |
526 | menu.append( | 612 | menu.append( |
527 | new MenuItem({ | 613 | new MenuItem({ |
@@ -538,7 +624,7 @@ module.exports = class ContextMenuBuilder { | |||
538 | /** | 624 | /** |
539 | * Adds the 'go forward' menu item | 625 | * Adds the 'go forward' menu item |
540 | */ | 626 | */ |
541 | goForward(menu) { | 627 | goForward(menu: Electron.CrossProcessExports.Menu) { |
542 | const webContents = this.getWebContents(); | 628 | const webContents = this.getWebContents(); |
543 | menu.append( | 629 | menu.append( |
544 | new MenuItem({ | 630 | new MenuItem({ |
@@ -555,7 +641,10 @@ module.exports = class ContextMenuBuilder { | |||
555 | /** | 641 | /** |
556 | * Adds the 'copy page url' menu item. | 642 | * Adds the 'copy page url' menu item. |
557 | */ | 643 | */ |
558 | copyPageUrl(menu, menuInfo) { | 644 | copyPageUrl( |
645 | menu: Electron.CrossProcessExports.Menu, | ||
646 | menuInfo: Electron.ContextMenuParams, | ||
647 | ) { | ||
559 | menu.append( | 648 | menu.append( |
560 | new MenuItem({ | 649 | new MenuItem({ |
561 | label: this.stringTable.copyPageUrl(), | 650 | label: this.stringTable.copyPageUrl(), |
@@ -563,7 +652,8 @@ module.exports = class ContextMenuBuilder { | |||
563 | click: () => { | 652 | click: () => { |
564 | clipboard.writeText(window.location.href); | 653 | clipboard.writeText(window.location.href); |
565 | this._sendNotificationOnClipboardEvent( | 654 | this._sendNotificationOnClipboardEvent( |
566 | menuInfo.clipboardNotifications, | 655 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. |
656 | menuInfo?.clipboardNotifications, | ||
567 | () => `Page URL copied: ${window.location.href}`, | 657 | () => `Page URL copied: ${window.location.href}`, |
568 | ); | 658 | ); |
569 | }, | 659 | }, |
@@ -576,8 +666,11 @@ module.exports = class ContextMenuBuilder { | |||
576 | /** | 666 | /** |
577 | * Adds the 'go to home' menu item. | 667 | * Adds the 'go to home' menu item. |
578 | */ | 668 | */ |
579 | goToHomePage(menu, menuInfo) { | 669 | goToHomePage( |
580 | const baseURL = new URL(menuInfo.pageURL); | 670 | menu: Electron.CrossProcessExports.Menu, |
671 | menuInfo: Electron.ContextMenuParams, | ||
672 | ) { | ||
673 | const baseURL = new window.URL(menuInfo.pageURL); | ||
581 | menu.append( | 674 | menu.append( |
582 | new MenuItem({ | 675 | new MenuItem({ |
583 | label: this.stringTable.goToHomePage(), | 676 | label: this.stringTable.goToHomePage(), |
@@ -596,7 +689,10 @@ module.exports = class ContextMenuBuilder { | |||
596 | /** | 689 | /** |
597 | * Adds the 'open in browser' menu item. | 690 | * Adds the 'open in browser' menu item. |
598 | */ | 691 | */ |
599 | openInBrowser(menu, menuInfo) { | 692 | openInBrowser( |
693 | menu: Electron.CrossProcessExports.Menu, | ||
694 | menuInfo: Electron.ContextMenuParams, | ||
695 | ) { | ||
600 | menu.append( | 696 | menu.append( |
601 | new MenuItem({ | 697 | new MenuItem({ |
602 | label: this.stringTable.openInBrowser(), | 698 | label: this.stringTable.openInBrowser(), |
@@ -610,7 +706,10 @@ module.exports = class ContextMenuBuilder { | |||
610 | return menu; | 706 | return menu; |
611 | } | 707 | } |
612 | 708 | ||
613 | _sendNotificationOnClipboardEvent(isDisabled, notificationText) { | 709 | _sendNotificationOnClipboardEvent( |
710 | isDisabled: boolean, | ||
711 | notificationText: () => string, | ||
712 | ) { | ||
614 | if (isDisabled) { | 713 | if (isDisabled) { |
615 | return; | 714 | return; |
616 | } | 715 | } |
@@ -619,4 +718,4 @@ module.exports = class ContextMenuBuilder { | |||
619 | body: notificationText(), | 718 | body: notificationText(), |
620 | }); | 719 | }); |
621 | } | 720 | } |
622 | }; | 721 | } |
diff --git a/src/webview/darkmode/custom.js b/src/webview/darkmode/custom.ts index f767f5755..f767f5755 100644 --- a/src/webview/darkmode/custom.js +++ b/src/webview/darkmode/custom.ts | |||
diff --git a/src/webview/darkmode/ignore.js b/src/webview/darkmode/ignore.js deleted file mode 100644 index 110df364f..000000000 --- a/src/webview/darkmode/ignore.js +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | export default [ | ||
2 | 'discordapp.com', | ||
3 | ]; | ||
diff --git a/src/webview/darkmode/ignore.ts b/src/webview/darkmode/ignore.ts new file mode 100644 index 000000000..daa25d10c --- /dev/null +++ b/src/webview/darkmode/ignore.ts | |||
@@ -0,0 +1 @@ | |||
export default ['discordapp.com']; | |||
diff --git a/src/webview/find.js b/src/webview/find.ts index 040811d68..0665d9670 100644 --- a/src/webview/find.js +++ b/src/webview/find.ts | |||
@@ -3,11 +3,15 @@ import { FindInPage as ElectronFindInPage } from 'electron-find'; | |||
3 | 3 | ||
4 | // Shim to expose webContents functionality to electron-find without @electron/remote | 4 | // Shim to expose webContents functionality to electron-find without @electron/remote |
5 | const webContentsShim = { | 5 | const webContentsShim = { |
6 | findInPage: (text, options = {}) => ipcRenderer.sendSync('find-in-page', text, options), | 6 | findInPage: (text: string, options = {}) => |
7 | stopFindInPage: (action) => { | 7 | ipcRenderer.sendSync('find-in-page', text, options), |
8 | stopFindInPage: (action: any) => { | ||
8 | ipcRenderer.sendSync('stop-find-in-page', action); | 9 | ipcRenderer.sendSync('stop-find-in-page', action); |
9 | }, | 10 | }, |
10 | on: (eventName, listener) => { | 11 | on: ( |
12 | eventName: string, | ||
13 | listener: (arg0: { sender: undefined }, arg1: any) => void, | ||
14 | ): void => { | ||
11 | if (eventName === 'found-in-page') { | 15 | if (eventName === 'found-in-page') { |
12 | ipcRenderer.on('found-in-page', (_, result) => { | 16 | ipcRenderer.on('found-in-page', (_, result) => { |
13 | listener({ sender: this }, result); | 17 | listener({ sender: this }, result); |
diff --git a/src/webview/notifications.js b/src/webview/notifications.ts index 22960d818..73124b9a9 100644 --- a/src/webview/notifications.js +++ b/src/webview/notifications.ts | |||
@@ -5,9 +5,10 @@ import { v1 as uuidV1 } from 'uuid'; | |||
5 | const debug = require('debug')('Ferdi:Notifications'); | 5 | const debug = require('debug')('Ferdi:Notifications'); |
6 | 6 | ||
7 | export class NotificationsHandler { | 7 | export class NotificationsHandler { |
8 | onNotify = data => data; | 8 | onNotify = (data: { title: string; options: any; notificationId: string }) => |
9 | data; | ||
9 | 10 | ||
10 | displayNotification(title, options) { | 11 | displayNotification(title: string, options: any) { |
11 | return new Promise(resolve => { | 12 | return new Promise(resolve => { |
12 | debug('New notification', title, options); | 13 | debug('New notification', title, options); |
13 | 14 | ||
@@ -23,7 +24,7 @@ export class NotificationsHandler { | |||
23 | ); | 24 | ); |
24 | 25 | ||
25 | ipcRenderer.once(`notification-onclick:${notificationId}`, () => { | 26 | ipcRenderer.once(`notification-onclick:${notificationId}`, () => { |
26 | resolve(); | 27 | resolve(true); |
27 | }); | 28 | }); |
28 | }); | 29 | }); |
29 | } | 30 | } |
diff --git a/src/webview/screenshare.js b/src/webview/screenshare.ts index e7e43c04e..91a1623bb 100644 --- a/src/webview/screenshare.js +++ b/src/webview/screenshare.ts | |||
@@ -9,8 +9,8 @@ export async function getDisplayMediaSelector() { | |||
9 | return `<div class="desktop-capturer-selection__scroller"> | 9 | return `<div class="desktop-capturer-selection__scroller"> |
10 | <ul class="desktop-capturer-selection__list"> | 10 | <ul class="desktop-capturer-selection__list"> |
11 | ${sources | 11 | ${sources |
12 | .map( | 12 | .map( |
13 | ({ id, name, thumbnail }) => ` | 13 | ({ id, name, thumbnail }) => ` |
14 | <li class="desktop-capturer-selection__item"> | 14 | <li class="desktop-capturer-selection__item"> |
15 | <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> | 15 | <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> |
16 | <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> | 16 | <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> |
@@ -18,8 +18,8 @@ export async function getDisplayMediaSelector() { | |||
18 | </button> | 18 | </button> |
19 | </li> | 19 | </li> |
20 | `, | 20 | `, |
21 | ) | 21 | ) |
22 | .join('')} | 22 | .join('')} |
23 | <li class="desktop-capturer-selection__item"> | 23 | <li class="desktop-capturer-selection__item"> |
24 | <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> | 24 | <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> |
25 | <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> | 25 | <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> |