Explorar o código

Audit app update functions

Manav Rathi hai 1 ano
pai
achega
af79f4f0c9

+ 2 - 2
desktop/src/main.ts

@@ -27,7 +27,7 @@ import { attachFSWatchIPCHandlers, attachIPCHandlers } from "./main/ipc";
 import log, { initLogging } from "./main/log";
 import log, { initLogging } from "./main/log";
 import { createApplicationMenu } from "./main/menu";
 import { createApplicationMenu } from "./main/menu";
 import { isDev } from "./main/util";
 import { isDev } from "./main/util";
-import { setupAutoUpdater } from "./services/appUpdater";
+import { setupAutoUpdater } from "./services/app-update";
 import { initWatcher } from "./services/chokidar";
 import { initWatcher } from "./services/chokidar";
 
 
 let appIsQuitting = false;
 let appIsQuitting = false;
@@ -145,7 +145,7 @@ const attachEventHandlers = (mainWindow: BrowserWindow) => {
     // Let ipcRenderer know when mainWindow is in the foreground so that it can
     // Let ipcRenderer know when mainWindow is in the foreground so that it can
     // in turn inform the renderer process.
     // in turn inform the renderer process.
     mainWindow.on("focus", () =>
     mainWindow.on("focus", () =>
-        mainWindow.webContents.send("onMainWindowFocus"),
+        mainWindow.webContents.send("mainWindowFocus"),
     );
     );
 };
 };
 
 

+ 8 - 11
desktop/src/main/ipc.ts

