diff --git a/frontend/src/components/SessionDetail/SessionDetail.tsx b/frontend/src/components/SessionDetail/SessionDetail.tsx index cfb2bd55..a268023c 100644 --- a/frontend/src/components/SessionDetail/SessionDetail.tsx +++ b/frontend/src/components/SessionDetail/SessionDetail.tsx @@ -12,15 +12,71 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { Alert, Body } from "@vector-im/compound-web"; +import { useAtomValue } from "jotai"; +import { atomFamily } from "jotai/utils"; +import { atomWithQuery } from "jotai-urql"; +import { useRef } from "react"; + +import { Link } from "../../Router"; +import { graphql } from "../../gql/gql"; +import CompatSession from "../CompatSession"; +import OAuth2Session from "../OAuth2Session"; + +const QUERY = graphql(/* GraphQL */ ` + query SessionQuery($userId: ID!, $deviceId: String!) { + session(userId: $userId, deviceId: $deviceId) { + __typename + ...CompatSession_session + ...OAuth2Session_session + } + } +`); + +const sessionFamily = atomFamily( + ({ userId, deviceId }: { userId: string; deviceId: string }) => { + const sessionQueryAtom = atomWithQuery({ + query: QUERY, + getVariables: () => ({ userId, deviceId }), + }); + + return sessionQueryAtom; + }, +); + const SessionDetail: React.FC<{ deviceId: string; userId: string; }> = ({ deviceId, userId }) => { + const props = useRef({ userId, deviceId }); + const result = useAtomValue(sessionFamily(props.current)); + + const session = result.data?.session; + + if (!session) { + // TODO put a back button here + return ( + + Cannot find session: {deviceId} + + Go back + + + ); + } + + const sessionType = session.__typename; + + if (sessionType === "Oauth2Session") { + return ; + } else if (sessionType === "CompatSession") { + return ; + } + return ( - <> - deviceId: {deviceId} - userId: {userId} - + + Unexpected session type + ); }; diff --git a/frontend/src/gql/gql.ts b/frontend/src/gql/gql.ts index 05745851..fb9b8e50 100644 --- a/frontend/src/gql/gql.ts +++ b/frontend/src/gql/gql.ts @@ -37,6 +37,8 @@ const documents = { types.EndOAuth2SessionDocument, "\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\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, + "\n query SessionQuery($userId: ID!, $deviceId: String!) {\n session(userId: $userId, deviceId: $deviceId) {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n": + types.SessionQueryDocument, "\n fragment UnverifiedEmailAlert on User {\n id\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n }\n": types.UnverifiedEmailAlertFragmentDoc, "\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n": @@ -159,6 +161,12 @@ export function graphql( export function graphql( source: "\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\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", ): (typeof documents)["\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\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"]; +/** + * 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 SessionQuery($userId: ID!, $deviceId: String!) {\n session(userId: $userId, deviceId: $deviceId) {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n", +): (typeof documents)["\n query SessionQuery($userId: ID!, $deviceId: String!) {\n session(userId: $userId, deviceId: $deviceId) {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index b3142785..807e27f9 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -1146,6 +1146,27 @@ export type OAuth2SessionListQueryQuery = { } | null; }; +export type SessionQueryQueryVariables = Exact<{ + userId: Scalars["ID"]["input"]; + deviceId: Scalars["String"]["input"]; +}>; + +export type SessionQueryQuery = { + __typename?: "Query"; + session?: + | ({ __typename: "CompatSession" } & { + " $fragmentRefs"?: { + CompatSession_SessionFragment: CompatSession_SessionFragment; + }; + }) + | ({ __typename: "Oauth2Session" } & { + " $fragmentRefs"?: { + OAuth2Session_SessionFragment: OAuth2Session_SessionFragment; + }; + }) + | null; +}; + export type UnverifiedEmailAlertFragment = { __typename?: "User"; id: string; @@ -2891,6 +2912,160 @@ export const OAuth2SessionListQueryDocument = { OAuth2SessionListQueryQuery, OAuth2SessionListQueryQueryVariables >; +export const SessionQueryDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { kind: "Name", value: "SessionQuery" }, + 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: "deviceId" }, + }, + type: { + kind: "NonNullType", + type: { + kind: "NamedType", + name: { kind: "Name", value: "String" }, + }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "session" }, + arguments: [ + { + kind: "Argument", + name: { kind: "Name", value: "userId" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "userId" }, + }, + }, + { + kind: "Argument", + name: { kind: "Name", value: "deviceId" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "deviceId" }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "__typename" } }, + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CompatSession_session" }, + }, + { + kind: "FragmentSpread", + name: { kind: "Name", value: "OAuth2Session_session" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "CompatSession_sso_login" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "CompatSsoLogin" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "redirectUri" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "CompatSession_session" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "CompatSession" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "createdAt" } }, + { kind: "Field", name: { kind: "Name", value: "deviceId" } }, + { kind: "Field", name: { kind: "Name", value: "finishedAt" } }, + { + kind: "Field", + name: { kind: "Name", value: "ssoLogin" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CompatSession_sso_login" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "OAuth2Session_session" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Oauth2Session" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "scope" } }, + { kind: "Field", name: { kind: "Name", value: "createdAt" } }, + { kind: "Field", name: { kind: "Name", value: "finishedAt" } }, + { + kind: "Field", + name: { kind: "Name", value: "client" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "clientId" } }, + { kind: "Field", name: { kind: "Name", value: "clientName" } }, + { kind: "Field", name: { kind: "Name", value: "clientUri" } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; export const RemoveEmailDocument = { kind: "Document", definitions: [