From 70141f46fde7753231716de78c3a7dbffdbe66b1 Mon Sep 17 00:00:00 2001 From: Bob Burningham Date: Fri, 27 Oct 2023 22:45:31 -0700 Subject: [PATCH] added chat messages component and query provider --- .../[serverId]/channels/[channelId]/page.tsx | 16 +++- app/api/messages/route.ts | 83 ++++++++++++++++ app/layout.tsx | 5 +- components/chat/chat-messages.tsx | 96 +++++++++++++++++++ components/chat/chat-welcome.tsx | 28 ++++++ components/providers/query-provider.tsx | 21 ++++ hooks/use-chat-query.ts | 54 +++++++++++ 7 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 app/api/messages/route.ts create mode 100644 components/chat/chat-messages.tsx create mode 100644 components/chat/chat-welcome.tsx create mode 100644 components/providers/query-provider.tsx create mode 100644 hooks/use-chat-query.ts diff --git a/app/(main)/(routes)/servers/[serverId]/channels/[channelId]/page.tsx b/app/(main)/(routes)/servers/[serverId]/channels/[channelId]/page.tsx index 030e423..f47071e 100644 --- a/app/(main)/(routes)/servers/[serverId]/channels/[channelId]/page.tsx +++ b/app/(main)/(routes)/servers/[serverId]/channels/[channelId]/page.tsx @@ -4,6 +4,7 @@ import { db } from "@/lib/db"; import { redirect } from "next/navigation"; import { ChatHeader } from "@/components/chat/chat-header"; import { ChatInput } from "@/components/chat/chat-input"; +import { ChatMessages } from "@/components/chat/chat-messages"; interface ChannelIdPageProps { params: { @@ -42,7 +43,20 @@ const ChannelIdPage = async ({ return (
-
Future Messages
+ - {children} + + {children} + diff --git a/components/chat/chat-messages.tsx b/components/chat/chat-messages.tsx new file mode 100644 index 0000000..af683f9 --- /dev/null +++ b/components/chat/chat-messages.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Member, Message, Profile } from "@prisma/client"; + +import { ChatWelcome } from "./chat-welcome"; +import { useChatQuery } from "@/hooks/use-chat-query"; +import { Loader2, ServerCrash } from "lucide-react"; +import { Fragment } from "react"; + +type MessageWithMemberWithProfile = Message & { + member: Member & { + profile: Profile + } +} + +interface ChatMessagesProps { + name: string; + member: Member; + chatId: string; + apiUrl: string; + socketUrl: string; + socketQuery: Record; + paramKey: "channelId" | "conversationId"; + paramValue: string; + type: "channel" | "conversation"; +} + +export const ChatMessages = ({ + name, + member, + chatId, + apiUrl, + socketUrl, + socketQuery, + paramKey, + paramValue, + type, +}: ChatMessagesProps) => { + const queryKey = `chat:${chatId}`; + + const { + data, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + status, + } = useChatQuery({ + queryKey, + apiUrl, + paramKey, + paramValue, + }) + + if (status === "pending") { + return ( +
+ +

+ Loading Messages... +

+
+ ) + } + + if (status === "error") { + return ( +
+ +

+ Something went wrong! +

+
+ ) + } + + return ( +
+
+ +
+ {data?.pages.map((group, i) => ( + + {group.items.map((message: MessageWithMemberWithProfile) => ( +
+ {message.content} +
+ ))} +
+ ))} +
+
+ ) +} \ No newline at end of file diff --git a/components/chat/chat-welcome.tsx b/components/chat/chat-welcome.tsx new file mode 100644 index 0000000..b7502a6 --- /dev/null +++ b/components/chat/chat-welcome.tsx @@ -0,0 +1,28 @@ +import { Hash } from "lucide-react"; + +interface ChatWelcomeProps { + name: string; + type: "channel" | "conversation"; +} + +export const ChatWelcome = ({ + name, + type, +}: ChatWelcomeProps) => { + return ( +
+ {type === "channel" && ( +
+ +
+ )} +

+ {type === "channel" ? "Welcome to #" : ""}{name} +

+

+ {type === "channel" ? `This is the start of the #${name} channel.` : `This is the start of your conversation with ${name}`} + +

+
+ ) +} \ No newline at end of file diff --git a/components/providers/query-provider.tsx b/components/providers/query-provider.tsx new file mode 100644 index 0000000..2f4db8d --- /dev/null +++ b/components/providers/query-provider.tsx @@ -0,0 +1,21 @@ +"use client" + +import { + QueryClient, + QueryClientProvider, +}from "@tanstack/react-query"; +import { useState } from "react"; + +export const QueryProvider = ({ + children +}: { + children: React.ReactNode; +}) => { + const [queryClient] = useState(() => new QueryClient()); + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/hooks/use-chat-query.ts b/hooks/use-chat-query.ts new file mode 100644 index 0000000..6d3a95b --- /dev/null +++ b/hooks/use-chat-query.ts @@ -0,0 +1,54 @@ +import qs from "query-string"; +import { useInfiniteQuery } from "@tanstack/react-query"; + +import { useSocket } from "@/components/providers/socket-provider"; + +interface ChatQueryProps { + queryKey: string; + apiUrl: string; + paramKey: "channelId" | "conversationId"; + paramValue: string; +}; + +export const useChatQuery = ({ + queryKey, + apiUrl, + paramKey, + paramValue +}: ChatQueryProps) => { + const { isConnected } = useSocket(); + + const fetchMessages = async ({ pageParam = undefined }) => { + const url = qs.stringifyUrl({ + url: apiUrl, + query: { + cursor: pageParam, + [paramKey]: paramValue, + } + }, { skipNull: true }); + + const res = await fetch(url); + return res.json(); + }; + + const { + data, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + status, + } = useInfiniteQuery({ + queryKey: [queryKey], + queryFn: fetchMessages, + getNextPageParam: (lastPage) => lastPage?.nextCursor, + refetchInterval: isConnected ? false : 1000, + }); + + return { + data, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + status, + }; +} \ No newline at end of file