From 8a159bfd32171370beb23cb2480eefe357b06b96 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 18 Jun 2024 12:02:18 +0100 Subject: [PATCH 1/6] 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 --- .../views/settings/UserProfileSettings.tsx | 19 +++++++++++++-- .../tabs/user/GeneralUserSettingsTab.tsx | 23 ++++++++++++++++--- .../settings/tabs/user/SessionManagerTab.tsx | 2 +- src/i18n/strings/en_EN.json | 1 + .../settings/UserProfileSettings-test.tsx | 2 +- .../tabs/user/GeneralUserSettingsTab-test.tsx | 10 ++++---- .../tabs/user/SessionManagerTab-test.tsx | 4 ++-- test/test-utils/client.ts | 2 +- 8 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/components/views/settings/UserProfileSettings.tsx b/src/components/views/settings/UserProfileSettings.tsx index bd61f762ff5..6d1bccb30e9 100644 --- a/src/components/views/settings/UserProfileSettings.tsx +++ b/src/components/views/settings/UserProfileSettings.tsx @@ -55,10 +55,17 @@ const UsernameBox: React.FC = ({ 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. */ -const UserProfileSettings: React.FC = () => { +const UserProfileSettings: React.FC = ({ canSetDisplayName, canSetAvatar }) => { const [avatarURL, setAvatarURL] = useState(OwnProfileStore.instance.avatarMxc); const [displayName, setDisplayName] = useState(OwnProfileStore.instance.displayName ?? ""); const [initialDisplayName, setInitialDisplayName] = useState(OwnProfileStore.instance.displayName ?? ""); @@ -143,10 +150,16 @@ const UserProfileSettings: React.FC = () => { [client], ); + const someFieldsDisabled = !canSetDisplayName || !canSetAvatar; + return (

{_t("common|profile")}

-
{_t("settings|general|profile_subtitle")}
+
+ {someFieldsDisabled + ? _t("settings|general|profile_subtitle_oidc") + : _t("settings|general|profile_subtitle")} +
{ removeAvatar={avatarURL ? onAvatarRemove : undefined} placeholderName={displayName} placeholderId={client.getUserId() ?? ""} + disabled={!canSetAvatar} /> { onCancel={onDisplayNameCancel} onSave={onDisplayNameSave} error={displayNameError ? _t("settings|general|display_name_error") : undefined} + disabled={!canSetDisplayName} />
{avatarError && ( diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx index 0e32b9126c3..3292d8bc11d 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx @@ -90,6 +90,8 @@ interface IState { idServerName?: string; externalAccountManagementUrl?: string; canMake3pidChanges: boolean; + canSetDisplayName: boolean; + canSetAvatar: boolean; } export default class GeneralUserSettingsTab extends React.Component { @@ -122,6 +124,8 @@ export default class GeneralUserSettingsTab extends React.Component { const cli = this.context.client!; - const capabilities = await cli.getCapabilities(); // this is cached + const capabilities = (await cli.getCachedCapabilities()) ?? {}; const changePasswordCap = capabilities["m.change_password"]; // You can change your password so long as the capability isn't explicitly disabled. The implicit @@ -182,7 +186,17 @@ export default class GeneralUserSettingsTab extends React.Component { @@ -561,7 +575,10 @@ export default class GeneralUserSettingsTab extends React.Component - + {this.renderAccountSection()} {this.renderLanguageSection()} {supportsMultiLanguageSpellCheck ? this.renderSpellCheckSection() : null} diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index ee51d0680fb..c733b53f7a0 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -189,7 +189,7 @@ const SessionManagerTab: React.FC<{ const userId = matrixClient?.getUserId(); const currentUserMember = (userId && matrixClient?.getUser(userId)) || undefined; const clientVersions = useAsyncMemo(() => matrixClient.getVersions(), [matrixClient]); - const capabilities = useAsyncMemo(async () => matrixClient?.getCapabilities(), [matrixClient]); + const capabilities = matrixClient.getCachedCapabilities(); const wellKnown = useMemo(() => matrixClient?.getClientWellKnown(), [matrixClient]); const oidcClientConfig = useAsyncMemo(async () => { try { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4550cfdad15..85933566251 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2529,6 +2529,7 @@ "password_change_section": "Set a new account password…", "password_change_success": "Your password was successfully changed.", "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_msisdn_prompt": "Remove %(phone)s?", "spell_check_locale_placeholder": "Choose a locale", diff --git a/test/components/views/settings/UserProfileSettings-test.tsx b/test/components/views/settings/UserProfileSettings-test.tsx index fd727729772..6b7f278e5a2 100644 --- a/test/components/views/settings/UserProfileSettings-test.tsx +++ b/test/components/views/settings/UserProfileSettings-test.tsx @@ -67,7 +67,7 @@ const renderProfileSettings = (toastRack: Partial, client: MatrixClie return render( - + , ); diff --git a/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx index 2532e98c600..57344b10e4f 100644 --- a/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx @@ -40,7 +40,7 @@ describe("", () => { const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), ...mockClientMethodsServer(), - getCapabilities: jest.fn(), + getCachedCapabilities: jest.fn(), getThreePids: jest.fn(), getIdentityServerUrl: jest.fn(), deleteThreePid: jest.fn(), @@ -63,7 +63,7 @@ describe("", () => { jest.spyOn(SettingsStore, "getValue").mockRestore(); jest.spyOn(logger, "error").mockRestore(); - mockClient.getCapabilities.mockResolvedValue({}); + mockClient.getCachedCapabilities.mockReturnValue({}); mockClient.getThreePids.mockResolvedValue({ threepids: [], }); @@ -198,7 +198,7 @@ describe("", () => { describe("3pids", () => { beforeEach(() => { - mockClient.getCapabilities.mockResolvedValue({ + mockClient.getCachedCapabilities.mockReturnValue({ "m.3pid_changes": { enabled: true, }, @@ -300,7 +300,7 @@ describe("", () => { it("should allow 3pid changes when capabilities does not have 3pid_changes", async () => { // 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 - mockClient.getCapabilities.mockResolvedValue({}); + mockClient.getCachedCapabilities.mockReturnValue({}); render(getComponent()); @@ -315,7 +315,7 @@ describe("", () => { describe("when 3pid changes capability is disabled", () => { beforeEach(() => { - mockClient.getCapabilities.mockResolvedValue({ + mockClient.getCachedCapabilities.mockReturnValue({ "m.3pid_changes": { enabled: false, }, diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index a88c322361c..8ce148ca786 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -1684,7 +1684,7 @@ describe("", () => { "org.matrix.msc3886": true, }, }); - mockClient.getCapabilities.mockResolvedValue({ + mockClient.getCachedCapabilities.mockReturnValue({ [GET_LOGIN_TOKEN_CAPABILITY.name]: { enabled: true, }, @@ -1726,7 +1726,7 @@ describe("", () => { "org.matrix.msc4108": true, }, }); - mockClient.getCapabilities.mockResolvedValue({ + mockClient.getCachedCapabilities.mockReturnValue({ [GET_LOGIN_TOKEN_CAPABILITY.name]: { enabled: true, }, diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index 00f5aa3f7b8..cf78f9b529e 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -129,7 +129,7 @@ export const mockClientMethodsEvents = () => ({ export const mockClientMethodsServer = (): Partial, unknown>> => ({ getIdentityServerUrl: jest.fn(), getHomeserverUrl: jest.fn(), - getCapabilities: jest.fn().mockReturnValue({}), + getCachedCapabilities: jest.fn().mockReturnValue({}), getClientWellKnown: jest.fn().mockReturnValue({}), waitForClientWellKnown: jest.fn().mockResolvedValue({}), doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false), From b88c78a8c2fee0da577af383a45a39cfc71f01d3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 19 Jun 2024 12:58:19 +0100 Subject: [PATCH 2/6] Remove unnecessary await --- .../views/settings/tabs/user/GeneralUserSettingsTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx index 3292d8bc11d..8859fd067bb 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx @@ -171,7 +171,7 @@ export default class GeneralUserSettingsTab extends React.Component { const cli = this.context.client!; - const capabilities = (await cli.getCachedCapabilities()) ?? {}; + const capabilities = cli.getCachedCapabilities() ?? {}; const changePasswordCap = capabilities["m.change_password"]; // You can change your password so long as the capability isn't explicitly disabled. The implicit From 28adbf4c55f398dc8ade5f35a4dcdcb76ba3a4e2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 19 Jun 2024 13:18:50 +0100 Subject: [PATCH 3/6] Pass disabled prop to accessiblebutton in avatarsetting --- src/components/views/settings/AvatarSetting.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/settings/AvatarSetting.tsx b/src/components/views/settings/AvatarSetting.tsx index 81f1cd5201c..0e95deed382 100644 --- a/src/components/views/settings/AvatarSetting.tsx +++ b/src/components/views/settings/AvatarSetting.tsx @@ -170,6 +170,7 @@ const AvatarSetting: React.FC = ({ aria-labelledby={disabled ? undefined : a11yId} // Inhibit tab stop as we have explicit upload/remove buttons tabIndex={-1} + disabled={disabled} > @@ -184,6 +185,7 @@ const AvatarSetting: React.FC = ({ onClick={uploadAvatar} // Inhibit tab stop as we have explicit upload/remove buttons tabIndex={-1} + disabled={disabled} /> ); } From aaa9f5f6c5527634bfae63da71588cbf2251e50e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2024 09:41:46 +0100 Subject: [PATCH 4/6] Use getCapabilities in case there are no cached capabilities --- .../views/settings/tabs/user/GeneralUserSettingsTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx index 8859fd067bb..0d3acf2eaa7 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx @@ -171,7 +171,7 @@ export default class GeneralUserSettingsTab extends React.Component { const cli = this.context.client!; - const capabilities = cli.getCachedCapabilities() ?? {}; + const capabilities = (await cli.getCapabilities()) ?? {}; const changePasswordCap = capabilities["m.change_password"]; // You can change your password so long as the capability isn't explicitly disabled. The implicit From 79dd3bfb7e8258c16723e8de9aa8cc804170d90e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2024 09:54:20 +0100 Subject: [PATCH 5/6] Fix test --- .../settings/tabs/user/GeneralUserSettingsTab-test.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx index 57344b10e4f..2532e98c600 100644 --- a/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/GeneralUserSettingsTab-test.tsx @@ -40,7 +40,7 @@ describe("", () => { const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), ...mockClientMethodsServer(), - getCachedCapabilities: jest.fn(), + getCapabilities: jest.fn(), getThreePids: jest.fn(), getIdentityServerUrl: jest.fn(), deleteThreePid: jest.fn(), @@ -63,7 +63,7 @@ describe("", () => { jest.spyOn(SettingsStore, "getValue").mockRestore(); jest.spyOn(logger, "error").mockRestore(); - mockClient.getCachedCapabilities.mockReturnValue({}); + mockClient.getCapabilities.mockResolvedValue({}); mockClient.getThreePids.mockResolvedValue({ threepids: [], }); @@ -198,7 +198,7 @@ describe("", () => { describe("3pids", () => { beforeEach(() => { - mockClient.getCachedCapabilities.mockReturnValue({ + mockClient.getCapabilities.mockResolvedValue({ "m.3pid_changes": { enabled: true, }, @@ -300,7 +300,7 @@ describe("", () => { it("should allow 3pid changes when capabilities does not have 3pid_changes", async () => { // 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 - mockClient.getCachedCapabilities.mockReturnValue({}); + mockClient.getCapabilities.mockResolvedValue({}); render(getComponent()); @@ -315,7 +315,7 @@ describe("", () => { describe("when 3pid changes capability is disabled", () => { beforeEach(() => { - mockClient.getCachedCapabilities.mockReturnValue({ + mockClient.getCapabilities.mockResolvedValue({ "m.3pid_changes": { enabled: false, }, From 6eaa73803fc39f753453c9fce2d6bd967c9c86b1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jun 2024 14:28:03 +0100 Subject: [PATCH 6/6] Go back to just using getCapabilities Rather than change the other places --- src/components/views/settings/tabs/user/SessionManagerTab.tsx | 2 +- .../views/settings/tabs/user/SessionManagerTab-test.tsx | 4 ++-- test/test-utils/client.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index c733b53f7a0..ee51d0680fb 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -189,7 +189,7 @@ const SessionManagerTab: React.FC<{ const userId = matrixClient?.getUserId(); const currentUserMember = (userId && matrixClient?.getUser(userId)) || undefined; const clientVersions = useAsyncMemo(() => matrixClient.getVersions(), [matrixClient]); - const capabilities = matrixClient.getCachedCapabilities(); + const capabilities = useAsyncMemo(async () => matrixClient?.getCapabilities(), [matrixClient]); const wellKnown = useMemo(() => matrixClient?.getClientWellKnown(), [matrixClient]); const oidcClientConfig = useAsyncMemo(async () => { try { diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index 8ce148ca786..a88c322361c 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -1684,7 +1684,7 @@ describe("", () => { "org.matrix.msc3886": true, }, }); - mockClient.getCachedCapabilities.mockReturnValue({ + mockClient.getCapabilities.mockResolvedValue({ [GET_LOGIN_TOKEN_CAPABILITY.name]: { enabled: true, }, @@ -1726,7 +1726,7 @@ describe("", () => { "org.matrix.msc4108": true, }, }); - mockClient.getCachedCapabilities.mockReturnValue({ + mockClient.getCapabilities.mockResolvedValue({ [GET_LOGIN_TOKEN_CAPABILITY.name]: { enabled: true, }, diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index cf78f9b529e..43d8392a150 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -129,7 +129,7 @@ export const mockClientMethodsEvents = () => ({ export const mockClientMethodsServer = (): Partial, unknown>> => ({ getIdentityServerUrl: jest.fn(), getHomeserverUrl: jest.fn(), - getCachedCapabilities: jest.fn().mockReturnValue({}), + getCapabilities: jest.fn().mockResolvedValue({}), getClientWellKnown: jest.fn().mockReturnValue({}), waitForClientWellKnown: jest.fn().mockResolvedValue({}), doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),