diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index e462a7e0..a34d881b 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -1,4 +1,5 @@ import { useLayoutEffect } from "react"; +import { createMemoryRouter, RouterProvider } from "react-router-dom"; import "../src/index.css"; export const parameters = { @@ -57,4 +58,15 @@ const withThemeProvider = (Story, context) => { ); }; -export const decorators = [withThemeProvider]; +const withRouter = (Story, context) => { + const router = createMemoryRouter([ + { + path: "/*", + element: , + }, + ]); + + return ; +}; + +export const decorators = [withThemeProvider, withRouter]; diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index f62b6bbd..e65ab3e0 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -19,6 +19,8 @@ import Layout from "./components/Layout"; import LoadingSpinner from "./components/LoadingSpinner"; const Home = lazy(() => import("./pages/Home")); +const OAuth2Client = lazy(() => import("./pages/OAuth2Client")); +const BrowserSession = lazy(() => import("./pages/BrowserSession")); export const router = createHashRouter([ { @@ -39,6 +41,14 @@ export const router = createHashRouter([ path: "dumb", element: <>Hello from another dumb page., }, + { + path: "client/:id", + element: , + }, + { + path: "session/:id", + element: , + }, ], }, ]); diff --git a/frontend/src/components/BrowserSession.tsx b/frontend/src/components/BrowserSession.tsx index cd7c75bd..f6c55cad 100644 --- a/frontend/src/components/BrowserSession.tsx +++ b/frontend/src/components/BrowserSession.tsx @@ -18,6 +18,7 @@ import { graphql, useFragment } from "react-relay"; import Block from "./Block"; import { Body, Subtitle } from "./Typography"; import DateTime from "./DateTime"; +import { Link } from "react-router-dom"; type Props = { session: BrowserSession_session$key; @@ -46,7 +47,12 @@ const BrowserSession: React.FC = ({ session, isCurrent }) => { {isCurrent && Current session} - Started: + + Started: + Last authentication:{" "} diff --git a/frontend/src/components/NavItem.tsx b/frontend/src/components/NavItem.tsx index acea1b13..7d2b4491 100644 --- a/frontend/src/components/NavItem.tsx +++ b/frontend/src/components/NavItem.tsx @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { NavLink } from "react-router-dom"; +import { NavLink, To } from "react-router-dom"; -const NavItem: React.FC<{ to: string; children: React.ReactNode }> = ({ +const NavItem: React.FC<{ to: To; children: React.ReactNode }> = ({ to, children, }) => ( diff --git a/frontend/src/components/OAuth2Session.tsx b/frontend/src/components/OAuth2Session.tsx index af9cfeef..8b7377a1 100644 --- a/frontend/src/components/OAuth2Session.tsx +++ b/frontend/src/components/OAuth2Session.tsx @@ -16,6 +16,7 @@ import type { OAuth2Session_session$key } from "./__generated__/OAuth2Session_se import { graphql, useFragment } from "react-relay"; import { Body, Bold, Code } from "./Typography"; import Block from "./Block"; +import { Link } from "react-router-dom"; type Props = { session: OAuth2Session_session$key; @@ -41,7 +42,12 @@ const OAuth2Session: React.FC = ({ session }) => { return ( - Client ID: {data.client.clientId} + + Client ID: {data.client.clientId} + {data.client.clientName && ( diff --git a/frontend/src/pages/BrowserSession.tsx b/frontend/src/pages/BrowserSession.tsx new file mode 100644 index 00000000..e9535964 --- /dev/null +++ b/frontend/src/pages/BrowserSession.tsx @@ -0,0 +1,53 @@ +// 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 { graphql, useLazyLoadQuery } from "react-relay"; +import { useParams } from "react-router-dom"; + +import type { BrowserSessionQuery } from "./__generated__/BrowserSessionQuery.graphql"; + +const BrowserSession: React.FC = () => { + const { id } = useParams(); + if (!id) { + throw new Error("Missing parameter"); + } + + const data = useLazyLoadQuery( + graphql` + query BrowserSessionQuery($id: ID!) { + browserSession(id: $id) { + id + createdAt + lastAuthentication { + id + createdAt + } + user { + id + username + } + } + } + `, + { id } + ); + + return ( +
+      {JSON.stringify(data.browserSession, null, 2)}
+    
+ ); +}; + +export default BrowserSession; diff --git a/frontend/src/pages/OAuth2Client.tsx b/frontend/src/pages/OAuth2Client.tsx new file mode 100644 index 00000000..d353b4d2 --- /dev/null +++ b/frontend/src/pages/OAuth2Client.tsx @@ -0,0 +1,50 @@ +// 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 { graphql, useLazyLoadQuery } from "react-relay"; +import { useParams } from "react-router-dom"; + +import type { OAuth2ClientQuery } from "./__generated__/OAuth2ClientQuery.graphql"; + +const OAuth2Client: React.FC = () => { + const { id } = useParams(); + if (!id) { + throw new Error("Missing parameter"); + } + + const data = useLazyLoadQuery( + graphql` + query OAuth2ClientQuery($id: ID!) { + oauth2Client(id: $id) { + id + clientId + clientName + clientUri + tosUri + policyUri + redirectUris + } + } + `, + { id } + ); + + return ( +
+      {JSON.stringify(data.oauth2Client, null, 2)}
+    
+ ); +}; + +export default OAuth2Client; diff --git a/frontend/src/pages/__generated__/BrowserSessionQuery.graphql.ts b/frontend/src/pages/__generated__/BrowserSessionQuery.graphql.ts new file mode 100644 index 00000000..dc06d026 --- /dev/null +++ b/frontend/src/pages/__generated__/BrowserSessionQuery.graphql.ts @@ -0,0 +1,139 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest, Query } from 'relay-runtime'; +export type BrowserSessionQuery$variables = { + id: string; +}; +export type BrowserSessionQuery$data = { + readonly browserSession: { + readonly createdAt: any; + readonly id: string; + readonly lastAuthentication: { + readonly createdAt: any; + readonly id: string; + } | null; + readonly user: { + readonly id: string; + readonly username: string; + }; + } | null; +}; +export type BrowserSessionQuery = { + response: BrowserSessionQuery$data; + variables: BrowserSessionQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null +}, +v3 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": "BrowserSession", + "kind": "LinkedField", + "name": "browserSession", + "plural": false, + "selections": [ + (v1/*: any*/), + (v2/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Authentication", + "kind": "LinkedField", + "name": "lastAuthentication", + "plural": false, + "selections": [ + (v1/*: any*/), + (v2/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "user", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "BrowserSessionQuery", + "selections": (v3/*: any*/), + "type": "RootQuery", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "BrowserSessionQuery", + "selections": (v3/*: any*/) + }, + "params": { + "cacheID": "5374afccfa4da28a64cdce6585ac1907", + "id": null, + "metadata": {}, + "name": "BrowserSessionQuery", + "operationKind": "query", + "text": "query BrowserSessionQuery(\n $id: ID!\n) {\n browserSession(id: $id) {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n user {\n id\n username\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "c73293a99a0214448861bed340594304"; + +export default node; diff --git a/frontend/src/pages/__generated__/OAuth2ClientQuery.graphql.ts b/frontend/src/pages/__generated__/OAuth2ClientQuery.graphql.ts new file mode 100644 index 00000000..ad9bcca3 --- /dev/null +++ b/frontend/src/pages/__generated__/OAuth2ClientQuery.graphql.ts @@ -0,0 +1,137 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest, Query } from 'relay-runtime'; +export type OAuth2ClientQuery$variables = { + id: string; +}; +export type OAuth2ClientQuery$data = { + readonly oauth2Client: { + readonly clientId: string; + readonly clientName: string | null; + readonly clientUri: any | null; + readonly id: string; + readonly policyUri: any | null; + readonly redirectUris: ReadonlyArray; + readonly tosUri: any | null; + } | null; +}; +export type OAuth2ClientQuery = { + response: OAuth2ClientQuery$data; + variables: OAuth2ClientQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": "Oauth2Client", + "kind": "LinkedField", + "name": "oauth2Client", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientName", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientUri", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "tosUri", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "policyUri", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "redirectUris", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "OAuth2ClientQuery", + "selections": (v1/*: any*/), + "type": "RootQuery", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "OAuth2ClientQuery", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "49e24d5c368a8c2c148643b971fe179c", + "id": null, + "metadata": {}, + "name": "OAuth2ClientQuery", + "operationKind": "query", + "text": "query OAuth2ClientQuery(\n $id: ID!\n) {\n oauth2Client(id: $id) {\n id\n clientId\n clientName\n clientUri\n tosUri\n policyUri\n redirectUris\n }\n}\n" + } +}; +})(); + +(node as any).hash = "31b4804eb435b5822480ff57775d13a4"; + +export default node;