@@ -12,14 +12,11 @@ import type { FSWatcher } from "chokidar";
 import { ipcMain } from "electron/main";
 import { ipcMain } from "electron/main";
 import {
 import {
     appVersion,
     appVersion,
-    muteUpdateNotification,
     skipAppUpdate,
     skipAppUpdate,
     updateAndRestart,
     updateAndRestart,
-} from "../services/appUpdater";
-import {
-    clipImageEmbedding,
-    clipTextEmbedding,
-} from "../services/clip";
+    updateOnNextRestart,
+} from "../services/app-update";
+import { clipImageEmbedding, clipTextEmbedding } from "../services/clip";
 import { runFFmpegCmd } from "../services/ffmpeg";
 import { runFFmpegCmd } from "../services/ffmpeg";
 import { getDirFiles } from "../services/fs";
 import { getDirFiles } from "../services/fs";
 import {
 import {
@@ -108,14 +105,14 @@ export const attachIPCHandlers = () => {
 
 
     // - App update
     // - App update
 
 
-    ipcMain.on("update-and-restart", () => updateAndRestart());
-
-    ipcMain.on("skip-app-update", (_, version) => skipAppUpdate(version));
+    ipcMain.on("updateAndRestart", () => updateAndRestart());
 
 
-    ipcMain.on("mute-update-notification", (_, version) =>
-        muteUpdateNotification(version),
+    ipcMain.on("updateOnNextRestart", (_, version) =>
+        updateOnNextRestart(version),
     );
     );
 
 
+    ipcMain.on("skipAppUpdate", (_, version) => skipAppUpdate(version));
+
     // - Conversion
     // - Conversion
 
 
     ipcMain.handle("convertToJPEG", (_, fileData, filename) =>
     ipcMain.handle("convertToJPEG", (_, fileData, filename) =>

+ 2 - 3
desktop/src/main/menu.ts

@@ -6,7 +6,7 @@ import {
     shell,
     shell,
 } from "electron";
 } from "electron";
 import { setIsAppQuitting } from "../main";
 import { setIsAppQuitting } from "../main";
-import { forceCheckForUpdateAndNotify } from "../services/appUpdater";
+import { forceCheckForAppUpdates } from "../services/app-update";
 import autoLauncher from "../services/autoLauncher";
 import autoLauncher from "../services/autoLauncher";
 import {
 import {
     getHideDockIconPreference,
     getHideDockIconPreference,
@@ -26,8 +26,7 @@ export const createApplicationMenu = async (mainWindow: BrowserWindow) => {
     const macOSOnly = (options: MenuItemConstructorOptions[]) =>
     const macOSOnly = (options: MenuItemConstructorOptions[]) =>
         process.platform == "darwin" ? options : [];
         process.platform == "darwin" ? options : [];
 
 
-    const handleCheckForUpdates = () =>
-        forceCheckForUpdateAndNotify(mainWindow);
+    const handleCheckForUpdates = () => forceCheckForAppUpdates(mainWindow);
 
 
     const handleViewChangelog = () =>
     const handleViewChangelog = () =>
         shell.openExternal(
         shell.openExternal(

+ 21 - 22
desktop/src/preload.ts

@@ -70,37 +70,36 @@ const saveEncryptionKey = (encryptionKey: string): Promise<void> =>
     ipcRenderer.invoke("saveEncryptionKey", encryptionKey);
     ipcRenderer.invoke("saveEncryptionKey", encryptionKey);
 
 
 const onMainWindowFocus = (cb?: () => void) => {
 const onMainWindowFocus = (cb?: () => void) => {
-    ipcRenderer.removeAllListeners("onMainWindowFocus");
-    if (cb) ipcRenderer.on("onMainWindowFocus", cb);
+    ipcRenderer.removeAllListeners("mainWindowFocus");
+    if (cb) ipcRenderer.on("mainWindowFocus", cb);
 };
 };
 
 
-const fsExists = (path: string): Promise<boolean> =>
-    ipcRenderer.invoke("fsExists", path);
-
-// - AUDIT below this
-
 // - App update
 // - App update
 
 
-const registerUpdateEventListener = (
-    showUpdateDialog: (updateInfo: AppUpdateInfo) => void,
+const onAppUpdateAvailable = (
+    cb?: ((updateInfo: AppUpdateInfo) => void) | undefined,
 ) => {
 ) => {
-    ipcRenderer.removeAllListeners("show-update-dialog");
-    ipcRenderer.on("show-update-dialog", (_, updateInfo: AppUpdateInfo) => {
-        showUpdateDialog(updateInfo);
-    });
+    ipcRenderer.removeAllListeners("appUpdateAvailable");
+    if (cb) {
+        ipcRenderer.on("appUpdateAvailable", (_, updateInfo: AppUpdateInfo) =>
+            cb(updateInfo),
+        );
+    }
 };
 };
 
 
-const updateAndRestart = () => {
-    ipcRenderer.send("update-and-restart");
-};
+const updateAndRestart = () => ipcRenderer.send("updateAndRestart");
+
+const updateOnNextRestart = (version: string) =>
+    ipcRenderer.send("updateOnNextRestart", version);
 
 
 const skipAppUpdate = (version: string) => {
 const skipAppUpdate = (version: string) => {
-    ipcRenderer.send("skip-app-update", version);
+    ipcRenderer.send("skipAppUpdate", version);
 };
 };
 
 
-const muteUpdateNotification = (version: string) => {
-    ipcRenderer.send("mute-update-notification", version);
-};
+const fsExists = (path: string): Promise<boolean> =>
+    ipcRenderer.invoke("fsExists", path);
+
+// - AUDIT below this
 
 
 // - Conversion
 // - Conversion
 
 
@@ -310,10 +309,10 @@ contextBridge.exposeInMainWorld("electron", {
     onMainWindowFocus,
     onMainWindowFocus,
 
 
     // - App update
     // - App update
+    onAppUpdateAvailable,
     updateAndRestart,
     updateAndRestart,
+    updateOnNextRestart,
     skipAppUpdate,
     skipAppUpdate,
-    muteUpdateNotification,
-    registerUpdateEventListener,
 
 
     // - Conversion
     // - Conversion
     convertToJPEG,
     convertToJPEG,

+ 98 - 0
desktop/src/services/app-update.ts

@@ -0,0 +1,98 @@
+import { compareVersions } from "compare-versions";
+import { app, BrowserWindow } from "electron";
+import { default as electronLog } from "electron-log";
+import { autoUpdater } from "electron-updater";
+import { setIsAppQuitting, setIsUpdateAvailable } from "../main";
+import log from "../main/log";
+import { userPreferencesStore } from "../stores/user-preferences";
+import { AppUpdateInfo } from "../types/ipc";
+
+export const setupAutoUpdater = (mainWindow: BrowserWindow) => {
+    autoUpdater.logger = electronLog;
+    autoUpdater.autoDownload = false;
+
+    const oneDay = 1 * 24 * 60 * 60 * 1000;
+    setInterval(() => checkForUpdatesAndNotify(mainWindow), oneDay);
+    checkForUpdatesAndNotify(mainWindow);
+};
+
+/**
+ * Check for app update check ignoring any previously saved skips / mutes.
+ */
+export const forceCheckForAppUpdates = (mainWindow: BrowserWindow) => {
+    userPreferencesStore.delete("skipAppVersion");
+    userPreferencesStore.delete("muteUpdateNotificationVersion");
+    checkForUpdatesAndNotify(mainWindow);
+};
+
+const checkForUpdatesAndNotify = async (mainWindow: BrowserWindow) => {
+    try {
+        const { updateInfo } = await autoUpdater.checkForUpdates();
+        const { version } = updateInfo;
+
+        log.debug(() => `Checking for updates found version ${version}`);
+
+        if (compareVersions(version, app.getVersion()) <= 0) {
+            log.debug(() => "Skipping update, already at latest version");
+            return;
+        }
+
+        if (version === userPreferencesStore.get("skipAppVersion")) {
+            log.info(`User chose to skip version ${version}`);
+            return;
+        }
+
+        const mutedVersion = userPreferencesStore.get(
+            "muteUpdateNotificationVersion",
+        );
+        if (version === mutedVersion) {
+            log.info(
+                `User has muted update notifications for version ${version}`,
+            );
+            return;
+        }
+
+        const showUpdateDialog = (updateInfo: AppUpdateInfo) =>
+            mainWindow.webContents.send("appUpdateAvailable", updateInfo);
+
+        log.debug(() => "Attempting auto update");
+        autoUpdater.downloadUpdate();
+
+        let timeout: NodeJS.Timeout;
+        const fiveMinutes = 5 * 60 * 1000;
+        autoUpdater.on("update-downloaded", () => {
+            timeout = setTimeout(
+                () => showUpdateDialog({ autoUpdatable: true, version }),
+                fiveMinutes,
+            );
+        });
+        autoUpdater.on("error", (error) => {
+            clearTimeout(timeout);
+            log.error("Auto update failed", error);
+            showUpdateDialog({ autoUpdatable: false, version });
+        });
+
+        setIsUpdateAvailable(true);
+    } catch (e) {
+        log.error("checkForUpdateAndNotify failed", e);
+    }
+};
+
+/**
+ * Return the version of the desktop app
+ *
+ * The return value is of the form `v1.2.3`.
+ */
+export const appVersion = () => `v${app.getVersion()}`;
+
+export const updateAndRestart = () => {
+    log.info("Restarting the app to apply update");
+    setIsAppQuitting(true);
+    autoUpdater.quitAndInstall();
+};
+
+export const updateOnNextRestart = (version: string) =>
+    userPreferencesStore.set("muteUpdateNotificationVersion", version);
+
+export const skipAppUpdate = (version: string) =>
+    userPreferencesStore.set("skipAppVersion", version);

+ 0 - 120
desktop/src/services/appUpdater.ts

@@ -1,120 +0,0 @@
-import { compareVersions } from "compare-versions";
-import { app, BrowserWindow } from "electron";
-import { default as electronLog } from "electron-log";
-import { autoUpdater } from "electron-updater";
-import { setIsAppQuitting, setIsUpdateAvailable } from "../main";
-import log from "../main/log";
-import { AppUpdateInfo } from "../types/ipc";
-import {
-    clearMuteUpdateNotificationVersion,
-    clearSkipAppVersion,
-    getMuteUpdateNotificationVersion,
-    getSkipAppVersion,
-    setMuteUpdateNotificationVersion,
-    setSkipAppVersion,
-} from "./userPreference";
-
-const FIVE_MIN_IN_MICROSECOND = 5 * 60 * 1000;
-const ONE_DAY_IN_MICROSECOND = 1 * 24 * 60 * 60 * 1000;
-
-export function setupAutoUpdater(mainWindow: BrowserWindow) {
-    autoUpdater.logger = electronLog;
-    autoUpdater.autoDownload = false;
-    checkForUpdateAndNotify(mainWindow);
-    setInterval(
-        () => checkForUpdateAndNotify(mainWindow),
-        ONE_DAY_IN_MICROSECOND,
-    );
-}
-
-export function forceCheckForUpdateAndNotify(mainWindow: BrowserWindow) {
-    try {
-        clearSkipAppVersion();
-        clearMuteUpdateNotificationVersion();
-        checkForUpdateAndNotify(mainWindow);
-    } catch (e) {
-        log.error("forceCheckForUpdateAndNotify failed", e);
-    }
-}
-
-async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
-    try {
-        log.debug(() => "checkForUpdateAndNotify");
-        const { updateInfo } = await autoUpdater.checkForUpdates();
-        log.debug(() => `Update version ${updateInfo.version}`);
-        if (compareVersions(updateInfo.version, app.getVersion()) <= 0) {
-            log.debug(() => "Skipping update, already at latest version");
-            return;
-        }
-        const skipAppVersion = getSkipAppVersion();
-        if (skipAppVersion && updateInfo.version === skipAppVersion) {
-            log.info(`User chose to skip version ${updateInfo.version}`);
-            return;
-        }
-
-        let timeout: NodeJS.Timeout;
-        log.debug(() => "Attempting auto update");
-        autoUpdater.downloadUpdate();
-        const muteUpdateNotificationVersion =
-            getMuteUpdateNotificationVersion();
-        if (
-            muteUpdateNotificationVersion &&
-            updateInfo.version === muteUpdateNotificationVersion
-        ) {
-            log.info(
-                `User has muted update notifications for version ${updateInfo.version}`,
-            );
-            return;
-        }
-        autoUpdater.on("update-downloaded", () => {
-            timeout = setTimeout(
-                () =>
-                    showUpdateDialog(mainWindow, {
-                        autoUpdatable: true,
-                        version: updateInfo.version,
-                    }),
-                FIVE_MIN_IN_MICROSECOND,
-            );
-        });
-        autoUpdater.on("error", (error) => {
-            clearTimeout(timeout);
-            log.error("Auto update failed", error);
-            showUpdateDialog(mainWindow, {
-                autoUpdatable: false,
-                version: updateInfo.version,
-            });
-        });
-
-        setIsUpdateAvailable(true);
-    } catch (e) {
-        log.error("checkForUpdateAndNotify failed", e);
-    }
-}
-
-export function updateAndRestart() {
-    log.info("user quit the app");
-    setIsAppQuitting(true);
-    autoUpdater.quitAndInstall();
-}
-
-/**
- * Return the version of the desktop app
- *
- * The return value is of the form `v1.2.3`.
- */
-export const appVersion = () => `v${app.getVersion()}`;
-
-export function skipAppUpdate(version: string) {
-    setSkipAppVersion(version);
-}
-
-export function muteUpdateNotification(version: string) {
-    setMuteUpdateNotificationVersion(version);
-}
-
-function showUpdateDialog(
-    mainWindow: BrowserWindow,
-    updateInfo: AppUpdateInfo,
-) {
-    mainWindow.webContents.send("show-update-dialog", updateInfo);
-}

+ 1 - 25
desktop/src/services/userPreference.ts

@@ -1,4 +1,4 @@
-import { userPreferencesStore } from "../stores/userPreferences.store";
+import { userPreferencesStore } from "../stores/user-preferences";
 
 
 export function getHideDockIconPreference() {
 export function getHideDockIconPreference() {
     return userPreferencesStore.get("hideDockIcon");
     return userPreferencesStore.get("hideDockIcon");
@@ -7,27 +7,3 @@ export function getHideDockIconPreference() {
 export function setHideDockIconPreference(shouldHideDockIcon: boolean) {
 export function setHideDockIconPreference(shouldHideDockIcon: boolean) {
     userPreferencesStore.set("hideDockIcon", shouldHideDockIcon);
     userPreferencesStore.set("hideDockIcon", shouldHideDockIcon);
 }
 }
-
-export function getSkipAppVersion() {
-    return userPreferencesStore.get("skipAppVersion");
-}
-
-export function setSkipAppVersion(version: string) {
-    userPreferencesStore.set("skipAppVersion", version);
-}
-
-export function getMuteUpdateNotificationVersion() {
-    return userPreferencesStore.get("muteUpdateNotificationVersion");
-}
-
-export function setMuteUpdateNotificationVersion(version: string) {
-    userPreferencesStore.set("muteUpdateNotificationVersion", version);
-}
-
-export function clearSkipAppVersion() {
-    userPreferencesStore.delete("skipAppVersion");
-}
-
-export function clearMuteUpdateNotificationVersion() {
-    userPreferencesStore.delete("muteUpdateNotificationVersion");
-}

+ 7 - 2
desktop/src/stores/userPreferences.store.ts → desktop/src/stores/user-preferences.ts

@@ -1,7 +1,12 @@
 import Store, { Schema } from "electron-store";
 import Store, { Schema } from "electron-store";
-import type { UserPreferencesType } from "../types/main";
 
 
-const userPreferencesSchema: Schema<UserPreferencesType> = {
+interface UserPreferencesSchema {
+    hideDockIcon: boolean;
+    skipAppVersion?: string;
+    muteUpdateNotificationVersion?: string;
+}
+
+const userPreferencesSchema: Schema<UserPreferencesSchema> = {
     hideDockIcon: {
     hideDockIcon: {
         type: "boolean",
         type: "boolean",
     },
     },

+ 0 - 6
desktop/src/types/main.ts

@@ -29,9 +29,3 @@ export const FILE_PATH_KEYS: {
 export interface SafeStorageStoreType {
 export interface SafeStorageStoreType {
     encryptionKey: string;
     encryptionKey: string;
 }
 }
-
-export interface UserPreferencesType {
-    hideDockIcon: boolean;
-    skipAppVersion: string;
-    muteUpdateNotificationVersion: string;
-}

+ 20 - 22
web/apps/photos/src/pages/_app.tsx

@@ -154,28 +154,26 @@ export default function App({ Component, pageProps }: AppProps) {
 
 
     useEffect(() => {
     useEffect(() => {
         const electron = globalThis.electron;
         const electron = globalThis.electron;
-        if (electron) {
-            const showUpdateDialog = (updateInfo: AppUpdateInfo) => {
-                if (updateInfo.autoUpdatable) {
-                    setDialogMessage(
-                        getUpdateReadyToInstallMessage(updateInfo),
-                    );
-                } else {
-                    setNotificationAttributes({
-                        endIcon: <ArrowForward />,
-                        variant: "secondary",
-                        message: t("UPDATE_AVAILABLE"),
-                        onClick: () =>
-                            setDialogMessage(
-                                getUpdateAvailableForDownloadMessage(
-                                    updateInfo,
-                                ),
-                            ),
-                    });
-                }
-            };
-            electron.registerUpdateEventListener(showUpdateDialog);
-        }
+        if (!electron) return;
+
+        const showUpdateDialog = (updateInfo: AppUpdateInfo) => {
+            if (updateInfo.autoUpdatable) {
+                setDialogMessage(getUpdateReadyToInstallMessage(updateInfo));
+            } else {
+                setNotificationAttributes({
+                    endIcon: <ArrowForward />,
+                    variant: "secondary",
+                    message: t("UPDATE_AVAILABLE"),
+                    onClick: () =>
+                        setDialogMessage(
+                            getUpdateAvailableForDownloadMessage(updateInfo),
+                        ),
+                });
+            }
+        };
+        electron.onAppUpdateAvailable(showUpdateDialog);
+
+        return () => electron.onAppUpdateAvailable(undefined);
     }, []);
     }, []);
 
 
     useEffect(() => {
     useEffect(() => {

+ 10 - 10
web/apps/photos/src/utils/ui/index.tsx

@@ -1,3 +1,4 @@
+import { ensureElectron } from "@/next/electron";
 import { AppUpdateInfo } from "@/next/types/ipc";
 import { AppUpdateInfo } from "@/next/types/ipc";
 import { logoutUser } from "@ente/accounts/services/user";
 import { logoutUser } from "@ente/accounts/services/user";
 import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types";
 import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types";
@@ -52,35 +53,34 @@ export const getTrashFileMessage = (deleteFileHelper): DialogBoxAttributes => ({
     close: { text: t("CANCEL") },
     close: { text: t("CANCEL") },
 });
 });
 
 
-export const getUpdateReadyToInstallMessage = (
-    updateInfo: AppUpdateInfo,
-): DialogBoxAttributes => ({
+export const getUpdateReadyToInstallMessage = ({
+    version,
+}: AppUpdateInfo): DialogBoxAttributes => ({
     icon: <AutoAwesomeOutlinedIcon />,
     icon: <AutoAwesomeOutlinedIcon />,
     title: t("UPDATE_AVAILABLE"),
     title: t("UPDATE_AVAILABLE"),
     content: t("UPDATE_INSTALLABLE_MESSAGE"),
     content: t("UPDATE_INSTALLABLE_MESSAGE"),
     proceed: {
     proceed: {
-        action: () => globalThis.electron?.updateAndRestart(),
+        action: () => ensureElectron().updateAndRestart(),
         text: t("INSTALL_NOW"),
         text: t("INSTALL_NOW"),
         variant: "accent",
         variant: "accent",
     },
     },
     close: {
     close: {
         text: t("INSTALL_ON_NEXT_LAUNCH"),
         text: t("INSTALL_ON_NEXT_LAUNCH"),
         variant: "secondary",
         variant: "secondary",
-        action: () =>
-            globalThis.electron?.muteUpdateNotification(updateInfo.version),
+        action: () => ensureElectron().updateOnNextRestart(version),
     },
     },
 });
 });
 
 
-export const getUpdateAvailableForDownloadMessage = (
-    updateInfo: AppUpdateInfo,
-): DialogBoxAttributes => ({
+export const getUpdateAvailableForDownloadMessage = ({
+    version,
+}: AppUpdateInfo): DialogBoxAttributes => ({
     icon: <AutoAwesomeOutlinedIcon />,
     icon: <AutoAwesomeOutlinedIcon />,
     title: t("UPDATE_AVAILABLE"),
     title: t("UPDATE_AVAILABLE"),
     content: t("UPDATE_AVAILABLE_MESSAGE"),
     content: t("UPDATE_AVAILABLE_MESSAGE"),
     close: {
     close: {
         text: t("IGNORE_THIS_VERSION"),
         text: t("IGNORE_THIS_VERSION"),
         variant: "secondary",
         variant: "secondary",
-        action: () => globalThis.electron?.skipAppUpdate(updateInfo.version),
+        action: () => ensureElectron().skipAppUpdate(version),
     },
     },
     proceed: {
     proceed: {
         action: downloadApp,
         action: downloadApp,

+ 0 - 1
web/packages/accounts/api/user.ts

@@ -1,4 +1,3 @@
-import log from "@/next/log";
 import {
 import {
     RecoveryKey,
     RecoveryKey,
     TwoFactorRecoveryResponse,
     TwoFactorRecoveryResponse,

+ 42 - 13
web/packages/next/types/ipc.ts

@@ -37,7 +37,11 @@ export enum PICKED_UPLOAD_TYPE {
 export interface Electron {
 export interface Electron {
     // - General
     // - General
 
 
-    /** Return the version of the desktop app. */
+    /**
+     * Return the version of the desktop app.
+     *
+     * The return value is of the form `v1.2.3`.
+     */
     appVersion: () => Promise<string>;
     appVersion: () => Promise<string>;
 
 
     /**
     /**
@@ -97,6 +101,43 @@ export interface Electron {
      */
      */
     onMainWindowFocus: (cb?: () => void) => void;
     onMainWindowFocus: (cb?: () => void) => void;
 
 
+    // - App update
+
+    /**
+     * Set or clear the callback {@link cb} to invoke whenever a new
+     * (actionable) app update is available. This allows the Node.js layer to
+     * ask the renderer to show an "Update available" dialog to the user.
+     *
+     * Note: Setting a callback clears any previous callbacks.
+     */
+    onAppUpdateAvailable: (
+        cb?: ((updateInfo: AppUpdateInfo) => void) | undefined,
+    ) => void;
+
+    /**
+     * Restart the app to apply the latest available update.
+     *
+     * This is expected to be called in response to {@link onAppUpdateAvailable}
+     * if the user so wishes.
+     */
+    updateAndRestart: () => void;
+
+    /**
+     * Mute update notifications for the given {@link version}. This allows us
+     * to implement the "Install on next launch" functionality in response to
+     * the {@link onAppUpdateAvailable} event.
+     */
+    updateOnNextRestart: (version: string) => void;
+
+    /**
+     * Skip the app update with the given {@link version}.
+     *
+     * This is expected to be called in response to {@link onAppUpdateAvailable}
+     * if the user so wishes. It will remember this {@link version} as having
+     * been marked as skipped so that we don't prompt the user again.
+     */
+    skipAppUpdate: (version: string) => void;
+
     /**
     /**
      * A subset of filesystem access APIs.
      * A subset of filesystem access APIs.
      *
      *
@@ -132,18 +173,6 @@ export interface Electron {
      * the dataflow.
      * the dataflow.
      */
      */
 
 
-    // - App update
-
-    updateAndRestart: () => void;
-
-    skipAppUpdate: (version: string) => void;
-
-    muteUpdateNotification: (version: string) => void;
-
-    registerUpdateEventListener: (
-        showUpdateDialog: (updateInfo: AppUpdateInfo) => void,
-    ) => void;
-
     // - Conversion
     // - Conversion
 
 
     convertToJPEG: (
     convertToJPEG: (