diff --git a/src/main/about.ts b/src/main/about.ts new file mode 100644 index 0000000..73bc9c6 --- /dev/null +++ b/src/main/about.ts @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3. + * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! + */ + +import { BrowserWindow } from "electron"; +import { join } from "path"; +import { ICON_PATH, VIEW_DIR } from "shared/paths"; + +import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; + +export function createAboutWindow() { + const about = new BrowserWindow({ + center: true, + autoHideMenuBar: true, + icon: ICON_PATH, + webPreferences: { + preload: join(__dirname, "updaterPreload.js") + } + }); + + makeLinksOpenExternally(about); + + about.loadFile(join(VIEW_DIR, "about.html")); + + return about; +} diff --git a/src/main/appBadge.ts b/src/main/appBadge.ts new file mode 100644 index 0000000..e662f1b --- /dev/null +++ b/src/main/appBadge.ts @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3. + * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! + */ + +import { app, NativeImage, nativeImage } from "electron"; +import { join } from "path"; +import { BADGE_DIR } from "shared/paths"; + +const imgCache = new Map(); +function loadBadge(index: number) { + const cached = imgCache.get(index); + if (cached) return cached; + + const img = nativeImage.createFromPath(join(BADGE_DIR, `${index}.ico`)); + imgCache.set(index, img); + + return img; +} + +let lastIndex: null | number = -1; + +export function setBadgeCount(count: number) { + switch (process.platform) { + case "linux": + if (count === -1) count = 0; + app.setBadgeCount(count); + break; + case "darwin": + if (count === 0) { + app.dock.setBadge(""); + break; + } + app.dock.setBadge(count === -1 ? "•" : count.toString()); + break; + case "win32": + const [index, description] = getBadgeIndexAndDescription(count); + if (lastIndex === index) break; + + lastIndex = index; + + // circular import shenanigans + const { mainWin } = require("./mainWindow") as typeof import("./mainWindow"); + mainWin.setOverlayIcon(index === null ? null : loadBadge(index), description); + break; + } +} + +function getBadgeIndexAndDescription(count: number): [number | null, string] { + if (count === -1) return [11, "Unread Messages"]; + if (count === 0) return [null, "No Notifications"]; + + const index = Math.max(1, Math.min(count, 10)); + return [index, `${index} Notification`]; +} diff --git a/src/main/arrpc.ts b/src/main/arrpc.ts new file mode 100644 index 0000000..89eb03d --- /dev/null +++ b/src/main/arrpc.ts @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3. + * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! + */ + +import Server from "arrpc"; +import { IpcEvents } from "shared/IpcEvents"; + +import { mainWin } from "./mainWindow"; +import { Settings } from "./settings"; + +let server: any; + +const inviteCodeRegex = /^(\w|-)+$/; + +export async function initArRPC() { + if (server || !Settings.store.arRPC) return; + + try { + server = await new Server(); + server.on("activity", (data: any) => mainWin.webContents.send(IpcEvents.ARRPC_ACTIVITY, JSON.stringify(data))); + server.on("invite", (invite: string, callback: (valid: boolean) => void) => { + invite = String(invite); + if (!inviteCodeRegex.test(invite)) return callback(false); + + mainWin.webContents + // Safety: Result of JSON.stringify should always be safe to equal + // Also, just to be super super safe, invite is regex validated above + .executeJavaScript(`Vesktop.openInviteModal(${JSON.stringify(invite)})`) + .then(callback); + }); + } catch (e) { + console.error("Failed to start arRPC server", e); + } +} + +Settings.addChangeListener("arRPC", initArRPC); diff --git a/src/main/autoStart.ts b/src/main/autoStart.ts new file mode 100644 index 0000000..efc6c92 --- /dev/null +++ b/src/main/autoStart.ts @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3. + * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! + */ + +import { app } from "electron"; +import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs"; +import { join } from "path"; + +interface AutoStart { + isEnabled(): boolean; + enable(): void; + disable(): void; +} + +function makeAutoStartLinux(): AutoStart { + const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config"); + const dir = join(configDir, "autostart"); + const file = join(dir, "vesktop.desktop"); + + // IM STUPID + const legacyName = join(dir, "vencord.desktop"); + if (existsSync(legacyName)) renameSync(legacyName, file); + + // "Quoting must be done by enclosing the argument between double quotes and escaping the double quote character, + // backtick character ("`"), dollar sign ("$") and backslash character ("\") by preceding it with an additional backslash character" + // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables + const commandLine = process.argv.map(arg => '"' + arg.replace(/["$`\\]/g, "\\$&") + '"').join(" "); + + return { + isEnabled: () => existsSync(file), + enable() { + const desktopFile = ` +[Desktop Entry] +Type=Application +Name=Vesktop +Comment=Vesktop autostart script +Exec=${commandLine} +StartupNotify=false +Terminal=false +`.trim(); + + mkdirSync(dir, { recursive: true }); + writeFileSync(file, desktopFile); + }, + disable: () => rmSync(file, { force: true }) + }; +} + +const autoStartWindowsMac: AutoStart = { + isEnabled: () => app.getLoginItemSettings().openAtLogin, + enable: () => app.setLoginItemSettings({ openAtLogin: true }), + disable: () => app.setLoginItemSettings({ openAtLogin: false }) +}; + +export const autoStart = process.platform === "linux" ? makeAutoStartLinux() : autoStartWindowsMac; diff --git a/src/main/constants.ts b/src/main/constants.ts new file mode 100644 index 0000000..e57eb76 --- /dev/null +++ b/src/main/constants.ts @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3. + * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! + */ + +import { app } from "electron"; +import { existsSync, mkdirSync, readdirSync, renameSync, rmdirSync } from "fs"; +import { dirname, join } from "path"; + +const vesktopDir = dirname(process.execPath); + +export const PORTABLE = + process.platform === "win32" && + !process.execPath.toLowerCase().endsWith("electron.exe") && + !existsSync(join(vesktopDir, "Uninstall Vesktop.exe")); + +const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop"); +export const DATA_DIR = + process.env.VENCORD_USER_DATA_DIR || (PORTABLE ? join(vesktopDir, "Aerocord_Data") : join(app.getPath("userData"))); + +mkdirSync(DATA_DIR, { recursive: true }); + +// TODO: remove eventually +if (existsSync(LEGACY_DATA_DIR)) { + try { + console.warn("Detected legacy settings dir", LEGACY_DATA_DIR + ".", "migrating to", DATA_DIR); + for (const file of readdirSync(LEGACY_DATA_DIR)) { + renameSync(join(LEGACY_DATA_DIR, file), join(DATA_DIR, file)); + } + rmdirSync(LEGACY_DATA_DIR); + renameSync( + join(app.getPath("appData"), "VencordDesktop", "IndexedDB"), + join(DATA_DIR, "sessionData", "IndexedDB") + ); + } catch (e) { + console.error("Migration failed", e); + } +} +app.setPath("sessionData", join(DATA_DIR, "sessionData")); + +export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings"); +export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css"); +export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json"); +export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes"); + +// needs to be inline require because of circular dependency +// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised +export const VENCORD_FILES_DIR = + (require("./settings") as typeof import("./settings")).Settings.store.vencordDir || join(DATA_DIR, "vencordDist"); + +export const USER_AGENT = `Vesktop/${app.getVersion()} (https://git.randomserver.top/aiek/aerocord)`; + +// dimensions shamelessly stolen from Discord Desktop :3 +export const MIN_WIDTH = 940; +export const MIN_HEIGHT = 500; +export const DEFAULT_WIDTH = 1280; +export const DEFAULT_HEIGHT = 720; + +export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"]; + +const BrowserUserAgents = { + darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + windows: + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" +}; + +export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows; + +export const enum MessageBoxChoice { + Default, + Cancel +} \ No newline at end of file