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
add app sessions list
This commit is contained in:
committed by
Quentin Gliech
parent
e646319f5a
commit
1567dbb6d6
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { H6, Text } from "@vector-im/compound-web";
|
||||
import { H5 } from "@vector-im/compound-web";
|
||||
import { atom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { atomFamily } from "jotai/utils";
|
||||
import { atomWithQuery } from "jotai-urql";
|
||||
@@ -28,6 +28,8 @@ import {
|
||||
} from "../../pagination";
|
||||
import { isOk, unwrap, unwrapOk } from "../../result";
|
||||
import BlockList from "../BlockList";
|
||||
import CompatSession from "../CompatSession";
|
||||
import OAuth2Session from "../OAuth2Session";
|
||||
import PaginationControls from "../PaginationControls";
|
||||
|
||||
const QUERY = graphql(/* GraphQL */ `
|
||||
@@ -53,6 +55,7 @@ const QUERY = graphql(/* GraphQL */ `
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
__typename
|
||||
...CompatSession_session
|
||||
...OAuth2Session_session
|
||||
}
|
||||
@@ -72,7 +75,7 @@ const QUERY = graphql(/* GraphQL */ `
|
||||
const filterAtom = atom<SessionState | null>(SessionState.Active);
|
||||
const currentPaginationAtom = atomForCurrentPagination();
|
||||
|
||||
const appSessionListFamily = atomFamily((userId: string) => {
|
||||
export const appSessionListFamily = atomFamily((userId: string) => {
|
||||
const appSessionListQuery = atomWithQuery({
|
||||
query: QUERY,
|
||||
getVariables: (get) => ({
|
||||
@@ -124,8 +127,23 @@ const AppSessionsList: React.FC<{ userId: string }> = ({ userId }) => {
|
||||
|
||||
return (
|
||||
<BlockList>
|
||||
<H6>Apps</H6>
|
||||
<Text>{`${appSessions.totalCount} active sessions`}</Text>
|
||||
<header>
|
||||
<H5>Apps</H5>
|
||||
</header>
|
||||
{appSessions.edges.map((session) => {
|
||||
switch (session.node.__typename) {
|
||||
case "Oauth2Session":
|
||||
return (
|
||||
<OAuth2Session key={session.cursor} session={session.node} />
|
||||
);
|
||||
case "CompatSession":
|
||||
return (
|
||||
<CompatSession key={session.cursor} session={session.node} />
|
||||
);
|
||||
default:
|
||||
throw new Error("Unexpected session type.");
|
||||
}
|
||||
})}
|
||||
<PaginationControls
|
||||
onPrev={prevPage ? (): void => paginate(prevPage) : null}
|
||||
onNext={nextPage ? (): void => paginate(nextPage) : null}
|
||||
|
||||
@@ -1,27 +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.
|
||||
*/
|
||||
|
||||
.session-list-block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--cpd-space-1x);
|
||||
}
|
||||
|
||||
.session-list-block-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2022 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 type { Meta, StoryObj } from "@storybook/react";
|
||||
import { Provider } from "jotai";
|
||||
import { useHydrateAtoms } from "jotai/utils";
|
||||
|
||||
import { makeFragmentData } from "../../gql";
|
||||
import { appConfigAtom, locationAtom } from "../../routing";
|
||||
import { FRAGMENT as EMAIL_FRAGMENT } from "../UserEmail";
|
||||
|
||||
import UserSessionsOverview, { FRAGMENT } from "./UserSessionsOverview";
|
||||
|
||||
type Props = {
|
||||
primaryEmail: string | null;
|
||||
unverifiedEmails: number;
|
||||
confirmedEmails: number;
|
||||
oauth2Sessions: number;
|
||||
browserSessions: number;
|
||||
compatSessions: number;
|
||||
};
|
||||
|
||||
const WithHomePage: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
|
||||
useHydrateAtoms([
|
||||
[appConfigAtom, { root: "/" }],
|
||||
[locationAtom, { pathname: "/" }],
|
||||
]);
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const Template: React.FC<Props> = ({
|
||||
primaryEmail: email,
|
||||
unverifiedEmails,
|
||||
confirmedEmails,
|
||||
oauth2Sessions,
|
||||
browserSessions,
|
||||
compatSessions,
|
||||
}) => {
|
||||
let primaryEmail = null;
|
||||
if (email) {
|
||||
primaryEmail = {
|
||||
id: "email:123",
|
||||
...makeFragmentData(
|
||||
{
|
||||
id: "email:123",
|
||||
email,
|
||||
confirmedAt: new Date(),
|
||||
},
|
||||
EMAIL_FRAGMENT,
|
||||
),
|
||||
};
|
||||
}
|
||||
const data = makeFragmentData(
|
||||
{
|
||||
id: "user:123",
|
||||
primaryEmail,
|
||||
unverifiedEmails: {
|
||||
totalCount: unverifiedEmails,
|
||||
},
|
||||
confirmedEmails: {
|
||||
totalCount: confirmedEmails,
|
||||
},
|
||||
oauth2Sessions: {
|
||||
totalCount: oauth2Sessions,
|
||||
},
|
||||
browserSessions: {
|
||||
totalCount: browserSessions,
|
||||
},
|
||||
compatSessions: {
|
||||
totalCount: compatSessions,
|
||||
},
|
||||
},
|
||||
FRAGMENT,
|
||||
);
|
||||
return (
|
||||
<Provider>
|
||||
<WithHomePage>
|
||||
<UserSessionsOverview user={data} />
|
||||
</WithHomePage>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const meta = {
|
||||
title: "Pages/User Sessions Overview",
|
||||
component: Template,
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta<typeof Template>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Template>;
|
||||
|
||||
export const Basic: Story = {
|
||||
args: {
|
||||
primaryEmail: "hello@example.com",
|
||||
unverifiedEmails: 0,
|
||||
confirmedEmails: 5,
|
||||
oauth2Sessions: 3,
|
||||
compatSessions: 1,
|
||||
browserSessions: 2,
|
||||
},
|
||||
};
|
||||
|
||||
export const Empty: Story = {
|
||||
args: {
|
||||
primaryEmail: "hello@example.com",
|
||||
unverifiedEmails: 0,
|
||||
confirmedEmails: 1,
|
||||
oauth2Sessions: 0,
|
||||
compatSessions: 0,
|
||||
browserSessions: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnverifiedEmails: Story = {
|
||||
args: {
|
||||
primaryEmail: "hello@example.com",
|
||||
unverifiedEmails: 1,
|
||||
confirmedEmails: 1,
|
||||
oauth2Sessions: 0,
|
||||
compatSessions: 0,
|
||||
browserSessions: 0,
|
||||
},
|
||||
};
|
||||
@@ -1,103 +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 { create } from "react-test-renderer";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { makeFragmentData } from "../../gql";
|
||||
import { FRAGMENT as EMAIL_FRAGMENT } from "../UserEmail";
|
||||
|
||||
import UserSessionsOverview, { FRAGMENT } from "./";
|
||||
|
||||
describe("UserSessionsOverview", () => {
|
||||
it("render an simple <UserSessionsOverview />", () => {
|
||||
const primaryEmail = makeFragmentData(
|
||||
{
|
||||
id: "email:123",
|
||||
email: "hello@example.com",
|
||||
confirmedAt: new Date(),
|
||||
},
|
||||
EMAIL_FRAGMENT,
|
||||
);
|
||||
|
||||
const user = makeFragmentData(
|
||||
{
|
||||
id: "user:123",
|
||||
primaryEmail: {
|
||||
id: "email:123",
|
||||
...primaryEmail,
|
||||
},
|
||||
compatSessions: {
|
||||
totalCount: 0,
|
||||
},
|
||||
browserSessions: {
|
||||
totalCount: 0,
|
||||
},
|
||||
oauth2Sessions: {
|
||||
totalCount: 0,
|
||||
},
|
||||
unverifiedEmails: {
|
||||
totalCount: 0,
|
||||
},
|
||||
confirmedEmails: {
|
||||
totalCount: 1,
|
||||
},
|
||||
},
|
||||
FRAGMENT,
|
||||
);
|
||||
const component = create(<UserSessionsOverview user={user} />);
|
||||
const tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("render a <UserSessionsOverview /> with sessions", () => {
|
||||
const primaryEmail = makeFragmentData(
|
||||
{
|
||||
id: "email:123",
|
||||
email: "hello@example.com",
|
||||
confirmedAt: new Date(),
|
||||
},
|
||||
EMAIL_FRAGMENT,
|
||||
);
|
||||
|
||||
const user = makeFragmentData(
|
||||
{
|
||||
id: "user:123",
|
||||
primaryEmail: {
|
||||
id: "email:123",
|
||||
...primaryEmail,
|
||||
},
|
||||
compatSessions: {
|
||||
totalCount: 1,
|
||||
},
|
||||
browserSessions: {
|
||||
totalCount: 2,
|
||||
},
|
||||
oauth2Sessions: {
|
||||
totalCount: 3,
|
||||
},
|
||||
unverifiedEmails: {
|
||||
totalCount: 0,
|
||||
},
|
||||
confirmedEmails: {
|
||||
totalCount: 1,
|
||||
},
|
||||
},
|
||||
FRAGMENT,
|
||||
);
|
||||
const component = create(<UserSessionsOverview user={user} />);
|
||||
const tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -12,90 +12,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Body, H3, H6 } from "@vector-im/compound-web";
|
||||
import { H3 } from "@vector-im/compound-web";
|
||||
|
||||
import { FragmentType, graphql, useFragment } from "../../gql";
|
||||
import { Link } from "../../routing";
|
||||
import Block from "../Block";
|
||||
import { FragmentType, useFragment } from "../../gql";
|
||||
import BlockList from "../BlockList";
|
||||
|
||||
import AppSessionsList from "./AppSessionsList";
|
||||
import styles from "./UserSessionsOverview.module.css";
|
||||
|
||||
export const FRAGMENT = graphql(/* GraphQL */ `
|
||||
fragment UserSessionsOverview_user on User {
|
||||
id
|
||||
|
||||
primaryEmail {
|
||||
id
|
||||
...UserEmail_email
|
||||
}
|
||||
|
||||
confirmedEmails: emails(first: 0, state: CONFIRMED) {
|
||||
totalCount
|
||||
}
|
||||
|
||||
browserSessions(first: 0, state: ACTIVE) {
|
||||
totalCount
|
||||
}
|
||||
|
||||
oauth2Sessions(first: 0, state: ACTIVE) {
|
||||
totalCount
|
||||
}
|
||||
|
||||
compatSessions(first: 0, state: ACTIVE) {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`);
|
||||
import BrowserSessionsOverview, { FRAGMENT } from "./BrowserSessionsOverview";
|
||||
|
||||
const UserSessionsOverview: React.FC<{
|
||||
user: FragmentType<typeof FRAGMENT>;
|
||||
}> = ({ user }) => {
|
||||
const data = useFragment(FRAGMENT, user);
|
||||
|
||||
// allow this until we get i18n
|
||||
const pluraliseSession = (count: number): string =>
|
||||
count === 1 ? "session" : "sessions";
|
||||
|
||||
// user friendly description of sessions is:
|
||||
// browser -> browser
|
||||
// oauth2 sessions -> New apps
|
||||
// compatibility sessions -> Regular apps
|
||||
|
||||
return (
|
||||
<BlockList>
|
||||
{/* This is a short term solution, so I won't bother extracting these blocks into components */}
|
||||
<H3>Where you're signed in</H3>
|
||||
<Block className={styles.sessionListBlock}>
|
||||
<div className={styles.sessionListBlockInfo}>
|
||||
<H6>Browser</H6>
|
||||
<Body>
|
||||
{data.browserSessions.totalCount} active{" "}
|
||||
{pluraliseSession(data.browserSessions.totalCount)}
|
||||
</Body>
|
||||
</div>
|
||||
<Link kind="button" route={{ type: "browser-session-list" }}>
|
||||
View all
|
||||
</Link>
|
||||
</Block>
|
||||
<div className={styles.sessionListBlockInfo}>
|
||||
<H6>compatSessions</H6>
|
||||
<Body>
|
||||
{data.compatSessions.totalCount} active{" "}
|
||||
{pluraliseSession(data.compatSessions.totalCount)}
|
||||
</Body>
|
||||
</div>
|
||||
<div className={styles.sessionListBlockInfo}>
|
||||
<H6>oauth2Sessions</H6>
|
||||
<Body>
|
||||
{data.oauth2Sessions.totalCount} active{" "}
|
||||
{pluraliseSession(data.oauth2Sessions.totalCount)}
|
||||
</Body>
|
||||
<Link kind="button" route={{ type: "oauth2-session-list" }}>
|
||||
View all
|
||||
</Link>
|
||||
</div>
|
||||
<BrowserSessionsOverview user={user} />
|
||||
<AppSessionsList userId={data.id} />
|
||||
</BlockList>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`BrowserSessionsOverview > renders with no browser sessions 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="_block_17898c _sessionListBlock_c5c17e"
|
||||
>
|
||||
<div
|
||||
class="_sessionListBlockInfo_c5c17e"
|
||||
>
|
||||
<h5
|
||||
class="_font-body-lg-semibold_1jx6b_83"
|
||||
>
|
||||
Browsers
|
||||
</h5>
|
||||
<p
|
||||
class="_font-body-md-regular_1jx6b_59"
|
||||
>
|
||||
0
|
||||
active
|
||||
|
||||
sessions
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
class="_linkButton_b80ad8"
|
||||
href="/browser-sessions"
|
||||
>
|
||||
View all
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`BrowserSessionsOverview > renders with sessions 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="_block_17898c _sessionListBlock_c5c17e"
|
||||
>
|
||||
<div
|
||||
class="_sessionListBlockInfo_c5c17e"
|
||||
>
|
||||
<h5
|
||||
class="_font-body-lg-semibold_1jx6b_83"
|
||||
>
|
||||
Browsers
|
||||
</h5>
|
||||
<p
|
||||
class="_font-body-md-regular_1jx6b_59"
|
||||
>
|
||||
2
|
||||
active
|
||||
|
||||
sessions
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
class="_linkButton_b80ad8"
|
||||
href="/browser-sessions"
|
||||
>
|
||||
View all
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`UserSessionsOverview > render an simple <UserSessionsOverview /> 1`] = `<div />`;
|
||||
@@ -12,4 +12,4 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
export { default, FRAGMENT } from "./UserSessionsOverview";
|
||||
export { default } from "./UserSessionsOverview";
|
||||
|
||||
@@ -57,10 +57,10 @@ const documents = {
|
||||
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 query AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||
"\n query AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||
types.AppSessionListDocument,
|
||||
"\n fragment UserSessionsOverview_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 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.UserSessionsOverview_UserFragmentDoc,
|
||||
"\n fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n":
|
||||
types.BrowserSessionsOverview_UserFragmentDoc,
|
||||
"\n fragment UserEmail_verifyEmail on UserEmail {\n id\n email\n }\n":
|
||||
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":
|
||||
@@ -73,7 +73,7 @@ const documents = {
|
||||
types.BrowserSessionQueryDocument,
|
||||
"\n query OAuth2ClientQuery($id: ID!) {\n oauth2Client(id: $id) {\n ...OAuth2Client_detail\n }\n }\n":
|
||||
types.OAuth2ClientQueryDocument,
|
||||
"\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...UserSessionsOverview_user\n }\n }\n }\n":
|
||||
"\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n":
|
||||
types.SessionsOverviewQueryDocument,
|
||||
"\n query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n":
|
||||
types.VerifyEmailQueryDocument,
|
||||
@@ -229,14 +229,14 @@ 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 AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n ...CompatSession_session\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 AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n ...CompatSession_session\n ...OAuth2Session_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
||||
source: "\n query AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\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 AppSessionList(\n $userId: ID!\n $state: SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n appSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: $state\n ) {\n totalCount\n\n edges {\n cursor\n node {\n __typename\n ...CompatSession_session\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.
|
||||
*/
|
||||
export function graphql(
|
||||
source: "\n fragment UserSessionsOverview_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 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 UserSessionsOverview_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 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 fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n",
|
||||
): (typeof documents)["\n fragment BrowserSessionsOverview_user on User {\n id\n\n browserSessions(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.
|
||||
*/
|
||||
@@ -277,8 +277,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 SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...UserSessionsOverview_user\n }\n }\n }\n",
|
||||
): (typeof documents)["\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...UserSessionsOverview_user\n }\n }\n }\n"];
|
||||
source: "\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n",
|
||||
): (typeof documents)["\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_user\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -1496,12 +1496,12 @@ export type AppSessionListQuery = {
|
||||
__typename?: "AppSessionEdge";
|
||||
cursor: string;
|
||||
node:
|
||||
| ({ __typename?: "CompatSession" } & {
|
||||
| ({ __typename: "CompatSession" } & {
|
||||
" $fragmentRefs"?: {
|
||||
CompatSession_SessionFragment: CompatSession_SessionFragment;
|
||||
};
|
||||
})
|
||||
| ({ __typename?: "Oauth2Session" } & {
|
||||
| ({ __typename: "Oauth2Session" } & {
|
||||
" $fragmentRefs"?: {
|
||||
OAuth2Session_SessionFragment: OAuth2Session_SessionFragment;
|
||||
};
|
||||
@@ -1518,28 +1518,14 @@ export type AppSessionListQuery = {
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type UserSessionsOverview_UserFragment = {
|
||||
export type BrowserSessionsOverview_UserFragment = {
|
||||
__typename?: "User";
|
||||
id: string;
|
||||
primaryEmail?:
|
||||
| ({ __typename?: "UserEmail"; id: string } & {
|
||||
" $fragmentRefs"?: { UserEmail_EmailFragment: UserEmail_EmailFragment };
|
||||
})
|
||||
| null;
|
||||
confirmedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
|
||||
browserSessions: {
|
||||
__typename?: "BrowserSessionConnection";
|
||||
totalCount: number;
|
||||
};
|
||||
oauth2Sessions: {
|
||||
__typename?: "Oauth2SessionConnection";
|
||||
totalCount: number;
|
||||
};
|
||||
compatSessions: {
|
||||
__typename?: "CompatSessionConnection";
|
||||
totalCount: number;
|
||||
};
|
||||
} & { " $fragmentName"?: "UserSessionsOverview_UserFragment" };
|
||||
} & { " $fragmentName"?: "BrowserSessionsOverview_UserFragment" };
|
||||
|
||||
export type UserEmail_VerifyEmailFragment = {
|
||||
__typename?: "UserEmail";
|
||||
@@ -1646,7 +1632,7 @@ export type SessionsOverviewQueryQuery = {
|
||||
| { __typename: "Anonymous" }
|
||||
| ({ __typename: "User"; id: string } & {
|
||||
" $fragmentRefs"?: {
|
||||
UserSessionsOverview_UserFragment: UserSessionsOverview_UserFragment;
|
||||
BrowserSessionsOverview_UserFragment: BrowserSessionsOverview_UserFragment;
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -1857,12 +1843,12 @@ export const UserEmail_EmailFragmentDoc = {
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<UserEmail_EmailFragment, unknown>;
|
||||
export const UserSessionsOverview_UserFragmentDoc = {
|
||||
export const BrowserSessionsOverview_UserFragmentDoc = {
|
||||
kind: "Document",
|
||||
definitions: [
|
||||
{
|
||||
kind: "FragmentDefinition",
|
||||
name: { kind: "Name", value: "UserSessionsOverview_user" },
|
||||
name: { kind: "Name", value: "BrowserSessionsOverview_user" },
|
||||
typeCondition: {
|
||||
kind: "NamedType",
|
||||
name: { kind: "Name", value: "User" },
|
||||
@@ -1871,43 +1857,6 @@ export const UserSessionsOverview_UserFragmentDoc = {
|
||||
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: "FragmentSpread",
|
||||
name: { kind: "Name", value: "UserEmail_email" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
alias: { kind: "Name", value: "confirmedEmails" },
|
||||
name: { kind: "Name", value: "emails" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "CONFIRMED" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "browserSessions" },
|
||||
@@ -1930,71 +1879,11 @@ export const UserSessionsOverview_UserFragmentDoc = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "oauth2Sessions" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "ACTIVE" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "compatSessions" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "ACTIVE" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
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<UserSessionsOverview_UserFragment, unknown>;
|
||||
} as unknown as DocumentNode<BrowserSessionsOverview_UserFragment, unknown>;
|
||||
export const UserEmail_VerifyEmailFragmentDoc = {
|
||||
kind: "Document",
|
||||
definitions: [
|
||||
@@ -4138,6 +4027,10 @@ export const AppSessionListDocument = {
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "__typename" },
|
||||
},
|
||||
{
|
||||
kind: "FragmentSpread",
|
||||
name: {
|
||||
@@ -4683,7 +4576,7 @@ export const SessionsOverviewQueryDocument = {
|
||||
kind: "FragmentSpread",
|
||||
name: {
|
||||
kind: "Name",
|
||||
value: "UserSessionsOverview_user",
|
||||
value: "BrowserSessionsOverview_user",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -4697,23 +4590,7 @@ export const SessionsOverviewQueryDocument = {
|
||||
},
|
||||
{
|
||||
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" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "FragmentDefinition",
|
||||
name: { kind: "Name", value: "UserSessionsOverview_user" },
|
||||
name: { kind: "Name", value: "BrowserSessionsOverview_user" },
|
||||
typeCondition: {
|
||||
kind: "NamedType",
|
||||
name: { kind: "Name", value: "User" },
|
||||
@@ -4722,43 +4599,6 @@ export const SessionsOverviewQueryDocument = {
|
||||
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: "FragmentSpread",
|
||||
name: { kind: "Name", value: "UserEmail_email" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
alias: { kind: "Name", value: "confirmedEmails" },
|
||||
name: { kind: "Name", value: "emails" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "CONFIRMED" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "browserSessions" },
|
||||
@@ -4781,50 +4621,6 @@ export const SessionsOverviewQueryDocument = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "oauth2Sessions" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "ACTIVE" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Field",
|
||||
name: { kind: "Name", value: "compatSessions" },
|
||||
arguments: [
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "first" },
|
||||
value: { kind: "IntValue", value: "0" },
|
||||
},
|
||||
{
|
||||
kind: "Argument",
|
||||
name: { kind: "Name", value: "state" },
|
||||
value: { kind: "EnumValue", value: "ACTIVE" },
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: "SelectionSet",
|
||||
selections: [
|
||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -130,7 +130,7 @@ const exchanges = [
|
||||
];
|
||||
|
||||
export const client = createClient({
|
||||
url: "/graphql",
|
||||
url: "/graphql?testtest",
|
||||
// Add the devtools exchange in development
|
||||
exchanges: import.meta.env.DEV ? [devtoolsExchange, ...exchanges] : exchanges,
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ const QUERY = graphql(/* GraphQL */ `
|
||||
|
||||
... on User {
|
||||
id
|
||||
...UserSessionsOverview_user
|
||||
...BrowserSessionsOverview_user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user