Skip to content
This repository was archived by the owner on Oct 22, 2024. It is now read-only.

Commit 9cd0c24

Browse files
authored
Convert general user to functional component (#12856)
1 parent 1270518 commit 9cd0c24

File tree

1 file changed

+122
-122
lines changed

1 file changed

+122
-122
lines changed

src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx

+122-122
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
1616
limitations under the License.
1717
*/
1818

19-
import React from "react";
19+
import React, { useCallback, useContext, useEffect } from "react";
2020
import { HTTPError } from "matrix-js-sdk/src/matrix";
2121
import { logger } from "matrix-js-sdk/src/logger";
2222

@@ -34,70 +34,106 @@ import { SettingsSection } from "../../shared/SettingsSection";
3434
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
3535
import { SDKContext } from "../../../../../contexts/SDKContext";
3636
import UserPersonalInfoSettings from "../../UserPersonalInfoSettings";
37+
import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext";
3738

3839
interface IProps {
3940
closeSettingsFn: () => void;
4041
}
4142

42-
interface IState {
43+
interface AccountSectionProps {
4344
canChangePassword: boolean;
44-
idServerName?: string;
45-
externalAccountManagementUrl?: string;
46-
canMake3pidChanges: boolean;
47-
canSetDisplayName: boolean;
48-
canSetAvatar: boolean;
45+
onPasswordChangeError: (e: Error) => void;
46+
onPasswordChanged: () => void;
4947
}
5048

51-
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
52-
public static contextType = SDKContext;
53-
public context!: React.ContextType<typeof SDKContext>;
54-
55-
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
56-
super(props, context);
57-
this.context = context;
58-
59-
this.state = {
60-
canChangePassword: false,
61-
canMake3pidChanges: false,
62-
canSetDisplayName: false,
63-
canSetAvatar: false,
64-
};
65-
66-
this.getCapabilities();
67-
}
68-
69-
private async getCapabilities(): Promise<void> {
70-
const cli = this.context.client!;
71-
72-
const capabilities = (await cli.getCapabilities()) ?? {};
73-
const changePasswordCap = capabilities["m.change_password"];
74-
75-
// You can change your password so long as the capability isn't explicitly disabled. The implicit
76-
// behaviour is you can change your password when the capability is missing or has not-false as
77-
// the enabled flag value.
78-
const canChangePassword = !changePasswordCap || changePasswordCap["enabled"] !== false;
79-
80-
await this.context.oidcClientStore.readyPromise; // wait for the store to be ready
81-
const externalAccountManagementUrl = this.context.oidcClientStore.accountManagementEndpoint;
82-
// https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability
83-
// We support as far back as v1.1 which doesn't have m.3pid_changes
84-
// so the behaviour for when it is missing has to be assume true
85-
const canMake3pidChanges = !capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true;
86-
87-
const canSetDisplayName =
88-
!capabilities["m.set_displayname"] || capabilities["m.set_displayname"].enabled === true;
89-
const canSetAvatar = !capabilities["m.set_avatar_url"] || capabilities["m.set_avatar_url"].enabled === true;
90-
91-
this.setState({
92-
canChangePassword,
93-
externalAccountManagementUrl,
94-
canMake3pidChanges,
95-
canSetDisplayName,
96-
canSetAvatar,
97-
});
98-
}
49+
const AccountSection: React.FC<AccountSectionProps> = ({
50+
canChangePassword,
51+
onPasswordChangeError,
52+
onPasswordChanged,
53+
}) => {
54+
if (!canChangePassword) return <></>;
55+
56+
return (
57+
<>
58+
<SettingsSubsection
59+
heading={_t("settings|general|account_section")}
60+
stretchContent
61+
data-testid="accountSection"
62+
>
63+
<SettingsSubsectionText>{_t("settings|general|password_change_section")}</SettingsSubsectionText>
64+
<ChangePassword
65+
className="mx_GeneralUserSettingsTab_section--account_changePassword"
66+
rowClassName=""
67+
buttonKind="primary"
68+
onError={onPasswordChangeError}
69+
onFinished={onPasswordChanged}
70+
/>
71+
</SettingsSubsection>
72+
</>
73+
);
74+
};
75+
76+
interface ManagementSectionProps {
77+
onDeactivateClicked: () => void;
78+
}
9979

100-
private onPasswordChangeError = (err: Error): void => {
80+
const ManagementSection: React.FC<ManagementSectionProps> = ({ onDeactivateClicked }) => {
81+
return (
82+
<SettingsSection heading={_t("settings|general|deactivate_section")}>
83+
<SettingsSubsection
84+
heading={_t("settings|general|account_management_section")}
85+
data-testid="account-management-section"
86+
description={_t("settings|general|deactivate_warning")}
87+
>
88+
<AccessibleButton onClick={onDeactivateClicked} kind="danger">
89+
{_t("settings|general|deactivate_section")}
90+
</AccessibleButton>
91+
</SettingsSubsection>
92+
</SettingsSection>
93+
);
94+
};
95+
96+
const GeneralUserSettingsTab: React.FC<IProps> = ({ closeSettingsFn }) => {
97+
const [externalAccountManagementUrl, setExternalAccountManagementUrl] = React.useState<string | undefined>();
98+
const [canMake3pidChanges, setCanMake3pidChanges] = React.useState<boolean>(false);
99+
const [canSetDisplayName, setCanSetDisplayName] = React.useState<boolean>(false);
100+
const [canSetAvatar, setCanSetAvatar] = React.useState<boolean>(false);
101+
const [canChangePassword, setCanChangePassword] = React.useState<boolean>(false);
102+
103+
const cli = useMatrixClientContext();
104+
const sdkContext = useContext(SDKContext);
105+
106+
useEffect(() => {
107+
(async () => {
108+
const capabilities = (await cli.getCapabilities()) ?? {};
109+
const changePasswordCap = capabilities["m.change_password"];
110+
111+
// You can change your password so long as the capability isn't explicitly disabled. The implicit
112+
// behaviour is you can change your password when the capability is missing or has not-false as
113+
// the enabled flag value.
114+
const canChangePassword = !changePasswordCap || changePasswordCap["enabled"] !== false;
115+
116+
await sdkContext.oidcClientStore.readyPromise; // wait for the store to be ready
117+
const externalAccountManagementUrl = sdkContext.oidcClientStore.accountManagementEndpoint;
118+
// https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability
119+
// We support as far back as v1.1 which doesn't have m.3pid_changes
120+
// so the behaviour for when it is missing has to be assume true
121+
const canMake3pidChanges =
122+
!capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true;
123+
124+
const canSetDisplayName =
125+
!capabilities["m.set_displayname"] || capabilities["m.set_displayname"].enabled === true;
126+
const canSetAvatar = !capabilities["m.set_avatar_url"] || capabilities["m.set_avatar_url"].enabled === true;
127+
128+
setCanMake3pidChanges(canMake3pidChanges);
129+
setCanSetDisplayName(canSetDisplayName);
130+
setCanSetAvatar(canSetAvatar);
131+
setExternalAccountManagementUrl(externalAccountManagementUrl);
132+
setCanChangePassword(canChangePassword);
133+
})();
134+
}, [cli, sdkContext.oidcClientStore]);
135+
136+
const onPasswordChangeError = useCallback((err: Error): void => {
101137
logger.error("Failed to change password: " + err);
102138

103139
let underlyingError = err;
@@ -127,85 +163,49 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
127163
title: _t("settings|general|error_password_change_title"),
128164
description: errorMessageToDisplay,
129165
});
130-
};
166+
}, []);
131167

