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
Nicer email management UI
This commit is contained in:
@@ -17,12 +17,16 @@ import { atomWithMutation } from "jotai-urql";
|
|||||||
import { useRef, useTransition } from "react";
|
import { useRef, useTransition } from "react";
|
||||||
|
|
||||||
import { graphql } from "../gql";
|
import { graphql } from "../gql";
|
||||||
|
import { LAST_PAGE } from "../pagination";
|
||||||
|
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import Input from "./Input";
|
import Input from "./Input";
|
||||||
import Typography from "./Typography";
|
import Typography from "./Typography";
|
||||||
import UserEmail from "./UserEmail";
|
import {
|
||||||
import { emailPageResultFamily } from "./UserEmailList";
|
currentPaginationAtom,
|
||||||
|
emailPageResultFamily,
|
||||||
|
primaryEmailResultFamily,
|
||||||
|
} from "./UserEmailList";
|
||||||
|
|
||||||
const ADD_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
const ADD_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
mutation AddEmail($userId: ID!, $email: String!) {
|
mutation AddEmail($userId: ID!, $email: String!) {
|
||||||
@@ -36,24 +40,36 @@ const ADD_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const addUserEmailAtom = atomWithMutation(ADD_EMAIL_MUTATION);
|
export const addUserEmailAtom = atomWithMutation(ADD_EMAIL_MUTATION);
|
||||||
|
|
||||||
const AddEmailForm: React.FC<{ userId: string }> = ({ userId }) => {
|
const AddEmailForm: React.FC<{ userId: string }> = ({ userId }) => {
|
||||||
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();
|
||||||
|
|
||||||
// XXX: is this the right way to do this?
|
// XXX: is this the right way to do this?
|
||||||
const refetchList = useSetAtom(emailPageResultFamily(userId));
|
const refetchList = useSetAtom(emailPageResultFamily(userId));
|
||||||
|
const refetchPrimaryEmail = useSetAtom(primaryEmailResultFamily(userId));
|
||||||
|
const setCurrentPagination = useSetAtom(currentPaginationAtom);
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const email = e.currentTarget.email.value;
|
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
const email = formData.get("email") as string;
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
addEmail({ userId, email }).then(() => {
|
addEmail({ userId, email }).then(() => {
|
||||||
|
startTransition(() => {
|
||||||
|
// Paginate to the last page
|
||||||
|
setCurrentPagination(LAST_PAGE);
|
||||||
|
|
||||||
|
// Make it refetch the list and the primary email, in case they changed
|
||||||
refetchList();
|
refetchList();
|
||||||
if (formRef.current) {
|
refetchPrimaryEmail();
|
||||||
formRef.current.reset();
|
|
||||||
}
|
// Reset the form
|
||||||
|
formRef.current?.reset();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -65,7 +81,6 @@ const AddEmailForm: React.FC<{ userId: string }> = ({ userId }) => {
|
|||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<Typography variant="subtitle">Email added!</Typography>
|
<Typography variant="subtitle">Email added!</Typography>
|
||||||
</div>
|
</div>
|
||||||
<UserEmail email={addEmailResult.data?.addEmail.email} />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{addEmailResult.data?.addEmail.status === "EXISTS" && (
|
{addEmailResult.data?.addEmail.status === "EXISTS" && (
|
||||||
@@ -73,14 +88,14 @@ const AddEmailForm: React.FC<{ userId: string }> = ({ userId }) => {
|
|||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<Typography variant="subtitle">Email already exists!</Typography>
|
<Typography variant="subtitle">Email already exists!</Typography>
|
||||||
</div>
|
</div>
|
||||||
<UserEmail email={addEmailResult.data?.addEmail.email} />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<form className="flex" onSubmit={handleSubmit} ref={formRef}>
|
<form className="flex" onSubmit={handleSubmit} ref={formRef}>
|
||||||
<Input
|
<Input
|
||||||
className="flex-1 mr-2"
|
className="flex-1 mr-2"
|
||||||
disabled={pending}
|
disabled={pending}
|
||||||
type="text"
|
type="email"
|
||||||
|
inputMode="email"
|
||||||
name="email"
|
name="email"
|
||||||
/>
|
/>
|
||||||
<Button disabled={pending} type="submit">
|
<Button disabled={pending} type="submit">
|
||||||
|
|||||||
@@ -14,11 +14,18 @@
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
highlight?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Block: React.FC<Props> = ({ children }) => {
|
const Block: React.FC<Props> = ({ children, highlight }) => {
|
||||||
return (
|
return (
|
||||||
<div className="p-4 bg-grey-50 dark:bg-grey-450 dark:text-white rounded">
|
<div
|
||||||
|
className={`p-4 dark:text-white rounded ${
|
||||||
|
highlight
|
||||||
|
? "border-2 border-grey-50 dark:border-grey-450 bg-white dark:bg-black"
|
||||||
|
: "bg-grey-50 dark:bg-grey-450"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ type Props = {
|
|||||||
|
|
||||||
const Input: React.FC<Props> = ({ disabled, className, ...props }) => {
|
const Input: React.FC<Props> = ({ disabled, className, ...props }) => {
|
||||||
const disabledClass = disabled
|
const disabledClass = disabled
|
||||||
? "bg-grey-50 dark:bg-grey-400"
|
? "bg-grey-100 dark:bg-grey-400"
|
||||||
: "bg-white dark:bg-grey-450";
|
: "bg-white dark:bg-grey-450";
|
||||||
const fullClassName = `${className} px-2 py-1 border-2 border-grey-50 dark:border-grey-400 dark:text-white placeholder-grey-100 dark:placeholder-grey-150 rounded-lg ${disabledClass}`;
|
const fullClassName = `${className} px-2 py-1 border-2 border-grey-100 dark:border-grey-400 dark:text-white placeholder-grey-100 dark:placeholder-grey-150 rounded-lg ${disabledClass}`;
|
||||||
return <input disabled={disabled} className={fullClassName} {...props} />;
|
return <input disabled={disabled} className={fullClassName} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,17 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
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 { FragmentType, graphql, useFragment } from "../gql";
|
||||||
|
|
||||||
import Block from "./Block";
|
import Block from "./Block";
|
||||||
|
import Button from "./Button";
|
||||||
import DateTime from "./DateTime";
|
import DateTime from "./DateTime";
|
||||||
|
import Input from "./Input";
|
||||||
import Typography, { Bold } from "./Typography";
|
import Typography, { Bold } from "./Typography";
|
||||||
|
|
||||||
const FRAGMENT = graphql(/* GraphQL */ `
|
const FRAGMENT = graphql(/* GraphQL */ `
|
||||||
@@ -27,24 +34,141 @@ const FRAGMENT = graphql(/* GraphQL */ `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const UserEmail: React.FC<{ email: FragmentType<typeof FRAGMENT> }> = ({
|
const VERIFY_EMAIL_MUTATION = graphql(/* GraphQL */ `
|
||||||
email,
|
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 UserEmail: React.FC<{
|
||||||
|
email: FragmentType<typeof FRAGMENT>;
|
||||||
|
isPrimary?: boolean;
|
||||||
|
highlight?: boolean;
|
||||||
|
}> = ({ email, isPrimary, highlight }) => {
|
||||||
|
const [pending, startTransition] = useTransition();
|
||||||
const data = useFragment(FRAGMENT, email);
|
const data = useFragment(FRAGMENT, email);
|
||||||
|
const [verifyEmailResult, verifyEmail] = useAtom(verifyEmailFamily(data.id));
|
||||||
|
const [resendVerificationEmailResult, resendVerificationEmail] = useAtom(
|
||||||
|
resendVerificationEmailFamily(data.id)
|
||||||
|
);
|
||||||
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
|
||||||
|
const onFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(e.currentTarget);
|
||||||
|
const code = formData.get("code") as string;
|
||||||
|
startTransition(() => {
|
||||||
|
verifyEmail(code).then(() => {
|
||||||
|
formRef.current?.reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onResendClick = () => {
|
||||||
|
startTransition(() => {
|
||||||
|
resendVerificationEmail().then(() => {
|
||||||
|
formRef.current?.code.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailSent =
|
||||||
|
resendVerificationEmailResult.data?.sendVerificationEmail.status === "SENT";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Block>
|
<Block highlight={highlight}>
|
||||||
|
{isPrimary && (
|
||||||
|
<Typography variant="body" bold>
|
||||||
|
Primary
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
<Bold>{data.email}</Bold>
|
<Bold>{data.email}</Bold>
|
||||||
{data.confirmedAt ? "" : " (not verified)"}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{data.confirmedAt ? (
|
{data.confirmedAt ? (
|
||||||
<Typography variant="micro">
|
<Typography variant="micro">
|
||||||
Verified <DateTime datetime={data.confirmedAt} />
|
Verified <DateTime datetime={data.confirmedAt} />
|
||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="micro">
|
<form
|
||||||
Added <DateTime datetime={data.createdAt} />
|
onSubmit={onFormSubmit}
|
||||||
</Typography>
|
className="mt-2 grid grid-cols-2 gap-2"
|
||||||
|
ref={formRef}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="col-span-2"
|
||||||
|
name="code"
|
||||||
|
placeholder="Code"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
/>
|
||||||
|
<Button type="submit" disabled={pending}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
<Button disabled={pending || emailSent} onClick={onResendClick}>
|
||||||
|
{emailSent ? "Sent!" : "Resend"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
)}
|
)}
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ import { useTransition } from "react";
|
|||||||
|
|
||||||
import { graphql } from "../gql";
|
import { graphql } from "../gql";
|
||||||
import { PageInfo } from "../gql/graphql";
|
import { PageInfo } from "../gql/graphql";
|
||||||
import { atomWithPagination, pageSizeAtom, Pagination } from "../pagination";
|
import {
|
||||||
|
atomForCurrentPagination,
|
||||||
|
atomWithPagination,
|
||||||
|
pageSizeAtom,
|
||||||
|
Pagination,
|
||||||
|
} from "../pagination";
|
||||||
|
|
||||||
import BlockList from "./BlockList";
|
import BlockList from "./BlockList";
|
||||||
import PaginationControls from "./PaginationControls";
|
import PaginationControls from "./PaginationControls";
|
||||||
@@ -35,6 +40,7 @@ const QUERY = graphql(/* GraphQL */ `
|
|||||||
) {
|
) {
|
||||||
user(id: $userId) {
|
user(id: $userId) {
|
||||||
id
|
id
|
||||||
|
|
||||||
emails(first: $first, after: $after, last: $last, before: $before) {
|
emails(first: $first, after: $after, last: $last, before: $before) {
|
||||||
edges {
|
edges {
|
||||||
cursor
|
cursor
|
||||||
@@ -55,15 +61,40 @@ const QUERY = graphql(/* GraphQL */ `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const currentPagination = atomWithDefault<Pagination>((get) => ({
|
const PRIMARY_EMAIL_QUERY = graphql(/* GraphQL */ `
|
||||||
first: get(pageSizeAtom),
|
query UserPrimaryEmail($userId: ID!) {
|
||||||
after: null,
|
user(id: $userId) {
|
||||||
}));
|
id
|
||||||
|
primaryEmail {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export const primaryEmailResultFamily = atomFamily((userId: string) => {
|
||||||
|
const primaryEmailResult = atomWithQuery({
|
||||||
|
query: PRIMARY_EMAIL_QUERY,
|
||||||
|
getVariables: () => ({ userId }),
|
||||||
|
});
|
||||||
|
return primaryEmailResult;
|
||||||
|
});
|
||||||
|
|
||||||
|
const primaryEmailIdFamily = atomFamily((userId: string) => {
|
||||||
|
const primaryEmailIdAtom = atom(async (get) => {
|
||||||
|
const result = await get(primaryEmailResultFamily(userId));
|
||||||
|
return result.data?.user?.primaryEmail?.id ?? null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return primaryEmailIdAtom;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const currentPaginationAtom = atomForCurrentPagination();
|
||||||
|
|
||||||
export const emailPageResultFamily = atomFamily((userId: string) => {
|
export const emailPageResultFamily = atomFamily((userId: string) => {
|
||||||
const emailPageResult = atomWithQuery({
|
const emailPageResult = atomWithQuery({
|
||||||
query: QUERY,
|
query: QUERY,
|
||||||
getVariables: (get) => ({ userId, ...get(currentPagination) }),
|
getVariables: (get) => ({ userId, ...get(currentPaginationAtom) }),
|
||||||
});
|
});
|
||||||
return emailPageResult;
|
return emailPageResult;
|
||||||
});
|
});
|
||||||
@@ -79,17 +110,21 @@ const pageInfoFamily = atomFamily((userId: string) => {
|
|||||||
|
|
||||||
const paginationFamily = atomFamily((userId: string) => {
|
const paginationFamily = atomFamily((userId: string) => {
|
||||||
const paginationAtom = atomWithPagination(
|
const paginationAtom = atomWithPagination(
|
||||||
currentPagination,
|
currentPaginationAtom,
|
||||||
pageInfoFamily(userId)
|
pageInfoFamily(userId)
|
||||||
);
|
);
|
||||||
return paginationAtom;
|
return paginationAtom;
|
||||||
});
|
});
|
||||||
|
|
||||||
const UserEmailList: React.FC<{ userId: string }> = ({ userId }) => {
|
const UserEmailList: React.FC<{
|
||||||
|
userId: string;
|
||||||
|
highlightedEmail?: string;
|
||||||
|
}> = ({ userId, highlightedEmail }) => {
|
||||||
const [pending, startTransition] = useTransition();
|
const [pending, startTransition] = useTransition();
|
||||||
const result = useAtomValue(emailPageResultFamily(userId));
|
const result = useAtomValue(emailPageResultFamily(userId));
|
||||||
const setPagination = useSetAtom(currentPagination);
|
const setPagination = useSetAtom(currentPaginationAtom);
|
||||||
const [prevPage, nextPage] = useAtomValue(paginationFamily(userId));
|
const [prevPage, nextPage] = useAtomValue(paginationFamily(userId));
|
||||||
|
const primaryEmailId = useAtomValue(primaryEmailIdFamily(userId));
|
||||||
|
|
||||||
const paginate = (pagination: Pagination) => {
|
const paginate = (pagination: Pagination) => {
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
@@ -106,7 +141,12 @@ const UserEmailList: React.FC<{ userId: string }> = ({ userId }) => {
|
|||||||
disabled={pending}
|
disabled={pending}
|
||||||
/>
|
/>
|
||||||
{result.data?.user?.emails?.edges?.map((edge) => (
|
{result.data?.user?.emails?.edges?.map((edge) => (
|
||||||
<UserEmail email={edge.node} key={edge.cursor} />
|
<UserEmail
|
||||||
|
email={edge.node}
|
||||||
|
key={edge.cursor}
|
||||||
|
isPrimary={primaryEmailId === edge.node.id}
|
||||||
|
highlight={highlightedEmail === edge.node.id}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</BlockList>
|
</BlockList>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { atomWithQuery } from "jotai-urql";
|
|||||||
|
|
||||||
import { graphql } from "../gql";
|
import { graphql } from "../gql";
|
||||||
|
|
||||||
import { Title } from "./Typography";
|
import Typography from "./Typography";
|
||||||
|
|
||||||
const QUERY = graphql(/* GraphQL */ `
|
const QUERY = graphql(/* GraphQL */ `
|
||||||
query UserGreeting($userId: ID!) {
|
query UserGreeting($userId: ID!) {
|
||||||
@@ -42,7 +42,11 @@ const UserGreeting: React.FC<{ userId: string }> = ({ userId }) => {
|
|||||||
const result = useAtomValue(userGreetingFamily(userId));
|
const result = useAtomValue(userGreetingFamily(userId));
|
||||||
|
|
||||||
if (result.data?.user) {
|
if (result.data?.user) {
|
||||||
return <Title>Hello, {result.data.user.username}!</Title>;
|
return (
|
||||||
|
<Typography variant="headline">
|
||||||
|
Hello, {result.data.user.username}!
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>Failed to load user</>;
|
return <>Failed to load user</>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import * as types from './graphql';
|
import * as types from "./graphql";
|
||||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
import { TypedDocumentNode as DocumentNode } from "@graphql-typed-document-node/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of all GraphQL operations in the project.
|
* Map of all GraphQL operations in the project.
|
||||||
@@ -13,20 +13,40 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
|
|||||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||||
*/
|
*/
|
||||||
const documents = {
|
const documents = {
|
||||||
"\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n": types.CurrentViewerQueryDocument,
|
"\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n":
|
||||||
"\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n": types.CurrentViewerSessionQueryDocument,
|
types.CurrentViewerQueryDocument,
|
||||||
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n email {\n id\n ...UserEmail_email\n }\n }\n }\n": types.AddEmailDocument,
|
"\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n":
|
||||||
"\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n }\n": types.BrowserSession_SessionFragmentDoc,
|
types.CurrentViewerSessionQueryDocument,
|
||||||
"\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.BrowserSessionListDocument,
|
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n email {\n id\n ...UserEmail_email\n }\n }\n }\n":
|
||||||
"\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n }\n": types.CompatSsoLogin_LoginFragmentDoc,
|
types.AddEmailDocument,
|
||||||
"\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.CompatSsoLoginListDocument,
|
"\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n }\n":
|
||||||
"\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n }\n": types.OAuth2Session_SessionFragmentDoc,
|
types.BrowserSession_SessionFragmentDoc,
|
||||||
"\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.OAuth2SessionListQueryDocument,
|
"\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||||
"\n fragment UserEmail_email on UserEmail {\n id\n email\n createdAt\n confirmedAt\n }\n": types.UserEmail_EmailFragmentDoc,
|
types.BrowserSessionListDocument,
|
||||||
"\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n": types.UserEmailListQueryDocument,
|
"\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n }\n":
|
||||||
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n }\n }\n": types.UserGreetingDocument,
|
types.CompatSsoLogin_LoginFragmentDoc,
|
||||||
"\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,
|
"\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\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.CompatSsoLoginListDocument,
|
||||||
|
"\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n }\n":
|
||||||
|
types.OAuth2Session_SessionFragmentDoc,
|
||||||
|
"\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||||
|
types.OAuth2SessionListQueryDocument,
|
||||||
|
"\n fragment UserEmail_email on UserEmail {\n id\n email\n createdAt\n confirmedAt\n }\n":
|
||||||
|
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 query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||||
|
types.UserEmailListQueryDocument,
|
||||||
|
"\n query UserPrimaryEmail($userId: ID!) {\n user(id: $userId) {\n id\n primaryEmail {\n id\n }\n }\n }\n":
|
||||||
|
types.UserPrimaryEmailDocument,
|
||||||
|
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n }\n }\n":
|
||||||
|
types.UserGreetingDocument,
|
||||||
|
"\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,
|
||||||
|
"\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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,62 +66,109 @@ export function graphql(source: string): unknown;
|
|||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"): (typeof documents)["\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on User {\n id\n }\n\n ... on Anonymous {\n id\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"): (typeof documents)["\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query CurrentViewerSessionQuery {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n }\n\n ... on Anonymous {\n id\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n email {\n id\n ...UserEmail_email\n }\n }\n }\n"): (typeof documents)["\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n email {\n id\n ...UserEmail_email\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n email {\n id\n ...UserEmail_email\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n }\n"): (typeof documents)["\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n }\n"
|
||||||
|
): (typeof documents)["\n fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"): (typeof documents)["\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query BrowserSessionList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n }\n"): (typeof documents)["\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n }\n"
|
||||||
|
): (typeof documents)["\n fragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"): (typeof documents)["\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query CompatSsoLoginList(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n compatSsoLogins(\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n }\n"): (typeof documents)["\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n }\n"
|
||||||
|
): (typeof documents)["\n fragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"): (typeof documents)["\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
export function graphql(
|
||||||
|
source: "\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\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 pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\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 createdAt\n confirmedAt\n }\n"];
|
export function graphql(
|
||||||
|
source: "\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 createdAt\n confirmedAt\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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"): (typeof documents)["\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
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.
|
* 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 UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n }\n }\n"): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n }\n }\n"];
|
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.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\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"): (typeof documents)["\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"];
|
export function graphql(
|
||||||
|
source: "\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query 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"];
|
export function graphql(
|
||||||
|
source: "\n query UserPrimaryEmail($userId: ID!) {\n user(id: $userId) {\n id\n primaryEmail {\n id\n }\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query UserPrimaryEmail($userId: ID!) {\n user(id: $userId) {\n id\n primaryEmail {\n id\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 query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n }\n }\n"
|
||||||
|
): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\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 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"
|
||||||
|
): (typeof documents)["\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"];
|
||||||
|
/**
|
||||||
|
* 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 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"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
|
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> =
|
||||||
|
TDocumentNode extends DocumentNode<infer TType, any> ? TType : never;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,15 +15,19 @@
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
|
|
||||||
import { currentUserIdAtom } from "../atoms";
|
import { currentUserIdAtom } from "../atoms";
|
||||||
import AddEmailForm from "../components/AddEmailForm";
|
import AddEmailForm, { addUserEmailAtom } from "../components/AddEmailForm";
|
||||||
import UserEmailList from "../components/UserEmailList";
|
import UserEmailList from "../components/UserEmailList";
|
||||||
import UserGreeting from "../components/UserGreeting";
|
import UserGreeting from "../components/UserGreeting";
|
||||||
|
|
||||||
const UserAccount: React.FC<{ id: string }> = ({ id }) => {
|
const UserAccount: React.FC<{ id: string }> = ({ id }) => {
|
||||||
|
const addUserEmail = useAtomValue(addUserEmailAtom);
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
<UserGreeting userId={id} />
|
<UserGreeting userId={id} />
|
||||||
<UserEmailList userId={id} />
|
<UserEmailList
|
||||||
|
userId={id}
|
||||||
|
highlightedEmail={addUserEmail.data?.addEmail?.email?.id}
|
||||||
|
/>
|
||||||
<AddEmailForm userId={id} />
|
<AddEmailForm userId={id} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ import { atom, Atom } from "jotai";
|
|||||||
|
|
||||||
import { PageInfo } from "./gql/graphql";
|
import { PageInfo } from "./gql/graphql";
|
||||||
|
|
||||||
|
export const FIRST_PAGE = Symbol("FIRST_PAGE");
|
||||||
|
export const LAST_PAGE = Symbol("LAST_PAGE");
|
||||||
|
const EMPTY = Symbol("EMPTY");
|
||||||
|
|
||||||
export type ForwardPagination = {
|
export type ForwardPagination = {
|
||||||
first: number;
|
first: number;
|
||||||
after: string | null;
|
after: string | null;
|
||||||
@@ -45,6 +49,42 @@ export const isBackwardPagination = (
|
|||||||
// This atom sets the default page size for pagination.
|
// This atom sets the default page size for pagination.
|
||||||
export const pageSizeAtom = atom(6);
|
export const pageSizeAtom = atom(6);
|
||||||
|
|
||||||
|
export const atomForCurrentPagination = () => {
|
||||||
|
const dataAtom = atom<typeof EMPTY | Pagination>(EMPTY);
|
||||||
|
|
||||||
|
const currentPaginationAtom = atom(
|
||||||
|
(get) => {
|
||||||
|
const data = get(dataAtom);
|
||||||
|
if (data === EMPTY) {
|
||||||
|
return {
|
||||||
|
first: get(pageSizeAtom),
|
||||||
|
after: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
(get, set, action: Pagination | typeof FIRST_PAGE | typeof LAST_PAGE) => {
|
||||||
|
if (action === FIRST_PAGE) {
|
||||||
|
set(dataAtom, EMPTY);
|
||||||
|
} else if (action === LAST_PAGE) {
|
||||||
|
set(dataAtom, {
|
||||||
|
last: get(pageSizeAtom),
|
||||||
|
before: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
set(dataAtom, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
currentPaginationAtom.onMount = (setAtom) => {
|
||||||
|
setAtom(FIRST_PAGE);
|
||||||
|
};
|
||||||
|
|
||||||
|
return currentPaginationAtom;
|
||||||
|
};
|
||||||
|
|
||||||
// This atom is used to create a pagination atom that gives the previous and
|
// This atom is used to create a pagination atom that gives the previous and
|
||||||
// next pagination objects, given the current pagination and the page info.
|
// next pagination objects, given the current pagination and the page info.
|
||||||
export const atomWithPagination = (
|
export const atomWithPagination = (
|
||||||
|
|||||||
Reference in New Issue
Block a user