✨ OAuth & Auth Guard
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							@@ -16,6 +16,7 @@ import AppShell from "@/components/AppShell.tsx";
 | 
				
			|||||||
import LandingPage from "@/pages/landing.tsx";
 | 
					import LandingPage from "@/pages/landing.tsx";
 | 
				
			||||||
import SignUpPage from "@/pages/auth/sign-up.tsx";
 | 
					import SignUpPage from "@/pages/auth/sign-up.tsx";
 | 
				
			||||||
import SignInPage from "@/pages/auth/sign-in.tsx";
 | 
					import SignInPage from "@/pages/auth/sign-in.tsx";
 | 
				
			||||||
 | 
					import OauthConnectPage from "@/pages/auth/connect.tsx";
 | 
				
			||||||
import DashboardPage from "@/pages/users/dashboard.tsx";
 | 
					import DashboardPage from "@/pages/users/dashboard.tsx";
 | 
				
			||||||
import ErrorBoundary from "@/error.tsx";
 | 
					import ErrorBoundary from "@/error.tsx";
 | 
				
			||||||
import AppLoader from "@/components/AppLoader.tsx";
 | 
					import AppLoader from "@/components/AppLoader.tsx";
 | 
				
			||||||
@@ -25,6 +26,8 @@ import PersonalizePage from "@/pages/users/personalize.tsx";
 | 
				
			|||||||
import SecurityPage from "@/pages/users/security.tsx";
 | 
					import SecurityPage from "@/pages/users/security.tsx";
 | 
				
			||||||
import { UserinfoProvider } from "@/stores/userinfo.tsx";
 | 
					import { UserinfoProvider } from "@/stores/userinfo.tsx";
 | 
				
			||||||
