diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json
index cb7fe01d0..3290efb4a 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 85d28b1ed..019affa1a 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/window.ts b/enjoy/src/main/window.ts
index 5821451b3..4b8ea56e1 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 687e5579b..4f3fe840c 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 101a257a7..f84efbb6e 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 82024d88b..97faf4ce6 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 db815fdab..d985c10cf 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;
};