1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-23 11:02:35 +03:00

Hide the displayname edit button if disabled in the config

This commit is contained in:
Quentin Gliech
2024-03-29 16:47:19 +01:00
parent 2976bee2ab
commit ee68521792
8 changed files with 142 additions and 71 deletions

View File

@@ -36,6 +36,9 @@ pub struct SiteConfig {
/// Whether user can change their email. /// Whether user can change their email.
email_change_allowed: bool, email_change_allowed: bool,
/// Whether user can change their display name.
display_name_change_allowed: bool,
} }
#[ComplexObject] #[ComplexObject]
@@ -56,6 +59,7 @@ impl SiteConfig {
tos_uri: data_model.tos_uri.clone(), tos_uri: data_model.tos_uri.clone(),
imprint: data_model.imprint.clone(), imprint: data_model.imprint.clone(),
email_change_allowed: data_model.email_change_allowed, email_change_allowed: data_model.email_change_allowed,
display_name_change_allowed: data_model.displayname_change_allowed,
} }
} }
} }

View File

@@ -1266,6 +1266,10 @@ type SiteConfig implements Node {
""" """
emailChangeAllowed: Boolean! emailChangeAllowed: Boolean!
""" """
Whether user can change their display name.
"""
displayNameChangeAllowed: Boolean!
"""
The ID of the site configuration. The ID of the site configuration.
""" """
id: ID! id: ID!

View File

@@ -22,12 +22,13 @@ import {
SetDisplayNameStatus, SetDisplayNameStatus,
} from "../../gql/graphql"; } from "../../gql/graphql";
import UserGreeting, { FRAGMENT } from "./UserGreeting"; import UserGreeting, { CONFIG_FRAGMENT, FRAGMENT } from "./UserGreeting";
const Template: React.FC<{ const Template: React.FC<{
displayName?: string; displayName?: string;
mxid: string; mxid: string;
}> = ({ displayName, mxid }) => { displayNameChangeAllowed: boolean;
}> = ({ displayName, mxid, displayNameChangeAllowed }) => {
const userId = "user id"; const userId = "user id";
const mockClient = { const mockClient = {
@@ -57,9 +58,17 @@ const Template: React.FC<{
FRAGMENT, FRAGMENT,
); );
const config = makeFragmentData(
{
id: "site config id",
displayNameChangeAllowed,
},
CONFIG_FRAGMENT,
);
return ( return (
<Provider value={mockClient}> <Provider value={mockClient}>
<UserGreeting user={user} /> <UserGreeting user={user} siteConfig={config} />
</Provider> </Provider>
); );
}; };
@@ -68,10 +77,14 @@ const meta = {
title: "UI/User Greeting", title: "UI/User Greeting",
component: Template, component: Template,
args: { args: {
displayNameChangeAllowed: true,
displayName: "Kilgore Trout", displayName: "Kilgore Trout",
mxid: "@kilgore:matrix.org", mxid: "@kilgore:matrix.org",
}, },
argTypes: { argTypes: {
displayNameChangeAllowed: {
control: "boolean",
},
displayName: { displayName: {
control: "text", control: "text",
}, },
@@ -91,3 +104,9 @@ export const NoDisplayName: Story = {
displayName: undefined, displayName: undefined,
}, },
}; };
export const DisplayNameChangeNotAllowed: Story = {
args: {
displayNameChangeAllowed: false,
},
};

View File

@@ -43,6 +43,13 @@ export const FRAGMENT = graphql(/* GraphQL */ `
} }
`); `);
export const CONFIG_FRAGMENT = graphql(/* GraphQL */ `
fragment UserGreeting_siteConfig on SiteConfig {
id
displayNameChangeAllowed
}
`);
const SET_DISPLAYNAME_MUTATION = graphql(/* GraphQL */ ` const SET_DISPLAYNAME_MUTATION = graphql(/* GraphQL */ `
mutation SetDisplayName($userId: ID!, $displayName: String) { mutation SetDisplayName($userId: ID!, $displayName: String) {
setDisplayName(input: { userId: $userId, displayName: $displayName }) { setDisplayName(input: { userId: $userId, displayName: $displayName }) {
@@ -77,11 +84,13 @@ const EditButton = forwardRef<
type Props = { type Props = {
user: FragmentType<typeof FRAGMENT>; user: FragmentType<typeof FRAGMENT>;
siteConfig: FragmentType<typeof CONFIG_FRAGMENT>;
}; };
const UserGreeting: React.FC<Props> = ({ user }) => { const UserGreeting: React.FC<Props> = ({ user, siteConfig }) => {
const fieldRef = useRef<HTMLInputElement>(null); const fieldRef = useRef<HTMLInputElement>(null);
const data = useFragment(FRAGMENT, user); const data = useFragment(FRAGMENT, user);
const { displayNameChangeAllowed } = useFragment(CONFIG_FRAGMENT, siteConfig);
const [setDisplayNameResult, setDisplayName] = useMutation( const [setDisplayNameResult, setDisplayName] = useMutation(
SET_DISPLAYNAME_MUTATION, SET_DISPLAYNAME_MUTATION,
@@ -131,6 +140,7 @@ const UserGreeting: React.FC<Props> = ({ user }) => {
)} )}
</div> </div>
{displayNameChangeAllowed && (
<Dialog.Dialog <Dialog.Dialog
trigger={<EditButton label={t("action.edit")} />} trigger={<EditButton label={t("action.edit")} />}
open={open} open={open}
@@ -140,7 +150,9 @@ const UserGreeting: React.FC<Props> = ({ user }) => {
setOpen(open); setOpen(open);
}} }}
> >
<Dialog.Title>{t("frontend.account.edit_profile.title")}</Dialog.Title> <Dialog.Title>
{t("frontend.account.edit_profile.title")}
</Dialog.Title>
<Avatar <Avatar
size="88px" size="88px"
@@ -200,6 +212,7 @@ const UserGreeting: React.FC<Props> = ({ user }) => {
<Button kind="tertiary">{t("action.cancel")}</Button> <Button kind="tertiary">{t("action.cancel")}</Button>
</Dialog.Close> </Dialog.Close>
</Dialog.Dialog> </Dialog.Dialog>
)}
</div> </div>
); );
}; };

View File

@@ -31,6 +31,7 @@ const documents = {
"\n mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n": types.RemoveEmailDocument, "\n mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n": types.RemoveEmailDocument,
"\n mutation SetPrimaryEmail($id: ID!) {\n setPrimaryEmail(input: { userEmailId: $id }) {\n status\n user {\n id\n primaryEmail {\n id\n }\n }\n }\n }\n": types.SetPrimaryEmailDocument, "\n mutation SetPrimaryEmail($id: ID!) {\n setPrimaryEmail(input: { userEmailId: $id }) {\n status\n user {\n id\n primaryEmail {\n id\n }\n }\n }\n }\n": types.SetPrimaryEmailDocument,
"\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n": types.UserGreeting_UserFragmentDoc, "\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n": types.UserGreeting_UserFragmentDoc,
"\n fragment UserGreeting_siteConfig on SiteConfig {\n id\n displayNameChangeAllowed\n }\n": types.UserGreeting_SiteConfigFragmentDoc,
"\n mutation SetDisplayName($userId: ID!, $displayName: String) {\n setDisplayName(input: { userId: $userId, displayName: $displayName }) {\n status\n user {\n id\n matrix {\n displayName\n }\n }\n }\n }\n": types.SetDisplayNameDocument, "\n mutation SetDisplayName($userId: ID!, $displayName: String) {\n setDisplayName(input: { userId: $userId, displayName: $displayName }) {\n status\n user {\n id\n matrix {\n displayName\n }\n }\n }\n }\n": types.SetDisplayNameDocument,
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n": types.AddEmailDocument, "\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n": types.AddEmailDocument,
"\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.UserEmailListQueryDocument, "\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.UserEmailListQueryDocument,
@@ -45,7 +46,7 @@ const documents = {
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": types.BrowserSessionListDocument, "\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": types.BrowserSessionListDocument,
"\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n": types.SessionsOverviewQueryDocument, "\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n": types.SessionsOverviewQueryDocument,
"\n query AppSessionsListQuery(\n $before: String\n $after: String\n $first: Int\n $last: Int\n ) {\n viewer {\n __typename\n\n ... on User {\n id\n appSessions(\n before: $before\n after: $after\n first: $first\n last: $last\n state: ACTIVE\n ) {\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n startCursor\n endCursor\n hasNextPage\n hasPreviousPage\n }\n }\n }\n }\n }\n": types.AppSessionsListQueryDocument, "\n query AppSessionsListQuery(\n $before: String\n $after: String\n $first: Int\n $last: Int\n ) {\n viewer {\n __typename\n\n ... on User {\n id\n appSessions(\n before: $before\n after: $after\n first: $first\n last: $last\n state: ACTIVE\n ) {\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n startCursor\n endCursor\n hasNextPage\n hasPreviousPage\n }\n }\n }\n }\n }\n": types.AppSessionsListQueryDocument,
"\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n }\n": types.CurrentUserGreetingDocument, "\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n\n siteConfig {\n id\n ...UserGreeting_siteConfig\n }\n }\n": types.CurrentUserGreetingDocument,
"\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n ...OAuth2Client_detail\n }\n }\n": types.OAuth2ClientQueryDocument, "\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n ...OAuth2Client_detail\n }\n }\n": types.OAuth2ClientQueryDocument,
"\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.CurrentViewerQueryDocument, "\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.CurrentViewerQueryDocument,
"\n query DeviceRedirectQuery($deviceId: String!, $userId: ID!) {\n session(deviceId: $deviceId, userId: $userId) {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.DeviceRedirectQueryDocument, "\n query DeviceRedirectQuery($deviceId: String!, $userId: ID!) {\n session(deviceId: $deviceId, userId: $userId) {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.DeviceRedirectQueryDocument,
@@ -139,6 +140,10 @@ export function graphql(source: "\n mutation SetPrimaryEmail($id: ID!) {\n s
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n"): (typeof documents)["\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n"]; export function graphql(source: "\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n"): (typeof documents)["\n fragment UserGreeting_user on User {\n id\n matrix {\n mxid\n displayName\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment UserGreeting_siteConfig on SiteConfig {\n id\n displayNameChangeAllowed\n }\n"): (typeof documents)["\n fragment UserGreeting_siteConfig on SiteConfig {\n id\n displayNameChangeAllowed\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
@@ -198,7 +203,7 @@ export function graphql(source: "\n query AppSessionsListQuery(\n $before: S
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n }\n"): (typeof documents)["\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n }\n"]; export function graphql(source: "\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n\n siteConfig {\n id\n ...UserGreeting_siteConfig\n }\n }\n"): (typeof documents)["\n query CurrentUserGreeting {\n viewerSession {\n __typename\n\n ... on BrowserSession {\n id\n\n user {\n id\n ...UnverifiedEmailAlert_user\n ...UserGreeting_user\n }\n }\n }\n\n siteConfig {\n id\n ...UserGreeting_siteConfig\n }\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

File diff suppressed because one or more lines are too long

View File

@@ -2417,6 +2417,17 @@ export default {
"kind": "OBJECT", "kind": "OBJECT",
"name": "SiteConfig", "name": "SiteConfig",
"fields": [ "fields": [
{
"name": "displayNameChangeAllowed",
"type": {
"kind": "NON_NULL",
"ofType": {
"kind": "SCALAR",
"name": "Any"
}
},
"args": []
},
{ {
"name": "emailChangeAllowed", "name": "emailChangeAllowed",
"type": { "type": {

View File

@@ -41,6 +41,11 @@ const QUERY = graphql(/* GraphQL */ `
} }
} }
} }
siteConfig {
id
...UserGreeting_siteConfig
}
} }
`); `);
@@ -66,6 +71,8 @@ function Account(): React.ReactElement {
if (result.error) throw result.error; if (result.error) throw result.error;
const session = result.data?.viewerSession; const session = result.data?.viewerSession;
if (session?.__typename !== "BrowserSession") throw notFound(); if (session?.__typename !== "BrowserSession") throw notFound();
const siteConfig = result.data?.siteConfig;
if (!siteConfig) throw Error(); // This should never happen
const onSessionEnd = useEndBrowserSession(session.id, true); const onSessionEnd = useEndBrowserSession(session.id, true);
return ( return (
@@ -79,7 +86,7 @@ function Account(): React.ReactElement {
<EndSessionButton endSession={onSessionEnd} /> <EndSessionButton endSession={onSessionEnd} />
</header> </header>
<UserGreeting user={session.user} /> <UserGreeting user={session.user} siteConfig={siteConfig} />
<UnverifiedEmailAlert user={session.user} /> <UnverifiedEmailAlert user={session.user} />