feat(core): add manual check for updates (#4957)

work for #4523

add `appBuildType` to `runtimeConfig`
add `useAppUpdater` to manage client updates

<!--
copilot:summary
-->
### <samp>🤖[[deprecated]](https://githubnext.com/copilot-for-prs-sunset) Generated by Copilot at cdd012c</samp>

This pull request refactors and enhances the update functionality for the frontend. It introduces a new custom hook `useAppUpdater` that simplifies the update logic and state management, and uses it in various components and commands. It also adds more options and feedback for the user to control and monitor the update process, such as manual download, auto-check, and auto-download toggles, and update status and progress indicators. It also updates the `AboutAffine` component to show the app icon, version, and build type. It also adds new translations, dependencies, types, and schemas related to the update functionality.

<img width="1073" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/16ae7a6a-0035-4e57-902b-6b8f63169501">
This commit is contained in:
JimmFly
2023-11-29 13:31:25 +00:00
parent 906d224fa9
commit 23518cae16
17 changed files with 581 additions and 142 deletions

View File

@@ -18,13 +18,53 @@ export const quitAndInstall = async () => {
};
let lastCheckTime = 0;
export const checkForUpdates = async (force = true) => {
// check every 30 minutes (1800 seconds) at most
if (!disabled && (force || lastCheckTime + 1000 * 1800 < Date.now())) {
let downloading = false;
export type UpdaterConfig = {
autoCheckUpdate: boolean;
autoDownloadUpdate: boolean;
};
const config: UpdaterConfig = {
autoCheckUpdate: true,
autoDownloadUpdate: true,
};
export const getConfig = (): UpdaterConfig => {
return { ...config };
};
export const setConfig = (newConfig: Partial<UpdaterConfig> = {}): void => {
Object.assign(config, newConfig);
};
export const checkForUpdates = async (force = false) => {
if (disabled) {
return;
}
if (
force ||
(config.autoCheckUpdate && lastCheckTime + 1000 * 1800 < Date.now())
) {
lastCheckTime = Date.now();
return await autoUpdater.checkForUpdates();
}
return void 0;
return;
};
export const downloadUpdate = async () => {
if (disabled) {
return;
}
downloading = true;
autoUpdater.downloadUpdate().catch(e => {
downloading = false;
logger.error('Failed to download update', e);
});
logger.info('Update available, downloading...');
return;
};
export const registerUpdater = async () => {
@@ -60,10 +100,9 @@ export const registerUpdater = async () => {
autoUpdater.on('checking-for-update', () => {
logger.info('Checking for update');
});
let downloading = false;
autoUpdater.on('update-available', info => {
logger.info('Update available', info);
if (allowAutoUpdate && !downloading) {
if (config.autoDownloadUpdate && allowAutoUpdate && !downloading) {
downloading = true;
autoUpdater?.downloadUpdate().catch(e => {
downloading = false;

View File

@@ -1,7 +1,14 @@
import { app } from 'electron';
import { app, type IpcMainInvokeEvent } from 'electron';
import type { NamespaceHandlers } from '../type';
import { checkForUpdates, quitAndInstall } from './electron-updater';
import {
checkForUpdates,
downloadUpdate,
getConfig,
quitAndInstall,
setConfig,
type UpdaterConfig,
} from './electron-updater';
export const updaterHandlers = {
currentVersion: async () => {
@@ -10,6 +17,18 @@ export const updaterHandlers = {
quitAndInstall: async () => {
return quitAndInstall();
},
downloadUpdate: async () => {
return downloadUpdate();
},
getConfig: async (): Promise<UpdaterConfig> => {
return getConfig();
},
setConfig: async (
_e: IpcMainInvokeEvent,
newConfig: Partial<UpdaterConfig>
): Promise<void> => {
return setConfig(newConfig);
},
checkForUpdatesAndNotify: async () => {
const res = await checkForUpdates(true);
if (res) {