Retro Bulletin Board Systems on atproto. Web app and TUI.
lazy mirror of alyraffauf/atbbs
atbbs.xyz
forums
python
tui
atproto
bbs
1import {
2 createContext,
3 useCallback,
4 useContext,
5 useEffect,
6 useState,
7 type ReactNode,
8} from "react";
9import { useLocation, useNavigate } from "react-router-dom";
10
11interface LoginModalCtx {
12 open: boolean;
13 openLogin: () => void;
14 closeLogin: () => void;
15}
16
17const LoginModalContext = createContext<LoginModalCtx | null>(null);
18
19export function LoginModalProvider({ children }: { children: ReactNode }) {
20 const [open, setOpen] = useState(false);
21 const openLogin = useCallback(() => setOpen(true), []);
22 const closeLogin = useCallback(() => setOpen(false), []);
23
24 // Open the modal when we land on a URL with ?login=1 (auth-required loader
25 // redirects use this), then strip the param so refreshes don't re-trigger.
26 const location = useLocation();
27 const navigate = useNavigate();
28 useEffect(() => {
29 const params = new URLSearchParams(location.search);
30 if (params.get("login") !== "1") return;
31 setOpen(true);
32 params.delete("login");
33 const remaining = params.toString();
34 navigate(location.pathname + (remaining ? `?${remaining}` : ""), {
35 replace: true,
36 });
37 }, [location.pathname, location.search, navigate]);
38
39 return (
40 <LoginModalContext.Provider value={{ open, openLogin, closeLogin }}>
41 {children}
42 </LoginModalContext.Provider>
43 );
44}
45
46export function useLoginModal(): LoginModalCtx {
47 const ctx = useContext(LoginModalContext);
48 if (!ctx)
49 throw new Error("useLoginModal must be used within LoginModalProvider");
50 return ctx;
51}