Fix platform settings race condition and make auto-launch tri-state (#30977)

* Fix race condition with platform settings not being read correctly

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Allow Desktop app to be auto-started minimised or focused

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* i18n

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Use onChange prop

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update res/css/views/elements/_SettingsDropdown.pcss

Co-authored-by: Florian Duros <florianduros@element.io>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: Florian Duros <florianduros@element.io>
This commit is contained in:
Michael Telatynski
2025-10-09 14:20:53 +01:00
committed by GitHub
parent 3098eba4f2
commit bc7b50f97c
14 changed files with 253 additions and 55 deletions

View File

@@ -0,0 +1,81 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, useCallback, useId, useState } from "react";
import SettingsStore from "../../../settings/SettingsStore";
import { type SettingLevel } from "../../../settings/SettingLevel";
import { SETTINGS, type StringSettingKey } from "../../../settings/Settings";
import { useSettingValueAt } from "../../../hooks/useSettings.ts";
import Dropdown, { type DropdownProps } from "./Dropdown.tsx";
import { _t } from "../../../shared-components/utils/i18n.tsx";
interface Props {
settingKey: StringSettingKey;
level: SettingLevel;
roomId?: string; // for per-room settings
label?: string;
isExplicit?: boolean;
hideIfCannotSet?: boolean;
onChange?(option: string): void;
}
const SettingsDropdown = ({
settingKey,
roomId,
level,
label: specificLabel,
isExplicit,
hideIfCannotSet,
onChange,
}: Props): JSX.Element => {
const id = useId();
const settingValue = useSettingValueAt(level, settingKey, roomId ?? null, isExplicit);
const [value, setValue] = useState(settingValue);
const setting = SETTINGS[settingKey];
const onOptionChange = useCallback(
(value: string): void => {
setValue(value); // local echo
SettingsStore.setValue(settingKey, roomId ?? null, level, value);
onChange?.(value);
},
[settingKey, roomId, level, onChange],
);
const disabled = !SettingsStore.canSetValue(settingKey, roomId ?? null, level);
if (disabled && hideIfCannotSet) return <></>;
if (!setting.options) {
console.error("SettingsDropdown used for a setting with no `options`");
return <></>;
}
const label = specificLabel ?? SettingsStore.getDisplayName(settingKey, level)!;
const options = setting.options.map((option) => {
return <div key={option.value}>{_t(option.label)}</div>;
}) as DropdownProps["children"];
return (
<div className="mx_SettingsDropdown">
<label className="mx_SettingsDropdown_label" htmlFor={id}>
<span className="mx_SettingsDropdown_labelText">{label}</span>
</label>
<Dropdown
id={id}
onOptionChange={onOptionChange}
menuWidth={360} // matches CSS width
value={value}
disabled={disabled}
label={label}
>
{options}
</Dropdown>
</div>
);
};
export default SettingsDropdown;

View File

@@ -34,6 +34,7 @@ import * as TimezoneHandler from "../../../../../TimezoneHandler";
import { type BooleanSettingKey } from "../../../../../settings/Settings.tsx";
import { MediaPreviewAccountSettings } from "./MediaPreviewAccountSettings.tsx";
import { InviteRulesAccountSetting } from "./InviteRulesAccountSettings.tsx";
import SettingsDropdown from "../../../elements/SettingsDropdown.tsx";
interface IProps {
closeSettingsFn(success: boolean): void;
@@ -248,11 +249,12 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
});
const newRoomListEnabled = SettingsStore.getValue("feature_new_room_list");
const brand = SdkConfig.get().brand;
// Always Preprend the default option
const timezones = this.state.timezones.map((tz) => {
return <div key={tz}>{tz}</div>;
});
// Always prepend the default option
timezones.unshift(<div key="">{browserTimezoneLabel}</div>);
return (
@@ -264,6 +266,17 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
<SpellCheckSection />
</SettingsSubsection>
{SettingsStore.canSetValue("Electron.autoLaunch", null, SettingLevel.PLATFORM) && (
<SettingsSubsection heading={_t("settings|preferences|startup_window_behaviour_label")}>
<SettingsDropdown
settingKey="Electron.autoLaunch"
label={_t("settings|start_automatically|label", { brand })}
level={SettingLevel.PLATFORM}
hideIfCannotSet
/>
</SettingsSubsection>
)}
<SettingsSubsection heading={_t("settings|preferences|room_list_heading")}>
{!newRoomListEnabled && this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
{/* The settings is on device level where the other room list settings are on account level */}
@@ -356,7 +369,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
level={SettingLevel.PLATFORM}
hideIfCannotSet
label={_t("settings|preferences|Electron.enableHardwareAcceleration", {
appName: SdkConfig.get().brand,
appName: brand,
})}
/>
<SettingsFlag
@@ -366,7 +379,6 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
label={_t("settings|preferences|Electron.enableContentProtection")}
/>
<SettingsFlag name="Electron.alwaysShowMenuBar" level={SettingLevel.PLATFORM} hideIfCannotSet />
<SettingsFlag name="Electron.autoLaunch" level={SettingLevel.PLATFORM} hideIfCannotSet />
<SettingsFlag name="Electron.warnBeforeExit" level={SettingLevel.PLATFORM} hideIfCannotSet />
<Field