import { WellKnownProvider } from "@/stores/wellKnown.tsx";
 | 
					import { WellKnownProvider } from "@/stores/wellKnown.tsx";
 | 
				
			||||||
 | 
					import AuthLayout from "@/pages/auth/layout.tsx";
 | 
				
			||||||
 | 
					import AuthGuard from "@/pages/guard.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare const __GARFISH_EXPORTS__: {
 | 
					declare const __GARFISH_EXPORTS__: {
 | 
				
			||||||
  provider: Object;
 | 
					  provider: Object;
 | 
				
			||||||
@@ -44,6 +47,10 @@ const router = createBrowserRouter([
 | 
				
			|||||||
    errorElement: <ErrorBoundary />,
 | 
					    errorElement: <ErrorBoundary />,
 | 
				
			||||||
    children: [
 | 
					    children: [
 | 
				
			||||||
      { path: "/", element: <LandingPage /> },
 | 
					      { path: "/", element: <LandingPage /> },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        path: "/",
 | 
				
			||||||
 | 
					        element: <AuthGuard />,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            path: "/users",
 | 
					            path: "/users",
 | 
				
			||||||
            element: <UserLayout />,
 | 
					            element: <UserLayout />,
 | 
				
			||||||
@@ -55,9 +62,19 @@ const router = createBrowserRouter([
 | 
				
			|||||||
            ]
 | 
					            ]
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: "/auth",
 | 
				
			||||||
 | 
					    element: <AuthLayout />,
 | 
				
			||||||
 | 
					    errorElement: <ErrorBoundary />,
 | 
				
			||||||
 | 
					    children: [
 | 
				
			||||||
      { path: "/auth/sign-up", element: <SignUpPage />, errorElement: <ErrorBoundary /> },
 | 
					      { path: "/auth/sign-up", element: <SignUpPage />, errorElement: <ErrorBoundary /> },
 | 
				
			||||||
  { path: "/auth/sign-in", element: <SignInPage />, errorElement: <ErrorBoundary /> }
 | 
					      { path: "/auth/sign-in", element: <SignInPage />, errorElement: <ErrorBoundary /> },
 | 
				
			||||||
 | 
					      { path: "/auth/o/connect", element: <OauthConnectPage />, errorElement: <ErrorBoundary /> }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const element = (
 | 
					const element = (
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										182
									
								
								pkg/views/src/pages/auth/connect.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								pkg/views/src/pages/auth/connect.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Alert,
 | 
				
			||||||
 | 
					  Avatar,
 | 
				
			||||||
 | 
					  Box,
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  Card,
 | 
				
			||||||
 | 
					  CardContent,
 | 
				
			||||||
 | 
					  Collapse,
 | 
				
			||||||
 | 
					  Grid,
 | 
				
			||||||
 | 
					  LinearProgress,
 | 
				
			||||||
 | 
					  Typography
 | 
				
			||||||
 | 
					} from "@mui/material";
 | 
				
			||||||
 | 
					import { request } from "@/scripts/request.ts";
 | 
				
			||||||
 | 
					import { useUserinfo } from "@/stores/userinfo.tsx";
 | 
				
			||||||
 | 
					import { useSearchParams } from "react-router-dom";
 | 
				
			||||||
 | 
					import OutletIcon from "@mui/icons-material/Outlet";
 | 
				
			||||||
 | 
					import WhatshotIcon from "@mui/icons-material/Whatshot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function OauthConnectPage() {
 | 
				
			||||||
 | 
					  const { getAtk } = useUserinfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [panel, setPanel] = useState(0);
 | 
				
			||||||
 | 
					  const [error, setError] = useState<string | null>(null);
 | 
				
			||||||
 | 
					  const [loading, setLoading] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [client, setClient] = useState<any>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [searchParams] = useSearchParams();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function preconnect() {
 | 
				
			||||||
 | 
					    const res = await request(`/api/auth/o/connect${location.search}`, {
 | 
				
			||||||
 | 
					      headers: { "Authorization": `Bearer ${getAtk()}` }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (res.status !== 200) {
 | 
				
			||||||
 | 
					      setError(await res.text());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (data["session"]) {
 | 
				
			||||||
 | 
					        setPanel(1);
 | 
				
			||||||
 | 
					        redirect(data["session"]);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        setClient(data["client"]);
 | 
				
			||||||
 | 
					        setLoading(false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    preconnect().then(() => console.log("Fetched metadata"));
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function decline() {
 | 
				
			||||||
 | 
					    if (window.history.length > 0) {
 | 
				
			||||||
 | 
					      window.history.back();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      window.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function approve() {
 | 
				
			||||||
 | 
					    setLoading(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const res = await request("/api/auth/o/connect?" + new URLSearchParams({
 | 
				
			||||||
 | 
					      client_id: searchParams.get("client_id") as string,
 | 
				
			||||||
 | 
					      redirect_uri: encodeURIComponent(searchParams.get("redirect_uri") as string),
 | 
				
			||||||
 | 
					      response_type: "code",
 | 
				
			||||||
 | 
					      scope: searchParams.get("scope") as string
 | 
				
			||||||
 | 
					    }), {
 | 
				
			||||||
 | 
					      method: "POST",
 | 
				
			||||||
 | 
					      headers: { "Authorization": `Bearer ${getAtk()}` }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (res.status !== 200) {
 | 
				
			||||||
 | 
					      setError(await res.text());
 | 
				
			||||||
 | 
					      setLoading(false);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
 | 
					      setPanel(1);
 | 
				
			||||||
 | 
					      setTimeout(() => redirect(data["session"]), 1850);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function redirect(session: any) {
 | 
				
			||||||
 | 
					    const url = `${searchParams.get("redirect_uri")}?code=${session["grant_token"]}&state=${searchParams.get("state")}`;
 | 
				
			||||||
 | 
					    window.open(url, "_self");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const elements = [
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      <>
 | 
				
			||||||
 | 
					        <Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
 | 
				
			||||||
 | 
					          <OutletIcon />
 | 
				
			||||||
 | 
					        </Avatar>
 | 
				
			||||||
 | 
					        <Typography component="h1" variant="h5">
 | 
				
			||||||
 | 
					          Sign in to {client?.name}
 | 
				
			||||||
 | 
					        </Typography>
 | 
				
			||||||
 | 
					        <Box sx={{ mt: 3, width: "100%" }}>
 | 
				
			||||||
 | 
					          <Grid container spacing={2}>
 | 
				
			||||||
 | 
					            <Grid item xs={12}>
 | 
				
			||||||
 | 
					              <Typography fontWeight="bold">About this app</Typography>
 | 
				
			||||||
 | 
					              <Typography variant="body2">{client?.description}</Typography>
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					            <Grid item xs={12}>
 | 
				
			||||||
 | 
					              <Typography fontWeight="bold">Make you trust this app</Typography>
 | 
				
			||||||
 | 
					              <Typography variant="body2">
 | 
				
			||||||
 | 
					                After you click Approve button, you will share your basic personal information to this application
 | 
				
			||||||
 | 
					                developer. Some of them will leak your data. Think twice.
 | 
				
			||||||
 | 
					              </Typography>
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					            <Grid item xs={12} md={6}>
 | 
				
			||||||
 | 
					              <Button
 | 
				
			||||||
 | 
					                fullWidth
 | 
				
			||||||
 | 
					                color="info"
 | 
				
			||||||
 | 
					                variant="outlined"
 | 
				
			||||||
 | 
					                disabled={loading}
 | 
				
			||||||
 | 
					                sx={{ mt: 3 }}
 | 
				
			||||||
 | 
					                onClick={() => decline()}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Decline
 | 
				
			||||||
 | 
					              </Button>
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					            <Grid item xs={12} md={6}>
 | 
				
			||||||
 | 
					              <Button
 | 
				
			||||||
 | 
					                fullWidth
 | 
				
			||||||
 | 
					                variant="outlined"
 | 
				
			||||||
 | 
					                disabled={loading}
 | 
				
			||||||
 | 
					                sx={{ mt: 3 }}
 | 
				
			||||||
 | 
					                onClick={() => approve()}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Approve
 | 
				
			||||||
 | 
					              </Button>
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					          </Grid>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      <>
 | 
				
			||||||
 | 
					        <Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
 | 
				
			||||||
 | 
					          <WhatshotIcon />
 | 
				
			||||||
 | 
					        </Avatar>
 | 
				
			||||||
 | 
					        <Typography component="h1" variant="h5">
 | 
				
			||||||
 | 
					          Authorized
 | 
				
			||||||
 | 
					        </Typography>
 | 
				
			||||||
 | 
					        <Box sx={{ mt: 3, width: "100%", textAlign: "center" }}>
 | 
				
			||||||
 | 
					          <Grid container spacing={2}>
 | 
				
			||||||
 | 
					            <Grid item xs={12} sx={{ my: 8 }}>
 | 
				
			||||||
 | 
					              <Typography variant="h6">Now Redirecting...</Typography>
 | 
				
			||||||
 | 
					              <Typography>Hold on a second, we are going to redirect you to the target.</Typography>
 | 
				
			||||||
 | 
					            </Grid>
 | 
				
			||||||
 | 
					          </Grid>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      {error && <Alert severity="error" className="capitalize" sx={{ mb: 2 }}>{error}</Alert>}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card variant="outlined">
 | 
				
			||||||
 | 
					        <Collapse in={loading}>
 | 
				
			||||||
 | 
					          <LinearProgress />
 | 
				
			||||||
 | 
					        </Collapse>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <CardContent
 | 
				
			||||||
 | 
					          style={{ padding: "40px 48px 36px" }}
 | 
				
			||||||
 | 
					          sx={{
 | 
				
			||||||
 | 
					            display: "flex",
 | 
				
			||||||
 | 
					            flexDirection: "column",
 | 
				
			||||||
 | 
					            alignItems: "center"
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {elements[panel]}
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								pkg/views/src/pages/auth/layout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								pkg/views/src/pages/auth/layout.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import { Box } from "@mui/material";
 | 
				
			||||||
 | 
					import { Outlet } from "react-router-dom";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function AuthLayout() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Box sx={{ height: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}>
 | 
				
			||||||
 | 
					      <Box style={{ width: "100vw", maxWidth: "450px" }}>
 | 
				
			||||||
 | 
					        <Outlet />
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -277,10 +277,15 @@ export default function SignInPage() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Box sx={{ height: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}>
 | 
					    <>
 | 
				
			||||||
      <Box style={{ width: "100vw", maxWidth: "450px" }}>
 | 
					 | 
				
			||||||
      {error && <Alert severity="error" className="capitalize" sx={{ mb: 2 }}>{error}</Alert>}
 | 
					      {error && <Alert severity="error" className="capitalize" sx={{ mb: 2 }}>{error}</Alert>}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Collapse in={searchParams.has("redirect_uri")}>
 | 
				
			||||||
 | 
					        <Alert severity="info" sx={{ mb: 2 }}>
 | 
				
			||||||
 | 
					          You need sign in before take an action. After that, we will take you back to your work.
 | 
				
			||||||
 | 
					        </Alert>
 | 
				
			||||||
 | 
					      </Collapse>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Card variant="outlined">
 | 
					      <Card variant="outlined">
 | 
				
			||||||
        <Collapse in={loading}>
 | 
					        <Collapse in={loading}>
 | 
				
			||||||
          <LinearProgress />
 | 
					          <LinearProgress />
 | 
				
			||||||
@@ -321,7 +326,6 @@ export default function SignInPage() {
 | 
				
			|||||||
          </Link>
 | 
					          </Link>
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
      </Grid>
 | 
					      </Grid>
 | 
				
			||||||
      </Box>
 | 
					    </>
 | 
				
			||||||
    </Box>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -166,8 +166,7 @@ export default function SignUpPage() {
 | 
				
			|||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Box sx={{ height: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}>
 | 
					    <>
 | 
				
			||||||
      <Box style={{ width: "100vw", maxWidth: "450px" }}>
 | 
					 | 
				
			||||||
      {error && <Alert severity="error" className="capitalize" sx={{ mb: 2 }}>{error}</Alert>}
 | 
					      {error && <Alert severity="error" className="capitalize" sx={{ mb: 2 }}>{error}</Alert>}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Card variant="outlined">
 | 
					      <Card variant="outlined">
 | 
				
			||||||
@@ -194,7 +193,6 @@ export default function SignUpPage() {
 | 
				
			|||||||
          </Link>
 | 
					          </Link>
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
      </Grid>
 | 
					      </Grid>
 | 
				
			||||||
      </Box>
 | 
					    </>
 | 
				
			||||||
    </Box>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								pkg/views/src/pages/guard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								pkg/views/src/pages/guard.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { useEffect } from "react";
 | 
				
			||||||
 | 
					import { Box, CircularProgress } from "@mui/material";
 | 
				
			||||||
 | 
					import { Outlet, useLocation, useNavigate } from "react-router-dom";
 | 
				
			||||||
 | 
					import { useUserinfo } from "@/stores/userinfo.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function AuthGuard() {
 | 
				
			||||||
 | 
					  const { userinfo } = useUserinfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const navigate = useNavigate();
 | 
				
			||||||
 | 
					  const location = useLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    console.log(userinfo)
 | 
				
			||||||
 | 
					    if (userinfo?.isReady) {
 | 
				
			||||||
 | 
					      if (!userinfo?.isLoggedIn) {
 | 
				
			||||||
 | 
					        const callback = location.pathname + location.search;
 | 
				
			||||||
 | 
					        navigate({ pathname: "/auth/sign-in", search: `redirect_uri=${callback}` });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [userinfo]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return !userinfo?.isReady ? (
 | 
				
			||||||
 | 
					    <Box sx={{ pt: 32, display: "flex", justifyContent: "center", alignItems: "center" }}>
 | 
				
			||||||
 | 
					      <Box>
 | 
				
			||||||
 | 
					        <CircularProgress />
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					  ) : <Outlet />;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,15 +3,17 @@ import { request } from "../scripts/request.ts";
 | 
				
			|||||||
import { createContext, useContext, useState } from "react";
 | 
					import { createContext, useContext, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Userinfo {
 | 
					export interface Userinfo {
 | 
				
			||||||
 | 
					  isReady: boolean,
 | 
				
			||||||
  isLoggedIn: boolean,
 | 
					  isLoggedIn: boolean,
 | 
				
			||||||
  displayName: string,
 | 
					  displayName: string,
 | 
				
			||||||
  data: any,
 | 
					  data: any,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultUserinfo: Userinfo = {
 | 
					const defaultUserinfo: Userinfo = {
 | 
				
			||||||
 | 
					  isReady: false,
 | 
				
			||||||
  isLoggedIn: false,
 | 
					  isLoggedIn: false,
 | 
				
			||||||
  displayName: "Citizen",
 | 
					  displayName: "Citizen",
 | 
				
			||||||
  data: null,
 | 
					  data: null
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const UserinfoContext = createContext<any>({ userinfo: defaultUserinfo });
 | 
					const UserinfoContext = createContext<any>({ userinfo: defaultUserinfo });
 | 
				
			||||||
@@ -28,10 +30,15 @@ export function UserinfoProvider(props: any) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function readProfiles() {
 | 
					  async function readProfiles() {
 | 
				
			||||||
    if (!checkLoggedIn()) return;
 | 
					    if (!checkLoggedIn()) {
 | 
				
			||||||
 | 
					      setUserinfo((data) => {
 | 
				
			||||||
 | 
					        data.isReady = true;
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const res = await request("/api/users/me", {
 | 
					    const res = await request("/api/users/me", {
 | 
				
			||||||
      credentials: "include"
 | 
					      headers: { "Authorization": `Bearer ${getAtk()}` }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (res.status !== 200) {
 | 
					    if (res.status !== 200) {
 | 
				
			||||||
@@ -42,6 +49,7 @@ export function UserinfoProvider(props: any) {
 | 
				
			|||||||
    const data = await res.json();
 | 
					    const data = await res.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setUserinfo({
 | 
					    setUserinfo({
 | 
				
			||||||
 | 
					      isReady: true,
 | 
				
			||||||
      isLoggedIn: true,
 | 
					      isLoggedIn: true,
 | 
				
			||||||
      displayName: data["nick"],
 | 
					      displayName: data["nick"],
 | 
				
			||||||
      data: data
 | 
					      data: data
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user