diff --git a/enjoy/package.json b/enjoy/package.json
index 148c2569..18c4feef 100644
--- a/enjoy/package.json
+++ b/enjoy/package.json
@@ -183,7 +183,6 @@
"tslib": "^2.8.1",
"turndown": "^7.2.0",
"typescript": "^5.7.2",
- "update-electron-app": "^3.0.0",
"vite": "^6.0.3",
"vite-plugin-static-copy": "^2.2.0",
"wavesurfer.js": "^7.8.10",
diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json
index cb7fe01d..3290efb4 100644
--- a/enjoy/src/i18n/en.json
+++ b/enjoy/src/i18n/en.json
@@ -1,4 +1,19 @@
{
+ "menu": {
+ "about": "About",
+ "toggleFullScreen": "Toggle Full Screen",
+ "hide": "Hide",
+ "unhide": "Unhide",
+ "quit": "Quit",
+ "undo": "Undo",
+ "redo": "Redo",
+ "cut": "Cut",
+ "copy": "Copy",
+ "paste": "Paste",
+ "selectAll": "Select All",
+ "checkForUpdates": "Check for Updates",
+ "reportIssue": "Report Issue"
+ },
"models": {
"user": {
"id": "ID",
@@ -360,7 +375,6 @@
"currentVersion": "Current version",
"checkUpdate": "Check update",
"checkingLatestVersion": "Checking latest version",
- "updateAvailable": "Update available",
"updateDownloaded": "A new version has been downloaded. Restart the application to apply the updates.",
"restart": "Restart",
"later": "Later",
@@ -938,5 +952,8 @@
"horizontal": "Horizontal",
"vertical": "Vertical",
"copied": "Copied",
- "copyFullText": "Copy full text"
+ "copyFullText": "Copy full text",
+ "checkingForUpdate": "Checking for update",
+ "updateAvailable": "Update available",
+ "quitAndInstall": "Quit and install"
}
diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json
index 85d28b1e..019affa1 100644
--- a/enjoy/src/i18n/zh-CN.json
+++ b/enjoy/src/i18n/zh-CN.json
@@ -1,4 +1,19 @@
{
+ "menu": {
+ "about": "关于",
+ "toggleFullScreen": "全屏",
+ "hide": "隐藏",
+ "unhide": "显示",
+ "quit": "退出",
+ "undo": "撤销",
+ "redo": "重做",
+ "cut": "剪切",
+ "copy": "复制",
+ "paste": "粘贴",
+ "selectAll": "全选",
+ "checkForUpdates": "检查更新",
+ "reportIssue": "报告问题"
+ },
"models": {
"user": {
"id": "ID",
@@ -360,7 +375,6 @@
"currentVersion": "当前版本",
"checkUpdate": "检查更新",
"checkingLatestVersion": "正在检查最新版本",
- "updateAvailable": "有新版本可用",
"updateDownloaded": "新版本已下载,点击重新启动以更新",
"restart": "Restart",
"later": "Later",
@@ -938,5 +952,8 @@
"horizontal": "水平",
"vertical": "垂直",
"copied": "已复制",
- "copyFullText": "复制全文"
+ "copyFullText": "复制全文",
+ "checkingForUpdate": "正在检查更新",
+ "updateAvailable": "有新版本",
+ "quitAndInstall": "退出并安装新版本"
}
diff --git a/enjoy/src/main.ts b/enjoy/src/main.ts
index 2a5f249c..7bd9cfdf 100644
--- a/enjoy/src/main.ts
+++ b/enjoy/src/main.ts
@@ -7,7 +7,6 @@ import mainWindow from "@main/window";
import ElectronSquirrelStartup from "electron-squirrel-startup";
import contextMenu from "electron-context-menu";
import { t } from "i18next";
-import { updateElectronApp, UpdateSourceType } from "update-electron-app";
import unhandled from "electron-unhandled";
import newGithubIssueUrl from "new-github-issue-url";
@@ -15,19 +14,6 @@ const logger = log.scope("main");
app.commandLine.appendSwitch("enable-features", "SharedArrayBuffer");
-// config auto updater
-if (!process.env.CI) {
- updateElectronApp({
- updateSource: {
- type: UpdateSourceType.StaticStorage,
- baseUrl: `https://dl.enjoy.bot/app/${process.platform}/${process.arch}`,
- },
- updateInterval: "1 hour",
- logger: logger,
- notifyUser: true,
- });
-}
-
if (!app.isPackaged) {
app.disableHardwareAcceleration();
app.commandLine.appendSwitch("disable-software-rasterizer");
diff --git a/enjoy/src/main/window.ts b/enjoy/src/main/window.ts
index 5821451b..4b8ea56e 100644
--- a/enjoy/src/main/window.ts
+++ b/enjoy/src/main/window.ts
@@ -8,6 +8,7 @@ import {
dialog,
systemPreferences,
MenuItemConstructorOptions,
+ autoUpdater,
} from "electron";
import path from "path";
import db from "@main/db";
@@ -25,7 +26,9 @@ import dict from "./dict";
import mdict from "./mdict";
import decompresser from "./decompresser";
import { UserSetting } from "@main/db/models";
-import { platform } from "os";
+import { t } from "i18next";
+import { format } from "util";
+import pkg from "../../package.json" assert { type: "json" };
const __dirname = import.meta.dirname;
@@ -37,6 +40,25 @@ const youtubeProvider = new YoutubeProvider();
const ffmpeg = new Ffmpeg();
const waveform = new Waveform();
+const FEED_BASE_URL = `https://dl.enjoy.bot/app/${process.platform}/${process.arch}`;
+autoUpdater.setFeedURL({
+ url:
+ process.platform === "darwin"
+ ? `${FEED_BASE_URL}/RELEASES.json`
+ : FEED_BASE_URL,
+ headers: {
+ "X-App-Version": app.getVersion(),
+ "User-Agent": format(
+ "%s/%s (%s: %s)",
+ pkg.name,
+ pkg.version,
+ process.platform,
+ process.arch
+ ),
+ },
+ serverType: process.platform === "darwin" ? "json" : "default",
+});
+
const main = {
win: null as BrowserWindow | null,
init: () => {},
@@ -386,6 +408,44 @@ main.init = async () => {
app.quit();
});
+ ipcMain.handle("app-check-for-updates", () => {
+ autoUpdater.checkForUpdates();
+ });
+
+ ipcMain.handle("app-quit-and-install", () => {
+ autoUpdater.quitAndInstall();
+ });
+
+ ipcMain.on("app-on-updater", () => {
+ autoUpdater.on("error", (error) => {
+ mainWindow.webContents.send("app-on-updater", "error", [error]);
+ });
+ autoUpdater.on("checking-for-update", () => {
+ mainWindow.webContents.send("app-on-updater", "checking-for-update", []);
+ });
+ autoUpdater.on("update-available", () => {
+ mainWindow.webContents.send("app-on-updater", "update-available", []);
+ });
+ autoUpdater.on(
+ "update-downloaded",
+ (_event, releaseNotes, releaseName, releaseDate, updateURL) => {
+ logger.info(
+ "update-downloaded",
+ releaseNotes,
+ releaseName,
+ releaseDate,
+ updateURL
+ );
+ mainWindow.webContents.send("app-on-updater", "update-downloaded", [
+ releaseNotes,
+ releaseName,
+ releaseDate,
+ updateURL,
+ ]);
+ }
+ );
+ });
+
ipcMain.handle("app-open-dev-tools", () => {
mainWindow.webContents.openDevTools();
});
@@ -646,38 +706,38 @@ ${log}
{
label: app.name,
submenu: [
- { role: "about" },
+ { role: "about", label: t("menu.about") },
{ type: "separator" },
- { role: "togglefullscreen" },
- { role: "hide" },
- { role: "unhide" },
+ { role: "togglefullscreen", label: t("menu.toggleFullScreen") },
+ { role: "hide", label: t("menu.hide") },
+ { role: "unhide", label: t("menu.unhide") },
{ type: "separator" },
- { role: "quit" },
+ { role: "quit", label: t("menu.quit") },
],
},
{
label: "Edit",
submenu: [
- { role: "undo" },
- { role: "redo" },
+ { role: "undo", label: t("menu.undo") },
+ { role: "redo", label: t("menu.redo") },
{ type: "separator" },
- { role: "cut" },
- { role: "copy" },
- { role: "paste" },
- { role: "selectAll" },
+ { role: "cut", label: t("menu.cut") },
+ { role: "copy", label: t("menu.copy") },
+ { role: "paste", label: t("menu.paste") },
+ { role: "selectAll", label: t("menu.selectAll") },
],
},
{
label: "Help",
submenu: [
{
- label: "Check for Updates...",
+ label: t("menu.checkForUpdates"),
click: () => {
shell.openExternal("https://1000h.org/enjoy-app/install.html");
},
},
{
- label: "Report Issue...",
+ label: t("menu.reportIssue"),
click: () => {
shell.openExternal(`${REPO_URL}/issues/new`);
},
diff --git a/enjoy/src/preload.ts b/enjoy/src/preload.ts
index 687e5579..4f3fe840 100644
--- a/enjoy/src/preload.ts
+++ b/enjoy/src/preload.ts
@@ -37,6 +37,22 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
quit: () => {
ipcRenderer.invoke("app-quit");
},
+ checkForUpdates: () => {
+ ipcRenderer.invoke("app-check-for-updates");
+ },
+ quitAndInstall: () => {
+ ipcRenderer.invoke("app-quit-and-install");
+ },
+ onUpdater: (
+ callback: (
+ event: IpcRendererEvent,
+ eventType: string,
+ args: any[]
+ ) => void
+ ) => ipcRenderer.on("app-on-updater", callback),
+ removeUpdaterListeners: () => {
+ ipcRenderer.removeAllListeners("app-on-updater");
+ },
openDevTools: () => {
ipcRenderer.invoke("app-open-dev-tools");
},
diff --git a/enjoy/src/renderer/components/layouts/title-bar.tsx b/enjoy/src/renderer/components/layouts/title-bar.tsx
index 101a257a..f84efbb6 100644
--- a/enjoy/src/renderer/components/layouts/title-bar.tsx
+++ b/enjoy/src/renderer/components/layouts/title-bar.tsx
@@ -32,7 +32,6 @@ import {
LightbulbIcon,
LightbulbOffIcon,
MaximizeIcon,
- MenuIcon,
MinimizeIcon,
MinusIcon,
SettingsIcon,
@@ -40,16 +39,31 @@ import {
} from "lucide-react";
import { useContext, useEffect, useState } from "react";
+const INSTALL_URL = "https://1000h.org/enjoy-app/install.html";
+
export const TitleBar = () => {
const [isMaximized, setIsMaximized] = useState(false);
const [isFullScreen, setIsFullScreen] = useState(false);
const [platform, setPlatform] = useState<"darwin" | "win32" | "linux">();
+ const [updaterState, setUpdaterState] = useState<
+ "checking-for-update" | "update-available" | "update-downloaded" | "error"
+ >();
const { EnjoyApp, setDisplayPreferences, initialized } = useContext(
AppSettingsProviderContext
);
const { active, setActive } = useContext(CopilotProviderContext);
+ const checkUpdate = () => {
+ if (platform === "linux") {
+ EnjoyApp.shell.openExternal(INSTALL_URL);
+ } else if (updaterState === "update-downloaded") {
+ EnjoyApp.app.quitAndInstall();
+ } else {
+ EnjoyApp.app.checkForUpdates();
+ }
+ };
+
const onWindowChange = (
_event: IpcRendererEvent,
state: { event: string }
@@ -65,14 +79,28 @@ export const TitleBar = () => {
}
};
+ const onUpdater = (
+ _event: IpcRendererEvent,
+ eventType:
+ | "checking-for-update"
+ | "update-available"
+ | "update-downloaded"
+ | "error",
+ args: any[]
+ ) => {
+ setUpdaterState(eventType);
+ };
+
useEffect(() => {
EnjoyApp.window.onChange(onWindowChange);
EnjoyApp.app.getPlatformInfo().then((info) => {
setPlatform(info.platform as "darwin" | "win32" | "linux");
});
+ EnjoyApp.app.onUpdater(onUpdater);
return () => {
EnjoyApp.window.removeListener(onWindowChange);
+ EnjoyApp.app.removeUpdaterListeners();
};
}, []);
@@ -123,9 +151,12 @@ export const TitleBar = () => {
@@ -181,14 +212,20 @@ export const TitleBar = () => {
- EnjoyApp.shell.openExternal(
- "https://1000h.org/enjoy-app/install.html"
- )
- }
- className="cursor-pointer"
+ onClick={checkUpdate}
+ className="cursor-pointer relative"
>
- {t("checkUpdate")}
+ {updaterState && (
+
+ )}
+
+ {updaterState === "checking-for-update" &&
+ t("checkingForUpdate")}
+ {updaterState === "update-available" && t("updateAvailable")}
+ {updaterState === "update-downloaded" && t("quitAndInstall")}
+ {!updaterState ||
+ (updaterState === "error" && t("checkUpdate"))}
+
diff --git a/enjoy/src/renderer/components/preferences/about.tsx b/enjoy/src/renderer/components/preferences/about.tsx
index 82024d88..97faf4ce 100644
--- a/enjoy/src/renderer/components/preferences/about.tsx
+++ b/enjoy/src/renderer/components/preferences/about.tsx
@@ -7,7 +7,13 @@ export const About = () => {
const { version, EnjoyApp } = useContext(AppSettingsProviderContext);
const checkUpdate = async () => {
- EnjoyApp.shell.openExternal("https://1000h.org/enjoy-app/install.html");
+ const platformInfo = await EnjoyApp.app.getPlatformInfo();
+ if (platformInfo.platform === "linux") {
+ EnjoyApp.shell.openExternal("https://1000h.org/enjoy-app/install.html");
+ } else {
+ EnjoyApp.app.checkForUpdates();
+ toast.info(t("checkingForUpdate"));
+ }
};
return (
diff --git a/enjoy/src/types/enjoy-app.d.ts b/enjoy/src/types/enjoy-app.d.ts
index db815fda..d985c10c 100644
--- a/enjoy/src/types/enjoy-app.d.ts
+++ b/enjoy/src/types/enjoy-app.d.ts
@@ -13,6 +13,16 @@ type EnjoyAppType = {
createIssue: (title: string, body: string) => Promise;
onCmdOutput: (callback: (event, output: string) => void) => void;
removeCmdOutputListeners: () => void;
+ checkForUpdates: () => Promise;
+ quitAndInstall: () => Promise;
+ onUpdater: (
+ callback: (
+ event: IpcRendererEvent,
+ eventType: string,
+ args: any[]
+ ) => void
+ ) => void;
+ removeUpdaterListeners: () => void;
diskUsage: () => Promise;
version: string;
};
diff --git a/yarn.lock b/yarn.lock
index ec461987..e0f7b55a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12197,7 +12197,6 @@ __metadata:
typescript: "npm:^5.7.2"
umzug: "npm:^3.8.2"
unzipper: "npm:^0.12.3"
- update-electron-app: "npm:^3.0.0"
vite: "npm:^6.0.3"
vite-plugin-static-copy: "npm:^2.2.0"
wavesurfer.js: "npm:^7.8.10"
@@ -13935,15 +13934,6 @@ __metadata:
languageName: node
linkType: hard
-"github-url-to-object@npm:^4.0.4":
- version: 4.0.6
- resolution: "github-url-to-object@npm:4.0.6"
- dependencies:
- is-url: "npm:^1.1.0"
- checksum: 10c0/b8ed9b2b93c55657299b7cc5ce9faf4441f143e59f995e126929d18f8fc692fc932d8530da9bde7655f35b29201beacbc17b066b7e26dbcc0dc983f8d3c918cf
- languageName: node
- linkType: hard
-
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
@@ -15402,7 +15392,7 @@ __metadata:
languageName: node
linkType: hard
-"is-url@npm:^1.1.0, is-url@npm:^1.2.4":
+"is-url@npm:^1.2.4":
version: 1.2.4
resolution: "is-url@npm:1.2.4"
checksum: 10c0/0157a79874f8f95fdd63540e3f38c8583c2ef572661cd0693cda80ae3e42dfe8e9a4a972ec1b827f861d9a9acf75b37f7d58a37f94a8a053259642912c252bc3
@@ -23524,17 +23514,6 @@ __metadata:
languageName: node
linkType: hard
-"update-electron-app@npm:^3.0.0":
- version: 3.0.0
- resolution: "update-electron-app@npm:3.0.0"
- dependencies:
- github-url-to-object: "npm:^4.0.4"
- is-url: "npm:^1.2.4"
- ms: "npm:^2.1.1"
- checksum: 10c0/5e0b9ce8fd2cbcf6ea64d8029f90d90394facf05d86c8dceda5dba79af6e21522ef4f37f212982be781f488d497882529d9b11b91174159f6567a24039c76e6a
- languageName: node
- linkType: hard
-
"uqr@npm:^0.1.2":
version: 0.1.2
resolution: "uqr@npm:0.1.2"