Create a context to store authentication
This commit is contained in:
parent
10e5c124fd
commit
1934354665
23
geneit_app/package-lock.json
generated
23
geneit_app/package-lock.json
generated
@ -23,7 +23,6 @@
|
||||
"@types/react": "^18.2.8",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"date-and-time": "^3.0.1",
|
||||
"jotai": "^2.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.11.2",
|
||||
@ -11966,22 +11965,6 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.1.1.tgz",
|
||||
"integrity": "sha512-LaaiuSaq+6XkwkrCtCkczyFVZOXe0dfjAFN4DVMsSZSRv/A/4xuLHnlpHMEDqvngjWYBotTIrnQ7OogMkUE6wA==",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@ -26267,12 +26250,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
||||
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg=="
|
||||
},
|
||||
"jotai": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.1.1.tgz",
|
||||
"integrity": "sha512-LaaiuSaq+6XkwkrCtCkczyFVZOXe0dfjAFN4DVMsSZSRv/A/4xuLHnlpHMEDqvngjWYBotTIrnQ7OogMkUE6wA==",
|
||||
"requires": {}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
@ -18,7 +18,6 @@
|
||||
"@types/react": "^18.2.8",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"date-and-time": "^3.0.1",
|
||||
"jotai": "^2.1.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.11.2",
|
||||
|
@ -1,24 +1,37 @@
|
||||
import React from "react";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import "./App.css";
|
||||
import { AuthApi } from "./api/AuthApi";
|
||||
import { NotFoundRoute } from "./routes/NotFound";
|
||||
import { BaseLoginPage } from "./widgets/BaseLoginpage";
|
||||
import { ProfileRoute } from "./routes/ProfileRoute";
|
||||
import { LoginRoute } from "./routes/auth/LoginRoute";
|
||||
import { NewAccountRoute } from "./routes/auth/NewAccountRoute";
|
||||
import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute";
|
||||
import { useAtom } from "jotai";
|
||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
||||
import { PasswordForgottenRoute } from "./routes/auth/PasswordForgottenRoute";
|
||||
import { ResetPasswordRoute } from "./routes/auth/ResetPasswordRoute";
|
||||
import { NewAccountRoute } from "./routes/auth/NewAccountRoute";
|
||||
import { ProfileRoute } from "./routes/ProfileRoute";
|
||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
||||
import { BaseLoginPage } from "./widgets/BaseLoginpage";
|
||||
|
||||
interface AuthContext {
|
||||
signedIn: boolean;
|
||||
setSignedIn: (signedIn: boolean) => void;
|
||||
}
|
||||
|
||||
const AuthContextK = React.createContext<AuthContext | null>(null);
|
||||
|
||||
/**
|
||||
* Core app
|
||||
*/
|
||||
function App() {
|
||||
const [signedIn] = useAtom(AuthApi.authStatus);
|
||||
export function App(): React.ReactElement {
|
||||
const [signedIn, setSignedIn] = React.useState(AuthApi.SignedIn);
|
||||
|
||||
const context: AuthContext = {
|
||||
signedIn: signedIn,
|
||||
setSignedIn: (s) => setSignedIn(s),
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContextK.Provider value={context}>
|
||||
<Routes>
|
||||
{signedIn ? (
|
||||
<Route path="*" element={<BaseAuthenticatedPage />}>
|
||||
@ -39,7 +52,10 @@ function App() {
|
||||
</Route>
|
||||
)}
|
||||
</Routes>
|
||||
</AuthContextK.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export function useAuth(): AuthContext {
|
||||
return React.useContext(AuthContextK)!;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { atom } from "jotai";
|
||||
import { APIClient } from "./ApiClient";
|
||||
|
||||
export enum CreateAccountResult {
|
||||
@ -30,8 +29,6 @@ export class AuthApi {
|
||||
return localStorage.getItem(TokenStateKey) !== null;
|
||||
}
|
||||
|
||||
static authStatus = atom(this.SignedIn);
|
||||
|
||||
/**
|
||||
* Get user auth token
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { App } from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { ServerApi } from "./api/ServerApi";
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||
import {
|
||||
Alert,
|
||||
CircularProgress,
|
||||
@ -14,11 +15,10 @@ import Grid from "@mui/material/Grid";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import * as React from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../../App";
|
||||
import { AuthApi, PasswordLoginResult } from "../../api/AuthApi";
|
||||
import { ServerApi } from "../../api/ServerApi";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { VisibilityOff, Visibility } from "@mui/icons-material";
|
||||
import { useSetAtom } from "jotai";
|
||||
|
||||
/**
|
||||
* Login form
|
||||
@ -27,7 +27,7 @@ export function LoginRoute(): React.ReactElement {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const setAuth = useSetAtom(AuthApi.authStatus);
|
||||
const auth = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [mail, setMail] = React.useState("");
|
||||
@ -64,7 +64,7 @@ export function LoginRoute(): React.ReactElement {
|
||||
|
||||
case PasswordLoginResult.Success:
|
||||
navigate("/");
|
||||
setAuth(true);
|
||||
auth.setSignedIn(true);
|
||||
break;
|
||||
|
||||
case PasswordLoginResult.Error:
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { CircularProgress } from "@mui/material";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { AuthApi } from "../../api/AuthApi";
|
||||
import { AuthSingleMessage } from "../../widgets/AuthSingleMessage";
|
||||
import { useAuth } from "../../App";
|
||||
|
||||
/**
|
||||
* OpenID login callback route
|
||||
*/
|
||||
export function OIDCCbRoute(): React.ReactElement {
|
||||
const setAuth = useSetAtom(AuthApi.authStatus);
|
||||
const auth = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [error, setError] = useState(false);
|
||||
@ -30,7 +30,7 @@ export function OIDCCbRoute(): React.ReactElement {
|
||||
|
||||
await AuthApi.FinishOpenIDLogin(code!, state!);
|
||||
navigate("/");
|
||||
setAuth(true);
|
||||
auth.setSignedIn(true);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(true);
|
||||
|
@ -6,18 +6,13 @@ import {
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
} from "@mui/material";
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useContext,
|
||||
useRef,
|
||||
} from "react";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
||||
interface AlertHook {
|
||||
interface AlertContext {
|
||||
alert: (message: string, title?: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const AlertContext = createContext<AlertHook | null>(null);
|
||||
const AlertContextK = React.createContext<AlertContext | null>(null);
|
||||
|
||||
export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
@ -34,7 +29,7 @@ export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||
cb.current = null;
|
||||
};
|
||||
|
||||
const hook: AlertHook = {
|
||||
const hook: AlertContext = {
|
||||
alert: (message, title) => {
|
||||
setTitle(title);
|
||||
setMessage(message);
|
||||
@ -48,7 +43,7 @@ export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AlertContext.Provider value={hook}>{p.children}</AlertContext.Provider>
|
||||
<AlertContextK.Provider value={hook}>{p.children}</AlertContextK.Provider>
|
||||
|
||||
<Dialog
|
||||
open={open}
|
||||
@ -72,6 +67,6 @@ export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
export function useAlert(): AlertHook {
|
||||
return useContext(AlertContext)!;
|
||||
export function useAlert(): AlertContext {
|
||||
return React.useContext(AlertContextK)!;
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useSetAtom } from "jotai";
|
||||
import * as React from "react";
|
||||
import { Link, Outlet, useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../App";
|
||||
import { AuthApi } from "../api/AuthApi";
|
||||
import { User, UserApi } from "../api/UserApi";
|
||||
import { AsyncWidget } from "./AsyncWidget";
|
||||
@ -18,7 +18,7 @@ import { RouterLink } from "./RouterLink";
|
||||
export function BaseAuthenticatedPage(): React.ReactElement {
|
||||
const [user, setUser] = React.useState<null | User>(null);
|
||||
|
||||
const setSignedIn = useSetAtom(AuthApi.authStatus);
|
||||
const auth = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
@ -39,7 +39,7 @@ export function BaseAuthenticatedPage(): React.ReactElement {
|
||||
handleCloseMenu();
|
||||
AuthApi.SignOut();
|
||||
navigate("/");
|
||||
setSignedIn(false);
|
||||
auth.setSignedIn(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -6,14 +6,9 @@ import {
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
} from "@mui/material";
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useContext,
|
||||
useRef,
|
||||
} from "react";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
||||
interface ConfirmHook {
|
||||
interface ConfirmContext {
|
||||
confirm: (
|
||||
message: string,
|
||||
title?: string,
|
||||
@ -21,7 +16,7 @@ interface ConfirmHook {
|
||||
) => Promise<boolean>;
|
||||
}
|
||||
|
||||
const ConfirmContext = createContext<ConfirmHook | null>(null);
|
||||
const ConfirmContextK = React.createContext<ConfirmContext | null>(null);
|
||||
|
||||
export function ConfirmDialogProvider(
|
||||
p: PropsWithChildren
|
||||
@ -43,7 +38,7 @@ export function ConfirmDialogProvider(
|
||||
cb.current = null;
|
||||
};
|
||||
|
||||
const hook: ConfirmHook = {
|
||||
const hook: ConfirmContext = {
|
||||
confirm: (message, title, confirmButton) => {
|
||||
setTitle(title);
|
||||
setMessage(message);
|
||||
@ -58,9 +53,9 @@ export function ConfirmDialogProvider(
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmContext.Provider value={hook}>
|
||||
<ConfirmContextK.Provider value={hook}>
|
||||
{p.children}
|
||||
</ConfirmContext.Provider>
|
||||
</ConfirmContextK.Provider>
|
||||
|
||||
<Dialog
|
||||
open={open}
|
||||
@ -87,6 +82,6 @@ export function ConfirmDialogProvider(
|
||||
);
|
||||
}
|
||||
|
||||
export function useConfirm(): ConfirmHook {
|
||||
return useContext(ConfirmContext)!;
|
||||
export function useConfirm(): ConfirmContext {
|
||||
return React.useContext(ConfirmContextK)!;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user