diff --git a/desktop/src/main/menu.ts b/desktop/src/main/menu.ts index 024a226f1..b6fa7acfe 100644 --- a/desktop/src/main/menu.ts +++ b/desktop/src/main/menu.ts @@ -19,7 +19,7 @@ export const createApplicationMenu = async (mainWindow: BrowserWindow) => { // Whenever the menu is redrawn the current value of these variables is used // to set the checked state for the various settings checkboxes. let isAutoLaunchEnabled = await autoLauncher.isEnabled(); - let shouldHideDockIcon = userPreferences.get("hideDockIcon"); + let shouldHideDockIcon = !!userPreferences.get("hideDockIcon"); const macOSOnly = (options: MenuItemConstructorOptions[]) => process.platform == "darwin" ? options : []; diff --git a/desktop/src/main/services/app-update.ts b/desktop/src/main/services/app-update.ts index bc4bd38d6..8d66cb8c3 100644 --- a/desktop/src/main/services/app-update.ts +++ b/desktop/src/main/services/app-update.ts @@ -36,18 +36,21 @@ const checkForUpdatesAndNotify = async (mainWindow: BrowserWindow) => { log.debug(() => `Update check found version ${version}`); + if (!version) + throw new Error("Unexpected empty version obtained from auto-updater"); + if (compareVersions(version, app.getVersion()) <= 0) { log.debug(() => "Skipping update, already at latest version"); return; } - if (version === userPreferences.get("skipAppVersion")) { + if (version == userPreferences.get("skipAppVersion")) { log.info(`User chose to skip version ${version}`); return; } const mutedVersion = userPreferences.get("muteUpdateNotificationVersion"); - if (version === mutedVersion) { + if (version == mutedVersion) { log.info(`User has muted update notifications for version ${version}`); return; } diff --git a/desktop/src/main/services/store.ts b/desktop/src/main/services/store.ts index 1884efbc5..20cc91ea4 100644 --- a/desktop/src/main/services/store.ts +++ b/desktop/src/main/services/store.ts @@ -9,8 +9,8 @@ import { watchStore } from "../stores/watch"; * This is useful to reset state when the user logs out. */ export const clearStores = () => { - uploadStatusStore.clear(); safeStorageStore.clear(); + uploadStatusStore.clear(); watchStore.clear(); }; diff --git a/desktop/src/main/services/upload.ts b/desktop/src/main/services/upload.ts index 7871b56fd..795ce48af 100644 --- a/desktop/src/main/services/upload.ts +++ b/desktop/src/main/services/upload.ts @@ -46,7 +46,7 @@ export const pathOrZipItemSize = async ( }; export const pendingUploads = async (): Promise => { - const collectionName = uploadStatusStore.get("collectionName"); + const collectionName = uploadStatusStore.get("collectionName") ?? undefined; const allFilePaths = uploadStatusStore.get("filePaths") ?? []; const filePaths = allFilePaths.filter((f) => existsSync(f)); @@ -62,7 +62,7 @@ export const pendingUploads = async (): Promise => { // // This potentially can be cause us to try reuploading an already uploaded // file, but the dedup logic will kick in at that point so no harm will come - // off it. + // of it. if (allZipItems === undefined) { const allZipPaths = uploadStatusStore.get("filePaths") ?? []; const zipPaths = allZipPaths.filter((f) => existsSync(f)); @@ -82,20 +82,65 @@ export const pendingUploads = async (): Promise => { }; }; -export const setPendingUploads = (pendingUploads: PendingUploads) => - uploadStatusStore.set(pendingUploads); +/** + * [Note: Missing values in electron-store] + * + * Suppose we were to create a store like this: + * + * const store = new Store({ + * schema: { + * foo: { type: "string" }, + * bars: { type: "array", items: { type: "string" } }, + * }, + * }); + * + * If we fetch `store.get("foo")` or `store.get("bars")`, we get `undefined`. + * But if we try to set these back to `undefined`, say `store.set("foo", + * someUndefValue)`, we get asked to + * + * TypeError: Use `delete()` to clear values + * + * This happens even if we do bulk object updates, e.g. with a JS object that + * has undefined keys: + * + * > TypeError: Setting a value of type `undefined` for key `collectionName` is + * > not allowed as it's not supported by JSON + * + * So what should the TypeScript type for "foo" be? + * + * If it is were to not include the possibility of `undefined`, then the type + * would lie because `store.get("foo")` can indeed be `undefined. But if we were + * to include the possibility of `undefined`, then trying to `store.set("foo", + * someUndefValue)` will throw. + * + * The approach we take is to rely on false-y values (empty strings and empty + * arrays) to indicate missing values, and then converting those to `undefined` + * when reading from the store, and converting `undefined` to the corresponding + * false-y value when writing. + */ +export const setPendingUploads = ({ + collectionName, + filePaths, + zipItems, +}: PendingUploads) => { + uploadStatusStore.set({ + collectionName: collectionName ?? "", + filePaths: filePaths, + zipItems: zipItems, + }); +}; export const markUploadedFiles = (paths: string[]) => { - const existing = uploadStatusStore.get("filePaths"); - const updated = existing?.filter((p) => !paths.includes(p)); + const existing = uploadStatusStore.get("filePaths") ?? []; + const updated = existing.filter((p) => !paths.includes(p)); uploadStatusStore.set("filePaths", updated); }; export const markUploadedZipItems = ( items: [zipPath: string, entryName: string][], ) => { - const existing = uploadStatusStore.get("zipItems"); - const updated = existing?.filter( + const existing = uploadStatusStore.get("zipItems") ?? []; + const updated = existing.filter( (z) => !items.some((e) => z[0] == e[0] && z[1] == e[1]), ); uploadStatusStore.set("zipItems", updated); diff --git a/desktop/src/main/stores/safe-storage.ts b/desktop/src/main/stores/safe-storage.ts index 1e1369db8..040af1f3e 100644 --- a/desktop/src/main/stores/safe-storage.ts +++ b/desktop/src/main/stores/safe-storage.ts @@ -1,7 +1,7 @@ import Store, { Schema } from "electron-store"; interface SafeStorageStore { - encryptionKey: string; + encryptionKey?: string; } const safeStorageSchema: Schema = { diff --git a/desktop/src/main/stores/upload-status.ts b/desktop/src/main/stores/upload-status.ts index f098e9fc5..472f38a7f 100644 --- a/desktop/src/main/stores/upload-status.ts +++ b/desktop/src/main/stores/upload-status.ts @@ -6,24 +6,24 @@ export interface UploadStatusStore { * * Not all pending uploads will have an associated collection. */ - collectionName: string | undefined; + collectionName?: string; /** * Paths to regular files that are pending upload. * * This should generally be present, albeit empty, but it is marked optional * in sympathy with its siblings. */ - filePaths: string[] | undefined; + filePaths?: string[]; /** * Each item is the path to a zip file and the name of an entry within it. * * This is marked optional since legacy stores will not have it. */ - zipItems: [zipPath: string, entryName: string][] | undefined; + zipItems?: [zipPath: string, entryName: string][]; /** * @deprecated Legacy paths to zip files, now subsumed into zipItems. */ - zipPaths: string[] | undefined; + zipPaths?: string[]; } const uploadStatusSchema: Schema = { diff --git a/desktop/src/main/stores/user-preferences.ts b/desktop/src/main/stores/user-preferences.ts index b4a02bc5b..f3b192989 100644 --- a/desktop/src/main/stores/user-preferences.ts +++ b/desktop/src/main/stores/user-preferences.ts @@ -1,7 +1,7 @@ import Store, { Schema } from "electron-store"; interface UserPreferences { - hideDockIcon: boolean; + hideDockIcon?: boolean; skipAppVersion?: string; muteUpdateNotificationVersion?: string; }