132-
private onPasswordChanged = (): void => {
168+
const onPasswordChanged = useCallback((): void => {
133169
const description = _t("settings|general|password_change_success");
134170
// TODO: Figure out a design that doesn't involve replacing the current dialog
135171
Modal.createDialog(ErrorDialog, {
136172
title: _t("common|success"),
137173
description,
138174
});
139-
};
175+
}, []);
140176

141-
private onDeactivateClicked = (): void => {
177+
const onDeactivateClicked = useCallback((): void => {
142178
Modal.createDialog(DeactivateAccountDialog, {
143179
onFinished: (success) => {
144-
if (success) this.props.closeSettingsFn();
180+
if (success) closeSettingsFn();
145181
},
146182
});
147-
};
148-
149-
private renderAccountSection(): JSX.Element | undefined {
150-
if (!this.state.canChangePassword) return undefined;
151-
152-
return (
153-
<>
154-
<SettingsSubsection
155-
heading={_t("settings|general|account_section")}
156-
stretchContent
157-
data-testid="accountSection"
158-
>
159-
<SettingsSubsectionText>{_t("settings|general|password_change_section")}</SettingsSubsectionText>
160-
<ChangePassword
161-
className="mx_GeneralUserSettingsTab_section--account_changePassword"
162-
rowClassName=""
163-
buttonKind="primary"
164-
onError={this.onPasswordChangeError}
165-
onFinished={this.onPasswordChanged}
166-
/>
167-
</SettingsSubsection>
168-
</>
169-
);
170-
}
183+
}, [closeSettingsFn]);
171184

