feat: implement minimize to tray functionality on close

This commit is contained in:
troyeguo
2026-03-30 09:34:43 +08:00
parent 550d23bb03
commit 2230deb8e1
4 changed files with 75 additions and 1 deletions

57
main.js
View File

@@ -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) {

View File

@@ -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",

View File

@@ -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;

View File

@@ -38,6 +38,7 @@ export interface SettingInfoState {
isAlwaysOnTop: boolean;
isAutoMaximizeWin: boolean;
isAutoLaunch: boolean;
isMinimizeToTray: boolean;
isOpenInMain: boolean;
isDisableUpdate: boolean;
isExportOriginalName: boolean;