diff --git a/configs/webpack.config.renderer.ts b/configs/webpack.config.renderer.ts index e4ffd64e..699ae020 100644 --- a/configs/webpack.config.renderer.ts +++ b/configs/webpack.config.renderer.ts @@ -24,6 +24,7 @@ export const config: webpack.Configuration = { new HtmlWebpackPlugin({ template: join(__dirname, '../public/renderer.html'), filename: 'renderer.html', + title: process.env.npm_package_build_productName, }), ], }; diff --git a/public/renderer.html b/public/renderer.html index 19409160..1604859f 100644 --- a/public/renderer.html +++ b/public/renderer.html @@ -4,7 +4,7 @@ - Breach Protocol Autosolver + <%= htmlWebpackPlugin.options.title %>
diff --git a/src/client-electron/common.ts b/src/client-electron/common.ts index 7a46c116..4a914763 100644 --- a/src/client-electron/common.ts +++ b/src/client-electron/common.ts @@ -62,6 +62,7 @@ export interface AppSettings { experimentalBufferSizeRecognition: boolean; format: 'png' | 'jpg'; activeDisplayId: string; + minimizeToTray: boolean; } export interface AppStats { diff --git a/src/client-electron/main/main.ts b/src/client-electron/main/main.ts index 1e642952..7895b9b9 100644 --- a/src/client-electron/main/main.ts +++ b/src/client-electron/main/main.ts @@ -5,9 +5,11 @@ import { ipcMain as ipc, Menu, shell, + Tray, } from 'electron'; import { copyFileSync, ensureDirSync, remove, writeJSONSync } from 'fs-extra'; import { extname, join } from 'path'; +import icon from '../renderer/assets/icon.png'; import { Store } from './store/store'; import { createBrowserWindows } from './windows'; @@ -37,6 +39,24 @@ export class Main { }, ]; + private trayMenu: Electron.MenuItemConstructorOptions[] = [ + { + label: 'Show', + click: () => { + this.renderer.show(); + }, + }, + { type: 'separator' }, + { + label: 'Exit', + click: () => { + this.renderer.close(); + }, + }, + ]; + + tray: Electron.Tray; + init() { const { worker, renderer } = createBrowserWindows(); this.store = new Store(worker.webContents, renderer.webContents); @@ -47,6 +67,19 @@ export class Main { this.registerListeners(); } + private createTray() { + const tray = new Tray(join(__dirname, icon)); + const contextMenu = Menu.buildFromTemplate(this.trayMenu); + + tray.on('double-click', () => { + this.renderer.show(); + }); + tray.setToolTip(process.env.npm_package_build_productName); + tray.setContextMenu(contextMenu); + + return tray; + } + private registerListeners() { ipc.on('renderer:close', this.onAppClose.bind(this)); ipc.on('renderer:minimize', this.onAppMinimize.bind(this)); @@ -60,10 +93,41 @@ export class Main { this.renderer.once('ready-to-show', () => this.renderer.show()); this.renderer.once('closed', this.onRendererClosed.bind(this)); + this.renderer.on('minimize', this.onRendererMinimize.bind(this)); + this.renderer.on('restore', this.onRendererRestore.bind(this)); + } + + private onRendererMinimize(event: Electron.Event) { + if (!this.getSettings().minimizeToTray) { + return; + } + + event.preventDefault(); + + this.tray = this.createTray(); + + this.renderer.setSkipTaskbar(true); + this.renderer.hide(); + } + + private onRendererRestore() { + if (!this.getSettings().minimizeToTray) { + return; + } + + this.renderer.show(); + this.renderer.setSkipTaskbar(false); + + this.tray.destroy(); } private removeAllListeners() { ipc.removeAllListeners(); + + this.renderer.removeAllListeners(); + this.worker.removeAllListeners(); + + globalShortcut.unregisterAll(); } private registerKeyBind(keyBind: Electron.Accelerator) { @@ -71,7 +135,7 @@ export class Main { } private onWorkerReady() { - const { keyBind } = this.store.getState().settings; + const { keyBind } = this.getSettings(); this.registerKeyBind(keyBind); } @@ -132,8 +196,6 @@ export class Main { this.removeAllListeners(); - globalShortcut.unregisterAll(); - app.quit(); } @@ -152,4 +214,8 @@ export class Main { this.renderer.maximize(); } } + + private getSettings() { + return this.store.getState().settings; + } } diff --git a/src/client-electron/main/windows.ts b/src/client-electron/main/windows.ts index 62d342a6..77b77518 100644 --- a/src/client-electron/main/windows.ts +++ b/src/client-electron/main/windows.ts @@ -1,6 +1,7 @@ import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron'; import isWsl from 'is-wsl'; import { join } from 'path'; +import icon from '../renderer/assets/icon.png'; const isDev = process.env.NODE_ENV === 'development'; @@ -27,6 +28,7 @@ const rendererOptions: BrowserWindowConstructorOptions = { minHeight: 720, // Maximize and drag does not work on wsl2. frame: isDev && isWsl ? true : false, + icon: join(__dirname, icon), autoHideMenuBar: true, webPreferences: { nodeIntegration: true, diff --git a/src/client-electron/options.ts b/src/client-electron/options.ts index 2a8b6b87..15d19bbd 100644 --- a/src/client-electron/options.ts +++ b/src/client-electron/options.ts @@ -104,6 +104,11 @@ const options = [ 'Use Windows scaling to calculate coordinates of squares.', false ), + new BreachProtocolOption( + 'minimizeToTray', + 'Minimize app to system tray.', + true + ), ]; function reduceOptions( diff --git a/src/client-electron/renderer/assets/icon.png b/src/client-electron/renderer/assets/icon.png new file mode 100755 index 00000000..29c948ad Binary files /dev/null and b/src/client-electron/renderer/assets/icon.png differ diff --git a/src/client-electron/renderer/pages/Settings.tsx b/src/client-electron/renderer/pages/Settings.tsx index 8be41d19..8311db22 100644 --- a/src/client-electron/renderer/pages/Settings.tsx +++ b/src/client-electron/renderer/pages/Settings.tsx @@ -80,6 +80,10 @@ const GeneralSettings = () => { return (
+ + + + diff --git a/types/fonts/index.d.ts b/types/assets/index.d.ts similarity index 50% rename from types/fonts/index.d.ts rename to types/assets/index.d.ts index 1764f41e..6d48a89c 100644 --- a/types/fonts/index.d.ts +++ b/types/assets/index.d.ts @@ -1 +1,2 @@ declare module '*.ttf'; +declare module '*.png';