Convert general user to functional component (#12856)

This commit is contained in:
David Baker 2024-08-01 16:28:11 +01:00 committed by GitHub
parent 127051892d
commit 9cd0c247a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from "react"; import React, { useCallback, useContext, useEffect } from "react";
import { HTTPError } from "matrix-js-sdk/src/matrix"; import { HTTPError } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
@ -34,70 +34,106 @@ import { SettingsSection } from "../../shared/SettingsSection";
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection"; import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
import { SDKContext } from "../../../../../contexts/SDKContext"; import { SDKContext } from "../../../../../contexts/SDKContext";
import UserPersonalInfoSettings from "../../UserPersonalInfoSettings"; import UserPersonalInfoSettings from "../../UserPersonalInfoSettings";
import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext";
interface IProps { interface IProps {
closeSettingsFn: () => void; closeSettingsFn: () => void;
} }
interface IState { interface AccountSectionProps {
canChangePassword: boolean; canChangePassword: boolean;
idServerName?: string; onPasswordChangeError: (e: Error) => void;
externalAccountManagementUrl?: string; onPasswordChanged: () => void;
canMake3pidChanges: boolean;
canSetDisplayName: boolean;
canSetAvatar: boolean;
} }
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> { const AccountSection: React.FC<AccountSectionProps> = ({
public static contextType = SDKContext; canChangePassword,
public context!: React.ContextType<typeof SDKContext>; onPasswordChangeError,
onPasswordChanged,
}) => {
if (!canChangePassword) return <></>;
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) { return (
super(props, context); <>
this.context = context; <SettingsSubsection
heading={_t("settings|general|account_section")}
stretchContent
data-testid="accountSection"
>
<SettingsSubsectionText>{_t("settings|general|password_change_section")}</SettingsSubsectionText>
<ChangePassword
className="mx_GeneralUserSettingsTab_section--account_changePassword"
rowClassName=""
buttonKind="primary"
onError={onPasswordChangeError}
onFinished={onPasswordChanged}
/>
</SettingsSubsection>
</>
);
};
this.state = { interface ManagementSectionProps {
canChangePassword: false, onDeactivateClicked: () => void;
canMake3pidChanges: false, }
canSetDisplayName: false,
canSetAvatar: false,
};
this.getCapabilities(); const ManagementSection: React.FC<ManagementSectionProps> = ({ onDeactivateClicked }) => {
} return (
<SettingsSection heading={_t("settings|general|deactivate_section")}>
<SettingsSubsection
heading={_t("settings|general|account_management_section")}
data-testid="account-management-section"
description={_t("settings|general|deactivate_warning")}
>
<AccessibleButton onClick={onDeactivateClicked} kind="danger">
{_t("settings|general|deactivate_section")}
</AccessibleButton>
</SettingsSubsection>
</SettingsSection>
);
};
private async getCapabilities(): Promise<void> { const GeneralUserSettingsTab: React.FC<IProps> = ({ closeSettingsFn }) => {
const cli = this.context.client!; const [externalAccountManagementUrl, setExternalAccountManagementUrl] = React.useState<string | undefined>();
const [canMake3pidChanges, setCanMake3pidChanges] = React.useState<boolean>(false);
const [canSetDisplayName, setCanSetDisplayName] = React.useState<boolean>(false);
const [canSetAvatar, setCanSetAvatar] = React.useState<boolean>(false);
const [canChangePassword, setCanChangePassword] = React.useState<boolean>(false);
const capabilities = (await cli.getCapabilities()) ?? {}; const cli = useMatrixClientContext();
const changePasswordCap = capabilities["m.change_password"]; const sdkContext = useContext(SDKContext);
// You can change your password so long as the capability isn't explicitly disabled. The implicit useEffect(() => {
// behaviour is you can change your password when the capability is missing or has not-false as (async () => {
// the enabled flag value. const capabilities = (await cli.getCapabilities()) ?? {};
const canChangePassword = !changePasswordCap || changePasswordCap["enabled"] !== false; const changePasswordCap = capabilities["m.change_password"];
await this.context.oidcClientStore.readyPromise; // wait for the store to be ready // You can change your password so long as the capability isn't explicitly disabled. The implicit
const externalAccountManagementUrl = this.context.oidcClientStore.accountManagementEndpoint; // behaviour is you can change your password when the capability is missing or has not-false as
// https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability // the enabled flag value.
// We support as far back as v1.1 which doesn't have m.3pid_changes const canChangePassword = !changePasswordCap || changePasswordCap["enabled"] !== false;
// so the behaviour for when it is missing has to be assume true
const canMake3pidChanges = !capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true;
const canSetDisplayName = await sdkContext.oidcClientStore.readyPromise; // wait for the store to be ready
!capabilities["m.set_displayname"] || capabilities["m.set_displayname"].enabled === true; const externalAccountManagementUrl = sdkContext.oidcClientStore.accountManagementEndpoint;
const canSetAvatar = !capabilities["m.set_avatar_url"] || capabilities["m.set_avatar_url"].enabled === true; // https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability
// We support as far back as v1.1 which doesn't have m.3pid_changes
// so the behaviour for when it is missing has to be assume true
const canMake3pidChanges =
!capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true;
this.setState({ const canSetDisplayName =
canChangePassword, !capabilities["m.set_displayname"] || capabilities["m.set_displayname"].enabled === true;
externalAccountManagementUrl, const canSetAvatar = !capabilities["m.set_avatar_url"] || capabilities["m.set_avatar_url"].enabled === true;
canMake3pidChanges,
canSetDisplayName,
canSetAvatar,
});
}
private onPasswordChangeError = (err: Error): void => { setCanMake3pidChanges(canMake3pidChanges);
setCanSetDisplayName(canSetDisplayName);
setCanSetAvatar(canSetAvatar);
setExternalAccountManagementUrl(externalAccountManagementUrl);
setCanChangePassword(canChangePassword);
})();
}, [cli, sdkContext.oidcClientStore]);
const onPasswordChangeError = useCallback((err: Error): void => {
logger.error("Failed to change password: " + err); logger.error("Failed to change password: " + err);
let underlyingError = err; let underlyingError = err;
@ -127,85 +163,49 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
title: _t("settings|general|error_password_change_title"), title: _t("settings|general|error_password_change_title"),
description: errorMessageToDisplay, description: errorMessageToDisplay,
}); });
}; }, []);
private onPasswordChanged = (): void => { const onPasswordChanged = useCallback((): void => {
const description = _t("settings|general|password_change_success"); const description = _t("settings|general|password_change_success");
// TODO: Figure out a design that doesn't involve replacing the current dialog // TODO: Figure out a design that doesn't involve replacing the current dialog
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("common|success"), title: _t("common|success"),
description, description,
}); });
}; }, []);
private onDeactivateClicked = (): void => { const onDeactivateClicked = useCallback((): void => {
Modal.createDialog(DeactivateAccountDialog, { Modal.createDialog(DeactivateAccountDialog, {
onFinished: (success) => { onFinished: (success) => {
if (success) this.props.closeSettingsFn(); if (success) closeSettingsFn();
}, },
}); });
}; }, [closeSettingsFn]);
private renderAccountSection(): JSX.Element | undefined { let accountManagementSection: JSX.Element | undefined;
if (!this.state.canChangePassword) return undefined; const isAccountManagedExternally = Boolean(externalAccountManagementUrl);
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
return ( accountManagementSection = <ManagementSection onDeactivateClicked={onDeactivateClicked} />;
<>
<SettingsSubsection
heading={_t("settings|general|account_section")}
stretchContent
data-testid="accountSection"
>
<SettingsSubsectionText>{_t("settings|general|password_change_section")}</SettingsSubsectionText>
<ChangePassword
className="mx_GeneralUserSettingsTab_section--account_changePassword"
rowClassName=""
buttonKind="primary"
onError={this.onPasswordChangeError}
onFinished={this.onPasswordChanged}
/>
</SettingsSubsection>
</>
);
} }
private renderManagementSection(): JSX.Element { return (
// TODO: Improve warning text for account deactivation <SettingsTab data-testid="mx_GeneralUserSettingsTab">
return ( <SettingsSection>
<SettingsSection heading={_t("settings|general|deactivate_section")}> <UserProfileSettings
<SettingsSubsection externalAccountManagementUrl={externalAccountManagementUrl}
heading={_t("settings|general|account_management_section")} canSetDisplayName={canSetDisplayName}
data-testid="account-management-section" canSetAvatar={canSetAvatar}
description={_t("settings|general|deactivate_warning")} />
> <UserPersonalInfoSettings canMake3pidChanges={canMake3pidChanges} />
<AccessibleButton onClick={this.onDeactivateClicked} kind="danger"> <AccountSection
{_t("settings|general|deactivate_section")} canChangePassword={canChangePassword}
</AccessibleButton> onPasswordChanged={onPasswordChanged}
</SettingsSubsection> onPasswordChangeError={onPasswordChangeError}
/>
</SettingsSection> </SettingsSection>
); {accountManagementSection}
} </SettingsTab>
);
};
public render(): React.ReactNode { export default GeneralUserSettingsTab;
let accountManagementSection: JSX.Element | undefined;
const isAccountManagedExternally = !!this.state.externalAccountManagementUrl;
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
accountManagementSection = this.renderManagementSection();
}
return (
<SettingsTab data-testid="mx_GeneralUserSettingsTab">
<SettingsSection>
<UserProfileSettings
externalAccountManagementUrl={this.state.externalAccountManagementUrl}
canSetDisplayName={this.state.canSetDisplayName}
canSetAvatar={this.state.canSetAvatar}
/>
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
{this.renderAccountSection()}
</SettingsSection>
{accountManagementSection}
</SettingsTab>
);
}
}