Handle nightly builds (or versions without changelogs)
This commit is contained in:
@@ -21,7 +21,6 @@ import {
|
||||
attachFSWatchIPCHandlers,
|
||||
attachIPCHandlers,
|
||||
attachLogoutIPCHandler,
|
||||
attachWindowIPCHandlers,
|
||||
} from "./main/ipc";
|
||||
import log, { initLogging } from "./main/log";
|
||||
import { createApplicationMenu, createTrayContextMenu } from "./main/menu";
|
||||
@@ -117,7 +116,6 @@ const main = () => {
|
||||
// Setup IPC and streams.
|
||||
const watcher = createWatcher(mainWindow);
|
||||
attachIPCHandlers();
|
||||
attachWindowIPCHandlers(mainWindow);
|
||||
attachFSWatchIPCHandlers(watcher);
|
||||
attachLogoutIPCHandler(watcher);
|
||||
registerStreamProtocol();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
import type { FSWatcher } from "chokidar";
|
||||
import { ipcMain, type BrowserWindow } from "electron/main";
|
||||
import { ipcMain } from "electron/main";
|
||||
import type {
|
||||
CollectionMapping,
|
||||
FolderWatch,
|
||||
@@ -19,8 +19,6 @@ import type {
|
||||
import { logToDisk } from "./log";
|
||||
import {
|
||||
appVersion,
|
||||
canShowWhatsNew,
|
||||
didShowWhatsNew,
|
||||
skipAppUpdate,
|
||||
updateAndRestart,
|
||||
updateOnNextRestart,
|
||||
@@ -48,7 +46,12 @@ import {
|
||||
computeCLIPTextEmbeddingIfAvailable,
|
||||
} from "./services/ml-clip";
|
||||
import { computeFaceEmbeddings, detectFaces } from "./services/ml-face";
|
||||
import { encryptionKey, saveEncryptionKey } from "./services/store";
|
||||
import {
|
||||
encryptionKey,
|
||||
lastShownChangelogVersion,
|
||||
saveEncryptionKey,
|
||||
setLastShownChangelogVersion,
|
||||
} from "./services/store";
|
||||
import {
|
||||
clearPendingUploads,
|
||||
listZipItems,
|
||||
@@ -103,11 +106,19 @@ export const attachIPCHandlers = () => {
|
||||
|
||||
ipcMain.handle("selectDirectory", () => selectDirectory());
|
||||
|
||||
ipcMain.handle("encryptionKey", () => encryptionKey());
|
||||
|
||||
ipcMain.handle("saveEncryptionKey", (_, encryptionKey: string) =>
|
||||
saveEncryptionKey(encryptionKey),
|
||||
);
|
||||
|
||||
ipcMain.handle("encryptionKey", () => encryptionKey());
|
||||
ipcMain.handle("lastShownChangelogVersion", () =>
|
||||
lastShownChangelogVersion(),
|
||||
);
|
||||
|
||||
ipcMain.handle("setLastShownChangelogVersion", (_, version: number) =>
|
||||
setLastShownChangelogVersion(version),
|
||||
);
|
||||
|
||||
// - App update
|
||||
|
||||
@@ -119,8 +130,6 @@ export const attachIPCHandlers = () => {
|
||||
|
||||
ipcMain.on("skipAppUpdate", (_, version: string) => skipAppUpdate(version));
|
||||
|
||||
ipcMain.on("didShowWhatsNew", () => didShowWhatsNew());
|
||||
|
||||
// - FS
|
||||
|
||||
ipcMain.handle("fsExists", (_, path: string) => fsExists(path));
|
||||
@@ -220,19 +229,6 @@ export const attachIPCHandlers = () => {
|
||||
ipcMain.handle("clearPendingUploads", () => clearPendingUploads());
|
||||
};
|
||||
|
||||
/**
|
||||
* Sibling of {@link attachIPCHandlers} that attaches handlers that need access
|
||||
* to the main window for their functioning.
|
||||
*
|
||||
* @param mainWindow Our app's main {@link BrowserWindow}. It is usually needed
|
||||
* by these handler to send messages back to the main window's `webContents`.
|
||||
*/
|
||||
export const attachWindowIPCHandlers = (mainWindow: BrowserWindow) => {
|
||||
// - App update
|
||||
|
||||
ipcMain.on("canShowWhatsNew", () => canShowWhatsNew(mainWindow));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sibling of {@link attachIPCHandlers} that attaches handlers specific to the
|
||||
* watch folder functionality.
|
||||
|
||||
@@ -180,26 +180,3 @@ export const updateOnNextRestart = (version: string) =>
|
||||
|
||||
export const skipAppUpdate = (version: string) =>
|
||||
userPreferences.set("skipAppVersion", version);
|
||||
|
||||
/**
|
||||
* Invoked when the renderer attaches a callback for {@link onShowWhatsNew}.
|
||||
* This can be taken as a signal that the UI is ready and will be able to show
|
||||
* the What's New screen if we need to tell it to.
|
||||
*
|
||||
* See: [Note: Conditions for showing the "What's new" screen]
|
||||
*/
|
||||
export const canShowWhatsNew = (mainWindow: BrowserWindow) => {
|
||||
const version = userPreferences.get("whatsNewShownVersion");
|
||||
if (version && compareVersions(app.getVersion(), version) <= 0) {
|
||||
// We have shown What's New earlier, but it was for a version same as or
|
||||
// newer than the current version. Nothing to do now.
|
||||
return;
|
||||
}
|
||||
mainWindow.webContents.send("showWhatsNew");
|
||||
};
|
||||
|
||||
/**
|
||||
* End the sequence started by {@link canShowWhatsNew}.
|
||||
*/
|
||||
export const didShowWhatsNew = () =>
|
||||
userPreferences.set("whatsNewShownVersion", app.getVersion());
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { safeStorage } from "electron/main";
|
||||
import { safeStorageStore } from "../stores/safe-storage";
|
||||
import { uploadStatusStore } from "../stores/upload-status";
|
||||
import { userPreferences } from "../stores/user-preferences";
|
||||
import { watchStore } from "../stores/watch";
|
||||
|
||||
/**
|
||||
* Clear all stores except user preferences.
|
||||
*
|
||||
* This is useful to reset state when the user logs out.
|
||||
* This function is useful to reset state when the user logs out. User
|
||||
* preferences are preserved since they contain things tied to the person using
|
||||
* the app or other machine specific state not tied to the account they were
|
||||
* using inside the app.
|
||||
*/
|
||||
export const clearStores = () => {
|
||||
safeStorageStore.clear();
|
||||
@@ -32,3 +36,9 @@ export const encryptionKey = (): string | undefined => {
|
||||
const keyBuffer = Buffer.from(b64EncryptedKey, "base64");
|
||||
return safeStorage.decryptString(keyBuffer);
|
||||
};
|
||||
|
||||
export const lastShownChangelogVersion = (): number | undefined =>
|
||||
userPreferences.get("lastShownChangelogVersion");
|
||||
|
||||
export const setLastShownChangelogVersion = (version: number) =>
|
||||
userPreferences.set("lastShownChangelogVersion", version);
|
||||
|
||||
@@ -10,9 +10,11 @@ interface UserPreferences {
|
||||
skipAppVersion?: string;
|
||||
muteUpdateNotificationVersion?: string;
|
||||
/**
|
||||
* The app version for which we last showed the What's New screen.
|
||||
* The changelog version for which we last showed the "What's new" screen.
|
||||
*
|
||||
* See: [Note: Conditions for showing "What's new"]
|
||||
*/
|
||||
whatsNewShownVersion?: string;
|
||||
lastShownChangelogVersion?: number;
|
||||
/**
|
||||
* The last position and size of our app's window.
|
||||
*
|
||||
@@ -37,7 +39,7 @@ const userPreferencesSchema: Schema<UserPreferences> = {
|
||||
hideDockIcon: { type: "boolean" },
|
||||
skipAppVersion: { type: "string" },
|
||||
muteUpdateNotificationVersion: { type: "string" },
|
||||
whatsNewShownVersion: { type: "string" },
|
||||
lastShownChangelogVersion: { type: "number" },
|
||||
windowBounds: {
|
||||
properties: {
|
||||
x: { type: "number" },
|
||||
|
||||
@@ -72,6 +72,12 @@ const encryptionKey = () => ipcRenderer.invoke("encryptionKey");
|
||||
const saveEncryptionKey = (encryptionKey: string) =>
|
||||
ipcRenderer.invoke("saveEncryptionKey", encryptionKey);
|
||||
|
||||
const lastShownChangelogVersion = () =>
|
||||
ipcRenderer.invoke("lastShownChangelogVersion");
|
||||
|
||||
const setLastShownChangelogVersion = (version: number) =>
|
||||
ipcRenderer.invoke("setLastShownChangelogVersion", version);
|
||||
|
||||
const onMainWindowFocus = (cb: (() => void) | undefined) => {
|
||||
ipcRenderer.removeAllListeners("mainWindowFocus");
|
||||
if (cb) ipcRenderer.on("mainWindowFocus", cb);
|
||||
@@ -104,18 +110,6 @@ const skipAppUpdate = (version: string) => {
|
||||
ipcRenderer.send("skipAppUpdate", version);
|
||||
};
|
||||
|
||||
const onShowWhatsNew = (cb: (() => boolean) | undefined) => {
|
||||
ipcRenderer.removeAllListeners("showWhatsNew");
|
||||
if (cb) {
|
||||
ipcRenderer.on("showWhatsNew", () => {
|
||||
if (cb()) {
|
||||
ipcRenderer.send("didShowWhatsNew");
|
||||
}
|
||||
});
|
||||
ipcRenderer.send("canShowWhatsNew");
|
||||
}
|
||||
};
|
||||
|
||||
// - FS
|
||||
|
||||
const fsExists = (path: string) => ipcRenderer.invoke("fsExists", path);
|
||||
@@ -323,6 +317,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
logout,
|
||||
encryptionKey,
|
||||
saveEncryptionKey,
|
||||
lastShownChangelogVersion,
|
||||
setLastShownChangelogVersion,
|
||||
onMainWindowFocus,
|
||||
onOpenURL,
|
||||
|
||||
@@ -332,7 +328,6 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
updateAndRestart,
|
||||
updateOnNextRestart,
|
||||
skipAppUpdate,
|
||||
onShowWhatsNew,
|
||||
|
||||
// - FS
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { WhatsNew } from "@/new/photos/components/WhatsNew";
|
||||
import { shouldShowWhatsNew } from "@/new/photos/services/changelog";
|
||||
import { fetchAndSaveFeatureFlagsIfNeeded } from "@/new/photos/services/feature-flags";
|
||||
import log from "@/next/log";
|
||||
import { CenteredFlex } from "@ente/shared/components/Container";
|
||||
@@ -391,17 +392,13 @@ export default function Gallery() {
|
||||
if (electron) {
|
||||
// void clipService.setupOnFileUploadListener();
|
||||
electron.onMainWindowFocus(() => syncWithRemote(false, true));
|
||||
electron.onShowWhatsNew(() => {
|
||||
setOpenWhatsNew(true);
|
||||
return true;
|
||||
});
|
||||
if (await shouldShowWhatsNew()) setOpenWhatsNew(true);
|
||||
}
|
||||
};
|
||||
main();
|
||||
return () => {
|
||||
clearInterval(syncInterval.current);
|
||||
if (electron) {
|
||||
electron.onShowWhatsNew(undefined);
|
||||
electron.onMainWindowFocus(undefined);
|
||||
clipService.removeOnFileUploadListener();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
} from "@mui/material";
|
||||
import Slide from "@mui/material/Slide";
|
||||
import type { TransitionProps } from "@mui/material/transitions";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { didShowWhatsNew } from "../services/changelog";
|
||||
|
||||
interface WhatsNewProps {
|
||||
/** If `true`, then the dialog is shown. */
|
||||
@@ -22,12 +23,16 @@ interface WhatsNewProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog showing a short summary of interesting-for-the-user things in
|
||||
* this release of the desktop app.
|
||||
* Show a dialog showing a short summary of interesting-for-the-user things
|
||||
* since the last time this dialog was shown.
|
||||
*/
|
||||
export const WhatsNew: React.FC<WhatsNewProps> = ({ open, onClose }) => {
|
||||
const fullScreen = useMediaQuery("(max-width: 428px)");
|
||||
|
||||
useEffect(() => {
|
||||
void didShowWhatsNew();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
{...{ open, fullScreen }}
|
||||
|
||||
39
web/packages/new/photos/services/changelog.ts
Normal file
39
web/packages/new/photos/services/changelog.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ensureElectron } from "@/next/electron";
|
||||
|
||||
/**
|
||||
* The current changelog version.
|
||||
*
|
||||
* [Note: Conditions for showing "What's new"]
|
||||
*
|
||||
* We maintain a "changelog version". This version is an incrementing positive
|
||||
* integer, we increment it whenever we want to show this dialog again. Usually
|
||||
* we'd do this for each app update, but not necessarily.
|
||||
*
|
||||
* The "What's new" dialog is shown when either we do not have a previously
|
||||
* saved changelog version, or if the saved changelog version is less than the
|
||||
* current {@link changelogVersion}.
|
||||
*
|
||||
* The shown changelog version is persisted on the Node.js layer since there we
|
||||
* can store it in the user preferences store, which is not cleared on logout.
|
||||
*
|
||||
* On app start, the Node.js layer waits for the {@link onShowWhatsNew} callback
|
||||
* to get attached. When a callback is attached, it checks the above conditions
|
||||
* and if they are satisfied, it invokes the callback. The callback should
|
||||
* return the current {@link changelogVersion} to allow the Node.js layer to
|
||||
* update the persisted state.
|
||||
*/
|
||||
const changelogVersion = 1;
|
||||
|
||||
/**
|
||||
* Return true if we should show the {@link WhatsNew} dialog.
|
||||
*/
|
||||
export const shouldShowWhatsNew = async () => {
|
||||
const electron = globalThis.electron;
|
||||
if (!electron) return false;
|
||||
const lastShownVersion = (await electron.lastShownChangelogVersion()) ?? 0;
|
||||
return lastShownVersion < changelogVersion;
|
||||
};
|
||||
|
||||
export const didShowWhatsNew = async () =>
|
||||
// We should only have been called if we're in electron.
|
||||
ensureElectron().setLastShownChangelogVersion(changelogVersion);
|
||||
@@ -149,26 +149,21 @@ export interface Electron {
|
||||
skipAppUpdate: (version: string) => void;
|
||||
|
||||
/**
|
||||
* Set or clear the callback {@link cb} to invoke when the app should show
|
||||
* the "What's new" screen for the current version.
|
||||
* Get the persisted version for the last shown changelog.
|
||||
*
|
||||
* Setting a callback clears any previous callbacks.
|
||||
*
|
||||
* [Note: Conditions for showing the "What's new" screen]
|
||||
*
|
||||
* This screen is shown only once per update, and only when the current
|
||||
* version is (sem-versionally) greater than the previous version for which
|
||||
* this dialog was shown (if any). The state about whether or not this
|
||||
* dialog has already been shown is persisted on the Node.js, in the user
|
||||
* preferences store (which is not cleared on logout).
|
||||
*
|
||||
* If the Node.js layer notices an attached callback (and the above
|
||||
* conditions are satisfied), then it invokes the callback. The callback
|
||||
* should return `true` to indicate that the whats new screen was shown so
|
||||
* that the Node.js layer can update the persisted state to avoid showing it
|
||||
* again.
|
||||
* See: [Note: Conditions for showing "What's new"]
|
||||
*/
|
||||
onShowWhatsNew: (cb: (() => boolean) | undefined) => void;
|
||||
lastShownChangelogVersion: () => Promise<number | undefined>;
|
||||
|
||||
/**
|
||||
* Save the given {@link version} to disk as the version of the last shown
|
||||
* changelog.
|
||||
*
|
||||
* The value is saved to a store which is not cleared during logout.
|
||||
*
|
||||
* @see {@link lastShownChangelogVersion}
|
||||
*/
|
||||
setLastShownChangelogVersion: (version: number) => Promise<void>;
|
||||
|
||||
// - FS
|
||||
|
||||
|
||||
Reference in New Issue
Block a user