Compare commits
3 Commits
c7530dcbab
...
acbb878811
| Author | SHA1 | Date | |
|---|---|---|---|
| acbb878811 | |||
| 852331f909 | |||
| c6b6d316cf |
62
app/components/guard.tsx
Normal file
62
app/components/guard.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import type { PropsWithChildren } from "react";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import { useAuth } from "~/hooks/auth";
|
||||||
|
import { Button, Modal, Spinner } from "@heroui/react";
|
||||||
|
|
||||||
|
export default function Guard({ children }: PropsWithChildren) {
|
||||||
|
const { user, loading, login, logout } = useAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col flex-1 h-full w-full justify-center items-center">
|
||||||
|
<Spinner color="current" size="xl" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user !== null) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="h-dvh w-dvw blur-2xl">
|
||||||
|
<Modal>
|
||||||
|
<Modal.Backdrop
|
||||||
|
isDismissable={false}
|
||||||
|
isKeyboardDismissDisabled
|
||||||
|
isOpen
|
||||||
|
>
|
||||||
|
<Modal.Container>
|
||||||
|
<Modal.Dialog>
|
||||||
|
<Modal.Header>
|
||||||
|
<Modal.Heading>
|
||||||
|
Login required.
|
||||||
|
</Modal.Heading>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<p>
|
||||||
|
To view this page, you need to be logged
|
||||||
|
in with your Steam account.
|
||||||
|
</p>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(-1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={login}>Login</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal.Dialog>
|
||||||
|
</Modal.Container>
|
||||||
|
</Modal.Backdrop>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,25 @@
|
|||||||
import { Button, Avatar, Typography, Dropdown, Label, Separator } from "@heroui/react";
|
import {
|
||||||
|
Button,
|
||||||
|
Avatar,
|
||||||
|
Typography,
|
||||||
|
Dropdown,
|
||||||
|
Label,
|
||||||
|
Separator,
|
||||||
|
Spinner,
|
||||||
|
} from "@heroui/react";
|
||||||
import { useAuth } from "~/hooks/auth";
|
import { useAuth } from "~/hooks/auth";
|
||||||
|
|
||||||
export default function User() {
|
export default function User() {
|
||||||
const {user, login, logout} = useAuth();
|
const { user, loading, login, logout } = useAuth();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Button isPending={true}>
|
||||||
|
<Spinner color="current" size="md" />
|
||||||
|
Loading...
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
return <Button onClick={() => login()}>Login</Button>;
|
return <Button onClick={() => login()}>Login</Button>;
|
||||||
@@ -17,12 +34,18 @@ export default function User() {
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown.Popover>
|
<Dropdown.Popover>
|
||||||
<Dropdown.Menu onAction={(key) => handleAction(String(key), logout)}>
|
<Dropdown.Menu
|
||||||
|
onAction={(key) => handleAction(String(key), logout)}
|
||||||
|
>
|
||||||
<Dropdown.Item id="settings" textValue="Settings">
|
<Dropdown.Item id="settings" textValue="Settings">
|
||||||
<Label>Settings</Label>
|
<Label>Settings</Label>
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
<Separator variant="tertiary" />
|
<Separator variant="tertiary" />
|
||||||
<Dropdown.Item id="logout" textValue="Logout" variant="danger">
|
<Dropdown.Item
|
||||||
|
id="logout"
|
||||||
|
textValue="Logout"
|
||||||
|
variant="danger"
|
||||||
|
>
|
||||||
<Label>Logout</Label>
|
<Label>Logout</Label>
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
|
|||||||
@@ -1,62 +1,83 @@
|
|||||||
import { useEffect, useState, createContext, useContext, type PropsWithChildren } from "react";
|
import {
|
||||||
import { useSearchParams } from "react-router";
|
useEffect,
|
||||||
|
useState,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
type PropsWithChildren,
|
||||||
|
} from "react";
|
||||||
|
import { useLocation, useSearchParams } from "react-router";
|
||||||
|
|
||||||
type User = {
|
type User = {
|
||||||
id: string,
|
id: string;
|
||||||
steamid: string,
|
steamid: string;
|
||||||
name: string,
|
name: string;
|
||||||
avatar: string
|
avatar: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type AuthContextType = {
|
type AuthContextType = {
|
||||||
user: User | null,
|
user: User | null;
|
||||||
login: () => void,
|
loading: boolean;
|
||||||
logout: () => Promise<void>
|
login: () => void;
|
||||||
}
|
logout: () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextType>({
|
const AuthContext = createContext<AuthContextType>({
|
||||||
user: null,
|
user: null,
|
||||||
|
loading: false,
|
||||||
login: () => {},
|
login: () => {},
|
||||||
logout: async () => {}
|
logout: async () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function AuthProvider({ children }: PropsWithChildren) {
|
export function AuthProvider({ children }: PropsWithChildren) {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
const [searchParams, setSeachParams] = useSearchParams();
|
const [searchParams, setSeachParams] = useSearchParams();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
|
|
||||||
if (searchParams.get("openid.ns") !== null) {
|
if (searchParams.get("openid.ns") !== null) {
|
||||||
let response = await fetch("http://localhost:8080/login?" + searchParams.toString(), {credentials: "include"}).then(r => r.text());
|
setLoading(true);
|
||||||
|
let query = searchParams.toString();
|
||||||
setSeachParams({}, { replace: true });
|
setSeachParams({}, { replace: true });
|
||||||
|
let response = await fetch(
|
||||||
|
"http://localhost:8080/login?" + query,
|
||||||
|
{ credentials: "include" },
|
||||||
|
).then((r) => r.text());
|
||||||
if (response === "nok") {
|
if (response === "nok") {
|
||||||
// TODO: signal error to user
|
// TODO: signal error to user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = await fetch("http://localhost:8080/me", {credentials: "include"})
|
let data = await fetch("http://localhost:8080/me", {
|
||||||
.then(r => r.json())
|
credentials: "include",
|
||||||
|
}).then((r) => r.json());
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
setUser(data);
|
setUser(data);
|
||||||
}
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = () => {
|
const login = () => {
|
||||||
location.replace("https://steamcommunity.com/openid/login?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=http://localhost:5173&openid.return_to=http://localhost:5173");
|
window.location.replace(
|
||||||
|
"https://steamcommunity.com/openid/login?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=http://localhost:5173&openid.return_to=http://localhost:5173" +
|
||||||
|
location.pathname,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
await fetch("http://localhost:8080/logout", {credentials: "include"})
|
await fetch("http://localhost:8080/logout", { credentials: "include" });
|
||||||
setUser(null);
|
setUser(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <AuthContext.Provider value={{user, login, logout}}>
|
return (
|
||||||
|
<AuthContext.Provider value={{ user, loading, login, logout }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAuth() {
|
export function useAuth() {
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
import { Footer } from "~/components/footer";
|
import { Footer } from "~/components/footer";
|
||||||
|
import Guard from "~/components/guard";
|
||||||
import { Header } from "~/components/header";
|
import { Header } from "~/components/header";
|
||||||
|
|
||||||
export default function Base({ children }: PropsWithChildren) {
|
export default function Base({
|
||||||
|
children,
|
||||||
|
needsLogin = false,
|
||||||
|
}: PropsWithChildren<{ needsLogin?: boolean }>) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col pt-20 min-h-dvh">
|
<div className="flex flex-col pt-20 min-h-dvh">
|
||||||
<div className="flex-1">
|
<div className="flex flex-col flex-1">
|
||||||
{children}
|
{needsLogin ? <Guard>{children}</Guard> : children}
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Base from "./base"
|
import Base from "./base";
|
||||||
|
|
||||||
export function GamemodesPage() {
|
export function GamemodesPage() {
|
||||||
return <Base>GAMEMODES</Base>
|
return <Base needsLogin>GAMEMODES</Base>;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import Base from "./base"
|
import Base from "./base";
|
||||||
|
|
||||||
export function InventoryPage() {
|
export function InventoryPage() {
|
||||||
return <Base>INVENTORY</Base>
|
return <Base needsLogin>INVENTORY</Base>;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import Base from "./base"
|
import Base from "./base"
|
||||||
|
|
||||||
export function MapsPage() {
|
export function MapsPage() {
|
||||||
return <Base>MAPS</Base>
|
return <Base needsLogin>MAPS</Base>
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user