Disable profile controls if the HS doesn't allow them to be set (#12652)
* Disable profile controls if the HS doesn't allow them to be set Also updates to the js-sdk interface changes in https://github.com/matrix-org/matrix-js-sdk/pull/4246 * Remove unnecessary await * Pass disabled prop to accessiblebutton in avatarsetting * Use getCapabilities in case there are no cached capabilities * Fix test * Go back to just using getCapabilities Rather than change the other places
This commit is contained in:
parent
922676a7cc
commit
510fb1ba2f
6 changed files with 42 additions and 7 deletions
|
@ -170,6 +170,7 @@ const AvatarSetting: React.FC<IProps> = ({
|
||||||
aria-labelledby={disabled ? undefined : a11yId}
|
aria-labelledby={disabled ? undefined : a11yId}
|
||||||
// Inhibit tab stop as we have explicit upload/remove buttons
|
// Inhibit tab stop as we have explicit upload/remove buttons
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<BaseAvatar idName={placeholderId} name={placeholderName} size="90px" />
|
<BaseAvatar idName={placeholderId} name={placeholderName} size="90px" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -184,6 +185,7 @@ const AvatarSetting: React.FC<IProps> = ({
|
||||||
onClick={uploadAvatar}
|
onClick={uploadAvatar}
|
||||||
// Inhibit tab stop as we have explicit upload/remove buttons
|
// Inhibit tab stop as we have explicit upload/remove buttons
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,17 @@ const UsernameBox: React.FC<UsernameBoxProps> = ({ username }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface UserProfileSettingsProps {
|
||||||
|
// Whether the homeserver allows the user to set their display name.
|
||||||
|
canSetDisplayName: boolean;
|
||||||
|
// Whether the homeserver allows the user to set their avatar.
|
||||||
|
canSetAvatar: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group of settings views to allow the user to set their profile information.
|
* A group of settings views to allow the user to set their profile information.
|
||||||
*/
|
*/
|
||||||
const UserProfileSettings: React.FC = () => {
|
const UserProfileSettings: React.FC<UserProfileSettingsProps> = ({ canSetDisplayName, canSetAvatar }) => {
|
||||||
const [avatarURL, setAvatarURL] = useState(OwnProfileStore.instance.avatarMxc);
|
const [avatarURL, setAvatarURL] = useState(OwnProfileStore.instance.avatarMxc);
|
||||||
const [displayName, setDisplayName] = useState(OwnProfileStore.instance.displayName ?? "");
|
const [displayName, setDisplayName] = useState(OwnProfileStore.instance.displayName ?? "");
|
||||||
const [initialDisplayName, setInitialDisplayName] = useState(OwnProfileStore.instance.displayName ?? "");
|
const [initialDisplayName, setInitialDisplayName] = useState(OwnProfileStore.instance.displayName ?? "");
|
||||||
|
@ -143,10 +150,16 @@ const UserProfileSettings: React.FC = () => {
|
||||||
[client],
|
[client],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const someFieldsDisabled = !canSetDisplayName || !canSetAvatar;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserProfileSettings">
|
<div className="mx_UserProfileSettings">
|
||||||
<h2>{_t("common|profile")}</h2>
|
<h2>{_t("common|profile")}</h2>
|
||||||
<div>{_t("settings|general|profile_subtitle")}</div>
|
<div>
|
||||||
|
{someFieldsDisabled
|
||||||
|
? _t("settings|general|profile_subtitle_oidc")
|
||||||
|
: _t("settings|general|profile_subtitle")}
|
||||||
|
</div>
|
||||||
<div className="mx_UserProfileSettings_profile">
|
<div className="mx_UserProfileSettings_profile">
|
||||||
<AvatarSetting
|
<AvatarSetting
|
||||||
avatar={avatarURL ?? undefined}
|
avatar={avatarURL ?? undefined}
|
||||||
|
@ -155,6 +168,7 @@ const UserProfileSettings: React.FC = () => {
|
||||||
removeAvatar={avatarURL ? onAvatarRemove : undefined}
|
removeAvatar={avatarURL ? onAvatarRemove : undefined}
|
||||||
placeholderName={displayName}
|
placeholderName={displayName}
|
||||||
placeholderId={client.getUserId() ?? ""}
|
placeholderId={client.getUserId() ?? ""}
|
||||||
|
disabled={!canSetAvatar}
|
||||||
/>
|
/>
|
||||||
<EditInPlace
|
<EditInPlace
|
||||||
className="mx_UserProfileSettings_profile_displayName"
|
className="mx_UserProfileSettings_profile_displayName"
|
||||||
|
@ -169,6 +183,7 @@ const UserProfileSettings: React.FC = () => {
|
||||||
onCancel={onDisplayNameCancel}
|
onCancel={onDisplayNameCancel}
|
||||||
onSave={onDisplayNameSave}
|
onSave={onDisplayNameSave}
|
||||||
error={displayNameError ? _t("settings|general|display_name_error") : undefined}
|
error={displayNameError ? _t("settings|general|display_name_error") : undefined}
|
||||||
|
disabled={!canSetDisplayName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{avatarError && (
|
{avatarError && (
|
||||||
|
|
|
@ -56,6 +56,8 @@ interface IState {
|
||||||
idServerName?: string;
|
idServerName?: string;
|
||||||
externalAccountManagementUrl?: string;
|
externalAccountManagementUrl?: string;
|
||||||
canMake3pidChanges: boolean;
|
canMake3pidChanges: boolean;
|
||||||
|
canSetDisplayName: boolean;
|
||||||
|
canSetAvatar: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
|
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
|
||||||
|
@ -72,6 +74,8 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
spellCheckLanguages: [],
|
spellCheckLanguages: [],
|
||||||
canChangePassword: false,
|
canChangePassword: false,
|
||||||
canMake3pidChanges: false,
|
canMake3pidChanges: false,
|
||||||
|
canSetDisplayName: false,
|
||||||
|
canSetAvatar: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getCapabilities();
|
this.getCapabilities();
|
||||||
|
@ -95,7 +99,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
private async getCapabilities(): Promise<void> {
|
private async getCapabilities(): Promise<void> {
|
||||||
const cli = this.context.client!;
|
const cli = this.context.client!;
|
||||||
|
|
||||||
const capabilities = await cli.getCapabilities(); // this is cached
|
const capabilities = (await cli.getCapabilities()) ?? {};
|
||||||
const changePasswordCap = capabilities["m.change_password"];
|
const changePasswordCap = capabilities["m.change_password"];
|
||||||
|
|
||||||
// You can change your password so long as the capability isn't explicitly disabled. The implicit
|
// You can change your password so long as the capability isn't explicitly disabled. The implicit
|
||||||
|
@ -110,7 +114,17 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
// so the behaviour for when it is missing has to be assume true
|
// 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 canMake3pidChanges = !capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true;
|
||||||
|
|
||||||
this.setState({ canChangePassword, externalAccountManagementUrl, canMake3pidChanges });
|
const canSetDisplayName =
|
||||||
|
!capabilities["m.set_displayname"] || capabilities["m.set_displayname"].enabled === true;
|
||||||
|
const canSetAvatar = !capabilities["m.set_avatar_url"] || capabilities["m.set_avatar_url"].enabled === true;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
canChangePassword,
|
||||||
|
externalAccountManagementUrl,
|
||||||
|
canMake3pidChanges,
|
||||||
|
canSetDisplayName,
|
||||||
|
canSetAvatar,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onLanguageChange = (newLanguage: string): void => {
|
private onLanguageChange = (newLanguage: string): void => {
|
||||||
|
@ -309,7 +323,10 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
return (
|
return (
|
||||||
<SettingsTab data-testid="mx_GeneralUserSettingsTab">
|
<SettingsTab data-testid="mx_GeneralUserSettingsTab">
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<UserProfileSettings />
|
<UserProfileSettings
|
||||||
|
canSetDisplayName={this.state.canSetDisplayName}
|
||||||
|
canSetAvatar={this.state.canSetAvatar}
|
||||||
|
/>
|
||||||
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
|
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
|
||||||
{this.renderAccountSection()}
|
{this.renderAccountSection()}
|
||||||
{this.renderLanguageSection()}
|
{this.renderLanguageSection()}
|
||||||
|
|
|
@ -2527,6 +2527,7 @@
|
||||||
"password_change_success": "Your password was successfully changed.",
|
"password_change_success": "Your password was successfully changed.",
|
||||||
"personal_info": "Personal info",
|
"personal_info": "Personal info",
|
||||||
"profile_subtitle": "This is how you appear to others on the app.",
|
"profile_subtitle": "This is how you appear to others on the app.",
|
||||||
|
"profile_subtitle_oidc": "Your account is managed separately by an identity provider and so some of your personal information can’t be changed here.",
|
||||||
"remove_email_prompt": "Remove %(email)s?",
|
"remove_email_prompt": "Remove %(email)s?",
|
||||||
"remove_msisdn_prompt": "Remove %(phone)s?",
|
"remove_msisdn_prompt": "Remove %(phone)s?",
|
||||||
"spell_check_locale_placeholder": "Choose a locale",
|
"spell_check_locale_placeholder": "Choose a locale",
|
||||||
|
|
|
@ -67,7 +67,7 @@ const renderProfileSettings = (toastRack: Partial<ToastRack>, client: MatrixClie
|
||||||
return render(
|
return render(
|
||||||
<MatrixClientContext.Provider value={client}>
|
<MatrixClientContext.Provider value={client}>
|
||||||
<ToastContext.Provider value={toastRack}>
|
<ToastContext.Provider value={toastRack}>
|
||||||
<UserProfileSettings />
|
<UserProfileSettings canSetAvatar={true} canSetDisplayName={true} />
|
||||||
</ToastContext.Provider>
|
</ToastContext.Provider>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -129,7 +129,7 @@ export const mockClientMethodsEvents = () => ({
|
||||||
export const mockClientMethodsServer = (): Partial<Record<MethodLikeKeys<MatrixClient>, unknown>> => ({
|
export const mockClientMethodsServer = (): Partial<Record<MethodLikeKeys<MatrixClient>, unknown>> => ({
|
||||||
getIdentityServerUrl: jest.fn(),
|
getIdentityServerUrl: jest.fn(),
|
||||||
getHomeserverUrl: jest.fn(),
|
getHomeserverUrl: jest.fn(),
|
||||||
getCapabilities: jest.fn().mockReturnValue({}),
|
getCapabilities: jest.fn().mockResolvedValue({}),
|
||||||
getClientWellKnown: jest.fn().mockReturnValue({}),
|
getClientWellKnown: jest.fn().mockReturnValue({}),
|
||||||
waitForClientWellKnown: jest.fn().mockResolvedValue({}),
|
waitForClientWellKnown: jest.fn().mockResolvedValue({}),
|
||||||
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),
|
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue