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 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");
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 { _t, _td } from "../../../languageHandler";
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 AppearanceUserSettingsTab from "../settings/tabs/user/AppearanceUserSettingsTab";
import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab";
@ -37,6 +37,7 @@ import SessionManagerTab from "../settings/tabs/user/SessionManagerTab";
import { UserTab } from "./UserTab";
import { NonEmptyArray } from "../../../@types/common";
import { SDKContext, SdkContextClass } from "../../../contexts/SDKContext";
import { useSettingValue } from "../../../hooks/useSettings";
interface IProps {
initialTabId?: UserTab;
@ -44,35 +45,11 @@ interface IProps {
onFinished(): void;
}
interface IState {
mjolnirEnabled: boolean;
}
export default function UserSettingsDialog(props: IProps): JSX.Element {
const voipEnabled = useSettingValue<boolean>(UIFeature.Voip);
const mjolnirEnabled = useSettingValue<boolean>("feature_mjolnir");
export default class UserSettingsDialog extends React.Component<IProps, IState> {
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 getTabs = (): NonEmptyArray<Tab<UserTab>> => {
const tabs: Tab<UserTab>[] = [];
tabs.push(
@ -80,7 +57,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.General,
_td("common|general"),
"mx_UserSettingsDialog_settingsIcon",
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />,
<GeneralUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsGeneral",
),
);
@ -90,7 +67,6 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
_td("settings|sessions|title"),
"mx_UserSettingsDialog_sessionsIcon",
<SessionManagerTab />,
// don't track with posthog while under construction
undefined,
),
);
@ -117,7 +93,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.Preferences,
_td("common|preferences"),
"mx_UserSettingsDialog_preferencesIcon",
<PreferencesUserSettingsTab closeSettingsFn={this.props.onFinished} />,
<PreferencesUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsPreferences",
),
);
@ -140,7 +116,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
),
);
if (SettingsStore.getValue(UIFeature.Voip)) {
if (voipEnabled) {
tabs.push(
new Tab(
UserTab.Voice,
@ -157,11 +133,11 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
UserTab.Security,
_td("room_settings|security|title"),
"mx_UserSettingsDialog_securityIcon",
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
<SecurityUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsSecurityPrivacy",
),
);
// Show the Labs tab if enabled or if there are any active betas
if (showLabsFlags() || SettingsStore.getFeatureSettingNames().some((k) => SettingsStore.getBetaInfo(k))) {
tabs.push(
new Tab(
@ -173,7 +149,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
),
);
}
if (this.state.mjolnirEnabled) {
if (mjolnirEnabled) {
tabs.push(
new Tab(
UserTab.Mjolnir,
@ -195,29 +171,23 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
);
return tabs as NonEmptyArray<Tab<UserTab>>;
}
};
public render(): React.ReactNode {
return (
// XXX: SDKContext is provided within the LoggedInView subtree.
// 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.
<SDKContext.Provider value={this.props.sdkContext}>
<BaseDialog
className="mx_UserSettingsDialog"
hasCancel={true}
onFinished={this.props.onFinished}
title={_t("common|settings")}
>
<div className="mx_SettingsDialog_content">
<TabbedView
tabs={this.getTabs()}
initialTabId={this.props.initialTabId}
screenName="UserSettings"
/>
</div>
</BaseDialog>
</SDKContext.Provider>
);
}
return (
// XXX: SDKContext is provided within the LoggedInView subtree.
// 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.
<SDKContext.Provider value={props.sdkContext}>
<BaseDialog
className="mx_UserSettingsDialog"
hasCancel={true}
onFinished={props.onFinished}
title={_t("common|settings")}
>
<div className="mx_SettingsDialog_content">
<TabbedView tabs={getTabs()} initialTabId={props.initialTabId} screenName="UserSettings" />
</div>
</BaseDialog>
</SDKContext.Provider>
);
}

View file

@ -152,14 +152,17 @@ describe("<UserSettingsDialog />", () => {
watchSettingCallbacks[settingName] = callback;
return `mock-watcher-id-${settingName}`;
});
mockSettingsStore.getValue.mockReturnValue(false);
const { queryByTestId, unmount } = render(getComponent());
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
mockSettingsStore.getValue.mockReturnValue(true);
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true);
// tab is rendered now
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();