begin migration to trpc/react-query integration
This commit is contained in:
+193
-136
@@ -22,165 +22,222 @@ 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 { useEffect, useState } from "react";
|
||||
import { TRPCProvider, useTRPC } from "../trpc/client.js";
|
||||
import { usePageContext } from "vike-react/usePageContext";
|
||||
import "./hover.css";
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
useMutation,
|
||||
useQuery,
|
||||
} from "@tanstack/react-query";
|
||||
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
||||
import type { AppRouter } from "../trpc/router.js";
|
||||
|
||||
function makeQueryClient() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// With SSR, we usually want to set some default staleTime
|
||||
// above 0 to avoid refetching immediately on the client
|
||||
staleTime: 60 * 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let browserQueryClient: QueryClient | undefined = undefined;
|
||||
function getQueryClient() {
|
||||
if (typeof window === "undefined") {
|
||||
// Server: always make a new query client
|
||||
return makeQueryClient();
|
||||
}
|
||||
// Browser: make a new query client if we don't already have one
|
||||
// This is very important, so we don't re-make a new client if React
|
||||
// suspends during the initial render. This may not be needed if we
|
||||
// have a suspense boundary BELOW the creation of the query client
|
||||
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
||||
return browserQueryClient;
|
||||
}
|
||||
|
||||
export default function LayoutDefault({
|
||||
children,
|
||||
}: { children: React.ReactNode }) {
|
||||
}: {
|
||||
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 queryClient = getQueryClient();
|
||||
const [trpc] = useState(() =>
|
||||
createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: "/api/trpc",
|
||||
methodOverride: "POST",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TRPCProvider trpcClient={trpc} queryClient={queryClient}>
|
||||
<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")}
|
||||
/>
|
||||
<NavLinkChat key="chat-new" />
|
||||
</AppShell.Navbar>
|
||||
<AppShell.Main> {children} </AppShell.Main>
|
||||
</AppShell>
|
||||
</MantineProvider>
|
||||
</TRPCProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function NavLinkChat() {
|
||||
const pageContext = usePageContext();
|
||||
const { urlPathname } = pageContext;
|
||||
const trpc = useTRPC();
|
||||
// const
|
||||
const startConversation = useMutation(
|
||||
trpc.chat.conversations.start.mutationOptions()
|
||||
);
|
||||
const deleteConversation = useMutation(
|
||||
trpc.chat.conversations.deleteOne.mutationOptions()
|
||||
);
|
||||
const { data: conversations } = useQuery(
|
||||
trpc.chat.conversations.fetchAll.queryOptions()
|
||||
);
|
||||
// TODO: should we be using zustand for this, or trpc/react-query's useMutation?
|
||||
const addConversation = useStore((state) => state.addConversation);
|
||||
const removeConversation = useStore((state) => state.removeConversation);
|
||||
const conversationId = useStore((state) => state.selectedConversationId);
|
||||
|
||||
useEffect(() => {
|
||||
trpc.chat.conversations.fetchAll.query().then((res) => {
|
||||
setConversations(res);
|
||||
});
|
||||
}, [setConversations]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (isConversationListExpanded) {
|
||||
// trpc.chat.listConversations.query().then((res) => {
|
||||
// setConversations(res);
|
||||
// });
|
||||
// }
|
||||
// }, [isConversationListExpanded]);
|
||||
|
||||
async function handleDeleteConversation(conversationId: string) {
|
||||
removeConversation(conversationId);
|
||||
await trpc.chat.conversations.deleteOne.mutate({ id: conversationId });
|
||||
const res = await trpc.chat.conversations.start.mutate();
|
||||
await deleteConversation.mutateAsync({ id: conversationId });
|
||||
const res = await startConversation.mutateAsync();
|
||||
if (!res?.id) return;
|
||||
addConversation(res);
|
||||
await navigate(`/chat/${res.id}`);
|
||||
}
|
||||
|
||||
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={
|
||||
<Group justify="space-between">
|
||||
<span>Chats</span>
|
||||
<IconPlus
|
||||
size={16}
|
||||
stroke={1.5}
|
||||
className="border-on-hover"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
startConversation.mutateAsync().then((res) => {
|
||||
if (!res?.id) return;
|
||||
addConversation(res);
|
||||
navigate(`/chat/${res.id}`);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<NavLink
|
||||
key="chat-new"
|
||||
href="#required-for-focus-management"
|
||||
label={
|
||||
<Group justify="space-between">
|
||||
<span>Chats</span>
|
||||
<IconPlus
|
||||
size={16}
|
||||
stroke={1.5}
|
||||
className="border-on-hover"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
trpc.chat.conversations.start.mutate().then((res) => {
|
||||
if (!res?.id) return;
|
||||
addConversation(res);
|
||||
navigate(`/chat/${res.id}`);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
}
|
||||
leftSection={<IconActivity size={16} stroke={1.5} />}
|
||||
rightSection={
|
||||
<IconChevronRight
|
||||
size={12}
|
||||
</Group>
|
||||
}
|
||||
leftSection={<IconActivity size={16} stroke={1.5} />}
|
||||
rightSection={
|
||||
<IconChevronRight
|
||||
size={12}
|
||||
stroke={1.5}
|
||||
className="mantine-rotate-rtl"
|
||||
/>
|
||||
}
|
||||
variant="subtle"
|
||||
active={urlPathname.startsWith("/chat")}
|
||||
defaultOpened={true}
|
||||
>
|
||||
{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="mantine-rotate-rtl"
|
||||
className="show-on-hover"
|
||||
/>
|
||||
}
|
||||
variant="subtle"
|
||||
active={urlPathname.startsWith("/chat")}
|
||||
defaultOpened={true}
|
||||
>
|
||||
{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}
|
||||
</>
|
||||
}
|
||||
rightSection={
|
||||
<>
|
||||
<IconTrash
|
||||
size={16}
|
||||
stroke={1.5}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
handleDeleteConversation(conversation.id);
|
||||
}}
|
||||
className="show-by-default"
|
||||
/>
|
||||
))}
|
||||
</NavLink>
|
||||
</AppShell.Navbar>
|
||||
<AppShell.Main> {children} </AppShell.Main>
|
||||
</AppShell>
|
||||
</MantineProvider>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user