Skip to content
This repository has been archived by the owner on Sep 10, 2024. It is now read-only.

Commit

Permalink
Only show email form if the user is allowed to modify emails
Browse files Browse the repository at this point in the history
  • Loading branch information
sandhose committed Apr 3, 2024
1 parent 731ccbb commit 3183c5f
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 10 deletions.
17 changes: 13 additions & 4 deletions frontend/src/components/UserEmail/UserEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ const FRAGMENT = graphql(/* GraphQL */ `
}
`);

const CONFIG_FRAGMENT = graphql(/* GraphQL */ `
fragment UserEmail_siteConfig on SiteConfig {
id
emailChangeAllowed
}
`);

const REMOVE_EMAIL_MUTATION = graphql(/* GraphQL */ `
mutation RemoveEmail($id: ID!) {
removeEmail(input: { userEmailId: $id }) {
Expand Down Expand Up @@ -126,11 +133,13 @@ const DeleteButtonWithConfirmation: React.FC<

const UserEmail: React.FC<{
email: FragmentType<typeof FRAGMENT>;
siteConfig: FragmentType<typeof CONFIG_FRAGMENT>;
onRemove?: () => void;
isPrimary?: boolean;
}> = ({ email, isPrimary, onRemove }) => {
}> = ({ email, siteConfig, isPrimary, onRemove }) => {
const { t } = useTranslation();
const data = useFragment(FRAGMENT, email);
const { emailChangeAllowed } = useFragment(CONFIG_FRAGMENT, siteConfig);

const [setPrimaryResult, setPrimary] = useMutation(
SET_PRIMARY_EMAIL_MUTATION,
Expand Down Expand Up @@ -167,7 +176,7 @@ const UserEmail: React.FC<{
value={data.email}
className={styles.userEmailField}
/>
{!isPrimary && (
{!isPrimary && emailChangeAllowed && (
<DeleteButtonWithConfirmation
email={data.email}
disabled={removeResult.fetching}
Expand All @@ -176,13 +185,13 @@ const UserEmail: React.FC<{
)}
</div>

{isPrimary && (
{isPrimary && emailChangeAllowed && (
<Form.HelpMessage>
{t("frontend.user_email.cant_delete_primary")}
</Form.HelpMessage>
)}

{data.confirmedAt && !isPrimary && (
{data.confirmedAt && !isPrimary && emailChangeAllowed && (
<Form.HelpMessage>
<button
type="button"
Expand Down
17 changes: 15 additions & 2 deletions frontend/src/components/UserProfile/UserEmailList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,20 @@ const FRAGMENT = graphql(/* GraphQL */ `
}
`);

const CONFIG_FRAGMENT = graphql(/* GraphQL */ `
fragment UserEmailList_siteConfig on SiteConfig {
id
emailChangeAllowed
...UserEmail_siteConfig
}
`);

const UserEmailList: React.FC<{
user: FragmentType<typeof FRAGMENT>;
}> = ({ user }) => {
siteConfig: FragmentType<typeof CONFIG_FRAGMENT>;
}> = ({ user, siteConfig }) => {
const data = useFragment(FRAGMENT, user);
const config = useFragment(CONFIG_FRAGMENT, siteConfig);
const { t } = useTranslation();
const [pending, startTransition] = useTransition();

Expand Down Expand Up @@ -125,6 +135,7 @@ const UserEmailList: React.FC<{
email={edge.node}
key={edge.cursor}
isPrimary={primaryEmailId === edge.node.id}
siteConfig={config}
onRemove={onRemove}
/>
))}
Expand All @@ -136,7 +147,9 @@ const UserEmailList: React.FC<{
onNext={nextPage ? (): void => paginate(nextPage) : null}
disabled={pending}
/>
<AddEmailForm userId={data.id} onAdd={onAdd} />
{config.emailChangeAllowed && (
<AddEmailForm userId={data.id} onAdd={onAdd} />
)}
</>
);
};
Expand Down
22 changes: 19 additions & 3 deletions frontend/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const documents = {
types.UnverifiedEmailAlert_UserFragmentDoc,
"\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n":
types.UserEmail_EmailFragmentDoc,
"\n fragment UserEmail_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n }\n":
types.UserEmail_SiteConfigFragmentDoc,
"\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":
Expand All @@ -55,6 +57,8 @@ const documents = {
types.UserEmailListQueryDocument,
"\n fragment UserEmailList_user on User {\n id\n primaryEmail {\n id\n }\n }\n":
types.UserEmailList_UserFragmentDoc,
"\n fragment UserEmailList_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n ...UserEmail_siteConfig\n }\n":
types.UserEmailList_SiteConfigFragmentDoc,
"\n fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n":
types.BrowserSessionsOverview_UserFragmentDoc,
"\n fragment UserEmail_verifyEmail on UserEmail {\n id\n email\n }\n":
Expand All @@ -63,7 +67,7 @@ const documents = {
types.VerifyEmailDocument,
"\n mutation ResendVerificationEmail($id: ID!) {\n sendVerificationEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n primaryEmail {\n id\n }\n }\n\n email {\n id\n ...UserEmail_email\n }\n }\n }\n":
types.ResendVerificationEmailDocument,
"\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n }\n":
"\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n\n siteConfig {\n id\n ...UserEmailList_siteConfig\n }\n }\n":
types.UserProfileQueryDocument,
"\n query SessionDetailQuery($id: ID!) {\n viewerSession {\n ... on Node {\n id\n }\n }\n\n node(id: $id) {\n __typename\n id\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n":
types.SessionDetailQueryDocument,
Expand Down Expand Up @@ -185,6 +189,12 @@ export function graphql(
export function graphql(
source: "\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n",
): (typeof documents)["\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\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 UserEmail_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n }\n",
): (typeof documents)["\n fragment UserEmail_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down Expand Up @@ -227,6 +237,12 @@ export function graphql(
export function graphql(
source: "\n fragment UserEmailList_user on User {\n id\n primaryEmail {\n id\n }\n }\n",
): (typeof documents)["\n fragment UserEmailList_user on User {\n id\n primaryEmail {\n id\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 UserEmailList_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n ...UserEmail_siteConfig\n }\n",
): (typeof documents)["\n fragment UserEmailList_siteConfig on SiteConfig {\n id\n emailChangeAllowed\n ...UserEmail_siteConfig\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down Expand Up @@ -255,8 +271,8 @@ export function graphql(
* 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 UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n }\n",
): (typeof documents)["\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n }\n"];
source: "\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n\n siteConfig {\n id\n ...UserEmailList_siteConfig\n }\n }\n",
): (typeof documents)["\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserEmailList_user\n }\n }\n\n siteConfig {\n id\n ...UserEmailList_siteConfig\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
143 changes: 143 additions & 0 deletions frontend/src/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,12 @@ export type UserEmail_EmailFragment = {
confirmedAt?: string | null;
} & { " $fragmentName"?: "UserEmail_EmailFragment" };

export type UserEmail_SiteConfigFragment = {
__typename?: "SiteConfig";
id: string;
emailChangeAllowed: boolean;
} & { " $fragmentName"?: "UserEmail_SiteConfigFragment" };

export type RemoveEmailMutationVariables = Exact<{
id: Scalars["ID"]["input"];
}>;
Expand Down Expand Up @@ -1549,6 +1555,16 @@ export type UserEmailList_UserFragment = {
primaryEmail?: { __typename?: "UserEmail"; id: string } | null;
} & { " $fragmentName"?: "UserEmailList_UserFragment" };

export type UserEmailList_SiteConfigFragment = ({
__typename?: "SiteConfig";
id: string;
emailChangeAllowed: boolean;
} & {
" $fragmentRefs"?: {
UserEmail_SiteConfigFragment: UserEmail_SiteConfigFragment;
};
}) & { " $fragmentName"?: "UserEmailList_SiteConfigFragment" };

export type BrowserSessionsOverview_UserFragment = {
__typename?: "User";
id: string;
Expand Down Expand Up @@ -1620,6 +1636,11 @@ export type UserProfileQueryQuery = {
UserEmailList_UserFragment: UserEmailList_UserFragment;
};
});
siteConfig: { __typename?: "SiteConfig"; id: string } & {
" $fragmentRefs"?: {
UserEmailList_SiteConfigFragment: UserEmailList_SiteConfigFragment;
};
};
};

export type SessionDetailQueryQueryVariables = Exact<{
Expand Down Expand Up @@ -2305,6 +2326,74 @@ export const UserEmailList_UserFragmentDoc = {
},
],
} as unknown as DocumentNode<UserEmailList_UserFragment, unknown>;
export const UserEmail_SiteConfigFragmentDoc = {
kind: "Document",
definitions: [
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "UserEmail_siteConfig" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "SiteConfig" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "Field",
name: { kind: "Name", value: "emailChangeAllowed" },
},
],
},
},
],
} as unknown as DocumentNode<UserEmail_SiteConfigFragment, unknown>;
export const UserEmailList_SiteConfigFragmentDoc = {
kind: "Document",
definitions: [
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "UserEmailList_siteConfig" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "SiteConfig" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "Field",
name: { kind: "Name", value: "emailChangeAllowed" },
},
{
kind: "FragmentSpread",
name: { kind: "Name", value: "UserEmail_siteConfig" },
},
],
},
},
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "UserEmail_siteConfig" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "SiteConfig" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "Field",
name: { kind: "Name", value: "emailChangeAllowed" },
},
],
},
},
],
} as unknown as DocumentNode<UserEmailList_SiteConfigFragment, unknown>;
export const BrowserSessionsOverview_UserFragmentDoc = {
kind: "Document",
definitions: [
Expand Down Expand Up @@ -3544,6 +3633,38 @@ export const UserProfileQueryDocument = {
],
},
},
{
kind: "Field",
name: { kind: "Name", value: "siteConfig" },
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "FragmentSpread",
name: { kind: "Name", value: "UserEmailList_siteConfig" },
},
],
},
},
],
},
},
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "UserEmail_siteConfig" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "SiteConfig" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "Field",
name: { kind: "Name", value: "emailChangeAllowed" },
},
],
},
},
Expand Down Expand Up @@ -3571,6 +3692,28 @@ export const UserProfileQueryDocument = {
],
},
},
{
kind: "FragmentDefinition",
name: { kind: "Name", value: "UserEmailList_siteConfig" },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: "SiteConfig" },
},
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{
kind: "Field",
name: { kind: "Name", value: "emailChangeAllowed" },
},
{
kind: "FragmentSpread",
name: { kind: "Name", value: "UserEmail_siteConfig" },
},
],
},
},
],
} as unknown as DocumentNode<
UserProfileQueryQuery,
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/routes/_account.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const QUERY = graphql(/* GraphQL */ `
...UserEmailList_user
}
}
siteConfig {
id
...UserEmailList_siteConfig
}
}
`);

Expand All @@ -56,14 +61,16 @@ function Index(): React.ReactElement {
if (result.error) throw result.error;
const user = result.data?.viewer;
if (user?.__typename !== "User") throw notFound();
const siteConfig = result.data?.siteConfig;
if (!siteConfig) throw Error(); // This should never happen

return (
<>
<BlockList>
{/* This wrapper is only needed for the anchor link */}
<div className="flex flex-col gap-4" id="emails">
<Suspense fallback={<LoadingSpinner className="self-center m-4" />}>
<UserEmailList user={user} />
<UserEmailList siteConfig={siteConfig} user={user} />
</Suspense>
</div>

Expand Down

0 comments on commit 3183c5f

Please sign in to comment.