From 539518de30b67978a1ea69b43147fdd3ff61cdf8 Mon Sep 17 00:00:00 2001 From: Kevin Lee <59052025+lgz5689@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:55:39 +0800 Subject: [PATCH] feat: add ipc invoke & shortcuts (#15) --- electron/constants/index.tsx | 34 +++++++++++++++++++++++++++++++ electron/i18n/i18next.d.ts | 2 +- electron/i18n/index.ts | 2 +- electron/main/appManage.ts | 1 + electron/main/index.ts | 6 +++++- electron/main/ipcHandlerManage.ts | 29 ++++++++++++++++++++++++++ electron/main/shortcutManage.ts | 11 ++++++++++ electron/main/trayManage.ts | 7 ++++--- electron/main/windowManage.ts | 34 +++++++++++++++++++++++++++++-- electron/preload/index.ts | 27 ++++++++++++++++++++++-- package.json | 10 ++++++--- src/App.tsx | 16 ++++++++++++++- src/types/global.d.ts | 7 +------ 13 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 electron/constants/index.tsx create mode 100644 electron/main/ipcHandlerManage.ts create mode 100644 electron/main/shortcutManage.ts diff --git a/electron/constants/index.tsx b/electron/constants/index.tsx new file mode 100644 index 0000000..e263449 --- /dev/null +++ b/electron/constants/index.tsx @@ -0,0 +1,34 @@ +export enum IpcRenderToMain { + ShowDevTools = "showDevTools", + ShowMainWindow = "showMainWindow", + ClearChildWindows = "clearChildWindows", +} + +export type IpcRenderToMainDataMap = { + [IpcRenderToMain.ShowDevTools]: void; + [IpcRenderToMain.ShowMainWindow]: void; + [IpcRenderToMain.ClearChildWindows]: void; +}; + +export enum IpcRenderToMainSync { + CheckChildWindowStatus = "checkChildWindowStatus", +} + +export type IpcRenderToMainSyncDataMap = { + [IpcRenderToMainSync.CheckChildWindowStatus]: boolean; +}; + +export interface IElectronAPI { + fileExists: (path: string) => boolean; + getPlatform: () => string; + ipcInvoke: ( + channel: T, + ...arg: unknown[] + ) => Promise; + ipcSendSync: ( + channel: T, + ...arg: unknown[] + ) => IpcRenderToMainSyncDataMap[T]; + openFile: (path: string) => Promise; + showFile: (path: string) => void; +} diff --git a/electron/i18n/i18next.d.ts b/electron/i18n/i18next.d.ts index 3444c32..ac25288 100644 --- a/electron/i18n/i18next.d.ts +++ b/electron/i18n/i18next.d.ts @@ -1,5 +1,5 @@ -import * as en from "./resources/en_US"; import { defaultNS } from "."; +import * as en from "./resources/en_US"; declare module "i18next" { interface CustomTypeOptions { diff --git a/electron/i18n/index.ts b/electron/i18n/index.ts index ec95245..2b2c95b 100644 --- a/electron/i18n/index.ts +++ b/electron/i18n/index.ts @@ -1,5 +1,5 @@ -import i18n from "i18next"; import { app } from "electron"; +import i18n from "i18next"; import * as en from "./resources/en_US"; import * as zh from "./resources/zh_CN"; diff --git a/electron/main/appManage.ts b/electron/main/appManage.ts index 3dbd4bc..ac8eb63 100644 --- a/electron/main/appManage.ts +++ b/electron/main/appManage.ts @@ -1,4 +1,5 @@ import { join } from "node:path"; + import { isProd, isWin } from "../utils"; export const electronDistPath = join(__dirname, "../"); diff --git a/electron/main/index.ts b/electron/main/index.ts index a0d0ec5..c5817aa 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -1,7 +1,9 @@ import { app } from "electron"; -import { createMainWindow } from "./windowManage"; + import { initI18n } from "../i18n"; +import { setIpcMainListener } from "./ipcHandlerManage"; import { createTray } from "./trayManage"; +import { createMainWindow } from "./windowManage"; process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true"; @@ -11,4 +13,6 @@ const init = () => { createTray(); }; +setIpcMainListener(); + app.whenReady().then(init); diff --git a/electron/main/ipcHandlerManage.ts b/electron/main/ipcHandlerManage.ts new file mode 100644 index 0000000..38d2c97 --- /dev/null +++ b/electron/main/ipcHandlerManage.ts @@ -0,0 +1,29 @@ +import { BrowserWindow, ipcMain } from "electron"; + +import { IpcRenderToMain, IpcRenderToMainSync } from "../constants"; +import { + childWindowMap, + clearChildWindows, + showMainWindow, + toggleDevTools, +} from "./windowManage"; +export const setIpcMainListener = () => { + ipcMain.handle(IpcRenderToMain.ShowMainWindow, () => { + showMainWindow(); + }); + + ipcMain.handle(IpcRenderToMain.ShowDevTools, () => { + toggleDevTools(); + }); + + ipcMain.handle(IpcRenderToMain.ClearChildWindows, () => { + clearChildWindows(); + }); + + ipcMain.on(IpcRenderToMainSync.CheckChildWindowStatus, (event, { key }) => { + const childWindow = BrowserWindow.getAllWindows().find( + (win) => win.id === childWindowMap[key], + ); + event.returnValue = Boolean(childWindow); + }); +}; diff --git a/electron/main/shortcutManage.ts b/electron/main/shortcutManage.ts new file mode 100644 index 0000000..cd4c23b --- /dev/null +++ b/electron/main/shortcutManage.ts @@ -0,0 +1,11 @@ +import { globalShortcut } from "electron"; + +import { toggleDevTools } from "./windowManage"; + +export const registerShortcuts = () => { + globalShortcut.register("CmdOrCtrl+F12", toggleDevTools); +}; + +export const unregisterShortcuts = () => { + globalShortcut.unregisterAll(); +}; diff --git a/electron/main/trayManage.ts b/electron/main/trayManage.ts index 23924ff..9b47b97 100644 --- a/electron/main/trayManage.ts +++ b/electron/main/trayManage.ts @@ -1,14 +1,15 @@ import { app, Menu, Tray } from "electron"; import { t } from "i18next"; -import { hideWindow, showWindow, toggleDevTools } from "./windowManage"; + import { trayIcon } from "./appManage"; +import { hideWindow, showMainWindow, toggleDevTools } from "./windowManage"; let appTray: Tray | null; export const createTray = () => { const trayMenu = Menu.buildFromTemplate([ { label: t("showWindow"), - click: showWindow, + click: showMainWindow, }, { label: t("hideWindow"), @@ -25,7 +26,7 @@ export const createTray = () => { ]); appTray = new Tray(trayIcon); appTray.setToolTip(app.getName()); - appTray.on("click", showWindow); + appTray.on("click", showMainWindow); appTray.setContextMenu(trayMenu); }; diff --git a/electron/main/windowManage.ts b/electron/main/windowManage.ts index 14b9537..1e95ec6 100644 --- a/electron/main/windowManage.ts +++ b/electron/main/windowManage.ts @@ -1,8 +1,29 @@ -import { BrowserWindow } from "electron"; import { join } from "node:path"; + +import { BrowserWindow } from "electron"; + import { distPath, preload } from "./appManage"; +import { registerShortcuts, unregisterShortcuts } from "./shortcutManage"; let mainWindow: BrowserWindow; + +export const childWindowMap: { [key: string]: number } = {}; + +export const clearChildWindows = () => { + for (const key in childWindowMap) { + const childWindow = BrowserWindow.getAllWindows().find( + (win) => win.id === childWindowMap[key], + ); + if (childWindow?.isDestroyed()) { + delete childWindowMap[key]; + } + if (childWindow && !childWindow?.isDestroyed()) { + childWindow.close(); + delete childWindowMap[key]; + } + } +}; + export function createMainWindow() { mainWindow = new BrowserWindow({ title: "CD", @@ -24,9 +45,18 @@ export function createMainWindow() { } else { mainWindow.loadFile(join(distPath, "index.html")); } + + mainWindow.on("focus", () => { + mainWindow?.flashFrame(false); + registerShortcuts(); + }); + + mainWindow.on("blur", () => { + unregisterShortcuts(); + }); } -export const showWindow = () => { +export const showMainWindow = () => { if (mainWindow.isMinimized()) { mainWindow.restore(); } diff --git a/electron/preload/index.ts b/electron/preload/index.ts index 2dbc32b..079cf72 100644 --- a/electron/preload/index.ts +++ b/electron/preload/index.ts @@ -1,11 +1,34 @@ -import { contextBridge, shell } from "electron"; import fs from "node:fs"; -import { IElectronAPI } from "../../src/types/global"; +import { contextBridge, ipcRenderer, shell } from "electron"; + +import { + IElectronAPI, + IpcRenderToMain, + IpcRenderToMainDataMap, + IpcRenderToMainSync, + IpcRenderToMainSyncDataMap, +} from "../constants"; + +const ipcInvoke = ( + channel: T, + ...arg: unknown[] +): Promise => { + return ipcRenderer.invoke(channel, ...arg); +}; + +const ipcSendSync = ( + channel: T, + ...arg: unknown[] +): IpcRenderToMainSyncDataMap[T] => { + return ipcRenderer.sendSync(channel, ...arg); +}; const Api: IElectronAPI = { fileExists: fs.existsSync, getPlatform: () => process.platform, + ipcInvoke, + ipcSendSync, openFile: shell.openPath, showFile: shell.showItemInFolder, }; diff --git a/package.json b/package.json index 35237a9..0d52946 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "dev": "vite --host", "build": "tsc && vite build", - "lint": "eslint --ext .js,.jsx,.ts,.tsx src", - "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx --fix --quiet src", - "format": "prettier --write src", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src electron", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx --fix --quiet src electron", + "format": "prettier --write src electron", "prepare": "husky install", "preview": "vite preview" }, @@ -18,6 +18,10 @@ ] }, "lint-staged": { + "electron": [ + "prettier --write", + "eslint --fix" + ], "src/**/*.{tsx,ts}": [ "prettier --write", "eslint --fix" diff --git a/src/App.tsx b/src/App.tsx index 7366f58..67429c1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,7 @@ import { useState } from "react"; +import { IpcRenderToMain, IpcRenderToMainSync } from "../electron/constants"; + const App = () => { // eslint has error // const [first, setfirst] = useState(""); @@ -16,10 +18,22 @@ const App = () => { console.log(first); } + const ipc = () => { + window.electronAPI?.ipcInvoke(IpcRenderToMain.ShowDevTools); + const b = window.electronAPI?.ipcSendSync( + IpcRenderToMainSync.CheckChildWindowStatus, + "main", + ); + console.log(b); + }; + return (
-
+
Web Template
diff --git a/src/types/global.d.ts b/src/types/global.d.ts index c8a0a35..8a76c4a 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,9 +1,4 @@ -export interface IElectronAPI { - fileExists: (path: string) => boolean; - getPlatform: () => string; - openFile: (path: string) => Promise; - showFile: (path: string) => void; -} +import { IElectronAPI } from "../../electron/constants/index"; declare global { interface Window {