Idk, quick backup
This commit is contained in:
parent
04f37be8f6
commit
396defef64
19 changed files with 519 additions and 108 deletions
|
|
@ -12,6 +12,7 @@
|
|||
"preview": "cd dist && nix-shell -p python3 --run 'python3 -m http.server 3000' && cd .."
|
||||
},
|
||||
"dependencies": {
|
||||
"@tensamin/call": "workspace:*",
|
||||
"@tensamin/chat": "workspace:*",
|
||||
"@tensamin/crypto": "workspace:*",
|
||||
"@tensamin/ttp": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Skeleton } from "@tensamin/ui/cmp/skeleton";
|
|||
|
||||
export function Basic(props: { user: User }) {
|
||||
return (
|
||||
<Card className="animate-in fade-in duration-300 rounded-xl py-0 m-px">
|
||||
<Card className="animate-in fade-in duration-300 rounded-2xl py-0 m-px">
|
||||
<CardHeader className="flex flex-row gap-2.5 items-center justify-start p-2">
|
||||
<Avatar>
|
||||
<AvatarImage src={props.user.avatar} />
|
||||
|
|
@ -21,5 +21,5 @@ export function Basic(props: { user: User }) {
|
|||
}
|
||||
|
||||
export function Loading() {
|
||||
return <Skeleton className="h-12.5 rounded-xl" />;
|
||||
return <Skeleton className="h-12.5 rounded-2xl" />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,95 @@
|
|||
import { Button } from "@tensamin/ui/cmp/button";
|
||||
import { House } from "lucide-react";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
import * as React from "react";
|
||||
import { useUser, type User } from "@tensamin/user/context";
|
||||
import { House, Phone } from "lucide-react";
|
||||
import { useLocation, useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import { useCall } from "@tensamin/call/context";
|
||||
import Wrapper from "@tensamin/user/wrapper";
|
||||
import { Skeleton } from "@tensamin/ui/cmp/skeleton";
|
||||
import { useConversation } from "@/features/conversation/context";
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
} from "@tensamin/ui/cmp/select";
|
||||
import { displayCallId } from "@tensamin/call/utils";
|
||||
|
||||
/**
|
||||
* Navigates to the home route when the navbar home button is clicked.
|
||||
* @param navigate Router navigate function from TanStack Router.
|
||||
* @returns Void.
|
||||
*/
|
||||
function handleHomeButtonClick(navigate: ReturnType<typeof useNavigate>): void {
|
||||
void navigate({ to: "/" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the top navigation bar and currently selected conversation user.
|
||||
* @returns Navbar JSX element.
|
||||
*/
|
||||
export default function Navbar() {
|
||||
const navigate = useNavigate();
|
||||
const { get } = useUser();
|
||||
const search = useRouterState({ select: (state) => state.location.search });
|
||||
const { joinCall, state } = useCall();
|
||||
const { conversations } = useConversation();
|
||||
|
||||
/**
|
||||
* Delegates navbar home button click to navigation helper.
|
||||
* @returns Void.
|
||||
*/
|
||||
const onHomeButtonClick = React.useCallback(() => {
|
||||
handleHomeButtonClick(navigate);
|
||||
}, [navigate]);
|
||||
const { pathname } = useLocation();
|
||||
const { id } = useSearch({ strict: false });
|
||||
|
||||
const [user, setUser] = React.useState<User | null>(null);
|
||||
|
||||
const currentId = React.useMemo(
|
||||
() => Number((search as Record<string, unknown>).id ?? Number.NaN),
|
||||
[search],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (Number.isNaN(currentId)) {
|
||||
setUser(null);
|
||||
return;
|
||||
}
|
||||
|
||||
get(currentId)
|
||||
.then(setUser)
|
||||
.catch(() => setUser(null));
|
||||
}, [currentId, get]);
|
||||
const currentCalls = id ? conversations[id]?.calls || [] : [];
|
||||
|
||||
return (
|
||||
<div className="w-full h-13.5 flex items-center justify-center">
|
||||
<Button
|
||||
onClick={onHomeButtonClick}
|
||||
onClick={() => navigate({ to: "/" })}
|
||||
className="w-9 h-9 aspect-square rounded-lg"
|
||||
variant="outline"
|
||||
>
|
||||
<House className="size-4.5" />
|
||||
</Button>
|
||||
<p className="font-medium pl-3 text-md">{user?.display}</p>
|
||||
{pathname === "/app/chat" && (
|
||||
<Wrapper
|
||||
userId={id}
|
||||
component={(user) => (
|
||||
<p className="font-medium pl-3 text-md">{user?.display}</p>
|
||||
)}
|
||||
loading={<Skeleton className="ml-3 w-40 h-5" />}
|
||||
/>
|
||||
)}
|
||||
<div className="w-full" />
|
||||
{pathname === "/app/chat" && id && (
|
||||
<>
|
||||
{currentCalls.length > 0 ? (
|
||||
<Button
|
||||
disabled={state !== "closed"}
|
||||
onClick={() => {
|
||||
joinCall(id);
|
||||
}}
|
||||
className="w-9 h-9 aspect-square rounded-lg mr-2"
|
||||
variant="outline"
|
||||
>
|
||||
<Phone />
|
||||
</Button>
|
||||
) : currentCalls.length === 1 ? (
|
||||
<Button
|
||||
disabled={state !== "closed"}
|
||||
onClick={() => {
|
||||
joinCall(id, currentCalls[0]);
|
||||
}}
|
||||
className="w-9 h-9 aspect-square rounded-lg mr-2"
|
||||
>
|
||||
<Phone />
|
||||
</Button>
|
||||
) : (
|
||||
<Select>
|
||||
<SelectTrigger
|
||||
render={
|
||||
<Button className="w-9 h-9 aspect-square rounded-lg mr-2">
|
||||
<Phone />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<SelectContent>
|
||||
{currentCalls.map((callId) => (
|
||||
<SelectItem
|
||||
key={callId}
|
||||
onSelect={() => {
|
||||
joinCall(id, callId);
|
||||
}}
|
||||
>
|
||||
{displayCallId(callId)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ export default function Sidebar() {
|
|||
|
||||
return (
|
||||
<div className="w-65 h-full flex flex-col gap-3 p-2">
|
||||
<Wrapper
|
||||
loading={<Loading />}
|
||||
userId={userId}
|
||||
component={(user) => <Basic user={user} />}
|
||||
/>
|
||||
<div>
|
||||
<Wrapper
|
||||
loading={<Loading />}
|
||||
userId={userId}
|
||||
component={(user) => <Basic user={user} />}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-full">
|
||||
<List />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,11 +15,18 @@ import AppLayout from "./routes/app/layout";
|
|||
|
||||
import Home from "@/routes/app/home";
|
||||
import ChatScreen from "@tensamin/chat/screen";
|
||||
import ChatContext from "@tensamin/chat/context";
|
||||
import CallScreen from "@tensamin/call/screen";
|
||||
import Login from "@/routes/screens/login";
|
||||
import Signup from "@/routes/screens/signup";
|
||||
|
||||
import ConversationContext from "@/features/conversation/context";
|
||||
import ChatContext from "@tensamin/chat/context";
|
||||
import CallContext from "@tensamin/call/context";
|
||||
import SocketContext from "@tensamin/ttp/context";
|
||||
import UserContext from "@tensamin/user/context";
|
||||
|
||||
import { ThemeProvider } from "@tensamin/ui/theme";
|
||||
import z from "zod";
|
||||
|
||||
const wrapper = document.getElementById("root");
|
||||
|
||||
|
|
@ -33,11 +40,6 @@ window.setLogLevelToMax = () => {
|
|||
location.reload();
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes RootShell.
|
||||
* @param none This function has no parameters.
|
||||
* @returns unknown.
|
||||
*/
|
||||
function RootShell() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
|
|
@ -50,24 +52,22 @@ function RootShell() {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes AppShell.
|
||||
* @param none This function has no parameters.
|
||||
* @returns unknown.
|
||||
*/
|
||||
function AppShell() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<Outlet />
|
||||
</AppLayout>
|
||||
<SocketContext>
|
||||
<UserContext>
|
||||
<CallContext>
|
||||
<ConversationContext>
|
||||
<AppLayout>
|
||||
<Outlet />
|
||||
</AppLayout>
|
||||
</ConversationContext>
|
||||
</CallContext>
|
||||
</UserContext>
|
||||
</SocketContext>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes Chat.
|
||||
* @param none This function has no parameters.
|
||||
* @returns unknown.
|
||||
*/
|
||||
function Chat() {
|
||||
return (
|
||||
<ChatContext>
|
||||
|
|
@ -77,6 +77,7 @@ function Chat() {
|
|||
}
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
|
||||
component: RootShell,
|
||||
});
|
||||
|
||||
|
|
@ -96,6 +97,15 @@ const chatRoute = createRoute({
|
|||
getParentRoute: () => appRoute,
|
||||
path: "chat",
|
||||
component: Chat,
|
||||
validateSearch: z.object({
|
||||
id: z.number().optional(),
|
||||
})
|
||||
});
|
||||
|
||||
const callRoute = createRoute({
|
||||
getParentRoute: () => appRoute,
|
||||
path: "call",
|
||||
component: CallScreen,
|
||||
});
|
||||
|
||||
const loginRoute = createRoute({
|
||||
|
|
@ -111,7 +121,7 @@ const signupRoute = createRoute({
|
|||
});
|
||||
|
||||
const routeTree = rootRoute.addChildren([
|
||||
appRoute.addChildren([homeRoute, chatRoute]),
|
||||
appRoute.addChildren([homeRoute, chatRoute, callRoute]),
|
||||
loginRoute,
|
||||
signupRoute,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import Socket from "@tensamin/ttp/context";
|
||||
import User from "@tensamin/user/context";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
import Sidebar from "@/components/sidebar";
|
||||
import Conversation from "@/features/conversation/context";
|
||||
import Navbar from "@/components/navbar";
|
||||
import { useStorage } from "@tensamin/storage/context";
|
||||
|
||||
|
|
@ -49,20 +46,14 @@ export default function Layout(props: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Socket>
|
||||
<User>
|
||||
<Conversation>
|
||||
<div className="w-full h-full flex bg-sidebar">
|
||||
<Sidebar />
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<Navbar />
|
||||
<div className="bg-background h-full w-full rounded-tl-3xl border-t border-l">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Conversation>
|
||||
</User>
|
||||
</Socket>
|
||||
<div className="w-full h-full flex bg-sidebar">
|
||||
<Sidebar />
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<Navbar />
|
||||
<div className="bg-background h-full w-full rounded-tl-3xl border-t border-l">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue