127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import React, { ReactNode } from "react";
|
|
import { NavLink } from "react-router-dom";
|
|
import useSWR from "swr";
|
|
import { cx } from "twind/core";
|
|
import { API_URL, fetchApi, handleResponse } from "./apiClient.ts";
|
|
|
|
function NavTab(props: { to: string; children: ReactNode }) {
|
|
return (
|
|
<NavLink
|
|
className={({ isActive }) => cx("tab", isActive && "tab-active")}
|
|
to={props.to}
|
|
>
|
|
{props.children}
|
|
</NavLink>
|
|
);
|
|
}
|
|
|
|
export function AppHeader(props: {
|
|
className?: string;
|
|
sessionId: string | null;
|
|
onLogOut: () => void;
|
|
}) {
|
|
const { className, sessionId, onLogOut } = props;
|
|
|
|
const getSession = useSWR(
|
|
sessionId ? ["/sessions/:sessionId", { method: "GET", params: { sessionId } }] as const : null,
|
|
(args) => fetchApi(...args).then(handleResponse),
|
|
{ onError: () => onLogOut() },
|
|
);
|
|
|
|
const getUser = useSWR(
|
|
getSession.data?.userId
|
|
? ["/users/:userId", {
|
|
method: "GET",
|
|
params: { userId: String(getSession.data.userId) },
|
|
}] as const
|
|
: null,
|
|
(args) => fetchApi(...args).then(handleResponse),
|
|
);
|
|
|
|
const getBot = useSWR(
|
|
["/bot", { method: "GET" }] as const,
|
|
(args) => fetchApi(...args).then(handleResponse),
|
|
);
|
|
|
|
const getUserPhoto = useSWR(
|
|
getSession.data?.userId
|
|
? ["/users/:userId/photo", {
|
|
method: "GET",
|
|
params: { userId: String(getSession.data.userId) },
|
|
}] as const
|
|
: null,
|
|
() =>
|
|
// elysia fetch can't download file
|
|
fetch(`${API_URL}/users/${getSession.data?.userId}/photo`)
|
|
.then((response) => {
|
|
if (!response.ok) throw new Error(response.statusText);
|
|
return response;
|
|
})
|
|
.then((response) => response.blob())
|
|
.then((blob) => blob ? URL.createObjectURL(blob) : null),
|
|
);
|
|
|
|
return (
|
|
<header
|
|
className={cx(
|
|
"bg-zinc-50 dark:bg-zinc-800 shadow-md flex items-center gap-2 px-4 py-1",
|
|
className,
|
|
)}
|
|
>
|
|
{/* logo */}
|
|
<img className="w-12 h-12" src="/favicon.png" alt="logo" />
|
|
|
|
{/* tabs */}
|
|
<nav className="flex-grow self-stretch flex items-stretch justify-center gap-2">
|
|
<NavTab to="/">Stats</NavTab>
|
|
<NavTab to="/admins">Admins</NavTab>
|
|
<NavTab to="/workers">Workers</NavTab>
|
|
<NavTab to="/queue">Queue</NavTab>
|
|
<NavTab to="/settings">Settings</NavTab>
|
|
<NavTab to="/disclaimer">Disclaimer</NavTab>
|
|
</nav>
|
|
|
|
{/* loading indicator */}
|
|
{getSession.isLoading || getUser.isLoading ? <div className="spinner" /> : null}
|
|
|
|
{/* user avatar */}
|
|
{getUser.data
|
|
? getUserPhoto.data
|
|
? (
|
|
<img
|
|
src={getUserPhoto.data}
|
|
alt="avatar"
|
|
className="w-9 h-9 rounded-full"
|
|
/>
|
|
)
|
|
: (
|
|
<div className="w-9 h-9 rounded-full bg-zinc-400 dark:bg-zinc-500 flex items-center justify-center text-white text-2xl font-bold select-none">
|
|
{getUser.data.first_name.at(0)?.toUpperCase()}
|
|
</div>
|
|
)
|
|
: null}
|
|
|
|
{/* login/logout button */}
|
|
{!getSession.isLoading && !getUser.isLoading && getBot.data && sessionId
|
|
? (
|
|
getUser.data
|
|
? (
|
|
<button className="button-outlined" onClick={() => onLogOut()}>
|
|
Logout
|
|
</button>
|
|
)
|
|
: (
|
|
<a
|
|
className="button-filled"
|
|
href={`https://t.me/${getBot.data.username}?start=${sessionId}`}
|
|
target="_blank"
|
|
>
|
|
Login
|
|
</a>
|
|
)
|
|
)
|
|
: null}
|
|
</header>
|
|
);
|
|
}
|