172-
private renderManagementSection(): JSX.Element {
173-
// TODO: Improve warning text for account deactivation
174-
return (
175-
<SettingsSection heading={_t("settings|general|deactivate_section")}>
176-
<SettingsSubsection
177-
heading={_t("settings|general|account_management_section")}
178-
data-testid="account-management-section"
179-
description={_t("settings|general|deactivate_warning")}
180-
>
181-
<AccessibleButton onClick={this.onDeactivateClicked} kind="danger">
182-
{_t("settings|general|deactivate_section")}
183-
</AccessibleButton>
184-
</SettingsSubsection>
185-
</SettingsSection>
186-
);
185+
let accountManagementSection: JSX.Element | undefined;
186+
const isAccountManagedExternally = Boolean(externalAccountManagementUrl);
187+
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
188+
accountManagementSection = <ManagementSection onDeactivateClicked={onDeactivateClicked} />;
187189
}
188190

189-
public render(): React.ReactNode {
190-
let accountManagementSection: JSX.Element | undefined;
191-
const isAccountManagedExternally = !!this.state.externalAccountManagementUrl;
192-
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
193-
accountManagementSection = this.renderManagementSection();
194-
}
191+
return (
192+
<SettingsTab data-testid="mx_GeneralUserSettingsTab">
193+
<SettingsSection>
194+
<UserProfileSettings
195+
externalAccountManagementUrl={externalAccountManagementUrl}
196+
canSetDisplayName={canSetDisplayName}
197+
canSetAvatar={canSetAvatar}
198+
/>
199+
<UserPersonalInfoSettings canMake3pidChanges={canMake3pidChanges} />
200+
<AccountSection
201+
canChangePassword={canChangePassword}
202+
onPasswordChanged={onPasswordChanged}
203+
onPasswordChangeError={onPasswordChangeError}
204+
/>
205+
</SettingsSection>
206+
{accountManagementSection}
207+
</SettingsTab>
208+
);
209+
};
195210

196-
return (
197-
<SettingsTab data-testid="mx_GeneralUserSettingsTab">
198-
<SettingsSection>
199-
<UserProfileSettings
200-
externalAccountManagementUrl={this.state.externalAccountManagementUrl}
201-
canSetDisplayName={this.state.canSetDisplayName}
202-
canSetAvatar={this.state.canSetAvatar}
203-
/>
204-
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
205-
{this.renderAccountSection()}
206-
</SettingsSection>
207-
{accountManagementSection}
208-
</SettingsTab>
209-
);
210-
}
211-
}
211+
export default GeneralUserSettingsTab;

0 commit comments

Comments
 (0)