Improve multi-instance behaviour (#604)

* Experiment with two windows

* Improve multi-instance behaviour

Previously, ArmCord would attempt to launch up a completely new
instance each time when multi-instances setting were enabled. This
doesn't work well as Electron doesn't support running multiple instances
of the same app pointing to the same user data directory
(which by default on GNU/Linux is `~/.config/ArmCord`). Doing so would
result in this error:
> Failed to open LevelDB database" "file currently in use"

It's possible to workaround this behaviour by passing in a parameter to
a different user data directory when launching subsequent instances of
armcord, like so:
```shell
$ armcord --user-data-directory=$HOME/.config/ArmCord-2
```

However, this method ends up taking disk storage in the multiples of the
number of armcord instances that are simultaneously running, which isn't
ideal.

Looking into this more, it looks like Electron can do multiple windows
fine with the same user data directory. I gave this a try and it seems
to be working nice. With this PR, running any subsequent instances of
armcord will open up a new window in the original armcord instance. This
should also help with better resource utilization when compared to
running multiple full blown instances of armcord.

* Fix lints
This commit is contained in:
Ritiek Malhotra 2024-06-15 13:50:44 +05:30 committed by GitHub
parent 10b7e638de
commit d9d24d9473
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 146 additions and 114 deletions

View file

@ -1,10 +1,11 @@
//ipc stuff //ipc stuff
import {app, clipboard, desktopCapturer, ipcMain, nativeImage, shell, SourcesOptions} from "electron"; import {app, clipboard, desktopCapturer, ipcMain, nativeImage, shell, SourcesOptions} from "electron";
import {mainWindow} from "./window.js"; import {BrowserWindow} from "electron";
import os from "os"; import os from "os";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import {mainWindows} from "./window.js";
import {getConfig, setConfigBulk, getConfigLocation} from "../common/config.js"; import {getConfig, setConfigBulk, getConfigLocation} from "../common/config.js";
import {setLang, getLang, getLangName} from "../common/lang.js"; import {setLang, getLang, getLangName} from "../common/lang.js";
import {getVersion, getDisplayVersion} from "../common/version.js"; import {getVersion, getDisplayVersion} from "../common/version.js";
@ -19,7 +20,20 @@ const userDataPath = app.getPath("userData");
const storagePath = path.join(userDataPath, "/storage/"); const storagePath = path.join(userDataPath, "/storage/");
const themesPath = path.join(userDataPath, "/themes/"); const themesPath = path.join(userDataPath, "/themes/");
const pluginsPath = path.join(userDataPath, "/plugins/"); const pluginsPath = path.join(userDataPath, "/plugins/");
export function registerIpc(): void { export function registerIpc(passedWindow: BrowserWindow): void {
ipcMain.on("splashEnd", () => {
splashWindow.close();
if (getConfig("startMinimized")) {
passedWindow.hide();
} else {
passedWindow.show();
}
});
if (mainWindows.length !== 1) {
return;
}
ipcMain.on("get-app-path", (event) => { ipcMain.on("get-app-path", (event) => {
event.reply("app-path", app.getAppPath()); event.reply("app-path", app.getAppPath());
}); });
@ -40,33 +54,33 @@ export function registerIpc(): void {
case "win32": case "win32":
if (pingCount > 0) { if (pingCount > 0) {
const image = nativeImage.createFromPath(path.join(import.meta.dirname, "../", `/assets/ping.png`)); const image = nativeImage.createFromPath(path.join(import.meta.dirname, "../", `/assets/ping.png`));
mainWindow.setOverlayIcon(image, "badgeCount"); passedWindow.setOverlayIcon(image, "badgeCount");
} else { } else {
mainWindow.setOverlayIcon(null, "badgeCount"); passedWindow.setOverlayIcon(null, "badgeCount");
} }
break; break;
} }
}); });
ipcMain.on("win-maximize", () => { ipcMain.on("win-maximize", () => {
mainWindow.maximize(); passedWindow.maximize();
}); });
ipcMain.on("win-isMaximized", (event) => { ipcMain.on("win-isMaximized", (event) => {
event.returnValue = mainWindow.isMaximized(); event.returnValue = passedWindow.isMaximized();
}); });
ipcMain.on("win-isNormal", (event) => { ipcMain.on("win-isNormal", (event) => {
event.returnValue = mainWindow.isNormal(); event.returnValue = passedWindow.isNormal();
}); });
ipcMain.on("win-minimize", () => { ipcMain.on("win-minimize", () => {
mainWindow.minimize(); passedWindow.minimize();
}); });
ipcMain.on("win-unmaximize", () => { ipcMain.on("win-unmaximize", () => {
mainWindow.unmaximize(); passedWindow.unmaximize();
}); });
ipcMain.on("win-show", () => { ipcMain.on("win-show", () => {
mainWindow.show(); passedWindow.show();
}); });
ipcMain.on("win-hide", () => { ipcMain.on("win-hide", () => {
mainWindow.hide(); passedWindow.hide();
}); });
ipcMain.on("win-quit", () => { ipcMain.on("win-quit", () => {
app.exit(); app.exit();
@ -80,14 +94,6 @@ export function registerIpc(): void {
ipcMain.on("modInstallState", (event) => { ipcMain.on("modInstallState", (event) => {
event.returnValue = modInstallState; event.returnValue = modInstallState;
}); });
ipcMain.on("splashEnd", () => {
splashWindow.close();
if (getConfig("startMinimized")) {
mainWindow.hide();
} else {
mainWindow.show();
}
});
ipcMain.on("restart", () => { ipcMain.on("restart", () => {
app.relaunch(); app.relaunch();
app.exit(); app.exit();

View file

@ -1,5 +1,5 @@
import {BrowserWindow, Menu, app} from "electron"; import {BrowserWindow, Menu, app} from "electron";
import {mainWindow} from "./window.js"; import {mainWindows} from "./window.js";
import {createSettingsWindow} from "../settings/main.js"; import {createSettingsWindow} from "../settings/main.js";
export function setMenu(): void { export function setMenu(): void {
@ -31,7 +31,9 @@ export function setMenu(): void {
label: "Reload", label: "Reload",
accelerator: "CmdOrCtrl+R", accelerator: "CmdOrCtrl+R",
click() { click() {
mainWindow.reload(); mainWindows.forEach((mainWindow) => {
mainWindow.reload();
});
} }
}, },
{ {

View file

@ -13,10 +13,10 @@ import contextMenu from "electron-context-menu";
import os from "os"; import os from "os";
import RPCServer from "arrpc"; import RPCServer from "arrpc";
import {tray} from "../tray.js"; import {tray} from "../tray.js";
import {iconPath} from "../main.js"; import {iconPath, init} from "../main.js";
import {getConfig, setConfig, firstRun} from "../common/config.js"; import {getConfig, setConfig, firstRun} from "../common/config.js";
import {getWindowState, setWindowState} from "../common/windowState.js"; import {getWindowState, setWindowState} from "../common/windowState.js";
export let mainWindow: BrowserWindow; export let mainWindows: BrowserWindow[] = [];
export let inviteWindow: BrowserWindow; export let inviteWindow: BrowserWindow;
let forceQuit = false; let forceQuit = false;
let osType = os.type(); let osType = os.type();
@ -43,49 +43,57 @@ contextMenu({
} }
] ]
}); });
function doAfterDefiningTheWindow(): void { function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void {
if (getWindowState("isMaximized") ?? false) { if (getWindowState("isMaximized") ?? false) {
mainWindow.setSize(835, 600); //just so the whole thing doesn't cover whole screen passedWindow.setSize(835, 600); //just so the whole thing doesn't cover whole screen
mainWindow.maximize(); passedWindow.maximize();
void mainWindow.webContents.executeJavaScript(`document.body.setAttribute("isMaximized", "");`); void passedWindow.webContents.executeJavaScript(`document.body.setAttribute("isMaximized", "");`);
mainWindow.hide(); // please don't flashbang the user passedWindow.hide(); // please don't flashbang the user
} }
if (getConfig("windowStyle") == "transparency" && process.platform === "win32") { if (getConfig("windowStyle") == "transparency" && process.platform === "win32") {
mainWindow.setBackgroundMaterial("mica"); passedWindow.setBackgroundMaterial("mica");
if (getConfig("startMinimized") == false) { if (getConfig("startMinimized") == false) {
mainWindow.show(); passedWindow.show();
} }
} }
// REVIEW - Test the protocol warning. I was not sure how to get it to pop up. For now I've voided the promises. // REVIEW - Test the protocol warning. I was not sure how to get it to pop up. For now I've voided the promises.
const ignoreProtocolWarning = getConfig("ignoreProtocolWarning"); const ignoreProtocolWarning = getConfig("ignoreProtocolWarning");
registerIpc(); registerIpc(passedWindow);
if (getConfig("mobileMode")) { if (getConfig("mobileMode")) {
mainWindow.webContents.userAgent = passedWindow.webContents.userAgent =
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.149 Mobile Safari/537.36"; "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.149 Mobile Safari/537.36";
} else { } else {
// A little sloppy but it works :p // A little sloppy but it works :p
if (osType == "Windows_NT") { if (osType == "Windows_NT") {
osType = `Windows ${os.release().split(".")[0]} (${os.release()})`; osType = `Windows ${os.release().split(".")[0]} (${os.release()})`;
} }
mainWindow.webContents.userAgent = `Mozilla/5.0 (X11; ${osType} ${os.arch()}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36`; //fake useragent for screenshare to work passedWindow.webContents.userAgent = `Mozilla/5.0 (X11; ${osType} ${os.arch()}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36`; //fake useragent for screenshare to work
} }
app.on("second-instance", (_event, _commandLine, _workingDirectory, additionalData) => { if (mainWindows.length === 1) {
// Print out data received from the second instance. app.on("second-instance", (_event, _commandLine, _workingDirectory, additionalData) => {
console.log(additionalData); void (async () => {
// Print out data received from the second instance.
console.log(additionalData);
// Someone tried to run a second instance, we should focus our window. if (getConfig("multiInstance") == (false ?? undefined)) {
if (mainWindow) { // Someone tried to run a second instance, we should focus our window.
if (mainWindow.isMinimized()) mainWindow.restore(); if (passedWindow) {
mainWindow.show(); if (passedWindow.isMinimized()) passedWindow.restore();
mainWindow.focus(); passedWindow.show();
} passedWindow.focus();
}); }
} else {
await init();
}
})();
});
}
app.on("activate", function () { app.on("activate", function () {
app.show(); app.show();
}); });
mainWindow.webContents.setWindowOpenHandler(({url}) => { passedWindow.webContents.setWindowOpenHandler(({url}) => {
// Allow about:blank (used by Vencord QuickCss popup) // Allow about:blank (used by Vencord QuickCss popup)
if (url === "about:blank") return {action: "allow"}; if (url === "about:blank") return {action: "allow"};
// Allow Discord stream popout // Allow Discord stream popout
@ -116,7 +124,7 @@ function doAfterDefiningTheWindow(): void {
checkboxChecked: false checkboxChecked: false
}; };
void dialog.showMessageBox(mainWindow, options).then(({response, checkboxChecked}) => { void dialog.showMessageBox(passedWindow, options).then(({response, checkboxChecked}) => {
console.log(response, checkboxChecked); console.log(response, checkboxChecked);
if (checkboxChecked) { if (checkboxChecked) {
if (response == 0) { if (response == 0) {
@ -137,15 +145,15 @@ function doAfterDefiningTheWindow(): void {
import("./screenshare/main.js"); import("./screenshare/main.js");
} }
mainWindow.webContents.session.webRequest.onBeforeRequest( passedWindow.webContents.session.webRequest.onBeforeRequest(
{urls: ["https://*/api/v*/science", "https://sentry.io/*", "https://*.nel.cloudflare.com/*"]}, {urls: ["https://*/api/v*/science", "https://sentry.io/*", "https://*.nel.cloudflare.com/*"]},
(_, callback) => callback({cancel: true}) (_, callback) => callback({cancel: true})
); );
if (getConfig("trayIcon") == "default" || getConfig("dynamicIcon")) { if (getConfig("trayIcon") == "default" || getConfig("dynamicIcon")) {
mainWindow.webContents.on("page-favicon-updated", () => { passedWindow.webContents.on("page-favicon-updated", () => {
// REVIEW - no need to await if we just .then() - This works! // REVIEW - no need to await if we just .then() - This works!
void mainWindow.webContents void passedWindow.webContents
.executeJavaScript( .executeJavaScript(
` `
var getFavicon = function(){ var getFavicon = function(){
@ -177,17 +185,17 @@ function doAfterDefiningTheWindow(): void {
} }
} }
if (getConfig("dynamicIcon")) { if (getConfig("dynamicIcon")) {
mainWindow.setIcon(trayPath); passedWindow.setIcon(trayPath);
} }
}); });
}); });
} }
mainWindow.webContents.on("page-title-updated", (e, title) => { passedWindow.webContents.on("page-title-updated", (e, title) => {
const armCordSuffix = " - ArmCord"; /* identify */ const armCordSuffix = " - ArmCord"; /* identify */
if (!title.endsWith(armCordSuffix)) { if (!title.endsWith(armCordSuffix)) {
e.preventDefault(); e.preventDefault();
// REVIEW - I don't see a reason to wait for the titlebar to update // REVIEW - I don't see a reason to wait for the titlebar to update
void mainWindow.webContents.executeJavaScript( void passedWindow.webContents.executeJavaScript(
`document.title = '${title.replace("Discord |", "") + armCordSuffix}'` `document.title = '${title.replace("Discord |", "") + armCordSuffix}'`
); );
} }
@ -201,7 +209,7 @@ function doAfterDefiningTheWindow(): void {
if (!fs.existsSync(`${userDataPath}/disabled.txt`)) { if (!fs.existsSync(`${userDataPath}/disabled.txt`)) {
fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), ""); fs.writeFileSync(path.join(userDataPath, "/disabled.txt"), "");
} }
mainWindow.webContents.on("did-finish-load", () => { passedWindow.webContents.on("did-finish-load", () => {
fs.readdirSync(themesFolder).forEach((file) => { fs.readdirSync(themesFolder).forEach((file) => {
try { try {
const manifest = fs.readFileSync(`${themesFolder}/${file}/manifest.json`, "utf8"); const manifest = fs.readFileSync(`${themesFolder}/${file}/manifest.json`, "utf8");
@ -214,7 +222,7 @@ function doAfterDefiningTheWindow(): void {
) { ) {
console.log(`%cSkipped ${themeFile.name} made by ${themeFile.author}`, "color:red"); console.log(`%cSkipped ${themeFile.name} made by ${themeFile.author}`, "color:red");
} else { } else {
mainWindow.webContents.send( passedWindow.webContents.send(
"themeLoader", "themeLoader",
fs.readFileSync(`${themesFolder}/${file}/${themeFile.theme}`, "utf-8") fs.readFileSync(`${themesFolder}/${file}/${themeFile.theme}`, "utf-8")
); );
@ -226,21 +234,24 @@ function doAfterDefiningTheWindow(): void {
}); });
}); });
setMenu(); setMenu();
mainWindow.on("close", (e) => { passedWindow.on("close", (e) => {
if (process.platform === "darwin" && forceQuit) { if (process.platform === "darwin" && forceQuit) {
mainWindow.close(); passedWindow.close();
} else if (mainWindows.length > 1) {
mainWindows = mainWindows.filter((mainWindow) => mainWindow.id != passedWindow.id);
passedWindow.destroy();
} else { } else {
const [width, height] = mainWindow.getSize(); const [width, height] = passedWindow.getSize();
setWindowState({ setWindowState({
width, width,
height, height,
isMaximized: mainWindow.isMaximized(), isMaximized: passedWindow.isMaximized(),
x: mainWindow.getPosition()[0], x: passedWindow.getPosition()[0],
y: mainWindow.getPosition()[1] y: passedWindow.getPosition()[1]
}); });
if (getConfig("minimizeToTray")) { if (getConfig("minimizeToTray")) {
e.preventDefault(); e.preventDefault();
mainWindow.hide(); passedWindow.hide();
} else if (!getConfig("minimizeToTray")) { } else if (!getConfig("minimizeToTray")) {
e.preventDefault(); e.preventDefault();
app.quit(); app.quit();
@ -258,25 +269,25 @@ function doAfterDefiningTheWindow(): void {
} }
// REVIEW - Awaiting javascript execution is silly // REVIEW - Awaiting javascript execution is silly
mainWindow.on("focus", () => { passedWindow.on("focus", () => {
void mainWindow.webContents.executeJavaScript(`document.body.removeAttribute("unFocused");`); void passedWindow.webContents.executeJavaScript(`document.body.removeAttribute("unFocused");`);
}); });
mainWindow.on("blur", () => { passedWindow.on("blur", () => {
void mainWindow.webContents.executeJavaScript(`document.body.setAttribute("unFocused", "");`); void passedWindow.webContents.executeJavaScript(`document.body.setAttribute("unFocused", "");`);
}); });
mainWindow.on("maximize", () => { passedWindow.on("maximize", () => {
void mainWindow.webContents.executeJavaScript(`document.body.setAttribute("isMaximized", "");`); void passedWindow.webContents.executeJavaScript(`document.body.setAttribute("isMaximized", "");`);
}); });
mainWindow.on("unmaximize", () => { passedWindow.on("unmaximize", () => {
void mainWindow.webContents.executeJavaScript(`document.body.removeAttribute("isMaximized");`); void passedWindow.webContents.executeJavaScript(`document.body.removeAttribute("isMaximized");`);
}); });
if (getConfig("inviteWebsocket")) { if (getConfig("inviteWebsocket") && mainWindows.length === 1) {
// NOTE - RPCServer appears to be untyped. cool. // NOTE - RPCServer appears to be untyped. cool.
// REVIEW - Whatever Ducko has done here to make an async constructor is awful. // REVIEW - Whatever Ducko has done here to make an async constructor is awful.
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
new RPCServer().then((server: EventEmitter) => { new RPCServer().then((server: EventEmitter) => {
server.on("activity", (data: string) => mainWindow.webContents.send("rpc", data)); server.on("activity", (data: string) => passedWindow.webContents.send("rpc", data));
server.on("invite", (code: string) => { server.on("invite", (code: string) => {
console.log(code); console.log(code);
createInviteWindow(code); createInviteWindow(code);
@ -284,17 +295,17 @@ function doAfterDefiningTheWindow(): void {
}); });
} }
if (firstRun) { if (firstRun) {
mainWindow.close(); passedWindow.close();
} }
//loadURL broke for no good reason after E28 //loadURL broke for no good reason after E28
void mainWindow.loadFile(`${import.meta.dirname}/../splash/redirect.html`); void passedWindow.loadFile(`${import.meta.dirname}/../splash/redirect.html`);
if (getConfig("skipSplash")) { if (getConfig("skipSplash")) {
mainWindow.show(); passedWindow.show();
} }
} }
export function createCustomWindow(): void { export function createCustomWindow(): void {
mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: getWindowState("width") ?? 835, width: getWindowState("width") ?? 835,
height: getWindowState("height") ?? 600, height: getWindowState("height") ?? 600,
x: getWindowState("x"), x: getWindowState("x"),
@ -313,10 +324,11 @@ export function createCustomWindow(): void {
spellcheck: getConfig("spellcheck") spellcheck: getConfig("spellcheck")
} }
}); });
doAfterDefiningTheWindow(); mainWindows.push(mainWindow);
doAfterDefiningTheWindow(mainWindow);
} }
export function createNativeWindow(): void { export function createNativeWindow(): void {
mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: getWindowState("width") ?? 835, width: getWindowState("width") ?? 835,
height: getWindowState("height") ?? 600, height: getWindowState("height") ?? 600,
x: getWindowState("x"), x: getWindowState("x"),
@ -335,10 +347,11 @@ export function createNativeWindow(): void {
spellcheck: getConfig("spellcheck") spellcheck: getConfig("spellcheck")
} }
}); });
doAfterDefiningTheWindow(); mainWindows.push(mainWindow);
doAfterDefiningTheWindow(mainWindow);
} }
export function createTransparentWindow(): void { export function createTransparentWindow(): void {
mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: getWindowState("width") ?? 835, width: getWindowState("width") ?? 835,
height: getWindowState("height") ?? 600, height: getWindowState("height") ?? 600,
x: getWindowState("x"), x: getWindowState("x"),
@ -357,7 +370,8 @@ export function createTransparentWindow(): void {
spellcheck: getConfig("spellcheck") spellcheck: getConfig("spellcheck")
} }
}); });
doAfterDefiningTheWindow(); mainWindows.push(mainWindow);
doAfterDefiningTheWindow(mainWindow);
} }
export function createInviteWindow(code: string): void { export function createInviteWindow(code: string): void {
inviteWindow = new BrowserWindow({ inviteWindow = new BrowserWindow({
@ -381,7 +395,7 @@ export function createInviteWindow(code: string): void {
// REVIEW - This shouldn't matter, since below we have an event on it // REVIEW - This shouldn't matter, since below we have an event on it
void inviteWindow.loadURL(formInviteURL); void inviteWindow.loadURL(formInviteURL);
inviteWindow.webContents.once("did-finish-load", () => { inviteWindow.webContents.once("did-finish-load", () => {
if (!mainWindow.webContents.isLoading()) { if (!mainWindows[0].webContents.isLoading()) {
inviteWindow.show(); inviteWindow.show();
inviteWindow.webContents.once("will-navigate", () => { inviteWindow.webContents.once("will-navigate", () => {
inviteWindow.close(); inviteWindow.close();

View file

@ -49,8 +49,33 @@ async function args(): Promise<void> {
}); });
} }
} }
export async function init(): Promise<void> {
if (getConfig("skipSplash") == false) {
void createSplashWindow(); // REVIEW - Awaiting will hang at start
}
if (firstRun == true) {
setLang(new Intl.DateTimeFormat().resolvedOptions().locale);
await createSetupWindow();
}
switch (getConfig("windowStyle")) {
case "default":
createCustomWindow();
customTitlebar = true;
break;
case "native":
createNativeWindow();
break;
case "transparent":
createTransparentWindow();
break;
default:
createCustomWindow();
customTitlebar = true;
break;
}
}
await args(); // i want my top level awaits - IMPLEMENTED :) await args(); // i want my top level awaits - IMPLEMENTED :)
if (!app.requestSingleInstanceLock() && getConfig("multiInstance") == (false ?? undefined)) { if (!app.requestSingleInstanceLock()) {
// if value isn't set after 3.2.4 // if value isn't set after 3.2.4
// kill if 2nd instance // kill if 2nd instance
app.quit(); app.quit();
@ -88,31 +113,6 @@ if (!app.requestSingleInstanceLock() && getConfig("multiInstance") == (false ??
} else { } else {
iconPath = path.join(import.meta.dirname, "../", "/assets/desktop.png"); iconPath = path.join(import.meta.dirname, "../", "/assets/desktop.png");
} }
async function init(): Promise<void> {
if (getConfig("skipSplash") == false) {
void createSplashWindow(); // REVIEW - Awaiting will hang at start
}
if (firstRun == true) {
setLang(new Intl.DateTimeFormat().resolvedOptions().locale);
await createSetupWindow();
}
switch (getConfig("windowStyle")) {
case "default":
createCustomWindow();
customTitlebar = true;
break;
case "native":
createNativeWindow();
break;
case "transparent":
createTransparentWindow();
break;
default:
createCustomWindow();
customTitlebar = true;
break;
}
}
await init(); await init();
await installModLoader(); await installModLoader();
session.fromPartition("some-partition").setPermissionRequestHandler((_webContents, permission, callback) => { session.fromPartition("some-partition").setPermissionRequestHandler((_webContents, permission, callback) => {

View file

@ -1,7 +1,7 @@
import {BrowserWindow, app, dialog, ipcMain, shell} from "electron"; import {BrowserWindow, app, dialog, ipcMain, shell} from "electron";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import {createInviteWindow, mainWindow} from "../discord/window.js"; import {createInviteWindow, mainWindows} from "../discord/window.js";
import type {ThemeManifest} from "../types/themeManifest.d.js"; import type {ThemeManifest} from "../types/themeManifest.d.js";
let themeWindow: BrowserWindow; let themeWindow: BrowserWindow;
let instance = 0; let instance = 0;
@ -125,7 +125,9 @@ export async function createTManagerWindow(): Promise<void> {
shell.showItemInFolder(themesPath); shell.showItemInFolder(themesPath);
}); });
ipcMain.on("reloadMain", () => { ipcMain.on("reloadMain", () => {
mainWindow.webContents.reload(); mainWindows.forEach((mainWindow) => {
mainWindow.webContents.reload();
});
}); });
ipcMain.on("addToDisabled", (_event, name: string) => { ipcMain.on("addToDisabled", (_event, name: string) => {
fs.appendFileSync(path.join(userDataPath, "/disabled.txt"), `${name}\n`); fs.appendFileSync(path.join(userDataPath, "/disabled.txt"), `${name}\n`);
@ -147,7 +149,9 @@ export async function createTManagerWindow(): Promise<void> {
console.log(`Removed ${id} folder`); console.log(`Removed ${id} folder`);
} }
themeWindow.webContents.reload(); themeWindow.webContents.reload();
mainWindow.webContents.reload(); mainWindows.forEach((mainWindow) => {
mainWindow.webContents.reload();
});
}); });
ipcMain.on("installBDTheme", (_event, link: string) => { ipcMain.on("installBDTheme", (_event, link: string) => {
return async () => { return async () => {
@ -170,7 +174,9 @@ export async function createTManagerWindow(): Promise<void> {
message: "Successfully imported theme from link." message: "Successfully imported theme from link."
}); });
themeWindow.webContents.reload(); themeWindow.webContents.reload();
mainWindow.webContents.reload(); mainWindows.forEach((mainWindow) => {
mainWindow.webContents.reload();
});
} catch (e) { } catch (e) {
dialog.showErrorBox( dialog.showErrorBox(
"BD Theme import fail", "BD Theme import fail",

View file

@ -1,6 +1,6 @@
import fs from "fs"; import fs from "fs";
import {Menu, MessageBoxOptions, Tray, app, dialog, nativeImage} from "electron"; import {Menu, MessageBoxOptions, Tray, app, dialog, nativeImage} from "electron";
import {createInviteWindow, mainWindow} from "./discord/window.js"; import {createInviteWindow, mainWindows} from "./discord/window.js";
import path from "path"; import path from "path";
import {createSettingsWindow} from "./settings/main.js"; import {createSettingsWindow} from "./settings/main.js";
import {getConfig, getConfigLocation, setConfig} from "./common/config.js"; import {getConfig, getConfigLocation, setConfig} from "./common/config.js";
@ -62,7 +62,9 @@ void app.whenReady().then(async () => {
{ {
label: `Open ${clientName}`, label: `Open ${clientName}`,
click() { click() {
mainWindow.show(); mainWindows.forEach((mainWindow) => {
mainWindow.show();
});
} }
}, },
{ {
@ -92,7 +94,9 @@ void app.whenReady().then(async () => {
} }
tray.setToolTip(clientName); tray.setToolTip(clientName);
tray.on("click", function () { tray.on("click", function () {
mainWindow.show(); mainWindows.forEach((mainWindow) => {
mainWindow.show();
});
}); });
} else { } else {
if (getConfig("tray") == undefined) { if (getConfig("tray") == undefined) {
@ -106,7 +110,7 @@ void app.whenReady().then(async () => {
detail: "Linux may not work well with tray icons. Depending on your system configuration, you may not be able to see the tray icon. Enable at your own risk. Can be changed later." detail: "Linux may not work well with tray icons. Depending on your system configuration, you may not be able to see the tray icon. Enable at your own risk. Can be changed later."
}; };
await dialog.showMessageBox(mainWindow, options).then(({response}) => { await dialog.showMessageBox(mainWindows[0], options).then(({response}) => {
if (response == 0) { if (response == 0) {
setConfig("tray", true); setConfig("tray", true);
} else { } else {