Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Convert UserSettings to functional component (#12474)
Browse files Browse the repository at this point in the history
* Convert UserSettings to functional component

* Put comment back

* Fix test

* Unused imports
  • Loading branch information
dbkr authored Apr 30, 2024
1 parent bb4f575 commit 906c9dd
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 62 deletions.
92 changes: 31 additions & 61 deletions src/components/views/dialogs/UserSettingsDialog.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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";
Expand All @@ -37,50 +37,27 @@ 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;
sdkContext: SdkContextClass;
onFinished(): void;
}

interface IState {
mjolnirEnabled: boolean;
}

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));
}
export default function UserSettingsDialog(props: IProps): JSX.Element {
const voipEnabled = useSettingValue<boolean>(UIFeature.Voip);
const mjolnirEnabled = useSettingValue<boolean>("feature_mjolnir");

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(
new Tab(
UserTab.General,
_td("common|general"),
"mx_UserSettingsDialog_settingsIcon",
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />,
<GeneralUserSettingsTab closeSettingsFn={props.onFinished} />,
"UserSettingsGeneral",
),
);
Expand All @@ -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,
),
);
Expand All @@ -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",
),
);
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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>
);
}
5 changes: 4 additions & 1 deletion test/components/views/dialogs/UserSettingsDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down

0 comments on commit 906c9dd

Please sign in to comment.