You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-11-23 11:02:35 +03:00
Make sure we load current session data in route loaders
This means we should almost never see a loading spinner when navigating
This commit is contained in:
@@ -21,7 +21,6 @@ import {
|
|||||||
parseUserAgent,
|
parseUserAgent,
|
||||||
sessionNameFromDeviceInformation,
|
sessionNameFromDeviceInformation,
|
||||||
} from "../utils/parseUserAgent";
|
} from "../utils/parseUserAgent";
|
||||||
import { useCurrentBrowserSessionId } from "../utils/session/useCurrentBrowserSessionId";
|
|
||||||
|
|
||||||
import EndSessionButton from "./Session/EndSessionButton";
|
import EndSessionButton from "./Session/EndSessionButton";
|
||||||
import Session from "./Session/Session";
|
import Session from "./Session/Session";
|
||||||
@@ -71,12 +70,11 @@ export const useEndBrowserSession = (
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
session: FragmentType<typeof FRAGMENT>;
|
session: FragmentType<typeof FRAGMENT>;
|
||||||
|
isCurrent: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BrowserSession: React.FC<Props> = ({ session }) => {
|
const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
|
||||||
const currentBrowserSessionId = useCurrentBrowserSessionId();
|
|
||||||
const data = useFragment(FRAGMENT, session);
|
const data = useFragment(FRAGMENT, session);
|
||||||
const isCurrent = data.id === currentBrowserSessionId;
|
|
||||||
|
|
||||||
const onSessionEnd = useEndBrowserSession(data.id, isCurrent);
|
const onSessionEnd = useEndBrowserSession(data.id, isCurrent);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
parseUserAgent,
|
parseUserAgent,
|
||||||
sessionNameFromDeviceInformation,
|
sessionNameFromDeviceInformation,
|
||||||
} from "../../utils/parseUserAgent";
|
} from "../../utils/parseUserAgent";
|
||||||
import { useCurrentBrowserSessionId } from "../../utils/session/useCurrentBrowserSessionId";
|
|
||||||
import BlockList from "../BlockList/BlockList";
|
import BlockList from "../BlockList/BlockList";
|
||||||
import { useEndBrowserSession } from "../BrowserSession";
|
import { useEndBrowserSession } from "../BrowserSession";
|
||||||
import DateTime from "../DateTime";
|
import DateTime from "../DateTime";
|
||||||
@@ -53,14 +52,13 @@ const FRAGMENT = graphql(/* GraphQL */ `
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
session: FragmentType<typeof FRAGMENT>;
|
session: FragmentType<typeof FRAGMENT>;
|
||||||
|
isCurrent: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BrowserSessionDetail: React.FC<Props> = ({ session }) => {
|
const BrowserSessionDetail: React.FC<Props> = ({ session, isCurrent }) => {
|
||||||
const data = useFragment(FRAGMENT, session);
|
const data = useFragment(FRAGMENT, session);
|
||||||
const currentBrowserSessionId = useCurrentBrowserSessionId();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const isCurrent = currentBrowserSessionId === data.id;
|
|
||||||
const onSessionEnd = useEndBrowserSession(data.id, isCurrent);
|
const onSessionEnd = useEndBrowserSession(data.id, isCurrent);
|
||||||
|
|
||||||
const deviceInformation = parseUserAgent(data.userAgent || undefined);
|
const deviceInformation = parseUserAgent(data.userAgent || undefined);
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ const documents = {
|
|||||||
types.ResendVerificationEmailDocument,
|
types.ResendVerificationEmailDocument,
|
||||||
"\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserName_user\n ...UserEmailList_user\n }\n }\n }\n":
|
"\n query UserProfileQuery {\n viewer {\n __typename\n ... on User {\n id\n ...UserName_user\n ...UserEmailList_user\n }\n }\n }\n":
|
||||||
types.UserProfileQueryDocument,
|
types.UserProfileQueryDocument,
|
||||||
"\n query SessionDetailQuery($id: ID!) {\n node(id: $id) {\n __typename\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n":
|
"\n query SessionDetailQuery($id: ID!) {\n viewerSession {\n ... on Node {\n id\n }\n }\n\n node(id: $id) {\n __typename\n id\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n":
|
||||||
types.SessionDetailQueryDocument,
|
types.SessionDetailQueryDocument,
|
||||||
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewer {\n __typename\n ... on User {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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":
|
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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 }\n":
|
||||||
types.BrowserSessionListDocument,
|
types.BrowserSessionListDocument,
|
||||||
"\n query SessionsOverviewQuery {\n viewer {\n __typename\n\n ... on User {\n id\n ...BrowserSessionsOverview_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,
|
types.SessionsOverviewQueryDocument,
|
||||||
@@ -255,14 +255,14 @@ export function graphql(
|
|||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n query SessionDetailQuery($id: ID!) {\n node(id: $id) {\n __typename\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n",
|
source: "\n query SessionDetailQuery($id: ID!) {\n viewerSession {\n ... on Node {\n id\n }\n }\n\n node(id: $id) {\n __typename\n id\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n",
|
||||||
): (typeof documents)["\n query SessionDetailQuery($id: ID!) {\n node(id: $id) {\n __typename\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\n }\n }\n"];
|
): (typeof documents)["\n query SessionDetailQuery($id: ID!) {\n viewerSession {\n ... on Node {\n id\n }\n }\n\n node(id: $id) {\n __typename\n id\n ...CompatSession_detail\n ...OAuth2Session_detail\n ...BrowserSession_detail\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(
|
export function graphql(
|
||||||
source: "\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewer {\n __typename\n ... on User {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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",
|
source: "\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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 }\n",
|
||||||
): (typeof documents)["\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewer {\n __typename\n ... on User {\n id\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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"];
|
): (typeof documents)["\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n state: ACTIVE\n ) {\n totalCount\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 }\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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1510,30 +1510,34 @@ export type SessionDetailQueryQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type SessionDetailQueryQuery = {
|
export type SessionDetailQueryQuery = {
|
||||||
__typename?: "Query";
|
__typename?: "Query";
|
||||||
|
viewerSession:
|
||||||
|
| { __typename?: "Anonymous"; id: string }
|
||||||
|
| { __typename?: "BrowserSession"; id: string }
|
||||||
|
| { __typename?: "Oauth2Session"; id: string };
|
||||||
node?:
|
node?:
|
||||||
| { __typename: "Anonymous" }
|
| { __typename: "Anonymous"; id: string }
|
||||||
| { __typename: "Authentication" }
|
| { __typename: "Authentication"; id: string }
|
||||||
| ({ __typename: "BrowserSession" } & {
|
| ({ __typename: "BrowserSession"; id: string } & {
|
||||||
" $fragmentRefs"?: {
|
" $fragmentRefs"?: {
|
||||||
BrowserSession_DetailFragment: BrowserSession_DetailFragment;
|
BrowserSession_DetailFragment: BrowserSession_DetailFragment;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
| ({ __typename: "CompatSession" } & {
|
| ({ __typename: "CompatSession"; id: string } & {
|
||||||
" $fragmentRefs"?: {
|
" $fragmentRefs"?: {
|
||||||
CompatSession_DetailFragment: CompatSession_DetailFragment;
|
CompatSession_DetailFragment: CompatSession_DetailFragment;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
| { __typename: "CompatSsoLogin" }
|
| { __typename: "CompatSsoLogin"; id: string }
|
||||||
| { __typename: "Oauth2Client" }
|
| { __typename: "Oauth2Client"; id: string }
|
||||||
| ({ __typename: "Oauth2Session" } & {
|
| ({ __typename: "Oauth2Session"; id: string } & {
|
||||||
" $fragmentRefs"?: {
|
" $fragmentRefs"?: {
|
||||||
OAuth2Session_DetailFragment: OAuth2Session_DetailFragment;
|
OAuth2Session_DetailFragment: OAuth2Session_DetailFragment;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
| { __typename: "UpstreamOAuth2Link" }
|
| { __typename: "UpstreamOAuth2Link"; id: string }
|
||||||
| { __typename: "UpstreamOAuth2Provider" }
|
| { __typename: "UpstreamOAuth2Provider"; id: string }
|
||||||
| { __typename: "User" }
|
| { __typename: "User"; id: string }
|
||||||
| { __typename: "UserEmail" }
|
| { __typename: "UserEmail"; id: string }
|
||||||
| null;
|
| null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1546,32 +1550,37 @@ export type BrowserSessionListQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type BrowserSessionListQuery = {
|
export type BrowserSessionListQuery = {
|
||||||
__typename?: "Query";
|
__typename?: "Query";
|
||||||
viewer:
|
viewerSession:
|
||||||
| { __typename: "Anonymous" }
|
| { __typename: "Anonymous" }
|
||||||
| {
|
| {
|
||||||
__typename: "User";
|
__typename: "BrowserSession";
|
||||||
id: string;
|
id: string;
|
||||||
browserSessions: {
|
user: {
|
||||||
__typename?: "BrowserSessionConnection";
|
__typename?: "User";
|
||||||
totalCount: number;
|
id: string;
|
||||||
edges: Array<{
|
browserSessions: {
|
||||||
__typename?: "BrowserSessionEdge";
|
__typename?: "BrowserSessionConnection";
|
||||||
cursor: string;
|
totalCount: number;
|
||||||
node: { __typename?: "BrowserSession"; id: string } & {
|
edges: Array<{
|
||||||
" $fragmentRefs"?: {
|
__typename?: "BrowserSessionEdge";
|
||||||
BrowserSession_SessionFragment: BrowserSession_SessionFragment;
|
cursor: string;
|
||||||
|
node: { __typename?: "BrowserSession"; id: string } & {
|
||||||
|
" $fragmentRefs"?: {
|
||||||
|
BrowserSession_SessionFragment: BrowserSession_SessionFragment;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
}>;
|
||||||
|
pageInfo: {
|
||||||
|
__typename?: "PageInfo";
|
||||||
|
hasNextPage: boolean;
|
||||||
|
hasPreviousPage: boolean;
|
||||||
|
startCursor?: string | null;
|
||||||
|
endCursor?: string | null;
|
||||||
};
|
};
|
||||||
}>;
|
|
||||||
pageInfo: {
|
|
||||||
__typename?: "PageInfo";
|
|
||||||
hasNextPage: boolean;
|
|
||||||
hasPreviousPage: boolean;
|
|
||||||
startCursor?: string | null;
|
|
||||||
endCursor?: string | null;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
| { __typename: "Oauth2Session" };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SessionsOverviewQueryQueryVariables = Exact<{
|
export type SessionsOverviewQueryQueryVariables = Exact<{
|
||||||
@@ -3417,6 +3426,28 @@ export const SessionDetailQueryDocument = {
|
|||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "viewerSession" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "InlineFragment",
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "Node" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "node" },
|
name: { kind: "Name", value: "node" },
|
||||||
@@ -3434,6 +3465,7 @@ export const SessionDetailQueryDocument = {
|
|||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
{ kind: "Field", name: { kind: "Name", value: "__typename" } },
|
{ kind: "Field", name: { kind: "Name", value: "__typename" } },
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
{
|
{
|
||||||
kind: "FragmentSpread",
|
kind: "FragmentSpread",
|
||||||
name: { kind: "Name", value: "CompatSession_detail" },
|
name: { kind: "Name", value: "CompatSession_detail" },
|
||||||
@@ -3604,7 +3636,7 @@ export const BrowserSessionListDocument = {
|
|||||||
selections: [
|
selections: [
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "viewer" },
|
name: { kind: "Name", value: "viewerSession" },
|
||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
@@ -3613,7 +3645,7 @@ export const BrowserSessionListDocument = {
|
|||||||
kind: "InlineFragment",
|
kind: "InlineFragment",
|
||||||
typeCondition: {
|
typeCondition: {
|
||||||
kind: "NamedType",
|
kind: "NamedType",
|
||||||
name: { kind: "Name", value: "User" },
|
name: { kind: "Name", value: "BrowserSession" },
|
||||||
},
|
},
|
||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
@@ -3621,117 +3653,140 @@ export const BrowserSessionListDocument = {
|
|||||||
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "browserSessions" },
|
name: { kind: "Name", value: "user" },
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "first" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "first" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "after" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "after" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "last" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "last" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "before" },
|
|
||||||
value: {
|
|
||||||
kind: "Variable",
|
|
||||||
name: { kind: "Name", value: "before" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "state" },
|
|
||||||
value: { kind: "EnumValue", value: "ACTIVE" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "totalCount" },
|
name: { kind: "Name", value: "id" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "edges" },
|
name: { kind: "Name", value: "browserSessions" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "first" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "first" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "after" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "after" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "last" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "last" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "before" },
|
||||||
|
value: {
|
||||||
|
kind: "Variable",
|
||||||
|
name: { kind: "Name", value: "before" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "state" },
|
||||||
|
value: { kind: "EnumValue", value: "ACTIVE" },
|
||||||
|
},
|
||||||
|
],
|
||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "cursor" },
|
name: { kind: "Name", value: "totalCount" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "node" },
|
name: { kind: "Name", value: "edges" },
|
||||||
selectionSet: {
|
selectionSet: {
|
||||||
kind: "SelectionSet",
|
kind: "SelectionSet",
|
||||||
selections: [
|
selections: [
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "id" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "FragmentSpread",
|
|
||||||
name: {
|
name: {
|
||||||
kind: "Name",
|
kind: "Name",
|
||||||
value: "BrowserSession_session",
|
value: "cursor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "node" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value: "id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "FragmentSpread",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value:
|
||||||
|
"BrowserSession_session",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "pageInfo" },
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: {
|
name: { kind: "Name", value: "pageInfo" },
|
||||||
kind: "Name",
|
selectionSet: {
|
||||||
value: "hasNextPage",
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value: "hasNextPage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value: "hasPreviousPage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value: "startCursor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: {
|
||||||
|
kind: "Name",
|
||||||
|
value: "endCursor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: {
|
|
||||||
kind: "Name",
|
|
||||||
value: "hasPreviousPage",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: {
|
|
||||||
kind: "Name",
|
|
||||||
value: "startCursor",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
name: { kind: "Name", value: "endCursor" },
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const router = createRouter({
|
|||||||
routeTree,
|
routeTree,
|
||||||
basepath: config.root,
|
basepath: config.root,
|
||||||
defaultErrorComponent: GenericError,
|
defaultErrorComponent: GenericError,
|
||||||
|
defaultPreload: "intent",
|
||||||
context: { client },
|
context: { client },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,15 @@ export const Route = createFileRoute("/_account/sessions/$id")({
|
|||||||
|
|
||||||
const QUERY = graphql(/* GraphQL */ `
|
const QUERY = graphql(/* GraphQL */ `
|
||||||
query SessionDetailQuery($id: ID!) {
|
query SessionDetailQuery($id: ID!) {
|
||||||
|
viewerSession {
|
||||||
|
... on Node {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node(id: $id) {
|
node(id: $id) {
|
||||||
__typename
|
__typename
|
||||||
|
id
|
||||||
...CompatSession_detail
|
...CompatSession_detail
|
||||||
...OAuth2Session_detail
|
...OAuth2Session_detail
|
||||||
...BrowserSession_detail
|
...BrowserSession_detail
|
||||||
@@ -72,6 +79,7 @@ function SessionDetail(): React.ReactElement {
|
|||||||
if (result.error) throw result.error;
|
if (result.error) throw result.error;
|
||||||
const node = result.data?.node;
|
const node = result.data?.node;
|
||||||
if (!node) throw notFound();
|
if (!node) throw notFound();
|
||||||
|
const currentSessionId = result.data?.viewerSession?.id;
|
||||||
|
|
||||||
switch (node.__typename) {
|
switch (node.__typename) {
|
||||||
case "CompatSession":
|
case "CompatSession":
|
||||||
@@ -79,7 +87,12 @@ function SessionDetail(): React.ReactElement {
|
|||||||
case "Oauth2Session":
|
case "Oauth2Session":
|
||||||
return <OAuth2SessionDetail session={node} />;
|
return <OAuth2SessionDetail session={node} />;
|
||||||
case "BrowserSession":
|
case "BrowserSession":
|
||||||
return <BrowserSessionDetail session={node} />;
|
return (
|
||||||
|
<BrowserSessionDetail
|
||||||
|
session={node}
|
||||||
|
isCurrent={node.id === currentSessionId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown session type");
|
throw new Error("Unknown session type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,32 +37,37 @@ const QUERY = graphql(/* GraphQL */ `
|
|||||||
$last: Int
|
$last: Int
|
||||||
$before: String
|
$before: String
|
||||||
) {
|
) {
|
||||||
viewer {
|
viewerSession {
|
||||||
__typename
|
__typename
|
||||||
... on User {
|
... on BrowserSession {
|
||||||
id
|
id
|
||||||
browserSessions(
|
|
||||||
first: $first
|
|
||||||
after: $after
|
|
||||||
last: $last
|
|
||||||
before: $before
|
|
||||||
state: ACTIVE
|
|
||||||
) {
|
|
||||||
totalCount
|
|
||||||
|
|
||||||
edges {
|
user {
|
||||||
cursor
|
id
|
||||||
node {
|
|
||||||
id
|
browserSessions(
|
||||||
...BrowserSession_session
|
first: $first
|
||||||
|
after: $after
|
||||||
|
last: $last
|
||||||
|
before: $before
|
||||||
|
state: ACTIVE
|
||||||
|
) {
|
||||||
|
totalCount
|
||||||
|
|
||||||
|
edges {
|
||||||
|
cursor
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
...BrowserSession_session
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
hasPreviousPage
|
hasPreviousPage
|
||||||
startCursor
|
startCursor
|
||||||
endCursor
|
endCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +89,8 @@ export const Route = createFileRoute("/_account/sessions/browsers")({
|
|||||||
fetchOptions: { signal },
|
fetchOptions: { signal },
|
||||||
});
|
});
|
||||||
if (result.error) throw result.error;
|
if (result.error) throw result.error;
|
||||||
if (result.data?.viewer?.__typename !== "User") throw notFound();
|
if (result.data?.viewerSession?.__typename !== "BrowserSession")
|
||||||
|
throw notFound();
|
||||||
},
|
},
|
||||||
|
|
||||||
component: BrowserSessions,
|
component: BrowserSessions,
|
||||||
@@ -95,26 +101,30 @@ function BrowserSessions(): React.ReactElement {
|
|||||||
const pagination = Route.useLoaderDeps();
|
const pagination = Route.useLoaderDeps();
|
||||||
const [list] = useQuery({ query: QUERY, variables: pagination });
|
const [list] = useQuery({ query: QUERY, variables: pagination });
|
||||||
if (list.error) throw list.error;
|
if (list.error) throw list.error;
|
||||||
const browserSessions =
|
const currentSession =
|
||||||
list.data?.viewer.__typename === "User"
|
list.data?.viewerSession.__typename === "BrowserSession"
|
||||||
? list.data.viewer.browserSessions
|
? list.data.viewerSession
|
||||||
: null;
|
: null;
|
||||||
if (browserSessions === null) throw notFound();
|
if (currentSession === null) throw notFound();
|
||||||
|
|
||||||
const [backwardPage, forwardPage] = usePages(
|
const [backwardPage, forwardPage] = usePages(
|
||||||
pagination,
|
pagination,
|
||||||
browserSessions.pageInfo,
|
currentSession.user.browserSessions.pageInfo,
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
);
|
);
|
||||||
|
|
||||||
// We reverse the list as we are paginating backwards
|
// We reverse the list as we are paginating backwards
|
||||||
const edges = [...browserSessions.edges].reverse();
|
const edges = [...currentSession.user.browserSessions.edges].reverse();
|
||||||
return (
|
return (
|
||||||
<BlockList>
|
<BlockList>
|
||||||
<H5>{t("frontend.browser_sessions_overview.heading")}</H5>
|
<H5>{t("frontend.browser_sessions_overview.heading")}</H5>
|
||||||
|
|
||||||
{edges.map((n) => (
|
{edges.map((n) => (
|
||||||
<BrowserSession key={n.cursor} session={n.node} />
|
<BrowserSession
|
||||||
|
key={n.cursor}
|
||||||
|
session={n.node}
|
||||||
|
isCurrent={currentSession.id === n.node.id}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="flex *:flex-1">
|
<div className="flex *:flex-1">
|
||||||
|
|||||||
Reference in New Issue
Block a user