stoat-for-desktop/src/native/window.ts

175 lines
4.2 KiB
TypeScript

import { join, resolve } from "node:path";
import {
BrowserWindow,
Menu,
MenuItem,
app,
ipcMain,
nativeImage,
} from "electron";
import { setBadgeCount } from "./badges";
import { config } from "./config";
import { updateTrayMenu } from "./tray";
// global reference to main window
export let mainWindow: BrowserWindow;
// currently in-use build
export const BUILD_URL = new URL(
app.commandLine.hasSwitch("force-server")
? app.commandLine.getSwitchValue("force-server")
: (MAIN_WINDOW_VITE_DEV_SERVER_URL ?? "https://beta.revolt.chat"),
);
// internal window state
let shouldQuit = false;
// load the window icon
const windowIcon = nativeImage.createFromPath(
resolve(process.resourcesPath, "icon.png"),
);
console.info(resolve(process.resourcesPath, "icon.png"));
// windowIcon.setTemplateImage(true);
/**
* Create the main application window
*/
export function createMainWindow() {
// create the window
mainWindow = new BrowserWindow({
minWidth: 300,
minHeight: 300,
width: 800,
height: 600,
backgroundColor: "#191919",
frame: !config.customFrame,
icon: windowIcon,
webPreferences: {
// relative to `.vite/build`
preload: join(__dirname, "preload.js"),
contextIsolation: true,
nodeIntegration: false,
spellcheck: true,
},
});
// maximise the window if it was maximised before
if (config.windowState.isMaximised) {
mainWindow.maximize();
}
// load the entrypoint
mainWindow.loadURL(BUILD_URL.toString());
// minimise window to tray
mainWindow.on("close", (event) => {
if (!shouldQuit && config.minimiseToTray) {
event.preventDefault();
mainWindow.hide();
}
});
// update tray menu when window is shown/hidden
mainWindow.on("show", updateTrayMenu);
mainWindow.on("hide", updateTrayMenu);
// keep track of window state
function generateState() {
config.windowState = {
isMaximised: mainWindow.isMaximized(),
};
}
mainWindow.on("maximize", generateState);
mainWindow.on("unmaximize", generateState);
// rebind zoom controls to be more sensible
mainWindow.webContents.on("before-input-event", (event, input) => {
if (input.control && input.key === "=") {
// zoom in (+)
event.preventDefault();
mainWindow.webContents.setZoomLevel(
mainWindow.webContents.getZoomLevel() + 1,
);
} else if (input.control && input.key === "-") {
// zoom out (-)
event.preventDefault();
mainWindow.webContents.setZoomLevel(
mainWindow.webContents.getZoomLevel() - 1,
);
}
});
// configure spellchecker context menu
mainWindow.webContents.on("context-menu", (_, params) => {
const menu = new Menu();
// add all suggestions
for (const suggestion of params.dictionarySuggestions) {
menu.append(
new MenuItem({
label: suggestion,
click: () => mainWindow.webContents.replaceMisspelling(suggestion),
}),
);
}
// allow users to add the misspelled word to the dictionary
if (params.misspelledWord) {
menu.append(
new MenuItem({
label: "Add to dictionary",
click: () =>
mainWindow.webContents.session.addWordToSpellCheckerDictionary(
params.misspelledWord,
),
}),
);
}
// add an option to toggle spellchecker
menu.append(
new MenuItem({
label: "Toggle spellcheck",
click() {
config.spellchecker = !config.spellchecker;
},
}),
);
// show menu if we've generated enough entries
if (menu.items.length > 0) {
menu.popup();
}
});
// push world events to the window
ipcMain.on("minimise", () => mainWindow.minimize());
ipcMain.on("maximise", () =>
mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize(),
);
ipcMain.on("close", () => mainWindow.close());
// mainWindow.webContents.openDevTools();
let i = 0;
setInterval(() => setBadgeCount((++i % 30) + 1), 1000);
}
/**
* Quit the entire app
*/
export function quitApp() {
shouldQuit = true;
mainWindow.close();
}
// Ensure global app quit works properly
app.on("before-quit", () => {
shouldQuit = true;
});