From ba2ce5ecba2982ae6587e586e71384e60f3b224d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 10 Jun 2022 22:38:50 +0100 Subject: [PATCH] Abstract electron settings properly to avoid boilerplate-hell (#8798) * Remove unused method `BasePlatform::screenCaptureErrorString` * Improve platform typescripting * Remove redundant awaits * Abstract electron settings properly to avoid boilerplate-hell * i18n * Fix stray semi-colons * Fix setting level order for Platform settings --- src/BasePlatform.ts | 77 ++------- .../views/elements/SettingsFlag.tsx | 3 + .../tabs/user/PreferencesUserSettingsTab.tsx | 152 ++---------------- .../payloads/CheckUpdatesPayload.ts | 14 +- src/i18n/strings/en_EN.json | 11 +- src/settings/SettingLevel.ts | 1 + src/settings/Settings.tsx | 28 ++++ src/settings/SettingsStore.ts | 16 +- .../handlers/PlatformSettingsHandler.ts | 40 +++++ 9 files changed, 122 insertions(+), 220 deletions(-) create mode 100644 src/settings/handlers/PlatformSettingsHandler.ts diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 630d56f06b..3c83229755 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -46,6 +46,17 @@ export enum UpdateCheckStatus { Ready = "READY", } +export interface UpdateStatus { + /** + * The current phase of the manual update check. + */ + status: UpdateCheckStatus; + /** + * Detail string relating to the current status, typically for error details. + */ + detail?: string; +} + const UPDATE_DEFER_KEY = "mx_defer_update"; /** @@ -225,79 +236,21 @@ export default abstract class BasePlatform { */ public abstract getAppVersion(): Promise; - /* - * If it's not expected that capturing the screen will work - * with getUserMedia, return a string explaining why not. - * Otherwise, return null. - */ - public screenCaptureErrorString(): string { - return "Not implemented"; - } - /** * Restarts the application, without necessarily reloading * any application code */ public abstract reload(): void; - public supportsAutoLaunch(): boolean { + public supportsSetting(settingName?: string): boolean { return false; } - // XXX: Surely this should be a setting like any other? - public async getAutoLaunchEnabled(): Promise { - return false; + public getSettingValue(settingName: string): Promise { + return undefined; } - public async setAutoLaunchEnabled(enabled: boolean): Promise { - throw new Error("Unimplemented"); - } - - public supportsWarnBeforeExit(): boolean { - return false; - } - - public async shouldWarnBeforeExit(): Promise { - return false; - } - - public async setWarnBeforeExit(enabled: boolean): Promise { - throw new Error("Unimplemented"); - } - - public supportsAutoHideMenuBar(): boolean { - return false; - } - - public async getAutoHideMenuBarEnabled(): Promise { - return false; - } - - public async setAutoHideMenuBarEnabled(enabled: boolean): Promise { - throw new Error("Unimplemented"); - } - - public supportsMinimizeToTray(): boolean { - return false; - } - - public async getMinimizeToTrayEnabled(): Promise { - return false; - } - - public async setMinimizeToTrayEnabled(enabled: boolean): Promise { - throw new Error("Unimplemented"); - } - - public supportsTogglingHardwareAcceleration(): boolean { - return false; - } - - public async getHardwareAccelerationEnabled(): Promise { - return true; - } - - public async setHardwareAccelerationEnabled(enabled: boolean): Promise { + public setSettingValue(settingName: string, value: any): Promise { throw new Error("Unimplemented"); } diff --git a/src/components/views/elements/SettingsFlag.tsx b/src/components/views/elements/SettingsFlag.tsx index 3437440f00..f433613a77 100644 --- a/src/components/views/elements/SettingsFlag.tsx +++ b/src/components/views/elements/SettingsFlag.tsx @@ -33,6 +33,7 @@ interface IProps { // XXX: once design replaces all toggles make this the default useCheckbox?: boolean; disabled?: boolean; + hideIfCannotSet?: boolean; onChange?(checked: boolean): void; } @@ -76,6 +77,8 @@ export default class SettingsFlag extends React.Component { public render() { const canChange = SettingsStore.canSetValue(this.props.name, this.props.roomId, this.props.level); + if (!canChange && this.props.hideIfCannotSet) return null; + const label = this.props.label ? _t(this.props.label) : SettingsStore.getDisplayName(this.props.name, this.props.level); diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index 64f052b1ac..2b67d22c0d 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -18,10 +18,8 @@ limitations under the License. import React from 'react'; import { _t } from "../../../../../languageHandler"; -import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; -import PlatformPeg from "../../../../../PlatformPeg"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsFlag from '../../../elements/SettingsFlag'; import AccessibleButton from "../../../elements/AccessibleButton"; @@ -36,16 +34,6 @@ interface IProps { } interface IState { - autoLaunch: boolean; - autoLaunchSupported: boolean; - warnBeforeExit: boolean; - warnBeforeExitSupported: boolean; - alwaysShowMenuBarSupported: boolean; - alwaysShowMenuBar: boolean; - minimizeToTraySupported: boolean; - minimizeToTray: boolean; - togglingHardwareAccelerationSupported: boolean; - enableHardwareAcceleration: boolean; autocompleteDelay: string; readMarkerInViewThresholdMs: string; readMarkerOutOfViewThresholdMs: string; @@ -112,16 +100,6 @@ export default class PreferencesUserSettingsTab extends React.Component { - PlatformPeg.get().setAutoLaunchEnabled(checked).then(() => this.setState({ autoLaunch: checked })); - }; - - private onWarnBeforeExitChange = (checked: boolean) => { - PlatformPeg.get().setWarnBeforeExit(checked).then(() => this.setState({ warnBeforeExit: checked })); - }; - - private onAlwaysShowMenuBarChange = (checked: boolean) => { - PlatformPeg.get().setAutoHideMenuBarEnabled(!checked).then(() => this.setState({ alwaysShowMenuBar: checked })); - }; - - private onMinimizeToTrayChange = (checked: boolean) => { - PlatformPeg.get().setMinimizeToTrayEnabled(checked).then(() => this.setState({ minimizeToTray: checked })); - }; - - private onHardwareAccelerationChange = (checked: boolean) => { - PlatformPeg.get().setHardwareAccelerationEnabled(checked).then( - () => this.setState({ enableHardwareAcceleration: checked })); - }; - private onAutocompleteDelayChange = (e: React.ChangeEvent) => { this.setState({ autocompleteDelay: e.target.value }); SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.value); @@ -232,49 +142,6 @@ export default class PreferencesUserSettingsTab extends React.Component; - } - - let warnBeforeExitOption = null; - if (this.state.warnBeforeExitSupported) { - warnBeforeExitOption = ; - } - - let autoHideMenuOption = null; - if (this.state.alwaysShowMenuBarSupported) { - autoHideMenuOption = ; - } - - let minimizeToTrayOption = null; - if (this.state.minimizeToTraySupported) { - minimizeToTrayOption = ; - } - - let hardwareAccelerationOption = null; - if (this.state.togglingHardwareAccelerationSupported) { - const appName = SdkConfig.get().brand; - hardwareAccelerationOption = ; - } - return (
{ _t("Preferences") }
@@ -331,11 +198,20 @@ export default class PreferencesUserSettingsTab extends React.Component { _t("General") } { this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS) } - { minimizeToTrayOption } - { hardwareAccelerationOption } - { autoHideMenuOption } - { autoLaunchOption } - { warnBeforeExitOption } + + + + + + + Learn more.": "Your homeserver does not currently support threads, so this feature may be unreliable. Some threaded messages may not be reliably available. Learn more.", "Do you want to enable threads anyway?": "Do you want to enable threads anyway?", @@ -1505,11 +1510,6 @@ "If this isn't what you want, please use a different tool to ignore users.": "If this isn't what you want, please use a different tool to ignore users.", "Room ID or address of ban list": "Room ID or address of ban list", "Subscribe": "Subscribe", - "Start automatically after system login": "Start automatically after system login", - "Warn before quitting": "Warn before quitting", - "Always show the window menu bar": "Always show the window menu bar", - "Show tray icon and minimise window to it on close": "Show tray icon and minimise window to it on close", - "Enable hardware acceleration (restart %(appName)s to take effect)": "Enable hardware acceleration (restart %(appName)s to take effect)", "Preferences": "Preferences", "Room list": "Room list", "Keyboard shortcuts": "Keyboard shortcuts", @@ -1519,6 +1519,7 @@ "Code blocks": "Code blocks", "Images, GIFs and videos": "Images, GIFs and videos", "Timeline": "Timeline", + "Enable hardware acceleration (restart %(appName)s to take effect)": "Enable hardware acceleration (restart %(appName)s to take effect)", "Autocomplete delay (ms)": "Autocomplete delay (ms)", "Read Marker lifetime (ms)": "Read Marker lifetime (ms)", "Read Marker off-screen lifetime (ms)": "Read Marker off-screen lifetime (ms)", diff --git a/src/settings/SettingLevel.ts b/src/settings/SettingLevel.ts index e4703be1a9..66d3834ac6 100644 --- a/src/settings/SettingLevel.ts +++ b/src/settings/SettingLevel.ts @@ -24,6 +24,7 @@ export enum SettingLevel { ROOM_ACCOUNT = "room-account", ACCOUNT = "account", ROOM = "room", + PLATFORM = "platform", CONFIG = "config", DEFAULT = "default", } diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 2cb5a25d8c..7a45447b1b 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -1042,4 +1042,32 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_UI_FEATURE, default: true, }, + + // Electron-specific settings, they are stored by Electron and set/read over an IPC. + // We store them over there are they are necessary to know before the renderer process launches. + "Electron.autoLaunch": { + supportedLevels: [SettingLevel.PLATFORM], + displayName: _td("Start automatically after system login"), + default: false, + }, + "Electron.warnBeforeExit": { + supportedLevels: [SettingLevel.PLATFORM], + displayName: _td("Warn before quitting"), + default: true, + }, + "Electron.alwaysShowMenuBar": { + supportedLevels: [SettingLevel.PLATFORM], + displayName: _td("Always show the window menu bar"), + default: false, + }, + "Electron.showTrayIcon": { + supportedLevels: [SettingLevel.PLATFORM], + displayName: _td("Show tray icon and minimise window to it on close"), + default: true, + }, + "Electron.enableHardwareAcceleration": { + supportedLevels: [SettingLevel.PLATFORM], + displayName: _td("Enable hardware acceleration"), + default: true, + }, }; diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index 19a419afb7..4f81ad3be6 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -34,6 +34,7 @@ import { SettingLevel } from "./SettingLevel"; import SettingsHandler from "./handlers/SettingsHandler"; import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload"; import { Action } from "../dispatcher/actions"; +import PlatformSettingsHandler from "./handlers/PlatformSettingsHandler"; const defaultWatchManager = new WatchManager(); @@ -61,6 +62,7 @@ const LEVEL_HANDLERS = { ), [SettingLevel.ACCOUNT]: new LocalEchoWrapper(new AccountSettingsHandler(defaultWatchManager), SettingLevel.ACCOUNT), [SettingLevel.ROOM]: new LocalEchoWrapper(new RoomSettingsHandler(defaultWatchManager), SettingLevel.ROOM), + [SettingLevel.PLATFORM]: new LocalEchoWrapper(new PlatformSettingsHandler(), SettingLevel.PLATFORM), [SettingLevel.CONFIG]: new ConfigSettingsHandler(featureNames), [SettingLevel.DEFAULT]: new DefaultSettingsHandler(defaultSettings, invertedDefaultSettings), }; @@ -75,6 +77,14 @@ export const LEVEL_ORDER = [ SettingLevel.DEFAULT, ]; +function getLevelOrder(setting: ISetting): SettingLevel[] { + // Settings which support only a single setting level are inherently ordered + if (setting.supportedLevelsAreOrdered || setting.supportedLevels.length === 1) { + return setting.supportedLevels; + } + return LEVEL_ORDER; +} + export type CallbackFn = ( settingName: string, roomId: string, @@ -316,7 +326,7 @@ export default class SettingsStore { } const setting = SETTINGS[settingName]; - const levelOrder = (setting.supportedLevelsAreOrdered ? setting.supportedLevels : LEVEL_ORDER); + const levelOrder = getLevelOrder(setting); return SettingsStore.getValueAt(levelOrder[0], settingName, roomId, false, excludeDefault); } @@ -345,7 +355,7 @@ export default class SettingsStore { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); } - const levelOrder = (setting.supportedLevelsAreOrdered ? setting.supportedLevels : LEVEL_ORDER); + const levelOrder = getLevelOrder(setting); if (!levelOrder.includes(SettingLevel.DEFAULT)) levelOrder.push(SettingLevel.DEFAULT); // always include default const minIndex = levelOrder.indexOf(level); @@ -518,7 +528,7 @@ export default class SettingsStore { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); } - const levelOrder = (setting.supportedLevelsAreOrdered ? setting.supportedLevels : LEVEL_ORDER); + const levelOrder = getLevelOrder(setting); if (!levelOrder.includes(SettingLevel.DEFAULT)) levelOrder.push(SettingLevel.DEFAULT); // always include default const handlers = SettingsStore.getHandlers(settingName); diff --git a/src/settings/handlers/PlatformSettingsHandler.ts b/src/settings/handlers/PlatformSettingsHandler.ts new file mode 100644 index 0000000000..ffdc731c2f --- /dev/null +++ b/src/settings/handlers/PlatformSettingsHandler.ts @@ -0,0 +1,40 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import SettingsHandler from "./SettingsHandler"; +import PlatformPeg from "../../PlatformPeg"; + +/** + * Gets and sets settings at the "platform" level for the current device. + * This handler does not make use of the roomId parameter. + */ +export default class PlatformSettingsHandler extends SettingsHandler { + public canSetValue(settingName: string, roomId: string): boolean { + return PlatformPeg.get().supportsSetting(settingName); + } + + public getValue(settingName: string, roomId: string): any { + return PlatformPeg.get().getSettingValue(settingName); + } + + public setValue(settingName: string, roomId: string, newValue: any): Promise { + return PlatformPeg.get().setSettingValue(settingName, newValue); + } + + public isSupported(): boolean { + return PlatformPeg.get().supportsSetting(); + } +}