Convert UserSettings to functional component (#12474)

* Convert UserSettings to functional component

* Put comment back

* Fix test

* Unused imports
This commit is contained in:
David Baker 2024-04-30 19:52:32 +01:00 committed by GitHub
parent bb4f57583f
commit 906c9dd948
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 62 deletions

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2019 New Vector Ltd Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2024 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import React from "react";
import TabbedView, { Tab } from "../../structures/TabbedView"; import TabbedView, { Tab } from "../../structures/TabbedView";
import { _t, _td } from "../../../languageHandler"; import { _t, _td } from "../../../languageHandler";
import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab";
import SettingsStore, { CallbackFn } from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import LabsUserSettingsTab, { showLabsFlags } from "../settings/tabs/user/LabsUserSettingsTab"; import LabsUserSettingsTab, { showLabsFlags } from "../settings/tabs/user/LabsUserSettingsTab";
import AppearanceUserSettingsTab from "../settings/tabs/user/AppearanceUserSettingsTab"; import AppearanceUserSettingsTab from "../settings/tabs/user/AppearanceUserSettingsTab";
import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab";
@ -37,6 +37,7 @@ import SessionManagerTab from "../settings/tabs/user/SessionManagerTab";
import { UserTab } from "./UserTab"; import { UserTab } from "./UserTab";
import { NonEmptyArray } from "../../../@types/common"; import { NonEmptyArray } from "../../../@types/common";
import { SDKContext, SdkContextClass } from "../../../contexts/SDKContext"; import { SDKContext, SdkContextClass } from "../../../contexts/SDKContext";
import { useSettingValue } from "../../../hooks/useSettings";
interface IProps { interface IProps {
initialTabId?: UserTab; initialTabId?: UserTab;
@ -44,35 +45,11 @@ interface IProps {
onFinished(): void; onFinished(): void;
} }
interface IState { export default function UserSettingsDialog(props: IProps): JSX.Element {
mjolnirEnabled: boolean; const voipEnabled = useSettingValue<boolean>(UIFeature.Voip);
} const mjolnirEnabled = useSettingValue<boolean>("feature_mjolnir");
export default class UserSettingsDialog extends React.Component<IProps, IState> { const getTabs = (): NonEmptyArray<Tab<UserTab>> => {
private settingsWatchers: string[] = [];
public constructor(props: IProps) {
super(props);
this.state = {
mjolnirEnabled: SettingsStore.getValue("feature_mjolnir"),
};
}
public componentDidMount(): void {
this.settingsWatchers = [SettingsStore.watchSetting("feature_mjolnir", null, this.mjolnirChanged)];
}
public componentWillUnmount(): void {
this.settingsWatchers.forEach((watcherRef) => SettingsStore.unwatchSetting(watcherRef));
}
private mjolnirChanged: CallbackFn = (settingName, roomId, atLevel, newValue) => {
// We can cheat because we know what levels a feature is tracked at, and how it is tracked
this.setState({ mjolnirEnabled: newValue });
};
private getTabs(): NonEmptyArray<Tab<UserTab>> {
const tabs: Tab<UserTab>[] = []; const tabs: Tab<UserTab>[] = [];
tabs.push( tabs.push(
@ -80,7 +57,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.General, UserTab.General,
_td("common|general"), _td("common|general"),
"mx_UserSettingsDialog_settingsIcon", "mx_UserSettingsDialog_settingsIcon",
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />, <GeneralUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsGeneral", "UserSettingsGeneral",
), ),
); );
@ -90,7 +67,6 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
_td("settings|sessions|title"), _td("settings|sessions|title"),
"mx_UserSettingsDialog_sessionsIcon", "mx_UserSettingsDialog_sessionsIcon",
<SessionManagerTab />, <SessionManagerTab />,
// don't track with posthog while under construction
undefined, undefined,
), ),
); );
@ -117,7 +93,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.Preferences, UserTab.Preferences,
_td("common|preferences"), _td("common|preferences"),
"mx_UserSettingsDialog_preferencesIcon", "mx_UserSettingsDialog_preferencesIcon",
<PreferencesUserSettingsTab closeSettingsFn={this.props.onFinished} />, <PreferencesUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsPreferences", "UserSettingsPreferences",
), ),
); );
@ -140,7 +116,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
), ),
); );
if (SettingsStore.getValue(UIFeature.Voip)) { if (voipEnabled) {
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.Voice, UserTab.Voice,
@ -157,11 +133,11 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.Security, UserTab.Security,
_td("room_settings|security|title"), _td("room_settings|security|title"),
"mx_UserSettingsDialog_securityIcon", "mx_UserSettingsDialog_securityIcon",
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />, <SecurityUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsSecurityPrivacy", "UserSettingsSecurityPrivacy",
), ),
); );
// Show the Labs tab if enabled or if there are any active betas
if (showLabsFlags() || SettingsStore.getFeatureSettingNames().some((k) => SettingsStore.getBetaInfo(k))) { if (showLabsFlags() || SettingsStore.getFeatureSettingNames().some((k) => SettingsStore.getBetaInfo(k))) {
tabs.push( tabs.push(
new Tab( new Tab(
@ -173,7 +149,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
), ),
); );
} }
if (this.state.mjolnirEnabled) { if (mjolnirEnabled) {
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.Mjolnir, UserTab.Mjolnir,
@ -195,29 +171,23 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
); );
return tabs as NonEmptyArray<Tab<UserTab>>; return tabs as NonEmptyArray<Tab<UserTab>>;
} };
public render(): React.ReactNode {
return ( return (
// XXX: SDKContext is provided within the LoggedInView subtree. // XXX: SDKContext is provided within the LoggedInView subtree.
// Modals function outside the MatrixChat React tree, so sdkContext is reprovided here to simulate that. // Modals function outside the MatrixChat React tree, so sdkContext is reprovided here to simulate that.
// The longer term solution is to move our ModalManager into the React tree to inherit contexts properly. // The longer term solution is to move our ModalManager into the React tree to inherit contexts properly.
<SDKContext.Provider value={this.props.sdkContext}> <SDKContext.Provider value={props.sdkContext}>
<BaseDialog <BaseDialog
className="mx_UserSettingsDialog" className="mx_UserSettingsDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={props.onFinished}
title={_t("common|settings")} title={_t("common|settings")}
> >
<div className="mx_SettingsDialog_content"> <div className="mx_SettingsDialog_content">
<TabbedView <TabbedView tabs={getTabs()} initialTabId={props.initialTabId} screenName="UserSettings" />
tabs={this.getTabs()}
initialTabId={this.props.initialTabId}
screenName="UserSettings"
/>
</div> </div>
</BaseDialog> </BaseDialog>
</SDKContext.Provider> </SDKContext.Provider>
); );
}
} }

View file

@ -152,14 +152,17 @@ describe("<UserSettingsDialog />", () => {
watchSettingCallbacks[settingName] = callback; watchSettingCallbacks[settingName] = callback;
return `mock-watcher-id-${settingName}`; return `mock-watcher-id-${settingName}`;
}); });
mockSettingsStore.getValue.mockReturnValue(false);
const { queryByTestId, unmount } = render(getComponent()); const { queryByTestId, unmount } = render(getComponent());
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy(); expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy();
expect(mockSettingsStore.watchSetting.mock.calls[0][0]).toEqual("feature_mjolnir"); expect(mockSettingsStore.watchSetting).toHaveBeenCalledWith("feature_mjolnir", null, expect.anything());
// call the watch setting callback // call the watch setting callback
mockSettingsStore.getValue.mockReturnValue(true);
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true); watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true);
// tab is rendered now // tab is rendered now
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy(); expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();