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

Allow user to view and change display name in My Account UI (#1628)

Co-authored-by: Quentin Gliech <quenting@element.io>
This commit is contained in:
Kerry
2023-08-29 23:40:00 +12:00
committed by GitHub
parent 438a10332a
commit 1826120f10
12 changed files with 578 additions and 214 deletions

View File

@@ -19,6 +19,20 @@
}
}
:root {
--size: var(--cpd-space-16x);
}
.inline {
display: inline;
margin-right: var(--cpd-space-4x);
.loading-spinner-inner {
height: var(--cpd-space-4x);
width: var(--cpd-space-4x);
}
}
.loading-spinner-inner {
height: var(--cpd-space-16x);
width: var(--cpd-space-16x);

View File

@@ -14,8 +14,8 @@
import styles from "./LoadingSpinner.module.css";
const LoadingSpinner: React.FC = () => (
<div role="status">
const LoadingSpinner: React.FC<{ inline?: boolean }> = ({ inline }) => (
<div role="status" className={inline ? styles.inline : undefined}>
<svg
className={styles.loadingSpinnerInner}
viewBox="0 0 100 101"

View File

@@ -32,7 +32,7 @@ const QUERY = graphql(/* GraphQL */ `
}
`);
const userGreetingFamily = atomFamily((userId: string) => {
export const userGreetingFamily = atomFamily((userId: string) => {
const userGreeting = atomWithQuery({
query: QUERY,
getVariables: () => ({ userId }),

View File

@@ -24,7 +24,7 @@ import { useAtom } from "jotai";
import { atomWithMutation } from "jotai-urql";
import { useRef, useTransition } from "react";
import { graphql } from "../gql";
import { graphql } from "../../gql";
const ADD_EMAIL_MUTATION = graphql(/* GraphQL */ `
mutation AddEmail($userId: ID!, $email: String!) {

View File

@@ -12,26 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Alert } from "@vector-im/compound-web";
import { Alert, H3 } from "@vector-im/compound-web";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { atomFamily } from "jotai/utils";
import { atomWithQuery } from "jotai-urql";
import { useTransition } from "react";
import { routeAtom } from "../Router";
import { graphql } from "../gql";
import { PageInfo } from "../gql/graphql";
import { routeAtom } from "../../Router";
import { graphql } from "../../gql";
import { PageInfo } from "../../gql/graphql";
import {
atomForCurrentPagination,
atomWithPagination,
FIRST_PAGE,
Pagination,
} from "../pagination";
} from "../../pagination";
import BlockList from "../BlockList";
import PaginationControls from "../PaginationControls";
import UserEmail from "../UserEmail";
import AddEmailForm from "./AddEmailForm";
import BlockList from "./BlockList";
import PaginationControls from "./PaginationControls";
import UserEmail from "./UserEmail";
const QUERY = graphql(/* GraphQL */ `
query UserEmailListQuery(
@@ -155,10 +155,11 @@ const UserEmailList: React.FC<{
setRoute({ type: "verify-email", id });
};
const showNoPrimaryEmailAlert = !!result && !primaryEmailId;
const showNoPrimaryEmailAlert = !!result?.data && !primaryEmailId;
return (
<BlockList>
<H3>Emails</H3>
<PaginationControls
count={result.data?.user?.emails?.totalCount ?? 0}
onPrev={prevPage ? (): void => paginate(prevPage) : null}

View File

@@ -0,0 +1,25 @@
/* 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.
*/
.form {
display: flex;
flex-direction: column;
align-items: stretch;
gap: var(--cpd-space-2x);
}
.save-button {
align-self: flex-start;
}

View File

@@ -0,0 +1,155 @@
// 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 {
Alert,
Control,
Field,
Label,
Root,
Button,
} from "@vector-im/compound-web";
import { useAtomValue, useAtom, useSetAtom, atom } from "jotai";
import { atomFamily } from "jotai/utils";
import { atomWithMutation } from "jotai-urql";
import { useState, useEffect, useRef } from "react";
import { graphql } from "../../gql";
import LoadingSpinner from "../LoadingSpinner/LoadingSpinner";
import { userGreetingFamily } from "../UserGreeting";
import styles from "./UserName.module.css";
const SET_DISPLAYNAME_MUTATION = graphql(/* GraphQL */ `
mutation SetDisplayName($userId: ID!, $displayName: String) {
setDisplayName(input: { userId: $userId, displayName: $displayName }) {
status
user {
id
matrix {
displayName
}
}
}
}
`);
const setDisplayNameFamily = atomFamily((userId: string) => {
const setDisplayName = atomWithMutation(SET_DISPLAYNAME_MUTATION);
// A proxy atom which pre-sets the id variable in the mutation
const setDisplayNameAtom = atom(
(get) => get(setDisplayName),
(get, set, displayName: string | null) =>
set(setDisplayName, { userId, displayName }),
);
return setDisplayNameAtom;
});
const getErrorMessage = (result: {
error?: unknown;
data?: { setDisplayName: { status: string } };
}): string | undefined => {
if (result.error) {
return "Failed to save display name. Please try again.";
}
if (result.data?.setDisplayName.status === "INVALID") {
return "Failed to save invalid display name.";
}
};
const UserName: React.FC<{ userId: string }> = ({ userId }) => {
const result = useAtomValue(userGreetingFamily(userId));
const fieldRef = useRef<HTMLInputElement>(null);
const [setDisplayNameResult, setDisplayName] = useAtom(
setDisplayNameFamily(userId),
);
const [inProgress, setInProgress] = useState(false);
const user = result.data?.user;
const displayName = user?.matrix.displayName || "";
const userGreeting = useSetAtom(userGreetingFamily(userId));
useEffect(() => {
if (fieldRef.current) {
fieldRef.current.value = displayName;
}
}, [displayName]);
const onSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
let newDisplayName = (formData.get("displayname") as string) || null;
// set null to remove an existing username
if (newDisplayName === "") {
newDisplayName = null;
}
// do nothing if no change
if ((!newDisplayName && !displayName) || newDisplayName === displayName) {
return;
}
setInProgress(true);
setDisplayName(newDisplayName).then((result) => {
if (!result.data) {
console.error("Failed to set display name", result.error);
} else if (result.data.setDisplayName.status === "SET") {
// refresh the user greeting after changing the display name
userGreeting({
requestPolicy: "network-only",
});
} else if (result.data.setDisplayName.status === "INVALID") {
// reset to current saved display name
if (fieldRef.current) {
fieldRef.current.value = displayName;
}
}
setInProgress(false);
});
};
const errorMessage = getErrorMessage(setDisplayNameResult);
return (
<Root onSubmit={onSubmit} className={styles.form}>
<Field name="displayname">
<Label>Display Name</Label>
<Control ref={fieldRef} inputMode="text" max={250} />
</Field>
{!inProgress && errorMessage && (
<Alert type="critical" title="Error">
{errorMessage}
</Alert>
)}
<Button
className={styles.saveButton}
disabled={inProgress}
kind="primary"
size="sm"
type="submit"
>
{!!inProgress && <LoadingSpinner inline />}Save
</Button>
</Root>
);
};
export default UserName;

View File

@@ -0,0 +1,29 @@
// 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 BlockList from "../BlockList/BlockList";
import UserEmailList from "./UserEmailList";
import UserName from "./UserName";
const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
return (
<BlockList>
<UserName userId={userId} />
<UserEmailList userId={userId} />
</BlockList>
);
};
export default UserProfile;

View 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 "./UserProfile";

View File

@@ -17,8 +17,6 @@ const documents = {
types.CurrentViewerQueryDocument,
"\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,
"\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 fragment BrowserSession_session on BrowserSession {\n id\n createdAt\n finishedAt\n lastAuthentication {\n id\n createdAt\n }\n }\n":
types.BrowserSession_SessionFragmentDoc,
"\n mutation EndBrowserSession($id: ID!) {\n endBrowserSession(input: { browserSessionId: $id }) {\n status\n browserSession {\n id\n ...BrowserSession_session\n }\n }\n }\n":
@@ -45,14 +43,18 @@ const documents = {
types.RemoveEmailDocument,
"\n mutation SetPrimaryEmail($id: ID!) {\n setPrimaryEmail(input: { userEmailId: $id }) {\n status\n user {\n id\n primaryEmail {\n id\n }\n }\n }\n }\n":
types.SetPrimaryEmailDocument,
"\n 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 matrix {\n mxid\n displayName\n }\n }\n }\n":
types.UserGreetingDocument,
"\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n":
types.UserHome_UserFragmentDoc,
"\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 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 mutation SetDisplayName($userId: ID!, $displayName: String) {\n setDisplayName(input: { userId: $userId, displayName: $displayName }) {\n status\n user {\n id\n matrix {\n displayName\n }\n }\n }\n }\n":
types.SetDisplayNameDocument,
"\n 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":
@@ -95,12 +97,6 @@ export function graphql(
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.
*/
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.
*/
@@ -179,6 +175,24 @@ export function graphql(
export function graphql(
source: "\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",
): (typeof documents)["\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"];
/**
* 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 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 UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n",
): (typeof documents)["\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\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 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.
*/
@@ -195,14 +209,8 @@ export function graphql(
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: "\n query 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 UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n",
): (typeof documents)["\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n"];
source: "\n mutation SetDisplayName($userId: ID!, $displayName: String) {\n setDisplayName(input: { userId: $userId, displayName: $displayName }) {\n status\n user {\n id\n matrix {\n displayName\n }\n }\n }\n }\n",
): (typeof documents)["\n mutation SetDisplayName($userId: ID!, $displayName: String) {\n setDisplayName(input: { userId: $userId, displayName: $displayName }) {\n status\n user {\n id\n matrix {\n displayName\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@@ -916,26 +916,6 @@ export type CurrentViewerSessionQueryQuery = {
| { __typename: "Oauth2Session" };
};
export type AddEmailMutationVariables = Exact<{
userId: Scalars["ID"]["input"];
email: Scalars["String"]["input"];
}>;
export type AddEmailMutation = {
__typename?: "Mutation";
addEmail: {
__typename?: "AddEmailPayload";
status: AddEmailStatus;
email?:
| ({ __typename?: "UserEmail"; id: string } & {
" $fragmentRefs"?: {
UserEmail_EmailFragment: UserEmail_EmailFragment;
};
})
| null;
};
};
export type BrowserSession_SessionFragment = {
__typename?: "BrowserSession";
id: string;
@@ -1186,6 +1166,68 @@ export type SetPrimaryEmailMutation = {
};
};
export type UserGreetingQueryVariables = Exact<{
userId: Scalars["ID"]["input"];
}>;
export type UserGreetingQuery = {
__typename?: "Query";
user?: {
__typename?: "User";
id: string;
username: string;
matrix: {
__typename?: "MatrixUser";
mxid: string;
displayName?: string | null;
};
} | null;
};
export type UserHome_UserFragment = {
__typename?: "User";
id: string;
primaryEmail?:
| ({ __typename?: "UserEmail"; id: string } & {
" $fragmentRefs"?: { UserEmail_EmailFragment: UserEmail_EmailFragment };
})
| null;
confirmedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
unverifiedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
browserSessions: {
__typename?: "BrowserSessionConnection";
totalCount: number;
};
oauth2Sessions: {
__typename?: "Oauth2SessionConnection";
totalCount: number;
};
compatSessions: {
__typename?: "CompatSessionConnection";
totalCount: number;
};
} & { " $fragmentName"?: "UserHome_UserFragment" };
export type AddEmailMutationVariables = Exact<{
userId: Scalars["ID"]["input"];
email: Scalars["String"]["input"];
}>;
export type AddEmailMutation = {
__typename?: "Mutation";
addEmail: {
__typename?: "AddEmailPayload";
status: AddEmailStatus;
email?:
| ({ __typename?: "UserEmail"; id: string } & {
" $fragmentRefs"?: {
UserEmail_EmailFragment: UserEmail_EmailFragment;
};
})
| null;
};
};
export type UserEmailListQueryQueryVariables = Exact<{
userId: Scalars["ID"]["input"];
first?: InputMaybe<Scalars["Int"]["input"]>;
@@ -1235,47 +1277,23 @@ export type UserPrimaryEmailQuery = {
} | null;
};
export type UserGreetingQueryVariables = Exact<{
export type SetDisplayNameMutationVariables = Exact<{
userId: Scalars["ID"]["input"];
displayName?: InputMaybe<Scalars["String"]["input"]>;
}>;
export type UserGreetingQuery = {
__typename?: "Query";
export type SetDisplayNameMutation = {
__typename?: "Mutation";
setDisplayName: {
__typename?: "SetDisplayNamePayload";
status: SetDisplayNameStatus;
user?: {
__typename?: "User";
id: string;
username: string;
matrix: {
__typename?: "MatrixUser";
mxid: string;
displayName?: string | null;
};
matrix: { __typename?: "MatrixUser"; displayName?: string | null };
} | null;
};
export type UserHome_UserFragment = {
__typename?: "User";
id: string;
primaryEmail?:
| ({ __typename?: "UserEmail"; id: string } & {
" $fragmentRefs"?: { UserEmail_EmailFragment: UserEmail_EmailFragment };
})
| null;
confirmedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
unverifiedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
browserSessions: {
__typename?: "BrowserSessionConnection";
totalCount: number;
};
oauth2Sessions: {
__typename?: "Oauth2SessionConnection";
totalCount: number;
};
compatSessions: {
__typename?: "CompatSessionConnection";
totalCount: number;
};
} & { " $fragmentName"?: "UserHome_UserFragment" };
export type UserEmail_VerifyEmailFragment = {
__typename?: "UserEmail";
@@ -1839,115 +1857,6 @@ export const CurrentViewerSessionQueryDocument = {
CurrentViewerSessionQueryQuery,
CurrentViewerSessionQueryQueryVariables
>;
export const AddEmailDocument = {
kind: "Document",
definitions: [
{
kind: "OperationDefinition",
operation: "mutation",
name: { kind: "Name", value: "AddEmail" },
variableDefinitions: [
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
type: {
kind: "NonNullType",
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
},
},
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "email" },
},
type: {
kind: "NonNullType",
type: {
kind: "NamedType",
name: { kind: "Name", value: "String" },
},
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{
kind: "Field",
name: { kind: "Name", value: "addEmail" },
arguments: [
{
kind: "Argument",
name: { kind: "Name", value: "input" },
value: {
kind: "ObjectValue",
fields: [
{
kind: "ObjectField",
name: { kind: "Name", value: "userId" },
value: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
},
{
kind: "ObjectField",
name: { kind: "Name", value: "email" },
value: {
kind: "Variable",
name: { kind: "Name", value: "email" },
},
},
],
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "status" } },
{
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<AddEmailMutation, AddEmailMutationVariables>;
export const EndBrowserSessionDocument = {
kind: "Document",
definitions: [
@@ -3073,6 +2982,178 @@ export const SetPrimaryEmailDocument = {
SetPrimaryEmailMutation,
SetPrimaryEmailMutationVariables
>;
export const UserGreetingDocument = {
kind: "Document",
definitions: [
{
kind: "OperationDefinition",
operation: "query",
name: { kind: "Name", value: "UserGreeting" },
variableDefinitions: [
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
type: {
kind: "NonNullType",
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{
kind: "Field",
name: { kind: "Name", value: "user" },
arguments: [
{
kind: "Argument",
name: { kind: "Name", value: "id" },
value: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
{ kind: "Field", name: { kind: "Name", value: "username" } },
{
kind: "Field",
name: { kind: "Name", value: "matrix" },
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "mxid" } },
{
kind: "Field",
name: { kind: "Name", value: "displayName" },
},
],
},
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<UserGreetingQuery, UserGreetingQueryVariables>;
export const AddEmailDocument = {
kind: "Document",
definitions: [
{
kind: "OperationDefinition",
operation: "mutation",
name: { kind: "Name", value: "AddEmail" },
variableDefinitions: [
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
type: {
kind: "NonNullType",
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
},
},
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "email" },
},
type: {
kind: "NonNullType",
type: {
kind: "NamedType",
name: { kind: "Name", value: "String" },
},
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{
kind: "Field",
name: { kind: "Name", value: "addEmail" },
arguments: [
{
kind: "Argument",
name: { kind: "Name", value: "input" },
value: {
kind: "ObjectValue",
fields: [
{
kind: "ObjectField",
name: { kind: "Name", value: "userId" },
value: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
},
{
kind: "ObjectField",
name: { kind: "Name", value: "email" },
value: {
kind: "Variable",
name: { kind: "Name", value: "email" },
},
},
],
},
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "status" } },
{
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<AddEmailMutation, AddEmailMutationVariables>;
export const UserEmailListQueryDocument = {
kind: "Document",
definitions: [
@@ -3335,13 +3416,13 @@ export const UserPrimaryEmailDocument = {
UserPrimaryEmailQuery,
UserPrimaryEmailQueryVariables
>;
export const UserGreetingDocument = {
export const SetDisplayNameDocument = {
kind: "Document",
definitions: [
{
kind: "OperationDefinition",
operation: "query",
name: { kind: "Name", value: "UserGreeting" },
operation: "mutation",
name: { kind: "Name", value: "SetDisplayName" },
variableDefinitions: [
{
kind: "VariableDefinition",
@@ -3354,35 +3435,65 @@ export const UserGreetingDocument = {
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
},
},
{
kind: "VariableDefinition",
variable: {
kind: "Variable",
name: { kind: "Name", value: "displayName" },
},
type: { kind: "NamedType", name: { kind: "Name", value: "String" } },
},
],
selectionSet: {
kind: "SelectionSet",
selections: [
{
kind: "Field",
name: { kind: "Name", value: "user" },
name: { kind: "Name", value: "setDisplayName" },
arguments: [
{
kind: "Argument",
name: { kind: "Name", value: "id" },
name: { kind: "Name", value: "input" },
value: {
kind: "ObjectValue",
fields: [
{
kind: "ObjectField",
name: { kind: "Name", value: "userId" },
value: {
kind: "Variable",
name: { kind: "Name", value: "userId" },
},
},
{
kind: "ObjectField",
name: { kind: "Name", value: "displayName" },
value: {
kind: "Variable",
name: { kind: "Name", value: "displayName" },
},
},
],
},
},
],
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: "username" } },
{
kind: "Field",
name: { kind: "Name", value: "matrix" },
selectionSet: {
kind: "SelectionSet",
selections: [
{ kind: "Field", name: { kind: "Name", value: "mxid" } },
{
kind: "Field",
name: { kind: "Name", value: "displayName" },
@@ -3397,7 +3508,13 @@ export const UserGreetingDocument = {
},
},
],
} as unknown as DocumentNode<UserGreetingQuery, UserGreetingQueryVariables>;
},
},
],
} as unknown as DocumentNode<
SetDisplayNameMutation,
SetDisplayNameMutationVariables
>;
export const VerifyEmailDocument = {
kind: "Document",
definitions: [

View File

@@ -17,7 +17,7 @@ import { useAtomValue } from "jotai";
import { currentUserIdAtom } from "../atoms";
import GraphQLError from "../components/GraphQLError";
import NotLoggedIn from "../components/NotLoggedIn";
import UserEmailList from "../components/UserEmailList";
import UserProfile from "../components/UserProfile";
import { isErr, unwrapErr, unwrapOk } from "../result";
const Profile: React.FC = () => {
@@ -27,7 +27,7 @@ const Profile: React.FC = () => {
const userId = unwrapOk(result);
if (userId === null) return <NotLoggedIn />;
return <UserEmailList userId={userId} />;
return <UserProfile userId={userId} />;
};
export default Profile;