From 70bfdcafd863f0cf1ba7bba73ab6d116dfb56337 Mon Sep 17 00:00:00 2001 From: Erik Moura Date: Tue, 20 Jun 2023 23:08:38 -0300 Subject: [PATCH 1/5] refactor: move localStorage keys to the `SettingKey` enum --- src/interfaces.ts | 31 +++++ .../components/settings-execution.tsx | 19 +-- src/renderer/state.ts | 129 +++++++++++------- src/renderer/versions.ts | 20 ++- tests/renderer/versions-spec.ts | 8 +- 5 files changed, 134 insertions(+), 73 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index a5dcf8dbe4..5125962d9b 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -184,3 +184,34 @@ export interface PMOperationOptions { dir: string; packageManager: IPackageManager; } + +export enum Setting { + acceleratorsToBlock = 'acceleratorsToBlock', + channelsToShow = 'channelsToShow', + electronMirror = 'electronMirror', + environmentVariables = 'environmentVariables', + executionFlags = 'executionFlags', + fontFamily = 'fontFamily', + fontSize = 'fontSize', + gitHubAvatarUrl = 'gitHubAvatarUrl', + gitHubLogin = 'gitHubLogin', + gitHubName = 'gitHubName', + gitHubPublishAsPublic = 'gitHubPublishAsPublic', + gitHubToken = 'gitHubToken', + hasShownTour = 'hasShownTour', + isClearingConsoleOnRun = 'isClearingConsoleOnRun', + isEnablingElectronLogging = 'isEnablingElectronLogging', + isKeepingUserDataDirs = 'isKeepingUserDataDirs', + isPublishingGistAsRevision = 'isPublishingGistAsRevision', + isUsingSystemTheme = 'isUsingSystemTheme', + knownVersion = 'known-electron-versions', + localVersion = 'local-electron-versions', + packageAuthor = 'packageAuthor', + packageManager = 'packageManager', + showObsoleteVersions = 'showObsoleteVersions', + showUndownloadedVersions = 'showUndownloadedVersions', + theme = 'theme', + version = 'version', +} + +export type SettingKey = keyof typeof Setting; diff --git a/src/renderer/components/settings-execution.tsx b/src/renderer/components/settings-execution.tsx index 95ab62c8ba..5276caad2e 100644 --- a/src/renderer/components/settings-execution.tsx +++ b/src/renderer/components/settings-execution.tsx @@ -12,13 +12,16 @@ import { } from '@blueprintjs/core'; import { observer } from 'mobx-react'; -import { IPackageManager } from '../../interfaces'; +import { IPackageManager, Setting, SettingKey } from '../../interfaces'; import { AppState } from '../state'; -export enum SettingItemType { - EnvVars = 'environmentVariables', - Flags = 'executionFlags', -} +/** + * @TODO make this a proper enum again once we update Typescript + */ +export const SettingItemType = { + EnvVars: Setting.environmentVariables, + Flags: Setting.executionFlags, +} as const; interface ExecutionSettingsProps { appState: AppState; @@ -119,7 +122,7 @@ export const ExecutionSettings = observer( */ public handleSettingsItemChange( event: React.ChangeEvent, - type: SettingItemType, + type: SettingKey, ) { const { name, value } = event.currentTarget; @@ -136,7 +139,7 @@ export const ExecutionSettings = observer( * * @param {SettingItemType} type */ - private addNewSettingsItem(type: SettingItemType) { + private addNewSettingsItem(type: SettingKey) { const array = Object.entries(this.state[type]); this.setState((prevState) => ({ @@ -160,7 +163,7 @@ export const ExecutionSettings = observer( appState.packageManager = value as IPackageManager; }; - public renderDeleteItem(idx: string, type: SettingItemType): JSX.Element { + public renderDeleteItem(idx: string, type: SettingKey): JSX.Element { const updated = this.state[type]; const removeFn = () => { diff --git a/src/renderer/state.ts b/src/renderer/state.ts index 9d59579737..53bb523500 100644 --- a/src/renderer/state.ts +++ b/src/renderer/state.ts @@ -27,6 +27,8 @@ import { OutputOptions, RunnableVersion, SetFiddleOptions, + Setting, + SettingKey, Version, VersionSource, } from '../interfaces'; @@ -64,62 +66,68 @@ export class AppState { }); // -- Persisted settings ------------------ - public theme: string | null = localStorage.getItem('theme'); + public theme: string | null = localStorage.getItem(Setting.theme); public gitHubAvatarUrl: string | null = localStorage.getItem( - 'gitHubAvatarUrl', + Setting.gitHubAvatarUrl, ); - public gitHubName: string | null = localStorage.getItem('gitHubName'); - public gitHubLogin: string | null = localStorage.getItem('gitHubLogin'); + public gitHubName: string | null = localStorage.getItem(Setting.gitHubName); + public gitHubLogin: string | null = localStorage.getItem(Setting.gitHubLogin); public gitHubToken: string | null = - localStorage.getItem('gitHubToken') || null; - public gitHubPublishAsPublic = !!this.retrieve('gitHubPublishAsPublic'); + localStorage.getItem(Setting.gitHubToken) || null; + public gitHubPublishAsPublic = !!this.retrieve(Setting.gitHubPublishAsPublic); public channelsToShow: Array = (this.retrieve( - 'channelsToShow', + Setting.channelsToShow, ) as Array) || [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta, ]; public showObsoleteVersions = !!( - this.retrieve('showObsoleteVersions') ?? false + this.retrieve(Setting.showObsoleteVersions) ?? false ); public showUndownloadedVersions = !!( - this.retrieve('showUndownloadedVersions') ?? true + this.retrieve(Setting.showUndownloadedVersions) ?? true ); - public isKeepingUserDataDirs = !!this.retrieve('isKeepingUserDataDirs'); + public isKeepingUserDataDirs = !!this.retrieve(Setting.isKeepingUserDataDirs); public isEnablingElectronLogging = !!this.retrieve( - 'isEnablingElectronLogging', + Setting.isEnablingElectronLogging, + ); + public isClearingConsoleOnRun = !!this.retrieve( + Setting.isClearingConsoleOnRun, + ); + public isUsingSystemTheme = !!( + this.retrieve(Setting.isUsingSystemTheme) ?? true ); - public isClearingConsoleOnRun = !!this.retrieve('isClearingConsoleOnRun'); - public isUsingSystemTheme = !!(this.retrieve('isUsingSystemTheme') ?? true); public isPublishingGistAsRevision = !!( - this.retrieve('isPublishingGistAsRevision') ?? true + this.retrieve(Setting.isPublishingGistAsRevision) ?? true ); public executionFlags: Array = - (this.retrieve('executionFlags') as Array) === null + (this.retrieve(Setting.executionFlags) as Array) === null ? [] - : (this.retrieve('executionFlags') as Array); + : (this.retrieve(Setting.executionFlags) as Array); public environmentVariables: Array = - (this.retrieve('environmentVariables') as Array) === null + (this.retrieve(Setting.environmentVariables) as Array) === null ? [] - : (this.retrieve('environmentVariables') as Array); + : (this.retrieve(Setting.environmentVariables) as Array); public packageManager: IPackageManager = - (localStorage.getItem('packageManager') as IPackageManager) || 'npm'; + (localStorage.getItem(Setting.packageManager) as IPackageManager) || 'npm'; public acceleratorsToBlock: Array = - (this.retrieve('acceleratorsToBlock') as Array) || []; + (this.retrieve( + Setting.acceleratorsToBlock, + ) as Array) || []; public packageAuthor = - (localStorage.getItem('packageAuthor') as string) ?? + (localStorage.getItem(Setting.packageAuthor) as string) ?? window.ElectronFiddle.getUsername(); public electronMirror: typeof ELECTRON_MIRROR = - (this.retrieve('electronMirror') as typeof ELECTRON_MIRROR) === null + (this.retrieve(Setting.electronMirror) as typeof ELECTRON_MIRROR) === null ? { ...ELECTRON_MIRROR, sourceType: navigator.language === 'zh-CN' ? 'CHINA' : 'DEFAULT', } - : (this.retrieve('electronMirror') as typeof ELECTRON_MIRROR); + : (this.retrieve(Setting.electronMirror) as typeof ELECTRON_MIRROR); public fontFamily: string | undefined = - (localStorage.getItem('fontFamily') as string) || undefined; + (localStorage.getItem(Setting.fontFamily) as string) || undefined; public fontSize: number | undefined = - ((localStorage.getItem('fontSize') as any) as number) || undefined; + ((localStorage.getItem(Setting.fontSize) as any) as number) || undefined; // -- Various session-only state ------------------ public gistId: string | undefined = undefined; @@ -158,7 +166,7 @@ export class AppState { public isSettingsShowing = false; public isThemeDialogShowing = false; public isTokenDialogShowing = false; - public isTourShowing = !localStorage.getItem('hasShownTour'); + public isTourShowing = !localStorage.getItem(Setting.hasShownTour); public isUpdatingElectronVersions = false; // -- Editor Values stored when we close the editor ------------------ @@ -330,40 +338,59 @@ export class AppState { window.ElectronFiddle.addEventListener('before-quit', this.setIsQuitting); // Setup auto-runs - autorun(() => this.save('theme', this.theme)); + autorun(() => this.save(Setting.theme, this.theme)); + autorun(() => + this.save(Setting.isClearingConsoleOnRun, this.isClearingConsoleOnRun), + ); + autorun(() => + this.save(Setting.isUsingSystemTheme, this.isUsingSystemTheme), + ); + autorun(() => + this.save( + Setting.isPublishingGistAsRevision, + this.isPublishingGistAsRevision, + ), + ); + autorun(() => this.save(Setting.gitHubAvatarUrl, this.gitHubAvatarUrl)); + autorun(() => this.save(Setting.gitHubLogin, this.gitHubLogin)); + autorun(() => this.save(Setting.gitHubName, this.gitHubName)); + autorun(() => this.save(Setting.gitHubToken, this.gitHubToken)); + autorun(() => + this.save(Setting.gitHubPublishAsPublic, this.gitHubPublishAsPublic), + ); + autorun(() => + this.save(Setting.isKeepingUserDataDirs, this.isKeepingUserDataDirs), + ); autorun(() => - this.save('isClearingConsoleOnRun', this.isClearingConsoleOnRun), + this.save( + Setting.isEnablingElectronLogging, + this.isEnablingElectronLogging, + ), ); - autorun(() => this.save('isUsingSystemTheme', this.isUsingSystemTheme)); + autorun(() => this.save(Setting.executionFlags, this.executionFlags)); + autorun(() => this.save(Setting.version, this.version)); + autorun(() => this.save(Setting.channelsToShow, this.channelsToShow)); autorun(() => - this.save('isPublishingGistAsRevision', this.isPublishingGistAsRevision), + this.save( + Setting.showUndownloadedVersions, + this.showUndownloadedVersions, + ), ); - autorun(() => this.save('gitHubAvatarUrl', this.gitHubAvatarUrl)); - autorun(() => this.save('gitHubLogin', this.gitHubLogin)); - autorun(() => this.save('gitHubName', this.gitHubName)); - autorun(() => this.save('gitHubToken', this.gitHubToken)); autorun(() => - this.save('gitHubPublishAsPublic', this.gitHubPublishAsPublic), + this.save(Setting.showObsoleteVersions, this.showObsoleteVersions), ); autorun(() => - this.save('isKeepingUserDataDirs', this.isKeepingUserDataDirs), + this.save(Setting.packageManager, this.packageManager ?? 'npm'), ); autorun(() => - this.save('isEnablingElectronLogging', this.isEnablingElectronLogging), + this.save(Setting.acceleratorsToBlock, this.acceleratorsToBlock), ); - autorun(() => this.save('executionFlags', this.executionFlags)); - autorun(() => this.save('version', this.version)); - autorun(() => this.save('channelsToShow', this.channelsToShow)); + autorun(() => this.save(Setting.packageAuthor, this.packageAuthor)); autorun(() => - this.save('showUndownloadedVersions', this.showUndownloadedVersions), + this.save(Setting.electronMirror, this.electronMirror as any), ); - autorun(() => this.save('showObsoleteVersions', this.showObsoleteVersions)); - autorun(() => this.save('packageManager', this.packageManager ?? 'npm')); - autorun(() => this.save('acceleratorsToBlock', this.acceleratorsToBlock)); - autorun(() => this.save('packageAuthor', this.packageAuthor)); - autorun(() => this.save('electronMirror', this.electronMirror as any)); - autorun(() => this.save('fontFamily', this.fontFamily as any)); - autorun(() => this.save('fontSize', this.fontSize as any)); + autorun(() => this.save(Setting.fontFamily, this.fontFamily as any)); + autorun(() => this.save(Setting.fontSize, this.fontSize as any)); // Update our known versions this.updateElectronVersions(); @@ -514,7 +541,7 @@ export class AppState { public disableTour() { this.resetView(); - localStorage.setItem('hasShownTour', 'true'); + localStorage.setItem(Setting.hasShownTour, 'true'); } public showTour() { @@ -935,7 +962,7 @@ export class AppState { * @param {(string | number | Array | Record | null | boolean)} [value] */ private save( - key: string, + key: SettingKey, value?: | string | number @@ -961,7 +988,7 @@ export class AppState { * @param {string} key * @returns {(T | string | null)} */ - private retrieve(key: string): T | string | null { + private retrieve(key: SettingKey): T | string | null { const value = localStorage.getItem(key); return JSON.parse(value || 'null') as T; diff --git a/src/renderer/versions.ts b/src/renderer/versions.ts index 6ccfa5e58a..f67bb018fb 100644 --- a/src/renderer/versions.ts +++ b/src/renderer/versions.ts @@ -2,6 +2,7 @@ import { ElectronReleaseChannel, InstallState, RunnableVersion, + Setting, Version, VersionSource, } from '../interfaces'; @@ -14,8 +15,10 @@ import { normalizeVersion } from './utils/normalize-version'; * @returns {string} */ export function getDefaultVersion(versions: RunnableVersion[]): string { - const key = localStorage.getItem('version'); - if (key && versions.some(({ version }) => version === key)) return key; + const key = localStorage.getItem(Setting.version); + if (key && versions.some(({ version }) => version === key)) { + return key; + } const latestStable = window.ElectronFiddle.getLatestStable(); if (latestStable) return latestStable.version; @@ -48,11 +51,6 @@ export function getReleaseChannel( return ElectronReleaseChannel.stable; } -export const enum VersionKeys { - local = 'local-electron-versions', - known = 'known-electron-versions', -} - export function makeRunnable(ver: Version): RunnableVersion { const ret: RunnableVersion = { ...ver, @@ -112,7 +110,7 @@ export function getLocalVersionForPath( * @returns {Array} */ export function getLocalVersions(): Array { - const fromLs = window.localStorage.getItem(VersionKeys.local); + const fromLs = window.localStorage.getItem(Setting.localVersion); if (fromLs) { try { @@ -148,12 +146,12 @@ export function saveLocalVersions( }); const stringified = JSON.stringify(filteredVersions); - window.localStorage.setItem(VersionKeys.local, stringified); + window.localStorage.setItem(Setting.localVersion, stringified); } function getReleasedVersions(): Array { const versions = window.ElectronFiddle.getReleasedVersions(); - const fromLs = window.localStorage.getItem(VersionKeys.known); + const fromLs = window.localStorage.getItem(Setting.knownVersion); if (fromLs) { try { @@ -181,7 +179,7 @@ export async function fetchVersions(): Promise { // Migrate away from known versions being stored in localStorage // Now that we've fetched new versions, it's safe to delete - window.localStorage.removeItem(VersionKeys.known); + window.localStorage.removeItem(Setting.knownVersion); console.log(`Fetched ${versions.length} new Electron versions`); return versions; diff --git a/tests/renderer/versions-spec.ts b/tests/renderer/versions-spec.ts index 53e710ae6a..75ccabcefd 100644 --- a/tests/renderer/versions-spec.ts +++ b/tests/renderer/versions-spec.ts @@ -1,10 +1,10 @@ import { ElectronReleaseChannel, RunnableVersion, + Setting, VersionSource, } from '../../src/interfaces'; import { - VersionKeys, addLocalVersion, fetchVersions, getDefaultVersion, @@ -109,7 +109,7 @@ describe('versions', () => { saveLocalVersions(mockLocalVersions as Array); expect(window.localStorage.setItem).toBeCalledWith( - VersionKeys.local, + Setting.localVersion, JSON.stringify(mockLocalVersions), ); }); @@ -152,7 +152,9 @@ describe('versions', () => { it('removes knownVersions from localStorage', async () => { (window.ElectronFiddle.fetchVersions as jest.Mock).mockResolvedValue([]); await fetchVersions(); - expect(localStorage.removeItem).toHaveBeenCalledWith(VersionKeys.known); + expect(localStorage.removeItem).toHaveBeenCalledWith( + Setting.knownVersion, + ); }); }); }); From 98420511cab3a15a4bbe9c540bf3cef7efb20435 Mon Sep 17 00:00:00 2001 From: Erik Moura Date: Wed, 21 Jun 2023 00:16:52 -0300 Subject: [PATCH 2/5] fix: make settings changes take effect in all open windows --- src/interfaces.ts | 11 +- .../components/settings-execution.tsx | 16 +- src/renderer/state.ts | 172 ++++++++++++------ src/renderer/versions.ts | 13 +- tests/renderer/versions-spec.ts | 6 +- 5 files changed, 147 insertions(+), 71 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index 5125962d9b..99b19a44d3 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -185,7 +185,7 @@ export interface PMOperationOptions { packageManager: IPackageManager; } -export enum Setting { +export enum GlobalSetting { acceleratorsToBlock = 'acceleratorsToBlock', channelsToShow = 'channelsToShow', electronMirror = 'electronMirror', @@ -196,7 +196,6 @@ export enum Setting { gitHubAvatarUrl = 'gitHubAvatarUrl', gitHubLogin = 'gitHubLogin', gitHubName = 'gitHubName', - gitHubPublishAsPublic = 'gitHubPublishAsPublic', gitHubToken = 'gitHubToken', hasShownTour = 'hasShownTour', isClearingConsoleOnRun = 'isClearingConsoleOnRun', @@ -211,7 +210,13 @@ export enum Setting { showObsoleteVersions = 'showObsoleteVersions', showUndownloadedVersions = 'showUndownloadedVersions', theme = 'theme', +} + +export type GlobalSettingKey = keyof typeof GlobalSetting; + +export enum WindowSpecificSetting { + gitHubPublishAsPublic = 'gitHubPublishAsPublic', version = 'version', } -export type SettingKey = keyof typeof Setting; +export type WindowSpecificSettingKey = keyof typeof WindowSpecificSetting; diff --git a/src/renderer/components/settings-execution.tsx b/src/renderer/components/settings-execution.tsx index 5276caad2e..43bf1cb59a 100644 --- a/src/renderer/components/settings-execution.tsx +++ b/src/renderer/components/settings-execution.tsx @@ -12,15 +12,19 @@ import { } from '@blueprintjs/core'; import { observer } from 'mobx-react'; -import { IPackageManager, Setting, SettingKey } from '../../interfaces'; +import { + GlobalSetting, + GlobalSettingKey, + IPackageManager, +} from '../../interfaces'; import { AppState } from '../state'; /** * @TODO make this a proper enum again once we update Typescript */ export const SettingItemType = { - EnvVars: Setting.environmentVariables, - Flags: Setting.executionFlags, + EnvVars: GlobalSetting.environmentVariables, + Flags: GlobalSetting.executionFlags, } as const; interface ExecutionSettingsProps { @@ -122,7 +126,7 @@ export const ExecutionSettings = observer( */ public handleSettingsItemChange( event: React.ChangeEvent, - type: SettingKey, + type: GlobalSettingKey, ) { const { name, value } = event.currentTarget; @@ -139,7 +143,7 @@ export const ExecutionSettings = observer( * * @param {SettingItemType} type */ - private addNewSettingsItem(type: SettingKey) { + private addNewSettingsItem(type: GlobalSettingKey) { const array = Object.entries(this.state[type]); this.setState((prevState) => ({ @@ -163,7 +167,7 @@ export const ExecutionSettings = observer( appState.packageManager = value as IPackageManager; }; - public renderDeleteItem(idx: string, type: SettingKey): JSX.Element { + public renderDeleteItem(idx: string, type: GlobalSettingKey): JSX.Element { const updated = this.state[type]; const removeFn = () => { diff --git a/src/renderer/state.ts b/src/renderer/state.ts index 53bb523500..ae9811c529 100644 --- a/src/renderer/state.ts +++ b/src/renderer/state.ts @@ -21,16 +21,18 @@ import { GenericDialogOptions, GenericDialogType, GistActionState, + GlobalSetting, + GlobalSettingKey, IPackageManager, InstallState, OutputEntry, OutputOptions, RunnableVersion, SetFiddleOptions, - Setting, - SettingKey, Version, VersionSource, + WindowSpecificSetting, + WindowSpecificSettingKey, } from '../interfaces'; import { Bisector } from './bisect'; import { ELECTRON_DOWNLOAD_PATH, ELECTRON_INSTALL_PATH } from './constants'; @@ -66,68 +68,80 @@ export class AppState { }); // -- Persisted settings ------------------ - public theme: string | null = localStorage.getItem(Setting.theme); + public theme: string | null = localStorage.getItem(GlobalSetting.theme); public gitHubAvatarUrl: string | null = localStorage.getItem( - Setting.gitHubAvatarUrl, + GlobalSetting.gitHubAvatarUrl, + ); + public gitHubName: string | null = localStorage.getItem( + GlobalSetting.gitHubName, + ); + public gitHubLogin: string | null = localStorage.getItem( + GlobalSetting.gitHubLogin, ); - public gitHubName: string | null = localStorage.getItem(Setting.gitHubName); - public gitHubLogin: string | null = localStorage.getItem(Setting.gitHubLogin); public gitHubToken: string | null = - localStorage.getItem(Setting.gitHubToken) || null; - public gitHubPublishAsPublic = !!this.retrieve(Setting.gitHubPublishAsPublic); + localStorage.getItem(GlobalSetting.gitHubToken) || null; + public gitHubPublishAsPublic = !!this.retrieve( + WindowSpecificSetting.gitHubPublishAsPublic, + ); public channelsToShow: Array = (this.retrieve( - Setting.channelsToShow, + GlobalSetting.channelsToShow, ) as Array) || [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta, ]; public showObsoleteVersions = !!( - this.retrieve(Setting.showObsoleteVersions) ?? false + this.retrieve(GlobalSetting.showObsoleteVersions) ?? false ); public showUndownloadedVersions = !!( - this.retrieve(Setting.showUndownloadedVersions) ?? true + this.retrieve(GlobalSetting.showUndownloadedVersions) ?? true + ); + public isKeepingUserDataDirs = !!this.retrieve( + GlobalSetting.isKeepingUserDataDirs, ); - public isKeepingUserDataDirs = !!this.retrieve(Setting.isKeepingUserDataDirs); public isEnablingElectronLogging = !!this.retrieve( - Setting.isEnablingElectronLogging, + GlobalSetting.isEnablingElectronLogging, ); public isClearingConsoleOnRun = !!this.retrieve( - Setting.isClearingConsoleOnRun, + GlobalSetting.isClearingConsoleOnRun, ); public isUsingSystemTheme = !!( - this.retrieve(Setting.isUsingSystemTheme) ?? true + this.retrieve(GlobalSetting.isUsingSystemTheme) ?? true ); public isPublishingGistAsRevision = !!( - this.retrieve(Setting.isPublishingGistAsRevision) ?? true + this.retrieve(GlobalSetting.isPublishingGistAsRevision) ?? true ); public executionFlags: Array = - (this.retrieve(Setting.executionFlags) as Array) === null + (this.retrieve(GlobalSetting.executionFlags) as Array) === null ? [] - : (this.retrieve(Setting.executionFlags) as Array); + : (this.retrieve(GlobalSetting.executionFlags) as Array); public environmentVariables: Array = - (this.retrieve(Setting.environmentVariables) as Array) === null + (this.retrieve(GlobalSetting.environmentVariables) as Array) === + null ? [] - : (this.retrieve(Setting.environmentVariables) as Array); + : (this.retrieve(GlobalSetting.environmentVariables) as Array); public packageManager: IPackageManager = - (localStorage.getItem(Setting.packageManager) as IPackageManager) || 'npm'; + (localStorage.getItem(GlobalSetting.packageManager) as IPackageManager) || + 'npm'; public acceleratorsToBlock: Array = (this.retrieve( - Setting.acceleratorsToBlock, + GlobalSetting.acceleratorsToBlock, ) as Array) || []; public packageAuthor = - (localStorage.getItem(Setting.packageAuthor) as string) ?? + (localStorage.getItem(GlobalSetting.packageAuthor) as string) ?? window.ElectronFiddle.getUsername(); public electronMirror: typeof ELECTRON_MIRROR = - (this.retrieve(Setting.electronMirror) as typeof ELECTRON_MIRROR) === null + (this.retrieve(GlobalSetting.electronMirror) as typeof ELECTRON_MIRROR) === + null ? { ...ELECTRON_MIRROR, sourceType: navigator.language === 'zh-CN' ? 'CHINA' : 'DEFAULT', } - : (this.retrieve(Setting.electronMirror) as typeof ELECTRON_MIRROR); + : (this.retrieve(GlobalSetting.electronMirror) as typeof ELECTRON_MIRROR); public fontFamily: string | undefined = - (localStorage.getItem(Setting.fontFamily) as string) || undefined; + (localStorage.getItem(GlobalSetting.fontFamily) as string) || undefined; public fontSize: number | undefined = - ((localStorage.getItem(Setting.fontSize) as any) as number) || undefined; + ((localStorage.getItem(GlobalSetting.fontSize) as any) as number) || + undefined; // -- Various session-only state ------------------ public gistId: string | undefined = undefined; @@ -166,7 +180,7 @@ export class AppState { public isSettingsShowing = false; public isThemeDialogShowing = false; public isTokenDialogShowing = false; - public isTourShowing = !localStorage.getItem(Setting.hasShownTour); + public isTourShowing = !localStorage.getItem(GlobalSetting.hasShownTour); public isUpdatingElectronVersions = false; // -- Editor Values stored when we close the editor ------------------ @@ -337,60 +351,110 @@ export class AppState { ); window.ElectronFiddle.addEventListener('before-quit', this.setIsQuitting); + /** + * Listens for changes in the app settings made in other windows + * and refreshes the current window settings accordingly. + */ + window.addEventListener('storage', (event) => { + const key = event.key as GlobalSettingKey; + const { newValue } = event; + + let parsedValue: unknown; + + try { + parsedValue = JSON.parse(newValue as string) as unknown; + } catch { + // The new value is a plain string, not a well-formed stringified object. + parsedValue = newValue; + } + + if (key in GlobalSetting && key in this) { + // Any settings listed here need morethan just updating the state directly. + const actions: Partial void>> = { + theme: () => { + this.setTheme(parsedValue as string); + }, + }; + + const action = actions[key]; + + if (typeof action === 'function') { + action(); + } else { + // Fall back to updating the state. + this[key] = parsedValue; + } + } + }); + // Setup auto-runs - autorun(() => this.save(Setting.theme, this.theme)); + autorun(() => this.save(GlobalSetting.theme, this.theme)); autorun(() => - this.save(Setting.isClearingConsoleOnRun, this.isClearingConsoleOnRun), + this.save( + GlobalSetting.isClearingConsoleOnRun, + this.isClearingConsoleOnRun, + ), ); autorun(() => - this.save(Setting.isUsingSystemTheme, this.isUsingSystemTheme), + this.save(GlobalSetting.isUsingSystemTheme, this.isUsingSystemTheme), ); autorun(() => this.save( - Setting.isPublishingGistAsRevision, + GlobalSetting.isPublishingGistAsRevision, this.isPublishingGistAsRevision, ), ); - autorun(() => this.save(Setting.gitHubAvatarUrl, this.gitHubAvatarUrl)); - autorun(() => this.save(Setting.gitHubLogin, this.gitHubLogin)); - autorun(() => this.save(Setting.gitHubName, this.gitHubName)); - autorun(() => this.save(Setting.gitHubToken, this.gitHubToken)); autorun(() => - this.save(Setting.gitHubPublishAsPublic, this.gitHubPublishAsPublic), + this.save(GlobalSetting.gitHubAvatarUrl, this.gitHubAvatarUrl), + ); + autorun(() => this.save(GlobalSetting.gitHubLogin, this.gitHubLogin)); + autorun(() => this.save(GlobalSetting.gitHubName, this.gitHubName)); + autorun(() => this.save(GlobalSetting.gitHubToken, this.gitHubToken)); + autorun(() => + this.save( + WindowSpecificSetting.gitHubPublishAsPublic, + this.gitHubPublishAsPublic, + ), ); autorun(() => - this.save(Setting.isKeepingUserDataDirs, this.isKeepingUserDataDirs), + this.save( + GlobalSetting.isKeepingUserDataDirs, + this.isKeepingUserDataDirs, + ), ); autorun(() => this.save( - Setting.isEnablingElectronLogging, + GlobalSetting.isEnablingElectronLogging, this.isEnablingElectronLogging, ), ); - autorun(() => this.save(Setting.executionFlags, this.executionFlags)); - autorun(() => this.save(Setting.version, this.version)); - autorun(() => this.save(Setting.channelsToShow, this.channelsToShow)); + autorun(() => this.save(GlobalSetting.executionFlags, this.executionFlags)); + autorun(() => + this.save(GlobalSetting.environmentVariables, this.environmentVariables), + ); + autorun(() => this.save(WindowSpecificSetting.version, this.version)); + autorun(() => this.save(GlobalSetting.channelsToShow, this.channelsToShow)); autorun(() => this.save( - Setting.showUndownloadedVersions, + GlobalSetting.showUndownloadedVersions, this.showUndownloadedVersions, ), ); autorun(() => - this.save(Setting.showObsoleteVersions, this.showObsoleteVersions), + this.save(GlobalSetting.showObsoleteVersions, this.showObsoleteVersions), ); autorun(() => - this.save(Setting.packageManager, this.packageManager ?? 'npm'), + this.save(GlobalSetting.packageManager, this.packageManager ?? 'npm'), ); autorun(() => - this.save(Setting.acceleratorsToBlock, this.acceleratorsToBlock), + this.save(GlobalSetting.acceleratorsToBlock, this.acceleratorsToBlock), ); - autorun(() => this.save(Setting.packageAuthor, this.packageAuthor)); + autorun(() => this.save(GlobalSetting.packageAuthor, this.packageAuthor)); autorun(() => - this.save(Setting.electronMirror, this.electronMirror as any), + this.save(GlobalSetting.electronMirror, this.electronMirror as any), ); - autorun(() => this.save(Setting.fontFamily, this.fontFamily as any)); - autorun(() => this.save(Setting.fontSize, this.fontSize as any)); + autorun(() => this.save(GlobalSetting.fontFamily, this.fontFamily as any)); + autorun(() => this.save(GlobalSetting.fontSize, this.fontSize as any)); // Update our known versions this.updateElectronVersions(); @@ -541,7 +605,7 @@ export class AppState { public disableTour() { this.resetView(); - localStorage.setItem(Setting.hasShownTour, 'true'); + localStorage.setItem(GlobalSetting.hasShownTour, 'true'); } public showTour() { @@ -962,7 +1026,7 @@ export class AppState { * @param {(string | number | Array | Record | null | boolean)} [value] */ private save( - key: SettingKey, + key: GlobalSettingKey | WindowSpecificSettingKey, value?: | string | number @@ -988,7 +1052,9 @@ export class AppState { * @param {string} key * @returns {(T | string | null)} */ - private retrieve(key: SettingKey): T | string | null { + private retrieve( + key: GlobalSettingKey | WindowSpecificSettingKey, + ): T | string | null { const value = localStorage.getItem(key); return JSON.parse(value || 'null') as T; diff --git a/src/renderer/versions.ts b/src/renderer/versions.ts index f67bb018fb..8e400af348 100644 --- a/src/renderer/versions.ts +++ b/src/renderer/versions.ts @@ -1,10 +1,11 @@ import { ElectronReleaseChannel, + GlobalSetting, InstallState, RunnableVersion, - Setting, Version, VersionSource, + WindowSpecificSetting, } from '../interfaces'; import { normalizeVersion } from './utils/normalize-version'; @@ -15,7 +16,7 @@ import { normalizeVersion } from './utils/normalize-version'; * @returns {string} */ export function getDefaultVersion(versions: RunnableVersion[]): string { - const key = localStorage.getItem(Setting.version); + const key = localStorage.getItem(WindowSpecificSetting.version); if (key && versions.some(({ version }) => version === key)) { return key; } @@ -110,7 +111,7 @@ export function getLocalVersionForPath( * @returns {Array} */ export function getLocalVersions(): Array { - const fromLs = window.localStorage.getItem(Setting.localVersion); + const fromLs = window.localStorage.getItem(GlobalSetting.localVersion); if (fromLs) { try { @@ -146,12 +147,12 @@ export function saveLocalVersions( }); const stringified = JSON.stringify(filteredVersions); - window.localStorage.setItem(Setting.localVersion, stringified); + window.localStorage.setItem(GlobalSetting.localVersion, stringified); } function getReleasedVersions(): Array { const versions = window.ElectronFiddle.getReleasedVersions(); - const fromLs = window.localStorage.getItem(Setting.knownVersion); + const fromLs = window.localStorage.getItem(GlobalSetting.knownVersion); if (fromLs) { try { @@ -179,7 +180,7 @@ export async function fetchVersions(): Promise { // Migrate away from known versions being stored in localStorage // Now that we've fetched new versions, it's safe to delete - window.localStorage.removeItem(Setting.knownVersion); + window.localStorage.removeItem(GlobalSetting.knownVersion); console.log(`Fetched ${versions.length} new Electron versions`); return versions; diff --git a/tests/renderer/versions-spec.ts b/tests/renderer/versions-spec.ts index 75ccabcefd..787e5169d3 100644 --- a/tests/renderer/versions-spec.ts +++ b/tests/renderer/versions-spec.ts @@ -1,7 +1,7 @@ import { ElectronReleaseChannel, + GlobalSetting, RunnableVersion, - Setting, VersionSource, } from '../../src/interfaces'; import { @@ -109,7 +109,7 @@ describe('versions', () => { saveLocalVersions(mockLocalVersions as Array); expect(window.localStorage.setItem).toBeCalledWith( - Setting.localVersion, + GlobalSetting.localVersion, JSON.stringify(mockLocalVersions), ); }); @@ -153,7 +153,7 @@ describe('versions', () => { (window.ElectronFiddle.fetchVersions as jest.Mock).mockResolvedValue([]); await fetchVersions(); expect(localStorage.removeItem).toHaveBeenCalledWith( - Setting.knownVersion, + GlobalSetting.knownVersion, ); }); }); From e1c15a95227c997cfb42c22e5c39f2ccb2d17052 Mon Sep 17 00:00:00 2001 From: Erik Moura Date: Thu, 22 Jun 2023 00:05:59 -0300 Subject: [PATCH 3/5] Update src/renderer/state.ts Co-authored-by: David Sanders --- src/renderer/state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/state.ts b/src/renderer/state.ts index ae9811c529..2068432403 100644 --- a/src/renderer/state.ts +++ b/src/renderer/state.ts @@ -369,7 +369,7 @@ export class AppState { } if (key in GlobalSetting && key in this) { - // Any settings listed here need morethan just updating the state directly. + // Any settings listed here need more than just updating the state directly. const actions: Partial void>> = { theme: () => { this.setTheme(parsedValue as string); From 3f651b88a0204b87e273abef1f8c5e5f77b85855 Mon Sep 17 00:00:00 2001 From: Erik Moura Date: Fri, 23 Jun 2023 00:47:21 -0300 Subject: [PATCH 4/5] fix: ensure all keys are checked --- src/renderer/state.ts | 51 +++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/renderer/state.ts b/src/renderer/state.ts index 2068432403..ca00d93c8c 100644 --- a/src/renderer/state.ts +++ b/src/renderer/state.ts @@ -67,6 +67,12 @@ export class AppState { timeStyle: 'medium', }); + private settingKeyTypeGuard(key: never): never { + throw new Error( + `Unhandled setting ${key}, please handle it in the \`AppState\`.`, + ); + } + // -- Persisted settings ------------------ public theme: string | null = localStorage.getItem(GlobalSetting.theme); public gitHubAvatarUrl: string | null = localStorage.getItem( @@ -369,20 +375,43 @@ export class AppState { } if (key in GlobalSetting && key in this) { - // Any settings listed here need more than just updating the state directly. - const actions: Partial void>> = { - theme: () => { + switch (key) { + case 'theme': { this.setTheme(parsedValue as string); - }, - }; + break; + } - const action = actions[key]; + case 'acceleratorsToBlock': + case 'channelsToShow': + case 'electronMirror': + case 'environmentVariables': + case 'executionFlags': + case 'fontFamily': + case 'fontSize': + case 'gitHubAvatarUrl': + case 'gitHubLogin': + case 'gitHubName': + case 'gitHubToken': + case 'hasShownTour': + case 'isClearingConsoleOnRun': + case 'isEnablingElectronLogging': + case 'isKeepingUserDataDirs': + case 'isPublishingGistAsRevision': + case 'isUsingSystemTheme': + case 'knownVersion': + case 'localVersion': + case 'packageAuthor': + case 'packageManager': + case 'showObsoleteVersions': + case 'showUndownloadedVersions': { + // Fall back to updating the state. + this[key] = parsedValue; + break; + } - if (typeof action === 'function') { - action(); - } else { - // Fall back to updating the state. - this[key] = parsedValue; + default: { + this.settingKeyTypeGuard(key); + } } } }); From b1b62cbee83998a871390690880961089bfeb8e5 Mon Sep 17 00:00:00 2001 From: Erik Moura Date: Fri, 30 Jun 2023 09:48:03 -0300 Subject: [PATCH 5/5] fix: add warning for unrecognized keys --- src/renderer/state.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/renderer/state.ts b/src/renderer/state.ts index ca00d93c8c..d2663563a4 100644 --- a/src/renderer/state.ts +++ b/src/renderer/state.ts @@ -413,6 +413,10 @@ export class AppState { this.settingKeyTypeGuard(key); } } + } else { + console.warn( + `"${key}" is not a recognized localStorage key. If you're using this key to persist a setting, please add it to the relevant enum.`, + ); } });