"use strict"; const ipcMain = require("electron").ipcMain; const { BrowserWindow } = require('electron'); const path = require('path'); const fs = require('fs'); const rimraf = require('rimraf'); const imIconHelper = require("./core/imIconHelper"); const { readJson } = require('./core/utils'); const http = require("./core/http"); const { tr } = require('./core/translate'); let panelShown = false; let newVersionChecked = false; module.exports = { async load() { Editor.Ipc.sendToPanel("im-plugin", "im-plugin:force-close"); this.onEditorReady = this.onEditorReady.bind(this); this.onUserLogin = this.onUserLogin.bind(this); this.onUserLogout = this.onUserLogout.bind(this); this.translate = this.translate.bind(this); this.getEntryUrl = this.getEntryUrl.bind(this); this.onDownloadNewVersionSucceed = this.onDownloadNewVersionSucceed.bind( this ); this.onDownloadNewVersionFailed = this.onDownloadNewVersionFailed.bind( this ); this.onInstallNewVersionSucceed = this.onInstallNewVersionSucceed.bind( this ); this.onInstallNewVersionFailed = this.onInstallNewVersionFailed.bind(this); // 注:热更新后,不会触发editor:ready,故这里我们才分出来一个initialSetup。 this.initialSetup(); ipcMain.on("editor:ready", this.onEditorReady); Editor.User.on("login", this.onUserLogin); Editor.User.on("logout", this.onUserLogout); ipcMain.on("im-plugin:translate", this.translate); ipcMain.on("im-plugin:get-entry-url", this.getEntryUrl); ipcMain.on("store:download-zip-succeed", this.onDownloadNewVersionSucceed); ipcMain.on("store:download-zip-failed", this.onDownloadNewVersionFailed); ipcMain.on("store:install-zip-succeed", this.onInstallNewVersionSucceed); ipcMain.on("store:install-zip-failed", this.onInstallNewVersionFailed); }, unload() { ipcMain.off("editor:ready", this.onEditorReady); Editor.User.removeListener("login", this.onUserLogin); Editor.User.removeListener("logout", this.onUserLogout); ipcMain.off("im-plugin:translate", this.translate); ipcMain.off("im-plugin:get-entry-url", this.getEntryUrl); ipcMain.off("store:download-zip-succeed", this.onDownloadNewVersionSucceed); ipcMain.off("store:download-zip-failed", this.onDownloadNewVersionFailed); ipcMain.off("store:install-zip-succeed", this.onInstallNewVersionSucceed); ipcMain.off("store:install-zip-failed", this.onInstallNewVersionFailed); imIconHelper.clearUpdateIconTimer(); }, async initialSetup() { if (!(await Editor.User.isLoggedIn())) { return; } await this.updateImSettings(); imIconHelper.setupUpdateIconTimer(); }, async onEditorReady() { if (!(await Editor.User.isLoggedIn())) { return; } // 可能由于之前用户未登录,initialSetup没有更新imSettings // 这里需要检查并更新下。 if (!imIconHelper.getImSettings()) { await this.updateImSettings(); } this.promptForUpdateIfNewVersionExists(); // initialSetup可能太早更新图标,直接被无视了,这里重新设置Timer。 imIconHelper.clearUpdateIconTimer(); imIconHelper.setupUpdateIconTimer(); }, async onUserLogin() { await this.updateImSettings(); imIconHelper.setupUpdateIconTimer(); this.promptForUpdateIfNewVersionExists(); }, onUserLogout() { Editor.Ipc.sendToPanel("im-plugin", "im-plugin:force-close"); imIconHelper.clearUpdateIconTimer(); }, async updateImSettings() { // 强制init,避免重新登录导致session改变的问题。 await http.init(true); const imSettings = await http.getIMSettings(); imIconHelper.updateImSettings(imSettings); }, async promptForUpdateIfNewVersionExists() { if (newVersionChecked) { return; } newVersionChecked = true; const imSettings = imIconHelper.getImSettings(); const newVersion = imSettings.plugin_version; if (!newVersion || !newVersion.match(/^(\d|\.)+$/)) { // 版本只做一些粗略的检查,只检查是否只包含数字和点。 return; } const packageJson = readJson(path.join(__dirname, "./package.json")); const oldVersion = packageJson.version; if (newVersion > oldVersion) { const newVersionMsg = tr("new-version-found").replace("_", newVersion); const result = await Editor.Dialog.messageBox({ type: "info", title: tr("info-title"), buttons: [tr("ok"), tr("cancel")], defaultId: 0, cancelId: 1, message: newVersionMsg, }); if (result === 0) { this.updateToNewVersion(); } } }, updateToNewVersion() { Editor.Ipc.sendToMain( "editor:package-query-info", "store", (err, storePackageInfo) => { if (err) { return; } const storeVersion = storePackageInfo.info.version; if (storeVersion >= "2.0.1") { const imSettings = imIconHelper.getImSettings(); Editor.Ipc.sendToMain( "store:download-zip", imSettings.plugin_url, `im-plugin_${imSettings.plugin_version}.zip` ); } else { try { // 打开不存在的Panel会有异常,这里忽略可能的异常。 Editor.Panel.open("store"); } catch (_) {} } } ); }, async onDownloadNewVersionSucceed(event, url, fileName, zipPath) { const imSettings = imIconHelper.getImSettings(); if (url !== imSettings.plugin_url) { return; } this.newVersionZip = zipPath; try { this.cleanRequireCaches(__dirname); await this.removeDir(__dirname); Editor.Ipc.sendToMain("store:install-zip", this.newVersionZip); } catch (e) { Editor.warn(e); Editor.Dialog.messageBox({ type: "warning", title: tr("warn-title"), message: tr("remove-current-version-failed"), }); } }, onDownloadNewVersionFailed(event, url, fileName) { const imSettings = imIconHelper.getImSettings(); if (url !== imSettings.plugin_url) { return; } Editor.Dialog.messageBox({ type: "warning", title: tr("warn-title"), message: tr("download-new-version-failed"), }); }, onInstallNewVersionSucceed(event, zipPath) { // 新版本插件会接收到这个消息,这里this.newVersionZip已经没了,不太好做精确验证。 // 简单验证下zip名称中是否包含im-plugin。 if (zipPath.indexOf("im-plugin") === -1) { return; } Editor.Dialog.messageBox({ type: "info", title: tr("info-title"), message: tr("install-new-version-succeed"), }); }, onInstallNewVersionFailed(event, zipPath) { if (zipPath !== this.newVersionZip) { return; } Editor.Dialog.messageBox({ type: "warning", title: tr("warn-title"), message: tr("install-new-version-failed"), }); }, translate(event, key) { event.returnValue = tr(key); }, getEntryUrl(event) { event.returnValue = imIconHelper.getImSettings().entry_url; }, cleanRequireCaches(...args) { const targetPath = path.join(...args); if (!fs.existsSync(path)) { return; } if (targetPath.endsWith(".js")) { return delete require.cache[require.resolve(targetPath)]; } if (!fs.statSync(path).isDirectory()) { return; } recursive( path, (filePath) => { if (filePath.endsWith(".js") && existsSync(filePath)) { delete require.cache[require.resolve(filePath)]; } }, (file) => { if (path.basename(file) === "node_modules") { return false; } if (path.basename(file).startsWith(".")) { return false; } return true; } ); function recursive(dir, handle, filter) { function step(filePath) { if (!existsSync(filePath)) { return; } if (filter ? filter(filePath) : true) { handle(filePath); if (fs.statSync(filePath).isDirectory()) { fs.readdirSync(filePath).forEach((file) => step(path.join(filePath, file)) ); } } } step(dir); } }, removeDir(dirPath) { return new Promise((resolve, reject) => { rimraf(dirPath, (err) => { if (!err) { resolve(null); } else { reject(err); } }); }); }, messages: { async open(event) { imIconHelper.clearUpdateIconTimer(); imIconHelper.setImPanelBlurred(false); if (!panelShown) { await this.updateImSettings(); // 更新下是否是VIP的信息 } imIconHelper.updateIcon(0, true); Editor.Panel.open("im-plugin"); if (!panelShown) { panelShown = true; } }, "panel-ready"(event) { const win = BrowserWindow.fromWebContents(event.sender); const eventNames = [ "blur", "focus", "show", "hide", "minimize", "restore", ]; for (const eventName of eventNames) { win.on(eventName, () => { event.sender.send("im-plugin:panel-win-state-changed", eventName); }); } win.on("blur", () => { imIconHelper.setImPanelBlurred(true); }); win.on("focus", () => { imIconHelper.updateIcon(0, true); imIconHelper.setImPanelBlurred(false); }); }, close(event) { // 如果Panel隐藏了,需要show之后才能关闭,否则会出现在下次面板显示时才瞬间关闭的情况。 BrowserWindow.fromWebContents(event.sender).show(); imIconHelper.setImPanelBlurred(true); Editor.Panel.close("im-plugin"); }, hide(event) { imIconHelper.setImPanelBlurred(true); BrowserWindow.fromWebContents(event.sender).hide(); }, "update-icon"(event, newMsgCount) { imIconHelper.updateIcon(newMsgCount); }, }, };