diff --git a/main.js b/main.js index f241f734..1a32afb1 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,8 @@ const { BrowserWindow, WebContentsView, Menu, + Tray, + nativeImage, ipcMain, dialog, powerSaveBlocker, @@ -21,6 +23,7 @@ const configDir = app.getPath("userData"); const dirPath = path.join(configDir, "uploads"); const packageJson = require("./package.json"); let mainWin; +let tray = null; let readerWindow; let readerWindowList = []; let dictWindow; @@ -168,6 +171,42 @@ const isWindowPartiallyVisible = (bounds) => { } return false; }; +const createTray = () => { + const iconPath = isDev + ? path.join(__dirname, "./public/assets/icon.png") + : path.join(__dirname, "./build/assets/icon.png"); + tray = new Tray(nativeImage.createFromPath(iconPath)); + const contextMenu = Menu.buildFromTemplate([ + { + label: "Open Koodo Reader", + click: () => { + if (mainWin) { + mainWin.show(); + mainWin.focus(); + } + }, + }, + { + label: "Quit", + click: () => { + if (tray) { + tray.destroy(); + tray = null; + } + store.set("isMinimizeToTray", "no"); + app.quit(); + }, + }, + ]); + tray.setToolTip("Koodo Reader"); + tray.setContextMenu(contextMenu); + tray.on("click", () => { + if (mainWin) { + mainWin.show(); + mainWin.focus(); + } + }); +}; const createMainWin = () => { const isMainWindVisible = isWindowPartiallyVisible({ width: parseInt(store.get("mainWinWidth") || 1050) / mainWinDisplayScale, @@ -195,7 +234,15 @@ const createMainWin = () => { ? "http://localhost:3000" : `file://${path.join(__dirname, "./build/index.html")}`; mainWin.loadURL(urlLocation); - mainWin.on("close", () => { + mainWin.on("close", (event) => { + if (store.get("isMinimizeToTray") === "yes") { + event.preventDefault(); + mainWin.hide(); + if (!tray) { + createTray(); + } + return; + } if (mainWin && !mainWin.isDestroyed()) { let bounds = mainWin.getBounds(); const currentDisplay = screen.getDisplayMatching(bounds); @@ -783,6 +830,14 @@ const createMainWin = () => { }); return "pong"; }); + ipcMain.handle("toggle-minimize-to-tray", async (event, config) => { + store.set("isMinimizeToTray", config.isMinimizeToTray); + if (config.isMinimizeToTray === "no" && tray) { + tray.destroy(); + tray = null; + } + return "pong"; + }); ipcMain.handle("open-explorer-folder", async (event, config) => { const { shell } = require("electron"); if (config.isFolder) { diff --git a/src/constants/settingList.tsx b/src/constants/settingList.tsx index dc253313..17cd06e0 100644 --- a/src/constants/settingList.tsx +++ b/src/constants/settingList.tsx @@ -46,6 +46,12 @@ export const generalSettingList = [ title: "Automatically launch on system startup", propName: "isAutoLaunch", }, + { + isElectron: true, + title: "Minimize to tray on close", + desc: "When closing the window, the app will minimize to the system tray instead of quitting", + propName: "isMinimizeToTray", + }, { isElectron: true, title: "Open book without adding it to library", diff --git a/src/containers/settings/generalSetting/component.tsx b/src/containers/settings/generalSetting/component.tsx index 1fe8cf18..657184b8 100644 --- a/src/containers/settings/generalSetting/component.tsx +++ b/src/containers/settings/generalSetting/component.tsx @@ -49,6 +49,8 @@ class GeneralSetting extends React.Component< isAutoMaximizeWin: ConfigService.getReaderConfig("isAutoMaximizeWin") === "yes", isAutoLaunch: ConfigService.getReaderConfig("isAutoLaunch") === "yes", + isMinimizeToTray: + ConfigService.getReaderConfig("isMinimizeToTray") === "yes", isOpenInMain: ConfigService.getReaderConfig("isOpenInMain") === "yes", isDisableAI: ConfigService.getReaderConfig("isDisableAI") === "yes", isUseOriginalName: @@ -220,6 +222,13 @@ class GeneralSetting extends React.Component< }); this.handleSetting("isAutoLaunch"); }; + handleMinimizeToTray = () => { + const { ipcRenderer } = window.require("electron"); + ipcRenderer.invoke("toggle-minimize-to-tray", { + isMinimizeToTray: this.state.isMinimizeToTray ? "no" : "yes", + }); + this.handleSetting("isMinimizeToTray"); + }; renderSwitchOption = (optionList: any[]) => { return optionList.map((item) => { return ( @@ -248,6 +257,9 @@ class GeneralSetting extends React.Component< case "isAutoLaunch": this.handleAutoLaunch(); break; + case "isMinimizeToTray": + this.handleMinimizeToTray(); + break; default: this.handleSetting(item.propName); break; diff --git a/src/containers/settings/generalSetting/interface.tsx b/src/containers/settings/generalSetting/interface.tsx index 61c8b20c..78239850 100644 --- a/src/containers/settings/generalSetting/interface.tsx +++ b/src/containers/settings/generalSetting/interface.tsx @@ -38,6 +38,7 @@ export interface SettingInfoState { isAlwaysOnTop: boolean; isAutoMaximizeWin: boolean; isAutoLaunch: boolean; + isMinimizeToTray: boolean; isOpenInMain: boolean; isDisableUpdate: boolean; isExportOriginalName: boolean;