diff --git a/browser/VencordNativeStub.ts b/browser/VencordNativeStub.ts index db2416b29..39600f24a 100644 --- a/browser/VencordNativeStub.ts +++ b/browser/VencordNativeStub.ts @@ -73,8 +73,8 @@ window.VencordNative = { updater: { getRepo: async () => ({ ok: true, value: "https://github.com/Equicord/Equicord" }), getUpdates: async () => ({ ok: true, value: [] }), - update: async () => ({ ok: true, value: false }), - rebuild: async () => ({ ok: true, value: true }), + fetchUpdate: async () => ({ ok: true, value: false }), + applyUpdate: async () => ({ ok: true, value: true }), }, quickCss: { @@ -136,9 +136,4 @@ window.VencordNative = { pluginHelpers: {} as any, csp: {} as any, - tray: { - setUpdateState: NOOP, - onCheckUpdates: NOOP, - onRepair: NOOP, - }, }; diff --git a/src/Vencord.ts b/src/Vencord.ts index adc06b7df..ac20ab8d3 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -38,13 +38,13 @@ import { StartAt } from "@utils/types"; import { SettingsRouter } from "@webpack/common"; import { get as dsGet } from "./api/DataStore"; -import { popNotice, showNotice } from "./api/Notices"; +import { showNotice } from "./api/Notices"; import { NotificationData, showNotification } from "./api/Notifications"; import { initPluginManager, PMLogger, startAllPlugins } from "./api/PluginManager"; import { PlainSettings, Settings, SettingsStore } from "./api/Settings"; import { areLocalSettingsDirty, getCloudSettings, getCloudSyncDirection, markLocalSettingsDirty, putCloudSettings, shouldCloudSync } from "./api/SettingsSync/cloudSync"; import { relaunch } from "./utils/native"; -import { checkForUpdates, isOutdated as getIsOutdated, update, UpdateLogger } from "./utils/updater"; +import { checkForUpdates, update, UpdateLogger } from "./utils/updater"; import { onceReady } from "./webpack"; import { patches } from "./webpack/patchWebpack"; @@ -137,7 +137,6 @@ async function runUpdateCheck() { try { const isOutdated = await checkForUpdates(); - if (IS_DISCORD_DESKTOP) VencordNative.tray.setUpdateState(isOutdated); if (!isOutdated) return; if (Settings.autoUpdate) { @@ -168,43 +167,11 @@ async function runUpdateCheck() { } } -function initTrayIpc() { - if (IS_WEB || IS_UPDATER_DISABLED) return; - - VencordNative.tray.onCheckUpdates(async () => { - try { - const isOutdated = await checkForUpdates(); - VencordNative.tray.setUpdateState(isOutdated); - - if (isOutdated) { - showNotice("An Equicord update is available!", "View Update", () => openSettingsTabModal(UpdaterTab!)); - } else { - showNotice("No updates available, you're on the latest version!", "OK", popNotice); - } - } catch (err) { - UpdateLogger.error("Failed to check for updates from tray", err); - showNotice("Failed to check for updates, check the console for more info", "OK", popNotice); - } - }); - - VencordNative.tray.onRepair(async () => { - try { - await update(); - relaunch(); - } catch (err) { - UpdateLogger.error("Failed to repair Equicord", err); - } - }); - - VencordNative.tray.setUpdateState(getIsOutdated); -} - async function init() { await onceReady; startAllPlugins(StartAt.WebpackReady); syncSettings(); - initTrayIpc(); if (!IS_DEV && !IS_WEB && !IS_UPDATER_DISABLED) { runUpdateCheck(); diff --git a/src/VencordNative.ts b/src/VencordNative.ts index a5ca42366..5b124ff90 100644 --- a/src/VencordNative.ts +++ b/src/VencordNative.ts @@ -8,6 +8,7 @@ import type { Settings } from "@api/Settings"; import type { CspRequestResult } from "@main/csp/manager"; import type { PluginIpcMappings } from "@main/ipcPlugins"; import { UserThemeHeader } from "@main/themes"; +import { UpdateData } from "@main/updater"; import { IpcEvents } from "@shared/IpcEvents"; import type { IpcRes } from "@utils/types"; import { ipcRenderer } from "electron/renderer"; @@ -46,10 +47,10 @@ export default { }, updater: { - getUpdates: () => invoke[]>>(IpcEvents.GET_UPDATES), - update: () => invoke>(IpcEvents.UPDATE), - rebuild: () => invoke>(IpcEvents.BUILD), - getRepo: () => invoke>(IpcEvents.GET_REPO), + getRepo: () => invoke>(IpcEvents.UPDATER_GET_REPO), + getUpdates: () => invoke>(IpcEvents.UPDATER_LIST_UPDATES), + fetchUpdate: () => invoke>(IpcEvents.UPDATER_FETCH_UPDATE), + applyUpdate: () => invoke>(IpcEvents.UPDATER_APPLY_UPDATE), }, settings: { @@ -101,11 +102,5 @@ export default { invoke(IpcEvents.CSP_REQUEST_ADD_OVERRIDE, url, directives, callerName), }, - tray: { - setUpdateState: (available: boolean) => ipcRenderer.send(IpcEvents.SET_TRAY_UPDATE_STATE, available), - onCheckUpdates: (cb: () => void) => { ipcRenderer.on(IpcEvents.TRAY_CHECK_UPDATES, cb); }, - onRepair: (cb: () => void) => { ipcRenderer.on(IpcEvents.TRAY_REPAIR, cb); }, - }, - pluginHelpers: PluginHelpers }; diff --git a/src/components/settings/tabs/changelog/index.tsx b/src/components/settings/tabs/changelog/index.tsx index b8e162181..e895d9261 100644 --- a/src/components/settings/tabs/changelog/index.tsx +++ b/src/components/settings/tabs/changelog/index.tsx @@ -354,8 +354,8 @@ function ChangelogContent() { if (!logged) { setChangelog([]); showToast("Already up to date with repository", "message", { - position: ToastPosition.BOTTOM, - }); + position: ToastPosition.BOTTOM, + }); } return; } @@ -381,16 +381,16 @@ function ChangelogContent() { setRecentlyChecked(true); showToast(`Found ${updates.value.length} commit${updates.value.length === 1 ? "" : "s"} from repository`, "success", { - position: ToastPosition.BOTTOM, - }); + position: ToastPosition.BOTTOM, + }); } else { const logged = await ensureLocalUpdateLogged(); setRecentlyChecked(true); showToast(logged - ? "Logged commits from your latest update" - : "Repository is up to date with your local copy", logged ? "success" : "message", { - position: ToastPosition.BOTTOM, - }); + ? "Logged commits from your latest update" + : "Repository is up to date with your local copy", logged ? "success" : "message", { + position: ToastPosition.BOTTOM, + }); if (!logged) { setChangelog([]); } @@ -409,8 +409,8 @@ function ChangelogContent() { // funny little error toast hopefully doesn't happen! showToast("Could not fetch commits from repository", "failure", { - position: ToastPosition.BOTTOM, - }); + position: ToastPosition.BOTTOM, + }); } finally { setIsLoading(false); } @@ -502,8 +502,8 @@ function ChangelogContent() { await loadChangelogHistory(); setShowHistory(false); showToast("All logs have been cleared", "success", { - position: ToastPosition.BOTTOM, - }); + position: ToastPosition.BOTTOM, + }); }, }); }} @@ -636,8 +636,8 @@ function ChangelogContent() { ), ); showToast("Log has been cleared", "success", { - position: ToastPosition.BOTTOM, - }); + position: ToastPosition.BOTTOM, + }); }, }); }} diff --git a/src/main/trayMenu.ts b/src/main/trayMenu.ts index d5c266490..d7a2c9b4f 100644 --- a/src/main/trayMenu.ts +++ b/src/main/trayMenu.ts @@ -4,27 +4,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { IpcEvents } from "@shared/IpcEvents"; -import { gitHashShort } from "@shared/vencordUserAgent"; -import { BrowserWindow, ipcMain, Menu, MenuItemConstructorOptions, shell } from "electron"; +import { gitHash } from "@shared/vencordUserAgent"; +import { app, BaseWindow, BrowserWindow, dialog, Menu, MenuItemConstructorOptions, shell } from "electron"; import aboutHtml from "file://about.html?minify"; +import updater from "./updater"; import { SETTINGS_DIR, THEMES_DIR } from "./utils/constants"; -let cachedUpdateAvailable = false; - -ipcMain.on(IpcEvents.SET_TRAY_UPDATE_STATE, (_, available: boolean) => { - cachedUpdateAvailable = available; -}); - -function getMainWindow(): BrowserWindow | undefined { - return BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; -} - -function sendToRenderer(event: IpcEvents): void { - getMainWindow()?.webContents.send(event); -} - function findInsertIndex(template: MenuItemConstructorOptions[]): number { const openIndex = template.findIndex(item => { const label = item.label?.toLowerCase() ?? ""; @@ -61,14 +47,11 @@ function openAboutWindow() { return; } - const height = 750; - const width = height * (4 / 3); - aboutWindow = new BrowserWindow({ center: true, autoHideMenuBar: true, - height, - width + height: 525, + width: 900 }); aboutWindow.webContents.setWindowOpenHandler(({ url }) => { @@ -83,7 +66,7 @@ function openAboutWindow() { const aboutParams = aboutHtml .replaceAll("{{VERSION}}", VERSION) - .replaceAll("{{GIT_HASH}}", gitHashShort); + .replaceAll("{{GIT_HASH}}", gitHash); // change to gitHashShort if/when its added const base64Html = Buffer.from(aboutParams).toString("base64"); aboutWindow.loadURL(`data:text/html;base64,${base64Html}`); aboutWindow.on("closed", () => { @@ -91,7 +74,9 @@ function openAboutWindow() { }); } -function createEquicordMenuItems(): MenuItemConstructorOptions[] { +const notify = (window: BaseWindow, title: string, message: string) => dialog.showMessageBox(window, { title, message }); + +function createVencordMenuItems(): MenuItemConstructorOptions[] { return [ { label: "Equicord", @@ -101,12 +86,28 @@ function createEquicordMenuItems(): MenuItemConstructorOptions[] { click: () => openAboutWindow() }, { - label: cachedUpdateAvailable ? "Update Equicord" : "Check for Updates", - click: () => sendToRenderer(IpcEvents.TRAY_CHECK_UPDATES) - }, - { - label: "Repair Equicord", - click: () => sendToRenderer(IpcEvents.TRAY_REPAIR) + label: "Update Equicord", + async click(_item, window) { + if (!window) return; + + try { + const updateAvailable = await updater.fetchUpdate(); + if (!updateAvailable) { + return notify(window, "No Update Available", "You are already using the latest version of Equicord."); + } + + const result = await updater.applyUpdate(); + if (!result) { + return notify(window, "Update Failed", "Failed to apply the update for Equicord."); + } + + await notify(window, "Update Successful", "Now relaunching Equicord to apply the update."); + app.relaunch(); + app.exit(); + } catch (e) { + notify(window, "Update Error", `An error occurred while updating Equicord.\n\n${String(e)}`); + } + } }, { type: "separator" }, { @@ -130,8 +131,8 @@ export function patchTrayMenu(): void { const alreadyPatched = template.some(item => item.label === "Equicord"); if (isTrayMenu(template) && !alreadyPatched) { const insertIndex = findInsertIndex(template); - const equicordItems = createEquicordMenuItems(); - template.splice(insertIndex, 0, ...equicordItems); + const vencordItems = createVencordMenuItems(); + template.splice(insertIndex, 0, ...vencordItems); } return originalBuildFromTemplate.call(this, template); diff --git a/src/main/updater/git.ts b/src/main/updater/git.ts index d5193ab20..2feb9d37d 100644 --- a/src/main/updater/git.ts +++ b/src/main/updater/git.ts @@ -16,13 +16,11 @@ * along with this program. If not, see . */ -import { IpcEvents } from "@shared/IpcEvents"; import { execFile as cpExecFile } from "child_process"; -import { ipcMain } from "electron"; import { join } from "path"; import { promisify } from "util"; -import { serializeErrors } from "./common"; +import { Updater } from "."; const VENCORD_SRC_DIR = join(__dirname, ".."); const EQUICORD_DIR = join(__dirname, "../../"); @@ -85,7 +83,11 @@ async function build() { return !res.stderr.includes("Build failed"); } -ipcMain.handle(IpcEvents.GET_REPO, serializeErrors(getRepo)); -ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(calculateGitChanges)); -ipcMain.handle(IpcEvents.UPDATE, serializeErrors(pull)); -ipcMain.handle(IpcEvents.BUILD, serializeErrors(build)); +const GitUpdater: Updater = { + getRepo, + listUpdates: calculateGitChanges, + fetchUpdate: pull, + applyUpdate: build +}; + +export default GitUpdater; diff --git a/src/main/updater/http.ts b/src/main/updater/http.ts index 226814913..bee7c1875 100644 --- a/src/main/updater/http.ts +++ b/src/main/updater/http.ts @@ -17,15 +17,14 @@ */ import { fetchBuffer, fetchJson } from "@main/utils/http"; -import { IpcEvents } from "@shared/IpcEvents"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; -import { ipcMain } from "electron"; import { writeFileSync } from "original-fs"; import gitHash from "~git-hash"; import gitRemote from "~git-remote"; -import { ASAR_FILE, serializeErrors } from "./common"; +import { Updater } from "."; +import { ASAR_FILE } from "./common"; const API_BASE = `https://api.github.com/repos/${gitRemote}`; let PendingUpdate: string | null = null; @@ -41,8 +40,8 @@ async function githubGet(endpoint: string) { }); } -async function calculateGitChanges() { - const isOutdated = await fetchUpdates(); +async function listUpdates() { + const isOutdated = await fetchUpdate(); if (!isOutdated) return []; const data = await githubGet(`/compare/${gitHash}...HEAD`); @@ -54,7 +53,7 @@ async function calculateGitChanges() { })); } -async function fetchUpdates() { +async function fetchUpdate() { const data = await githubGet("/releases/latest"); const hash = data.name.slice(data.name.lastIndexOf(" ") + 1); @@ -67,7 +66,7 @@ async function fetchUpdates() { return true; } -async function applyUpdates() { +async function applyUpdate() { if (!PendingUpdate) return true; const data = await fetchBuffer(PendingUpdate); @@ -78,7 +77,11 @@ async function applyUpdates() { return true; } -ipcMain.handle(IpcEvents.GET_REPO, serializeErrors(() => `https://github.com/${gitRemote}`)); -ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(calculateGitChanges)); -ipcMain.handle(IpcEvents.UPDATE, serializeErrors(fetchUpdates)); -ipcMain.handle(IpcEvents.BUILD, serializeErrors(applyUpdates)); +const HttpUpdater: Updater = { + getRepo: async () => `https://github.com/${gitRemote}`, + listUpdates, + fetchUpdate, + applyUpdate +}; + +export default HttpUpdater; diff --git a/src/main/updater/index.ts b/src/main/updater/index.ts index 2fb7669dd..482441b8b 100644 --- a/src/main/updater/index.ts +++ b/src/main/updater/index.ts @@ -1,20 +1,8 @@ /* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2026 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ import { IpcEvents } from "@shared/IpcEvents"; import { ipcMain } from "electron"; @@ -23,9 +11,27 @@ import gitRemote from "~git-remote"; import { serializeErrors } from "./common"; -if (!IS_UPDATER_DISABLED) { - require(IS_STANDALONE ? "./http" : "./git"); -} else { - ipcMain.handle(IpcEvents.GET_REPO, serializeErrors(() => `https://github.com/${gitRemote}`)); - ipcMain.handle(IpcEvents.GET_UPDATES, serializeErrors(() => [])); +export type UpdateData = Record<"hash" | "message" | "author", string>; + +export interface Updater { + getRepo(): Promise; + listUpdates(): Promise; + fetchUpdate(): Promise; + applyUpdate(): Promise; } + +const updater: Updater = IS_UPDATER_DISABLED + ? { + getRepo: async () => `https://github.com/${gitRemote}`, + listUpdates: async () => [], + fetchUpdate: async () => false, + applyUpdate: async () => false, + } + : require(IS_STANDALONE ? "./http" : "./git").default; + +ipcMain.handle(IpcEvents.UPDATER_GET_REPO, serializeErrors(updater.getRepo)); +ipcMain.handle(IpcEvents.UPDATER_LIST_UPDATES, serializeErrors(updater.listUpdates)); +ipcMain.handle(IpcEvents.UPDATER_FETCH_UPDATE, serializeErrors(updater.fetchUpdate)); +ipcMain.handle(IpcEvents.UPDATER_APPLY_UPDATE, serializeErrors(updater.applyUpdate)); + +export default updater; diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index e1927d735..5bdc0e5ae 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -32,10 +32,12 @@ export const enum IpcEvents { SET_SETTINGS = "VencordSetSettings", THEME_UPDATE = "VencordThemeUpdate", OPEN_EXTERNAL = "VencordOpenExternal", - GET_UPDATES = "VencordGetUpdates", - GET_REPO = "VencordGetRepo", - UPDATE = "VencordUpdate", - BUILD = "VencordBuild", + + UPDATER_LIST_UPDATES = "VencordListUpdates", + UPDATER_GET_REPO = "VencordGetRepo", + UPDATER_FETCH_UPDATE = "VencordFetchUpdate", + UPDATER_APPLY_UPDATE = "VencordApplyUpdate", + OPEN_MONACO_EDITOR = "VencordOpenMonacoEditor", GET_MONACO_THEME = "VencordGetMonacoTheme", @@ -51,9 +53,5 @@ export const enum IpcEvents { RENDERER_CSS_UPDATE = "VencordRendererCssUpdate", PRELOAD_GET_RENDERER_JS = "VencordPreloadGetRendererJs", - SET_TRAY_UPDATE_STATE = "VencordSetTrayUpdateState", - TRAY_REPAIR = "VencordTrayRepair", - TRAY_CHECK_UPDATES = "VencordTrayCheckUpdates", - TRAY_ABOUT = "VencordTrayAbout", SUPPORTS_WINDOWS_MATERIAL = "VencordSupportsWindowsMaterial", } diff --git a/src/utils/updater.ts b/src/utils/updater.ts index 25fe6ee8c..ded6fe335 100644 --- a/src/utils/updater.ts +++ b/src/utils/updater.ts @@ -54,12 +54,12 @@ export async function checkForUpdates() { export async function update() { if (!isOutdated) return true; - const res = await Unwrap(VencordNative.updater.update()); + const res = await Unwrap(VencordNative.updater.fetchUpdate()); if (res) { isOutdated = false; - if (!await Unwrap(VencordNative.updater.rebuild())) - throw new Error("The Build failed. Please try manually building the new update"); + if (!await Unwrap(VencordNative.updater.applyUpdate())) + throw new Error("Failed to apply the update."); } return res;