import {
AtSign,
BadgeCheck,
Bot,
ChevronDown,
Database,
Film,
LayoutDashboard,
LogOut,
Megaphone,
MessageSquareText,
Settings,
ShieldAlert,
ShieldCheck,
Smile,
Stamp,
Users,
Sticker
} from "lucide-react";
import { useEffect, useState, type ReactNode } from "react";
import { api } from "../api";
import { permissionBotVerificationReview, permissionServerManage, permissionVerificationReview, useCan, useThirdPartyVerificationHidden } from "../permissions";
import { type Navigate, type RouteState, routeTitle } from "../routing";
import { ThemeSwitch } from "../theme";
import { AppLink } from "./AppLink";
export function BootScreen() {
return (
OwpenGram
{"Admin Console"}
);
}
export function Shell({
actor,
build,
route,
navigate,
onLogout,
children
}: {
actor: string;
build?: { commit: string; short_commit: string; dirty: boolean; build_time: string };
route: RouteState;
navigate: Navigate;
onLogout: () => void;
children: ReactNode;
}) {
// The verification queue is hidden for a session without verification.review:
// the entry would only lead to a 403 (and the route itself is gated as well).
const canReviewVerification = useCan(permissionVerificationReview);
// Same reasoning for the third-party queue, which has its own right: the two
// sections are granted independently, so one entry can be visible without the other.
const canReviewBotVerification = useCan(permissionBotVerificationReview);
const canManageServer = useCan(permissionServerManage);
// Server identity (name/icon) is admin-editable per Server Settings ->
// Server identity, and takes over the sidebar branding when set -- the
// operator's own server should look like their server, not like the
// "OwpenGram" reference build, once they've bothered to configure it.
// Only fetched for sessions that can even see Server Settings; a session
// without that permission just gets the default branding.
const [identity, setIdentity] = useState<{ name: string; iconExt?: string } | null>(null);
useEffect(() => {
if (!canManageServer) return;
api.serverIdentity()
.then((info) => setIdentity({ name: info.name, iconExt: info.icon_ext }))
.catch(() => undefined);
}, [canManageServer]);
const [brandIconFailed, setBrandIconFailed] = useState(false);
const brandName = identity?.name?.trim() || "OwpenGram";
const brandIconSrc = identity?.iconExt && !brandIconFailed ? api.serverIconURL() : "/logo.png";
// Third-party verification is additionally hidden by default (not fully
// finished) regardless of what the session was granted -- see permissions.tsx.
const thirdPartyVerificationHidden = useThirdPartyVerificationHidden();
const messagesActive = route.path.startsWith("/messages");
const [messagesOpen, setMessagesOpen] = useState(messagesActive);
useEffect(() => {
if (messagesActive) {
setMessagesOpen(true);
}
}, [messagesActive]);
async function logout() {
await api.logout().catch(() => undefined);
onLogout();
}
return (