(feat): conversations now get moved to the top when engaged with (qol): update todo
282 lines
6.4 KiB
TypeScript
282 lines
6.4 KiB
TypeScript
import { createRoot } from "react-dom/client";
|
|
import {
|
|
Outlet,
|
|
RouterProvider,
|
|
createRootRoute,
|
|
createRoute,
|
|
createRouter,
|
|
} from "@tanstack/react-router";
|
|
|
|
import "./index.css";
|
|
import "@tensamin/ui/index.css";
|
|
|
|
import NotFound from "@/routes/404";
|
|
|
|
import AppLayout from "@/routes/app/layout";
|
|
import SettingsLayout from "@/features/settings/layout";
|
|
|
|
import Home from "@/routes/app/home";
|
|
import ChatScreen from "@tensamin/chat/screen";
|
|
import CallScreen from "@tensamin/call/screen";
|
|
import Login from "@/routes/screens/login";
|
|
|
|
import ChatContext from "@tensamin/chat/context";
|
|
import { useInitializeCall } from "@tensamin/call/store";
|
|
import { Provider as TTPProvider } from "@tensamin/ttp";
|
|
import UserContext from "@tensamin/user/context";
|
|
import DeeplinkContext from "@tensamin/tauri/deeplinkHandler";
|
|
import NotificationsProvider from "@tensamin/notifications/context";
|
|
|
|
import TAuthWrapper from "@tensamin/tauth/context";
|
|
|
|
import { ThemeProvider } from "@tensamin/ui";
|
|
import z from "zod";
|
|
|
|
import { useEffect, useState, type ReactNode } from "react";
|
|
|
|
import Storage from "@tensamin/storage/context";
|
|
import Session from "@tensamin/storage/session";
|
|
import Crypto from "@tensamin/crypto/context";
|
|
import Mobile from "@tensamin/tauri/context";
|
|
|
|
import LegalWrapper from "@/features/legal/screen";
|
|
|
|
import { useStorage } from "@tensamin/storage/context";
|
|
import { useLocation, useNavigate } from "@tanstack/react-router";
|
|
import { useIsMobile, Toaster, TooltipProvider } from "@tensamin/ui";
|
|
import { isTauri } from "@tauri-apps/api/core";
|
|
|
|
const wrapper = document.getElementById("root");
|
|
|
|
if (!wrapper) {
|
|
throw new Error("Missing root element");
|
|
}
|
|
|
|
if (localStorage.getItem("log_level") === null) {
|
|
console.log(
|
|
"%cDo not paste anything in here, there is a 101.1% chance you're being scammed.",
|
|
"color: red; font-size: 24px; font-weight: bold; background: #220000; padding: 8px 12px; border: 3px solid red;",
|
|
);
|
|
}
|
|
|
|
// @ts-expect-error Declare global function
|
|
window.setLogLevelToMax = () => {
|
|
localStorage.setItem("log_level", String(1000));
|
|
location.reload();
|
|
};
|
|
|
|
function LoginWrapper({ children }: { children: ReactNode }) {
|
|
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
|
|
|
|
const { load } = useStorage();
|
|
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
|
|
load("user_id").then((userId) => {
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
if (userId !== 0) {
|
|
setLoggedIn(true);
|
|
return;
|
|
}
|
|
|
|
setLoggedIn(false);
|
|
navigate({
|
|
to: "/login",
|
|
});
|
|
});
|
|
|
|
return () => {
|
|
active = false;
|
|
};
|
|
}, [load, navigate]);
|
|
|
|
if (loggedIn !== true && location.pathname !== "/login") {
|
|
return null;
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
function RootShell() {
|
|
const isMobile = useIsMobile();
|
|
|
|
return (
|
|
<DeeplinkContext>
|
|
<ThemeProvider>
|
|
<div className="w-screen h-screen overflow-hidden">
|
|
<Toaster
|
|
position={isMobile ? "top-center" : "bottom-right"}
|
|
{...(isTauri() && isMobile
|
|
? {
|
|
mobileOffset: {
|
|
top: "env(safe-area-inset-top)",
|
|
},
|
|
}
|
|
: {})}
|
|
/>
|
|
<TooltipProvider>
|
|
<Storage>
|
|
<LoginWrapper>
|
|
<LegalWrapper>
|
|
<Crypto>
|
|
<Mobile>
|
|
<Outlet />
|
|
</Mobile>
|
|
</Crypto>
|
|
</LegalWrapper>
|
|
</LoginWrapper>
|
|
</Storage>
|
|
</TooltipProvider>
|
|
</div>
|
|
</ThemeProvider>
|
|
</DeeplinkContext>
|
|
);
|
|
}
|
|
|
|
function AppShell() {
|
|
return (
|
|
<TTPProvider>
|
|
<Session>
|
|
<UserContext>
|
|
<CallInit />
|
|
<TAuthWrapper>
|
|
<AppLayout>
|
|
<ChatContext>
|
|
<NotificationsProvider>
|
|
<Outlet />
|
|
</NotificationsProvider>
|
|
</ChatContext>
|
|
</AppLayout>
|
|
</TAuthWrapper>
|
|
</UserContext>
|
|
</Session>
|
|
</TTPProvider>
|
|
);
|
|
}
|
|
|
|
function CallInit() {
|
|
useInitializeCall();
|
|
|
|
return null;
|
|
}
|
|
|
|
const rootRoute = createRootRoute({
|
|
component: RootShell,
|
|
});
|
|
|
|
const appRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
id: "app",
|
|
component: AppShell,
|
|
});
|
|
|
|
const settingsRoute = createRoute({
|
|
getParentRoute: () => appRoute,
|
|
path: "settings",
|
|
component: SettingsLayout,
|
|
staticData: {
|
|
showMobileNavbar: true,
|
|
},
|
|
});
|
|
|
|
type SettingsRouteModule = {
|
|
default?: () => React.JSX.Element;
|
|
component?: () => React.JSX.Element;
|
|
};
|
|
|
|
const settingsRouteModules = import.meta.glob<SettingsRouteModule>(
|
|
"./routes/settings/*.tsx",
|
|
{ eager: true },
|
|
);
|
|
|
|
const settingsChildren = Object.entries(settingsRouteModules).map(
|
|
([filePath, module]) => {
|
|
const fileName = filePath.split("/").pop()?.replace(".tsx", "") ?? "";
|
|
const path = fileName === "index" ? "/" : fileName;
|
|
const component = module.component ?? module.default;
|
|
|
|
if (!component) {
|
|
throw new Error(
|
|
`Settings route module "${filePath}" must export a default component`,
|
|
);
|
|
}
|
|
|
|
return createRoute({
|
|
getParentRoute: () => settingsRoute,
|
|
path,
|
|
component,
|
|
staticData: {
|
|
showMobileNavbar: true,
|
|
},
|
|
});
|
|
},
|
|
);
|
|
|
|
const homeRoute = createRoute({
|
|
getParentRoute: () => appRoute,
|
|
path: "/",
|
|
component: Home,
|
|
staticData: {
|
|
showMobileNavbar: true,
|
|
},
|
|
});
|
|
|
|
const chatRoute = createRoute({
|
|
getParentRoute: () => appRoute,
|
|
path: "chat",
|
|
component: ChatScreen,
|
|
validateSearch: z.object({
|
|
id: z.number().optional(),
|
|
}),
|
|
staticData: {
|
|
showMobileNavbar: false,
|
|
},
|
|
});
|
|
|
|
const callRoute = createRoute({
|
|
getParentRoute: () => appRoute,
|
|
path: "call",
|
|
component: CallScreen,
|
|
staticData: {
|
|
showMobileNavbar: false,
|
|
},
|
|
});
|
|
|
|
const loginRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "login",
|
|
component: Login,
|
|
staticData: {
|
|
showMobileNavbar: false,
|
|
},
|
|
});
|
|
|
|
const routeTree = rootRoute.addChildren([
|
|
appRoute.addChildren([
|
|
homeRoute,
|
|
chatRoute,
|
|
callRoute,
|
|
settingsRoute.addChildren(settingsChildren),
|
|
]),
|
|
loginRoute,
|
|
]);
|
|
|
|
const router = createRouter({
|
|
routeTree,
|
|
defaultNotFoundComponent: NotFound,
|
|
});
|
|
|
|
declare module "@tanstack/react-router" {
|
|
interface Register {
|
|
router: typeof router;
|
|
}
|
|
}
|
|
|
|
createRoot(wrapper).render(<RouterProvider router={router} />);
|