diff --git a/config/webpack.config.main.base.ts b/config/webpack.config.main.base.ts index 7bff5e9f3..43c4b0922 100644 --- a/config/webpack.config.main.base.ts +++ b/config/webpack.config.main.base.ts @@ -1,6 +1,7 @@ import path from 'node:path'; import type webpack from 'webpack'; import { merge } from 'webpack-merge'; + import baseConfig from './webpack.config.common'; import webpackPaths from './webpack.paths'; diff --git a/config/webpack.config.main.prod.ts b/config/webpack.config.main.prod.ts index d099bd13d..3ebfe192f 100644 --- a/config/webpack.config.main.prod.ts +++ b/config/webpack.config.main.prod.ts @@ -1,6 +1,7 @@ import TerserPlugin from 'terser-webpack-plugin'; import type webpack from 'webpack'; import { merge } from 'webpack-merge'; + import baseConfig from './webpack.config.main.base'; const configuration: webpack.Configuration = { diff --git a/config/webpack.config.preload.base.ts b/config/webpack.config.preload.base.ts new file mode 100644 index 000000000..44a5a71f3 --- /dev/null +++ b/config/webpack.config.preload.base.ts @@ -0,0 +1,26 @@ +import path from 'node:path'; +import type webpack from 'webpack'; +import { merge } from 'webpack-merge'; + +import baseConfig from './webpack.config.common'; +import webpackPaths from './webpack.paths'; + +const configuration: webpack.Configuration = { + devtool: 'inline-source-map', + + mode: 'development', + + target: 'electron-preload', + + entry: [path.join(webpackPaths.srcMainPath, 'preload.ts')], + + output: { + path: webpackPaths.buildPath, + filename: 'preload.js', + library: { + type: 'umd', + }, + }, +}; + +export default merge(baseConfig, configuration); diff --git a/config/webpack.config.preload.prod.ts b/config/webpack.config.preload.prod.ts new file mode 100644 index 000000000..60b91419d --- /dev/null +++ b/config/webpack.config.preload.prod.ts @@ -0,0 +1,18 @@ +import TerserPlugin from 'terser-webpack-plugin'; +import type webpack from 'webpack'; +import { merge } from 'webpack-merge'; + +import baseConfig from './webpack.config.preload.base'; + +const configuration: webpack.Configuration = { + devtool: 'source-map', + + mode: 'production', + + optimization: { + minimize: true, + minimizer: [new TerserPlugin()], + }, +}; + +export default merge(baseConfig, configuration); diff --git a/config/webpack.config.renderer.base.ts b/config/webpack.config.renderer.base.ts index 384e22993..ec20cc9de 100644 --- a/config/webpack.config.renderer.base.ts +++ b/config/webpack.config.renderer.base.ts @@ -4,6 +4,7 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import webpack from 'webpack'; import { merge } from 'webpack-merge'; + import baseConfig from './webpack.config.common'; import webpackPaths from './webpack.paths'; @@ -14,7 +15,7 @@ const configuration: webpack.Configuration = { mode: 'development', - target: 'electron-renderer', + target: ['web', 'electron-renderer'], entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')], diff --git a/config/webpack.config.renderer.prod.ts b/config/webpack.config.renderer.prod.ts index 819e1aa5e..babaaf36a 100644 --- a/config/webpack.config.renderer.prod.ts +++ b/config/webpack.config.renderer.prod.ts @@ -2,6 +2,7 @@ import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; import TerserPlugin from 'terser-webpack-plugin'; import type webpack from 'webpack'; import { merge } from 'webpack-merge'; + import baseConfig from './webpack.config.renderer.base'; const configuration: webpack.Configuration = { diff --git a/package.json b/package.json index 27fa036b7..131d2c271 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,13 @@ "main": "build/main.js", "scripts": { "clean": "rimraf build coverage dist node_modules", - "build": "concurrently --names \"main,renderer\" --prefix-colors \"blue,green\" \"pnpm build:main\" \"pnpm build:renderer\"", + "build": "concurrently --names \"main,preload,renderer\" --prefix-colors \"blue,magenta,green\" \"pnpm build:main\" \"pnpm build:preload\" \"pnpm build:renderer\"", "build:main": "webpack --config ./config/webpack.config.main.prod.ts", + "build:preload": "webpack --config ./config/webpack.config.preload.prod.ts", "build:renderer": "webpack --config ./config/webpack.config.renderer.prod.ts", - "watch": "concurrently --names \"main,renderer\" --prefix-colors \"blue,green\" \"pnpm watch:main\" \"pnpm watch:renderer\"", + "watch": "concurrently --names \"main,preload,renderer\" --prefix-colors \"blue,magenta,green\" \"pnpm watch:main\" \"pnpm watch:preload\" \"pnpm watch:renderer\"", "watch:main": "webpack --watch --config ./config/webpack.config.main.base.ts", + "watch:preload": "webpack --watch --config ./config/webpack.config.preload.base.ts", "watch:renderer": "webpack --watch --config ./config/webpack.config.renderer.base.ts", "prepare:remove-source-maps": "ts-node ./scripts/delete-source-maps.ts", "package:linux": "electron-builder --linux", diff --git a/src/main/main.ts b/src/main/main.ts index 9336e2c8d..c45eb2da2 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { app, globalShortcut, ipcMain as ipc, safeStorage } from 'electron'; import log from 'electron-log'; import { menubar } from 'menubar'; @@ -20,10 +21,11 @@ const browserWindowOpts = { minHeight: 400, resizable: false, skipTaskbar: true, // Hide the app from the Windows taskbar - // TODO #700 refactor to use preload script with a context bridge webPreferences: { - nodeIntegration: true, - contextIsolation: false, + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + enableRemoteModule: false, + nodeIntegration: false, }, }; @@ -125,7 +127,10 @@ app.whenReady().then(async () => { ipc.on(namespacedEvent('window-hide'), () => mb.hideWindow()); - ipc.on(namespacedEvent('quit'), () => mb.app.quit()); + ipc.on(namespacedEvent('quit'), () => { + console.log('MAIN DEBUGGING - quit app event'); + mb.app.quit(); + }); ipc.on( namespacedEvent('use-alternate-idle-icon'), diff --git a/src/main/preload.ts b/src/main/preload.ts new file mode 100644 index 000000000..c0e60ed29 --- /dev/null +++ b/src/main/preload.ts @@ -0,0 +1,85 @@ +import { contextBridge, ipcRenderer, shell } from 'electron'; +import { namespacedEvent } from '../shared/events'; + +import { type Link, OpenPreference } from '../renderer/types'; +import { Constants } from '../renderer/utils/constants'; +import { isLinux, isMacOS, isWindows } from '../shared/platform'; + +const api = { + openExternalLink: (url: Link, openPreference: OpenPreference) => { + console.log('PRELOAD OPEN LINK'); + + shell.openExternal(url, { + activate: openPreference === OpenPreference.FOREGROUND, + }); + }, + + getAppVersion: () => { + if (process.env.NODE_ENV === 'development') { + return 'dev'; + } + + ipcRenderer.invoke(namespacedEvent('version')); + }, + + encryptValue: (value: string) => + ipcRenderer.invoke(namespacedEvent('safe-storage-encrypt'), value), + + decryptValue: (value: string) => + ipcRenderer.invoke(namespacedEvent('safe-storage-decrypt'), value), + + quitApp: () => ipcRenderer.send(namespacedEvent('quit')), + + showWindow: () => ipcRenderer.send(namespacedEvent('window-show')), + + hideWindow: () => ipcRenderer.send(namespacedEvent('window-hide')), + + setAutoLaunch: (value: boolean) => + ipcRenderer.send(namespacedEvent('update-auto-launch'), { + openAtLogin: value, + openAsHidden: value, + }), + + setAlternateIdleIcon: (value: boolean) => + ipcRenderer.send(namespacedEvent('use-alternate-idle-icon'), value), + + setKeyboardShortcut: (keyboardShortcut: boolean) => { + ipcRenderer.send(namespacedEvent('update-keyboard-shortcut'), { + enabled: keyboardShortcut, + keyboardShortcut: Constants.DEFAULT_KEYBOARD_SHORTCUT, + }); + }, + + updateTrayIcon: (notificationsLength = 0) => { + if (notificationsLength < 0) { + ipcRenderer.send(namespacedEvent('icon-error')); + return; + } + + if (notificationsLength > 0) { + ipcRenderer.send(namespacedEvent('icon-active')); + return; + } + + ipcRenderer.send(namespacedEvent('icon-idle')); + }, + + updateTrayTitle: (title = '') => + ipcRenderer.send(namespacedEvent('update-title'), title), + + isLinux: () => { + return isLinux(); + }, + + isMacOS: () => { + return isMacOS(); + }, + + isWindows: () => { + return isWindows(); + }, +}; + +contextBridge.exposeInMainWorld('gitify', api); + +export type GitifyAPI = typeof api; diff --git a/src/renderer/__mocks__/utils.ts b/src/renderer/__mocks__/utils.ts index 2b832d466..296adddca 100644 --- a/src/renderer/__mocks__/utils.ts +++ b/src/renderer/__mocks__/utils.ts @@ -2,5 +2,5 @@ * Ensure stable snapshots for our randomized emoji use-cases */ export function ensureStableEmojis() { - global.Math.random = jest.fn(() => 0.1); + // global.Math.random = jest.fn(() => 0.1); } diff --git a/src/renderer/components/__snapshots__/AllRead.test.tsx.snap b/src/renderer/components/__snapshots__/AllRead.test.tsx.snap index 851456cc8..e2ed17dc7 100644 --- a/src/renderer/components/__snapshots__/AllRead.test.tsx.snap +++ b/src/renderer/components/__snapshots__/AllRead.test.tsx.snap @@ -34,12 +34,7 @@ exports[`renderer/components/AllRead.tsx should render itself & its children - n