You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
5.6 KiB
TypeScript

import "@mantine/core/styles.css";
import { navigate } from "vike/client/router";
import {
AppShell,
Burger,
Group,
Image,
MantineProvider,
NavLink,
} from "@mantine/core";
import {
IconHome2,
IconChevronRight,
IconActivity,
IconTrash,
IconCircle,
IconCircleFilled,
IconTrashFilled,
IconPlus,
} from "@tabler/icons-react";
import { useDisclosure } from "@mantine/hooks";
import theme from "./theme.js";
import logoUrl from "../assets/logo.svg";
import { useStore } from "../state.js";
import { useEffect } from "react";
import { trpc } from "../trpc/client.js";
import { usePageContext } from "vike-react/usePageContext";
import "./hover.css";
import type { ConversationsId } from "../database/generated/public/Conversations.js";
export default function LayoutDefault({
children,
}: { children: React.ReactNode }) {
const pageContext = usePageContext();
const { urlPathname } = pageContext;
const [opened, { toggle }] = useDisclosure();
const conversations = useStore((state) => state.conversations);
const setConversations = useStore((state) => state.setConversations);
const addConversation = useStore((state) => state.addConversation);
const removeConversation = useStore((state) => state.removeConversation);
const conversationId = useStore((state) => state.selectedConversationId);
useEffect(() => {
trpc.chat.listConversations.query().then((res) => {
setConversations(res);
});
}, [setConversations]);
// useEffect(() => {
// if (isConversationListExpanded) {
// trpc.chat.listConversations.query().then((res) => {
// setConversations(res);
// });
// }
// }, [isConversationListExpanded]);
function handleDeleteConversation(conversationId: ConversationsId) {
removeConversation(conversationId);
trpc.chat.deleteConversation.mutate({ id: conversationId });
}
return (
<MantineProvider theme={theme}>
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened },
}}
padding="lg"
>
<AppShell.Header>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggle}
hiddenFrom="sm"
size="sm"
/>
<a href="/">
{" "}
<Image h={50} fit="contain" src={logoUrl} />{" "}
</a>
</Group>
</AppShell.Header>
<AppShell.Navbar p="md">
<NavLink href="/" label="Welcome" active={urlPathname === "/"} />
<NavLink href="/todo" label="Todo" active={urlPathname === "/todo"} />
<NavLink
href="/star-wars"
label="Data Fetching"
active={urlPathname.startsWith("/star-wars")}
/>
<NavLink
key="chat-new"
href="#required-for-focus-management"
label="Chats"
leftSection={<IconActivity size={16} stroke={1.5} />}
rightSection={
<>
<IconPlus
size={16}
stroke={1.5}
className="border-on-hover"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
trpc.chat.createConversation.mutate().then((res) => {
if (!res?.id) return;
addConversation(res);
navigate(`/chat/${res.id}`);
});
}}
/>
<IconChevronRight
size={12}
stroke={1.5}
className="mantine-rotate-rtl"
/>
</>
}
variant="subtle"
active={urlPathname.startsWith("/chat")}
>
{conversations.map((conversation) => (
<NavLink
key={conversation.id}
href={`/chat/${conversation.id}`}
label={conversation.title}
className="hover-container"
leftSection={
<>
<IconCircle
size={16}
stroke={1.5}
className="show-by-default"
/>
<IconCircleFilled
size={16}
stroke={1.5}
className="show-on-hover"
/>
</>
}
rightSection={
<>
<IconTrash
size={16}
stroke={1.5}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleDeleteConversation(conversation.id);
}}
className="show-by-default"
/>
<IconTrashFilled
size={16}
stroke={1.5}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleDeleteConversation(conversation.id);
}}
className="show-on-hover border-on-hover"
/>
</>
}
variant="subtle"
active={conversation.id === conversationId}
/>
))}
</NavLink>
</AppShell.Navbar>
<AppShell.Main> {children} </AppShell.Main>
</AppShell>
</MantineProvider>
);
}