You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-11-20 12:02:22 +03:00
frontend: move the email verification form to a dedicated page
This commit is contained in:
@@ -28,6 +28,7 @@ type HomeRoute = { type: "home" };
|
|||||||
type AccountRoute = { type: "account" };
|
type AccountRoute = { type: "account" };
|
||||||
type OAuth2ClientRoute = { type: "client"; id: string };
|
type OAuth2ClientRoute = { type: "client"; id: string };
|
||||||
type BrowserSessionRoute = { type: "session"; id: string };
|
type BrowserSessionRoute = { type: "session"; id: string };
|
||||||
|
type VerifyEmailRoute = { type: "verify-email"; id: string };
|
||||||
type UnknownRoute = { type: "unknown"; segments: string[] };
|
type UnknownRoute = { type: "unknown"; segments: string[] };
|
||||||
|
|
||||||
export type Route =
|
export type Route =
|
||||||
@@ -35,6 +36,7 @@ export type Route =
|
|||||||
| AccountRoute
|
| AccountRoute
|
||||||
| OAuth2ClientRoute
|
| OAuth2ClientRoute
|
||||||
| BrowserSessionRoute
|
| BrowserSessionRoute
|
||||||
|
| VerifyEmailRoute
|
||||||
| UnknownRoute;
|
| UnknownRoute;
|
||||||
|
|
||||||
const routeToSegments = (route: Route): string[] => {
|
const routeToSegments = (route: Route): string[] => {
|
||||||
@@ -47,6 +49,8 @@ const routeToSegments = (route: Route): string[] => {
|
|||||||
return ["client", route.id];
|
return ["client", route.id];
|
||||||
case "session":
|
case "session":
|
||||||
return ["session", route.id];
|
return ["session", route.id];
|
||||||
|
case "verify-email":
|
||||||
|
return ["verify-email", route.id];
|
||||||
case "unknown":
|
case "unknown":
|
||||||
return route.segments;
|
return route.segments;
|
||||||
}
|
}
|
||||||
@@ -69,6 +73,10 @@ const segmentsToRoute = (segments: string[]): Route => {
|
|||||||
return { type: "session", id: segments[1] };
|
return { type: "session", id: segments[1] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (segments.length === 2 && segments[0] === "verify-email") {
|
||||||
|
return { type: "verify-email", id: segments[1] };
|
||||||
|
}
|
||||||
|
|
||||||
return { type: "unknown", segments };
|
return { type: "unknown", segments };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,6 +120,7 @@ const Home = lazy(() => import("./pages/Home"));
|
|||||||
const Account = lazy(() => import("./pages/Account"));
|
const Account = lazy(() => import("./pages/Account"));
|
||||||
const OAuth2Client = lazy(() => import("./pages/OAuth2Client"));
|
const OAuth2Client = lazy(() => import("./pages/OAuth2Client"));
|
||||||
const BrowserSession = lazy(() => import("./pages/BrowserSession"));
|
const BrowserSession = lazy(() => import("./pages/BrowserSession"));
|
||||||
|
const VerifyEmail = lazy(() => import("./pages/VerifyEmail"));
|
||||||
|
|
||||||
const InnerRouter: React.FC = () => {
|
const InnerRouter: React.FC = () => {
|
||||||
const route = useAtomValue(routeAtom);
|
const route = useAtomValue(routeAtom);
|
||||||
@@ -125,6 +134,8 @@ const InnerRouter: React.FC = () => {
|
|||||||
return <OAuth2Client id={route.id} />;
|
return <OAuth2Client id={route.id} />;
|
||||||
case "session":
|
case "session":
|
||||||
return <BrowserSession id={route.id} />;
|
return <BrowserSession id={route.id} />;
|
||||||
|
case "verify-email":
|
||||||
|
return <VerifyEmail id={route.id} />;
|
||||||
case "unknown":
|
case "unknown":
|
||||||
return <>Unknown route {JSON.stringify(route.segments)}</>;
|
return <>Unknown route {JSON.stringify(route.segments)}</>;
|
||||||
}
|
}
|
||||||
@@ -154,14 +165,10 @@ export const Link: React.FC<
|
|||||||
<a
|
<a
|
||||||
href={path}
|
href={path}
|
||||||
onClick={(e: React.MouseEvent): void => {
|
onClick={(e: React.MouseEvent): void => {
|
||||||
// Local links should be handled by the internal routers
|
|
||||||
// external links do not require a transition
|
|
||||||
if (!path.startsWith("http")) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
setRoute(route);
|
setRoute(route);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Control, Field, Root, Submit } from "@vector-im/compound-web";
|
import { Control, Field, Root, Submit } from "@vector-im/compound-web";
|
||||||
import { atom, useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { atomWithMutation } from "jotai-urql";
|
import { atomWithMutation } from "jotai-urql";
|
||||||
import { useRef, useTransition } from "react";
|
import { useRef, useTransition } from "react";
|
||||||
|
|
||||||
@@ -35,15 +35,10 @@ const ADD_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|||||||
|
|
||||||
const addUserEmailAtom = atomWithMutation(ADD_EMAIL_MUTATION);
|
const addUserEmailAtom = atomWithMutation(ADD_EMAIL_MUTATION);
|
||||||
|
|
||||||
export const latestAddedEmailAtom = atom(async (get) => {
|
const AddEmailForm: React.FC<{
|
||||||
const result = await get(addUserEmailAtom);
|
userId: string;
|
||||||
return result.data?.addEmail.email?.id ?? null;
|
onAdd?: (id: string) => void;
|
||||||
});
|
}> = ({ userId, onAdd }) => {
|
||||||
|
|
||||||
const AddEmailForm: React.FC<{ userId: string; onAdd?: () => void }> = ({
|
|
||||||
userId,
|
|
||||||
onAdd,
|
|
||||||
}) => {
|
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
const [addEmailResult, addEmail] = useAtom(addUserEmailAtom);
|
const [addEmailResult, addEmail] = useAtom(addUserEmailAtom);
|
||||||
const [pending, startTransition] = useTransition();
|
const [pending, startTransition] = useTransition();
|
||||||
@@ -60,8 +55,12 @@ const AddEmailForm: React.FC<{ userId: string; onAdd?: () => void }> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result.data?.addEmail.email?.id) {
|
||||||
|
throw new Error("Unexpected response from server");
|
||||||
|
}
|
||||||
|
|
||||||
// Call the onAdd callback if provided
|
// Call the onAdd callback if provided
|
||||||
onAdd?.();
|
onAdd?.(result.data?.addEmail.email?.id);
|
||||||
|
|
||||||
// Reset the form
|
// Reset the form
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Control,
|
|
||||||
Field,
|
|
||||||
Label,
|
|
||||||
Message,
|
|
||||||
Root as Form,
|
|
||||||
Submit,
|
|
||||||
} from "@vector-im/compound-web";
|
|
||||||
import { atom, useAtom, useSetAtom } from "jotai";
|
|
||||||
import { atomFamily } from "jotai/utils";
|
|
||||||
import { atomWithMutation } from "jotai-urql";
|
|
||||||
import { useRef, useTransition } from "react";
|
|
||||||
|
|
||||||
import { FragmentType, graphql, useFragment } from "../gql";
|
|
||||||
|
|
||||||
import Block from "./Block";
|
|
||||||
import DateTime from "./DateTime";
|
|
||||||
import Typography from "./Typography";
|
|
||||||
|
|
||||||
// This component shows a single user email address, with controls to verify it,
|
|
||||||
// resend the verification email, remove it, and set it as the primary email address.
|
|
||||||
|
|
||||||
const FRAGMENT = graphql(/* GraphQL */ `
|
|
||||||
fragment UserEmail_email on UserEmail {
|
|
||||||
id
|
|
||||||
email
|
|
||||||
createdAt
|
|
||||||
confirmedAt
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const VERIFY_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|
||||||
mutation VerifyEmail($id: ID!, $code: String!) {
|
|
||||||
verifyEmail(input: { userEmailId: $id, code: $code }) {
|
|
||||||
status
|
|
||||||
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
primaryEmail {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
email {
|
|
||||||
id
|
|
||||||
...UserEmail_email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const RESEND_VERIFICATION_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|
||||||
mutation ResendVerificationEmail($id: ID!) {
|
|
||||||
sendVerificationEmail(input: { userEmailId: $id }) {
|
|
||||||
status
|
|
||||||
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
primaryEmail {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
email {
|
|
||||||
id
|
|
||||||
...UserEmail_email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const REMOVE_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|
||||||
mutation RemoveEmail($id: ID!) {
|
|
||||||
removeEmail(input: { userEmailId: $id }) {
|
|
||||||
status
|
|
||||||
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const SET_PRIMARY_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|
||||||
mutation SetPrimaryEmail($id: ID!) {
|
|
||||||
setPrimaryEmail(input: { userEmailId: $id }) {
|
|
||||||
status
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
primaryEmail {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const verifyEmailFamily = atomFamily((id: string) => {
|
|
||||||
const verifyEmail = atomWithMutation(VERIFY_EMAIL_MUTATION);
|
|
||||||
|
|
||||||
// A proxy atom which pre-sets the id variable in the mutation
|
|
||||||
const verifyEmailAtom = atom(
|
|
||||||
(get) => get(verifyEmail),
|
|
||||||
(get, set, code: string) => set(verifyEmail, { id, code }),
|
|
||||||
);
|
|
||||||
|
|
||||||
return verifyEmailAtom;
|
|
||||||
});
|
|
||||||
|
|
||||||
const resendVerificationEmailFamily = atomFamily((id: string) => {
|
|
||||||
const resendVerificationEmail = atomWithMutation(
|
|
||||||
RESEND_VERIFICATION_EMAIL_MUTATION,
|
|
||||||
);
|
|
||||||
|
|
||||||
// A proxy atom which pre-sets the id variable in the mutation
|
|
||||||
const resendVerificationEmailAtom = atom(
|
|
||||||
(get) => get(resendVerificationEmail),
|
|
||||||
(get, set) => set(resendVerificationEmail, { id }),
|
|
||||||
);
|
|
||||||
|
|
||||||
return resendVerificationEmailAtom;
|
|
||||||
});
|
|
||||||
|
|
||||||
const removeEmailFamily = atomFamily((id: string) => {
|
|
||||||
const removeEmail = atomWithMutation(REMOVE_EMAIL_MUTATION);
|
|
||||||
|
|
||||||
// A proxy atom which pre-sets the id variable in the mutation
|
|
||||||
const removeEmailAtom = atom(
|
|
||||||
(get) => get(removeEmail),
|
|
||||||
(get, set) => set(removeEmail, { id }),
|
|
||||||
);
|
|
||||||
|
|
||||||
return removeEmailAtom;
|
|
||||||
});
|
|
||||||
|
|
||||||
const setPrimaryEmailFamily = atomFamily((id: string) => {
|
|
||||||
const setPrimaryEmail = atomWithMutation(SET_PRIMARY_EMAIL_MUTATION);
|
|
||||||
|
|
||||||
// A proxy atom which pre-sets the id variable in the mutation
|
|
||||||
const setPrimaryEmailAtom = atom(
|
|
||||||
(get) => get(setPrimaryEmail),
|
|
||||||
(get, set) => set(setPrimaryEmail, { id }),
|
|
||||||
);
|
|
||||||
|
|
||||||
return setPrimaryEmailAtom;
|
|
||||||
});
|
|
||||||
|
|
||||||
const UserEmail: React.FC<{
|
|
||||||
email: FragmentType<typeof FRAGMENT>;
|
|
||||||
onRemove?: () => void;
|
|
||||||
onSetPrimary?: () => void;
|
|
||||||
isPrimary?: boolean;
|
|
||||||
highlight?: boolean;
|
|
||||||
}> = ({ email, isPrimary, highlight, onSetPrimary, onRemove }) => {
|
|
||||||
const [pending, startTransition] = useTransition();
|
|
||||||
const data = useFragment(FRAGMENT, email);
|
|
||||||
const [verifyEmailResult, verifyEmail] = useAtom(verifyEmailFamily(data.id));
|
|
||||||
const [resendVerificationEmailResult, resendVerificationEmail] = useAtom(
|
|
||||||
resendVerificationEmailFamily(data.id),
|
|
||||||
);
|
|
||||||
const setPrimaryEmail = useSetAtom(setPrimaryEmailFamily(data.id));
|
|
||||||
const removeEmail = useSetAtom(removeEmailFamily(data.id));
|
|
||||||
const fieldRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const onFormSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
|
|
||||||
e.preventDefault();
|
|
||||||
const formData = new FormData(e.currentTarget);
|
|
||||||
const code = formData.get("code") as string;
|
|
||||||
startTransition(() => {
|
|
||||||
verifyEmail(code).then((result) => {
|
|
||||||
// Clear the form
|
|
||||||
e.currentTarget?.reset();
|
|
||||||
|
|
||||||
if (result.data?.verifyEmail.status === "VERIFIED") {
|
|
||||||
// Call the onSetPrimary callback if provided
|
|
||||||
// XXX: do we need a dedicated onVerify callback?
|
|
||||||
onSetPrimary?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResendClick = (): void => {
|
|
||||||
startTransition(() => {
|
|
||||||
resendVerificationEmail().then(() => {
|
|
||||||
fieldRef.current?.focus();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRemoveClick = (): void => {
|
|
||||||
startTransition(() => {
|
|
||||||
removeEmail().then(() => {
|
|
||||||
// Call the onRemove callback if provided
|
|
||||||
onRemove?.();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSetPrimaryClick = (): void => {
|
|
||||||
startTransition(() => {
|
|
||||||
setPrimaryEmail().then(() => {
|
|
||||||
// Call the onSetPrimary callback if provided
|
|
||||||
onSetPrimary?.();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const emailSent =
|
|
||||||
resendVerificationEmailResult.data?.sendVerificationEmail.status === "SENT";
|
|
||||||
const invalidCode =
|
|
||||||
verifyEmailResult.data?.verifyEmail.status === "INVALID_CODE";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Block
|
|
||||||
highlight={highlight}
|
|
||||||
className="grid grid-col-1 gap-2 pb-4 border-b-2 border-b-grey-200"
|
|
||||||
>
|
|
||||||
{isPrimary && (
|
|
||||||
<Typography variant="body" bold>
|
|
||||||
Primary
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
<Typography variant="caption" bold className="flex-1">
|
|
||||||
{data.email}
|
|
||||||
</Typography>
|
|
||||||
{data.confirmedAt ? (
|
|
||||||
<Typography variant="micro">
|
|
||||||
Verified <DateTime datetime={data.confirmedAt} />
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
<Form onSubmit={onFormSubmit} className="grid grid-cols-2 gap-2">
|
|
||||||
<Field name="code" className="col-span-2">
|
|
||||||
<Label>Code</Label>
|
|
||||||
<Control
|
|
||||||
ref={fieldRef}
|
|
||||||
placeholder="xxxxxx"
|
|
||||||
type="text"
|
|
||||||
inputMode="numeric"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
{invalidCode && (
|
|
||||||
<Message className="col-span-2 text-alert font-bold">
|
|
||||||
Invalid code
|
|
||||||
</Message>
|
|
||||||
)}
|
|
||||||
<Submit size="sm" type="submit" disabled={pending}>
|
|
||||||
Submit
|
|
||||||
</Submit>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
kind="secondary"
|
|
||||||
disabled={pending || emailSent}
|
|
||||||
onClick={onResendClick}
|
|
||||||
>
|
|
||||||
{emailSent ? "Sent!" : "Resend"}
|
|
||||||
</Button>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
{!isPrimary && (
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
{/* The primary email can only be set if the email was verified */}
|
|
||||||
{data.confirmedAt ? (
|
|
||||||
<Button size="sm" disabled={pending} onClick={onSetPrimaryClick}>
|
|
||||||
Set primary
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
kind="destructive"
|
|
||||||
size="sm"
|
|
||||||
disabled={pending}
|
|
||||||
onClick={onRemoveClick}
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Block>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UserEmail;
|
|
||||||
81
frontend/src/components/UserEmail/UserEmail.module.css
Normal file
81
frontend/src/components/UserEmail/UserEmail.module.css
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.user-email {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--cpd-space-2x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-line {
|
||||||
|
display: flex;
|
||||||
|
align-self: stretch;
|
||||||
|
gap: var(--cpd-space-2x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-field {
|
||||||
|
border: 1px solid var(--cpd-color-border-interactive-primary);
|
||||||
|
background: var(--cpd-color-bg-canvas-default);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: var(--cpd-space-3x) var(--cpd-space-4x);
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-delete {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: var(--cpd-space-12x);
|
||||||
|
width: var(--cpd-space-12x);
|
||||||
|
border-radius: 50%;
|
||||||
|
color: var(--cpd-color-icon-critical-primary);
|
||||||
|
padding: var(--cpd-space-2x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-delete:hover:not([disabled]) {
|
||||||
|
background-color: var(--cpd-color-red-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-delete[disabled] {
|
||||||
|
color: var(--cpd-color-icon-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-delete-icon {
|
||||||
|
display: block;
|
||||||
|
height: var(--cpd-space-8x);
|
||||||
|
width: var(--cpd-space-8x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email-unverified {
|
||||||
|
color: var(--cpd-color-text-critical-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
font-weight: var(--cpd-font-weight-medium);
|
||||||
|
border-radius: var(--cpd-radius-pill-effect);
|
||||||
|
padding-inline: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
background: var(--cpd-color-gray-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:active {
|
||||||
|
color: var(--cpd-color-text-on-solid-primary);
|
||||||
|
}
|
||||||
|
|
||||||
164
frontend/src/components/UserEmail/UserEmail.tsx
Normal file
164
frontend/src/components/UserEmail/UserEmail.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import IconDelete from "@vector-im/compound-design-tokens/icons/delete.svg";
|
||||||
|
import { Body } from "@vector-im/compound-web";
|
||||||
|
import { atom, useSetAtom } from "jotai";
|
||||||
|
import { atomFamily } from "jotai/utils";
|
||||||
|
import { atomWithMutation } from "jotai-urql";
|
||||||
|
import { useTransition } from "react";
|
||||||
|
|
||||||
|
import { Link } from "../../Router";
|
||||||
|
import { FragmentType, graphql, useFragment } from "../../gql";
|
||||||
|
|
||||||
|
import styles from "./UserEmail.module.css";
|
||||||
|
|
||||||
|
// This component shows a single user email address, with controls to verify it,
|
||||||
|
// resend the verification email, remove it, and set it as the primary email address.
|
||||||
|
|
||||||
|
const FRAGMENT = graphql(/* GraphQL */ `
|
||||||
|
fragment UserEmail_email on UserEmail {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
confirmedAt
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const REMOVE_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
|
mutation RemoveEmail($id: ID!) {
|
||||||
|
removeEmail(input: { userEmailId: $id }) {
|
||||||
|
status
|
||||||
|
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const SET_PRIMARY_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
|
mutation SetPrimaryEmail($id: ID!) {
|
||||||
|
setPrimaryEmail(input: { userEmailId: $id }) {
|
||||||
|
status
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
primaryEmail {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const removeEmailFamily = atomFamily((id: string) => {
|
||||||
|
const removeEmail = atomWithMutation(REMOVE_EMAIL_MUTATION);
|
||||||
|
|
||||||
|
// A proxy atom which pre-sets the id variable in the mutation
|
||||||
|
const removeEmailAtom = atom(
|
||||||
|
(get) => get(removeEmail),
|
||||||
|
(_get, set) => set(removeEmail, { id }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return removeEmailAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const setPrimaryEmailFamily = atomFamily((id: string) => {
|
||||||
|
const setPrimaryEmail = atomWithMutation(SET_PRIMARY_EMAIL_MUTATION);
|
||||||
|
|
||||||
|
// A proxy atom which pre-sets the id variable in the mutation
|
||||||
|
const setPrimaryEmailAtom = atom(
|
||||||
|
(get) => get(setPrimaryEmail),
|
||||||
|
(_get, set) => set(setPrimaryEmail, { id }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return setPrimaryEmailAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const DeleteButton: React.FC<{ disabled?: boolean; onClick?: () => void }> = ({
|
||||||
|
disabled,
|
||||||
|
onClick,
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onClick}
|
||||||
|
className={styles.userEmailDelete}
|
||||||
|
title="Remove email address"
|
||||||
|
>
|
||||||
|
<IconDelete className={styles.userEmailDeleteIcon} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const UserEmail: React.FC<{
|
||||||
|
email: FragmentType<typeof FRAGMENT>;
|
||||||
|
onRemove?: () => void;
|
||||||
|
onSetPrimary?: () => void;
|
||||||
|
isPrimary?: boolean;
|
||||||
|
highlight?: boolean;
|
||||||
|
}> = ({ email, isPrimary, highlight, onSetPrimary, onRemove }) => {
|
||||||
|
const [pending, startTransition] = useTransition();
|
||||||
|
const data = useFragment(FRAGMENT, email);
|
||||||
|
const setPrimaryEmail = useSetAtom(setPrimaryEmailFamily(data.id));
|
||||||
|
const removeEmail = useSetAtom(removeEmailFamily(data.id));
|
||||||
|
|
||||||
|
const onRemoveClick = (): void => {
|
||||||
|
startTransition(() => {
|
||||||
|
removeEmail().then(() => {
|
||||||
|
// Call the onRemove callback if provided
|
||||||
|
onRemove?.();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSetPrimaryClick = (): void => {
|
||||||
|
startTransition(() => {
|
||||||
|
setPrimaryEmail().then(() => {
|
||||||
|
// Call the onSetPrimary callback if provided
|
||||||
|
onSetPrimary?.();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.userEmail}>
|
||||||
|
{isPrimary ? <Body>Primary email</Body> : <Body>Email</Body>}
|
||||||
|
|
||||||
|
<div className={styles.userEmailLine}>
|
||||||
|
<div className={styles.userEmailField}>{data.email}</div>
|
||||||
|
<DeleteButton disabled={isPrimary || pending} onClick={onRemoveClick} />
|
||||||
|
</div>
|
||||||
|
{data.confirmedAt && !isPrimary && (
|
||||||
|
<button
|
||||||
|
className={styles.link}
|
||||||
|
disabled={pending}
|
||||||
|
onClick={onSetPrimaryClick}
|
||||||
|
>
|
||||||
|
Make primary
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{!data.confirmedAt && (
|
||||||
|
<div>
|
||||||
|
<span className={styles.userEmailUnverified}>Unverified</span> |{" "}
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
route={{ type: "verify-email", id: data.id }}
|
||||||
|
>
|
||||||
|
Retry verification
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserEmail;
|
||||||
15
frontend/src/components/UserEmail/index.ts
Normal file
15
frontend/src/components/UserEmail/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
export { default } from "./UserEmail";
|
||||||
@@ -17,17 +17,17 @@ import { atomFamily } from "jotai/utils";
|
|||||||
import { atomWithQuery } from "jotai-urql";
|
import { atomWithQuery } from "jotai-urql";
|
||||||
import { useTransition } from "react";
|
import { useTransition } from "react";
|
||||||
|
|
||||||
|
import { routeAtom } from "../Router";
|
||||||
import { graphql } from "../gql";
|
import { graphql } from "../gql";
|
||||||
import { PageInfo } from "../gql/graphql";
|
import { PageInfo } from "../gql/graphql";
|
||||||
import {
|
import {
|
||||||
atomForCurrentPagination,
|
atomForCurrentPagination,
|
||||||
atomWithPagination,
|
atomWithPagination,
|
||||||
FIRST_PAGE,
|
FIRST_PAGE,
|
||||||
LAST_PAGE,
|
|
||||||
Pagination,
|
Pagination,
|
||||||
} from "../pagination";
|
} from "../pagination";
|
||||||
|
|
||||||
import AddEmailForm, { latestAddedEmailAtom } from "./AddEmailForm";
|
import AddEmailForm from "./AddEmailForm";
|
||||||
import BlockList from "./BlockList";
|
import BlockList from "./BlockList";
|
||||||
import PaginationControls from "./PaginationControls";
|
import PaginationControls from "./PaginationControls";
|
||||||
import UserEmail from "./UserEmail";
|
import UserEmail from "./UserEmail";
|
||||||
@@ -129,12 +129,11 @@ const UserEmailList: React.FC<{
|
|||||||
const [pending, startTransition] = useTransition();
|
const [pending, startTransition] = useTransition();
|
||||||
const [result, refreshList] = useAtom(emailPageResultFamily(userId));
|
const [result, refreshList] = useAtom(emailPageResultFamily(userId));
|
||||||
const setPagination = useSetAtom(currentPaginationAtom);
|
const setPagination = useSetAtom(currentPaginationAtom);
|
||||||
|
const setRoute = useSetAtom(routeAtom);
|
||||||
const [prevPage, nextPage] = useAtomValue(paginationFamily(userId));
|
const [prevPage, nextPage] = useAtomValue(paginationFamily(userId));
|
||||||
const [primaryEmailId, refreshPrimaryEmailId] = useAtom(
|
const [primaryEmailId, refreshPrimaryEmailId] = useAtom(
|
||||||
primaryEmailIdFamily(userId),
|
primaryEmailIdFamily(userId),
|
||||||
);
|
);
|
||||||
// XXX: we may not want to directly use that atom here, but rather have a local state
|
|
||||||
const latestAddedEmail = useAtomValue(latestAddedEmailAtom);
|
|
||||||
|
|
||||||
const paginate = (pagination: Pagination): void => {
|
const paginate = (pagination: Pagination): void => {
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
@@ -150,12 +149,9 @@ const UserEmailList: React.FC<{
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// When adding an email, we want to refresh the list and go to the last page
|
// When adding an email, we want to go to the email verification form
|
||||||
const onAdd = (): void => {
|
const onAdd = (id: string): void => {
|
||||||
startTransition(() => {
|
setRoute({ type: "verify-email", id });
|
||||||
setPagination(LAST_PAGE);
|
|
||||||
refreshList();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -173,7 +169,6 @@ const UserEmailList: React.FC<{
|
|||||||
isPrimary={primaryEmailId === edge.node.id}
|
isPrimary={primaryEmailId === edge.node.id}
|
||||||
onSetPrimary={refreshPrimaryEmailId}
|
onSetPrimary={refreshPrimaryEmailId}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
highlight={latestAddedEmail === edge.node.id}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<AddEmailForm userId={userId} onAdd={onAdd} />
|
<AddEmailForm userId={userId} onAdd={onAdd} />
|
||||||
|
|||||||
57
frontend/src/components/VerifyEmail/VerifyEmail.module.css
Normal file
57
frontend/src/components/VerifyEmail/VerifyEmail.module.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin: var(--cpd-space-8x) 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font: var(--cpd-font-heading-xl-semibold);
|
||||||
|
line-height: var(--cpd-font-line-height-regular);
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline {
|
||||||
|
font: var(--cpd-font-body-lg-regular);
|
||||||
|
line-height: var(--cpd-font-line-height-regular);
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
margin-top: var(--cpd-space-2x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
height: var(--cpd-space-16x);
|
||||||
|
width: var(--cpd-space-16x);
|
||||||
|
color: var(--cpd-color-icon-secondary);
|
||||||
|
background: var(--cpd-color-bg-subtle-secondary);
|
||||||
|
padding: var(--cpd-space-2x);
|
||||||
|
border-radius: var(--cpd-space-2x);
|
||||||
|
margin: var(--cpd-space-4x) auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email {
|
||||||
|
font: var(--cpd-font-body-lg-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
margin: var(--cpd-space-8x) 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--cpd-space-6x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
199
frontend/src/components/VerifyEmail/VerifyEmail.tsx
Normal file
199
frontend/src/components/VerifyEmail/VerifyEmail.tsx
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import IconSend from "@vector-im/compound-design-tokens/icons/check.svg";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Control,
|
||||||
|
Field,
|
||||||
|
Label,
|
||||||
|
Submit,
|
||||||
|
Root as Form,
|
||||||
|
Alert,
|
||||||
|
} from "@vector-im/compound-web";
|
||||||
|
import { useSetAtom, atom, useAtom } from "jotai";
|
||||||
|
import { atomFamily } from "jotai/utils";
|
||||||
|
import { atomWithMutation } from "jotai-urql";
|
||||||
|
import { useEffect, useRef, useTransition } from "react";
|
||||||
|
|
||||||
|
import { routeAtom } from "../../Router";
|
||||||
|
import { FragmentType, graphql, useFragment } from "../../gql";
|
||||||
|
|
||||||
|
import styles from "./VerifyEmail.module.css";
|
||||||
|
|
||||||
|
const FRAGMENT = graphql(/* GraphQL */ `
|
||||||
|
fragment UserEmail_verifyEmail on UserEmail {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const VERIFY_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
|
mutation VerifyEmail($id: ID!, $code: String!) {
|
||||||
|
verifyEmail(input: { userEmailId: $id, code: $code }) {
|
||||||
|
status
|
||||||
|
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
primaryEmail {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
email {
|
||||||
|
id
|
||||||
|
...UserEmail_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const RESEND_VERIFICATION_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
|
mutation ResendVerificationEmail($id: ID!) {
|
||||||
|
sendVerificationEmail(input: { userEmailId: $id }) {
|
||||||
|
status
|
||||||
|
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
primaryEmail {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
email {
|
||||||
|
id
|
||||||
|
...UserEmail_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const verifyEmailFamily = atomFamily((id: string) => {
|
||||||
|
const verifyEmail = atomWithMutation(VERIFY_EMAIL_MUTATION);
|
||||||
|
|
||||||
|
// A proxy atom which pre-sets the id variable in the mutation
|
||||||
|
const verifyEmailAtom = atom(
|
||||||
|
(get) => get(verifyEmail),
|
||||||
|
(get, set, code: string) => set(verifyEmail, { id, code }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return verifyEmailAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const resendVerificationEmailFamily = atomFamily((id: string) => {
|
||||||
|
const resendVerificationEmail = atomWithMutation(
|
||||||
|
RESEND_VERIFICATION_EMAIL_MUTATION,
|
||||||
|
);
|
||||||
|
|
||||||
|
// A proxy atom which pre-sets the id variable in the mutation
|
||||||
|
const resendVerificationEmailAtom = atom(
|
||||||
|
(get) => get(resendVerificationEmail),
|
||||||
|
(_get, set) => set(resendVerificationEmail, { id }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return resendVerificationEmailAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const VerifyEmail: React.FC<{
|
||||||
|
email: FragmentType<typeof FRAGMENT>;
|
||||||
|
}> = ({ email }) => {
|
||||||
|
const data = useFragment(FRAGMENT, email);
|
||||||
|
const [pending, startTransition] = useTransition();
|
||||||
|
const [verifyEmailResult, verifyEmail] = useAtom(verifyEmailFamily(data.id));
|
||||||
|
const [resendVerificationEmailResult, resendVerificationEmail] = useAtom(
|
||||||
|
resendVerificationEmailFamily(data.id),
|
||||||
|
);
|
||||||
|
const setRoute = useSetAtom(routeAtom);
|
||||||
|
const fieldRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const onFormSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
const code = formData.get("code") as string;
|
||||||
|
startTransition(() => {
|
||||||
|
verifyEmail(code).then((result) => {
|
||||||
|
// Clear the form
|
||||||
|
e.currentTarget?.reset();
|
||||||
|
|
||||||
|
if (result.data?.verifyEmail.status === "VERIFIED") {
|
||||||
|
setRoute({ type: "account" });
|
||||||
|
} else {
|
||||||
|
fieldRef.current?.focus();
|
||||||
|
fieldRef.current?.select();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Focus the field on mount
|
||||||
|
useEffect(() => {
|
||||||
|
fieldRef.current?.focus();
|
||||||
|
}, [fieldRef]);
|
||||||
|
|
||||||
|
const onResendClick = (): void => {
|
||||||
|
startTransition(() => {
|
||||||
|
resendVerificationEmail().then(() => {
|
||||||
|
fieldRef.current?.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailSent =
|
||||||
|
resendVerificationEmailResult.data?.sendVerificationEmail.status === "SENT";
|
||||||
|
const invalidCode =
|
||||||
|
verifyEmailResult.data?.verifyEmail.status === "INVALID_CODE";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header className={styles.header}>
|
||||||
|
<IconSend className={styles.icon} />
|
||||||
|
<h1 className={styles.title}>Verify your email</h1>
|
||||||
|
<p className={styles.tagline}>
|
||||||
|
Enter the 6-digit code sent to{" "}
|
||||||
|
<span className={styles.email}>{data.email}</span>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<Form onSubmit={onFormSubmit} className={styles.form}>
|
||||||
|
{invalidCode && <Alert type="critical" title="Invalid code" />}
|
||||||
|
<Field name="code" serverInvalid={invalidCode}>
|
||||||
|
<Label>6-digit code</Label>
|
||||||
|
<Control
|
||||||
|
ref={fieldRef}
|
||||||
|
placeholder="xxxxxx"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Submit
|
||||||
|
type="submit"
|
||||||
|
disabled={pending}
|
||||||
|
className={styles.submitButton}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Submit>
|
||||||
|
<Button
|
||||||
|
kind="tertiary"
|
||||||
|
disabled={pending || emailSent}
|
||||||
|
onClick={onResendClick}
|
||||||
|
>
|
||||||
|
{emailSent ? "Sent!" : "Resend email"}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
15
frontend/src/components/VerifyEmail/index.ts
Normal file
15
frontend/src/components/VerifyEmail/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
export { default } from "./VerifyEmail";
|
||||||
@@ -39,12 +39,8 @@ const documents = {
|
|||||||
types.EndOAuth2SessionDocument,
|
types.EndOAuth2SessionDocument,
|
||||||
"\n query OAuth2SessionListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
"\n query OAuth2SessionListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||||
types.OAuth2SessionListQueryDocument,
|
types.OAuth2SessionListQueryDocument,
|
||||||
"\n fragment UserEmail_email on UserEmail {\n id\n email\n createdAt\n confirmedAt\n }\n":
|
"\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n":
|
||||||
types.UserEmail_EmailFragmentDoc,
|
types.UserEmail_EmailFragmentDoc,
|
||||||
"\n mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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.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 mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n":
|
"\n mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n":
|
||||||
types.RemoveEmailDocument,
|
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":
|
"\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":
|
||||||
@@ -55,10 +51,18 @@ const documents = {
|
|||||||
types.UserPrimaryEmailDocument,
|
types.UserPrimaryEmailDocument,
|
||||||
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n":
|
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n":
|
||||||
types.UserGreetingDocument,
|
types.UserGreetingDocument,
|
||||||
|
"\n fragment UserEmail_verifyEmail on UserEmail {\n id\n email\n }\n":
|
||||||
|
types.UserEmail_VerifyEmailFragmentDoc,
|
||||||
|
"\n mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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.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 BrowserSessionQuery($id: ID!) {\n browserSession(id: $id) {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n }\n":
|
"\n query BrowserSessionQuery($id: ID!) {\n browserSession(id: $id) {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n }\n":
|
||||||
types.BrowserSessionQueryDocument,
|
types.BrowserSessionQueryDocument,
|
||||||
"\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n }\n":
|
"\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n }\n":
|
||||||
types.OAuth2ClientQueryDocument,
|
types.OAuth2ClientQueryDocument,
|
||||||
|
"\n query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n":
|
||||||
|
types.VerifyEmailQueryDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,20 +161,8 @@ export function graphql(
|
|||||||
* 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(
|
export function graphql(
|
||||||
source: "\n fragment UserEmail_email on UserEmail {\n id\n email\n createdAt\n confirmedAt\n }\n",
|
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 createdAt\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 mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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",
|
|
||||||
): (typeof documents)["\n mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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"];
|
|
||||||
/**
|
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
||||||
*/
|
|
||||||
export function graphql(
|
|
||||||
source: "\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",
|
|
||||||
): (typeof documents)["\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"];
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@@ -201,6 +193,24 @@ export function graphql(
|
|||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n",
|
source: "\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n",
|
||||||
): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n"];
|
): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\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 UserEmail_verifyEmail on UserEmail {\n id\n email\n }\n",
|
||||||
|
): (typeof documents)["\n fragment UserEmail_verifyEmail on UserEmail {\n id\n email\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 mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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",
|
||||||
|
): (typeof documents)["\n mutation VerifyEmail($id: ID!, $code: String!) {\n verifyEmail(input: { userEmailId: $id, code: $code }) {\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"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(
|
||||||
|
source: "\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",
|
||||||
|
): (typeof documents)["\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"];
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@@ -213,6 +223,12 @@ export function graphql(
|
|||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n }\n",
|
source: "\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n }\n",
|
||||||
): (typeof documents)["\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n }\n"];
|
): (typeof documents)["\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\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 query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n",
|
||||||
|
): (typeof documents)["\n query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
|
|||||||
@@ -1128,55 +1128,9 @@ export type UserEmail_EmailFragment = {
|
|||||||
__typename?: "UserEmail";
|
__typename?: "UserEmail";
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
createdAt: any;
|
|
||||||
confirmedAt?: any | null;
|
confirmedAt?: any | null;
|
||||||
} & { " $fragmentName"?: "UserEmail_EmailFragment" };
|
} & { " $fragmentName"?: "UserEmail_EmailFragment" };
|
||||||
|
|
||||||
export type VerifyEmailMutationVariables = Exact<{
|
|
||||||
id: Scalars["ID"]["input"];
|
|
||||||
code: Scalars["String"]["input"];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export type VerifyEmailMutation = {
|
|
||||||
__typename?: "Mutation";
|
|
||||||
verifyEmail: {
|
|
||||||
__typename?: "VerifyEmailPayload";
|
|
||||||
status: VerifyEmailStatus;
|
|
||||||
user?: {
|
|
||||||
__typename?: "User";
|
|
||||||
id: string;
|
|
||||||
primaryEmail?: { __typename?: "UserEmail"; id: string } | null;
|
|
||||||
} | null;
|
|
||||||
email?:
|
|
||||||
| ({ __typename?: "UserEmail"; id: string } & {
|
|
||||||
" $fragmentRefs"?: {
|
|
||||||
UserEmail_EmailFragment: UserEmail_EmailFragment;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ResendVerificationEmailMutationVariables = Exact<{
|
|
||||||
id: Scalars["ID"]["input"];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export type ResendVerificationEmailMutation = {
|
|
||||||
__typename?: "Mutation";
|
|
||||||
sendVerificationEmail: {
|
|
||||||
__typename?: "SendVerificationEmailPayload";
|
|
||||||
status: SendVerificationEmailStatus;
|
|
||||||
user: {
|
|
||||||
__typename?: "User";
|
|
||||||
id: string;
|
|
||||||
primaryEmail?: { __typename?: "UserEmail"; id: string } | null;
|
|
||||||
};
|
|
||||||
email: { __typename?: "UserEmail"; id: string } & {
|
|
||||||
" $fragmentRefs"?: { UserEmail_EmailFragment: UserEmail_EmailFragment };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RemoveEmailMutationVariables = Exact<{
|
export type RemoveEmailMutationVariables = Exact<{
|
||||||
id: Scalars["ID"]["input"];
|
id: Scalars["ID"]["input"];
|
||||||
}>;
|
}>;
|
||||||
@@ -1274,6 +1228,57 @@ export type UserGreetingQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserEmail_VerifyEmailFragment = {
|
||||||
|
__typename?: "UserEmail";
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
} & { " $fragmentName"?: "UserEmail_VerifyEmailFragment" };
|
||||||
|
|
||||||
|
export type VerifyEmailMutationVariables = Exact<{
|
||||||
|
id: Scalars["ID"]["input"];
|
||||||
|
code: Scalars["String"]["input"];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type VerifyEmailMutation = {
|
||||||
|
__typename?: "Mutation";
|
||||||
|
verifyEmail: {
|
||||||
|
__typename?: "VerifyEmailPayload";
|
||||||
|
status: VerifyEmailStatus;
|
||||||
|
user?: {
|
||||||
|
__typename?: "User";
|
||||||
|
id: string;
|
||||||
|
primaryEmail?: { __typename?: "UserEmail"; id: string } | null;
|
||||||
|
} | null;
|
||||||
|
email?:
|
||||||
|
| ({ __typename?: "UserEmail"; id: string } & {
|
||||||
|
" $fragmentRefs"?: {
|
||||||
|
UserEmail_EmailFragment: UserEmail_EmailFragment;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResendVerificationEmailMutationVariables = Exact<{
|
||||||
|
id: Scalars["ID"]["input"];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type ResendVerificationEmailMutation = {
|
||||||
|
__typename?: "Mutation";
|
||||||
|
sendVerificationEmail: {
|
||||||
|
__typename?: "SendVerificationEmailPayload";
|
||||||
|
status: SendVerificationEmailStatus;
|
||||||
|
user: {
|
||||||
|
__typename?: "User";
|
||||||
|
id: string;
|
||||||
|
primaryEmail?: { __typename?: "UserEmail"; id: string } | null;
|
||||||
|
};
|
||||||
|
email: { __typename?: "UserEmail"; id: string } & {
|
||||||
|
" $fragmentRefs"?: { UserEmail_EmailFragment: UserEmail_EmailFragment };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type BrowserSessionQueryQueryVariables = Exact<{
|
export type BrowserSessionQueryQueryVariables = Exact<{
|
||||||
id: Scalars["ID"]["input"];
|
id: Scalars["ID"]["input"];
|
||||||
}>;
|
}>;
|
||||||
@@ -1311,6 +1316,21 @@ export type OAuth2ClientQueryQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type VerifyEmailQueryQueryVariables = Exact<{
|
||||||
|
id: Scalars["ID"]["input"];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type VerifyEmailQueryQuery = {
|
||||||
|
__typename?: "Query";
|
||||||
|
userEmail?:
|
||||||
|
| ({ __typename?: "UserEmail" } & {
|
||||||
|
" $fragmentRefs"?: {
|
||||||
|
UserEmail_VerifyEmailFragment: UserEmail_VerifyEmailFragment;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
};
|
||||||
|
|
||||||
export const BrowserSession_SessionFragmentDoc = {
|
export const BrowserSession_SessionFragmentDoc = {
|
||||||
kind: "Document",
|
kind: "Document",
|
||||||
definitions: [
|
definitions: [
|
||||||
@@ -1463,13 +1483,32 @@ export const UserEmail_EmailFragmentDoc = {
|
|||||||
selections: [
|
selections: [
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as DocumentNode<UserEmail_EmailFragment, unknown>;
|
} as unknown as DocumentNode<UserEmail_EmailFragment, unknown>;
|
||||||
|
export const UserEmail_VerifyEmailFragmentDoc = {
|
||||||
|
kind: "Document",
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UserEmail_verifyEmail" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "UserEmail" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<UserEmail_VerifyEmailFragment, unknown>;
|
||||||
export const CurrentViewerQueryDocument = {
|
export const CurrentViewerQueryDocument = {
|
||||||
kind: "Document",
|
kind: "Document",
|
||||||
definitions: [
|
definitions: [
|
||||||
@@ -1681,7 +1720,6 @@ export const AddEmailDocument = {
|
|||||||
selections: [
|
selections: [
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -2615,244 +2653,6 @@ export const OAuth2SessionListQueryDocument = {
|
|||||||
OAuth2SessionListQueryQuery,
|
OAuth2SessionListQueryQuery,
|
||||||
OAuth2SessionListQueryQueryVariables
|
OAuth2SessionListQueryQueryVariables
|
||||||
>;
|
>;
|
||||||
export const VerifyEmailDocument = {
|
|
||||||
kind: "Document",
|
|
||||||
definitions: [
|
|
||||||
{
|
|
||||||
kind: "OperationDefinition",
|
|
||||||
operation: "mutation",
|
|
||||||
name: { kind: "Name", value: "VerifyEmail" },
|
|
||||||
variableDefinitions: [
|
|
||||||
{
|
|
||||||
kind: "VariableDefinition",
|
|
||||||
variable: { kind: "Variable", name: { kind: "Name", value: "id" } },
|
|
||||||
type: {
|
|
||||||
kind: "NonNullType",
|
|
||||||
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "VariableDefinition",
|
|
||||||
variable: { kind: "Variable", name: { kind: "Name", value: "code" } },
|
|
||||||
type: {
|
|
||||||
kind: "NonNullType",
|
|
||||||
type: {
|
|
||||||
kind: "NamedType",
|
|
||||||
name: { kind: "Name", value: "String" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "verifyEmail" },
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "input" },
|
|
||||||
value: {
|
|
||||||
kind: "ObjectValue",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
kind: "ObjectField",
|
|
||||||
name: { kind: "Name", value: "userEmailId" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "id" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "ObjectField",
|
|
||||||
name: { kind: "Name", value: "code" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "code" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "status" } },
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "user" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "primaryEmail" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "id" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "email" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{
|
|
||||||
kind: "FragmentSpread",
|
|
||||||
name: { kind: "Name", value: "UserEmail_email" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "FragmentDefinition",
|
|
||||||
name: { kind: "Name", value: "UserEmail_email" },
|
|
||||||
typeCondition: {
|
|
||||||
kind: "NamedType",
|
|
||||||
name: { kind: "Name", value: "UserEmail" },
|
|
||||||
},
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as unknown as DocumentNode<VerifyEmailMutation, VerifyEmailMutationVariables>;
|
|
||||||
export const ResendVerificationEmailDocument = {
|
|
||||||
kind: "Document",
|
|
||||||
definitions: [
|
|
||||||
{
|
|
||||||
kind: "OperationDefinition",
|
|
||||||
operation: "mutation",
|
|
||||||
name: { kind: "Name", value: "ResendVerificationEmail" },
|
|
||||||
variableDefinitions: [
|
|
||||||
{
|
|
||||||
kind: "VariableDefinition",
|
|
||||||
variable: { kind: "Variable", name: { kind: "Name", value: "id" } },
|
|
||||||
type: {
|
|
||||||
kind: "NonNullType",
|
|
||||||
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "sendVerificationEmail" },
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "input" },
|
|
||||||
value: {
|
|
||||||
kind: "ObjectValue",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
kind: "ObjectField",
|
|
||||||
name: { kind: "Name", value: "userEmailId" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "id" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "status" } },
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "user" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "primaryEmail" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "id" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "email" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{
|
|
||||||
kind: "FragmentSpread",
|
|
||||||
name: { kind: "Name", value: "UserEmail_email" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "FragmentDefinition",
|
|
||||||
name: { kind: "Name", value: "UserEmail_email" },
|
|
||||||
typeCondition: {
|
|
||||||
kind: "NamedType",
|
|
||||||
name: { kind: "Name", value: "UserEmail" },
|
|
||||||
},
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as unknown as DocumentNode<
|
|
||||||
ResendVerificationEmailMutation,
|
|
||||||
ResendVerificationEmailMutationVariables
|
|
||||||
>;
|
|
||||||
export const RemoveEmailDocument = {
|
export const RemoveEmailDocument = {
|
||||||
kind: "Document",
|
kind: "Document",
|
||||||
definitions: [
|
definitions: [
|
||||||
@@ -3189,7 +2989,6 @@ export const UserEmailListQueryDocument = {
|
|||||||
selections: [
|
selections: [
|
||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -3323,6 +3122,242 @@ export const UserGreetingDocument = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as DocumentNode<UserGreetingQuery, UserGreetingQueryVariables>;
|
} as unknown as DocumentNode<UserGreetingQuery, UserGreetingQueryVariables>;
|
||||||
|
export const VerifyEmailDocument = {
|
||||||
|
kind: "Document",
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: "OperationDefinition",
|
||||||
|
operation: "mutation",
|
||||||
|
name: { kind: "Name", value: "VerifyEmail" },
|
||||||
|
variableDefinitions: [
|
||||||
|
{
|
||||||
|
kind: "VariableDefinition",
|
||||||
|
variable: { kind: "Variable", name: { kind: "Name", value: "id" } },
|
||||||
|
type: {
|
||||||
|
kind: "NonNullType",
|
||||||
|
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "VariableDefinition",
|
||||||
|
variable: { kind: "Variable", name: { kind: "Name", value: "code" } },
|
||||||
|
type: {
|
||||||
|
kind: "NonNullType",
|
||||||
|
type: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "String" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "verifyEmail" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "input" },
|
||||||
|
value: {
|
||||||
|
kind: "ObjectValue",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
kind: "ObjectField",
|
||||||
|
name: { kind: "Name", value: "userEmailId" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "ObjectField",
|
||||||
|
name: { kind: "Name", value: "code" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "code" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "status" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "user" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "primaryEmail" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "email" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "FragmentSpread",
|
||||||
|
name: { kind: "Name", value: "UserEmail_email" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UserEmail_email" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "UserEmail" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<VerifyEmailMutation, VerifyEmailMutationVariables>;
|
||||||
|
export const ResendVerificationEmailDocument = {
|
||||||
|
kind: "Document",
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: "OperationDefinition",
|
||||||
|
operation: "mutation",
|
||||||
|
name: { kind: "Name", value: "ResendVerificationEmail" },
|
||||||
|
variableDefinitions: [
|
||||||
|
{
|
||||||
|
kind: "VariableDefinition",
|
||||||
|
variable: { kind: "Variable", name: { kind: "Name", value: "id" } },
|
||||||
|
type: {
|
||||||
|
kind: "NonNullType",
|
||||||
|
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "sendVerificationEmail" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "input" },
|
||||||
|
value: {
|
||||||
|
kind: "ObjectValue",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
kind: "ObjectField",
|
||||||
|
name: { kind: "Name", value: "userEmailId" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "status" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "user" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "primaryEmail" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "email" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "FragmentSpread",
|
||||||
|
name: { kind: "Name", value: "UserEmail_email" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UserEmail_email" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "UserEmail" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "confirmedAt" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<
|
||||||
|
ResendVerificationEmailMutation,
|
||||||
|
ResendVerificationEmailMutationVariables
|
||||||
|
>;
|
||||||
export const BrowserSessionQueryDocument = {
|
export const BrowserSessionQueryDocument = {
|
||||||
kind: "Document",
|
kind: "Document",
|
||||||
definitions: [
|
definitions: [
|
||||||
@@ -3457,3 +3492,69 @@ export const OAuth2ClientQueryDocument = {
|
|||||||
OAuth2ClientQueryQuery,
|
OAuth2ClientQueryQuery,
|
||||||
OAuth2ClientQueryQueryVariables
|
OAuth2ClientQueryQueryVariables
|
||||||
>;
|
>;
|
||||||
|
export const VerifyEmailQueryDocument = {
|
||||||
|
kind: "Document",
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: "OperationDefinition",
|
||||||
|
operation: "query",
|
||||||
|
name: { kind: "Name", value: "VerifyEmailQuery" },
|
||||||
|
variableDefinitions: [
|
||||||
|
{
|
||||||
|
kind: "VariableDefinition",
|
||||||
|
variable: { kind: "Variable", name: { kind: "Name", value: "id" } },
|
||||||
|
type: {
|
||||||
|
kind: "NonNullType",
|
||||||
|
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "userEmail" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "id" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "FragmentSpread",
|
||||||
|
name: { kind: "Name", value: "UserEmail_verifyEmail" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UserEmail_verifyEmail" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "UserEmail" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "email" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<
|
||||||
|
VerifyEmailQueryQuery,
|
||||||
|
VerifyEmailQueryQueryVariables
|
||||||
|
>;
|
||||||
|
|||||||
57
frontend/src/pages/VerifyEmail.tsx
Normal file
57
frontend/src/pages/VerifyEmail.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { atomFamily } from "jotai/utils";
|
||||||
|
import { atomWithQuery } from "jotai-urql";
|
||||||
|
|
||||||
|
import { mapQueryAtom } from "../atoms";
|
||||||
|
import GraphQLError from "../components/GraphQLError";
|
||||||
|
import VerifyEmailComponent from "../components/VerifyEmail";
|
||||||
|
import { graphql } from "../gql";
|
||||||
|
import { isErr, unwrapErr, unwrapOk } from "../result";
|
||||||
|
|
||||||
|
const QUERY = graphql(/* GraphQL */ `
|
||||||
|
query VerifyEmailQuery($id: ID!) {
|
||||||
|
userEmail(id: $id) {
|
||||||
|
...UserEmail_verifyEmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const verifyEmailFamily = atomFamily((id: string) => {
|
||||||
|
const verifyEmailQueryAtom = atomWithQuery({
|
||||||
|
query: QUERY,
|
||||||
|
getVariables: () => ({ id }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const verifyEmailAtom = mapQueryAtom(
|
||||||
|
verifyEmailQueryAtom,
|
||||||
|
(data) => data?.userEmail,
|
||||||
|
);
|
||||||
|
|
||||||
|
return verifyEmailAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const VerifyEmail: React.FC<{ id: string }> = ({ id }) => {
|
||||||
|
const result = useAtomValue(verifyEmailFamily(id));
|
||||||
|
if (isErr(result)) return <GraphQLError error={unwrapErr(result)} />;
|
||||||
|
|
||||||
|
const email = unwrapOk(result);
|
||||||
|
if (email == null) return <>Unknown email</>;
|
||||||
|
|
||||||
|
return <VerifyEmailComponent email={email} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
Reference in New Issue
Block a user