diff --git a/app/(main)/(routes)/servers/[serverId]/conversations/[memberId]/page.tsx b/app/(main)/(routes)/servers/[serverId]/conversations/[memberId]/page.tsx
index 8377153..07640f3 100644
--- a/app/(main)/(routes)/servers/[serverId]/conversations/[memberId]/page.tsx
+++ b/app/(main)/(routes)/servers/[serverId]/conversations/[memberId]/page.tsx
@@ -1,4 +1,6 @@
import { ChatHeader } from "@/components/chat/chat-header";
+import { ChatInput } from "@/components/chat/chat-input";
+import { ChatMessages } from "@/components/chat/chat-messages";
import { getOrCreateConversation } from "@/lib/conversation";
import { currentProfile } from "@/lib/current-profile";
import { db } from "@/lib/db";
@@ -53,6 +55,27 @@ const MemberIdPage = async ({
serverId={params.serverId}
type={"conversation"}
/>
+
+
)
}
diff --git a/app/api/direct-messages/route.ts b/app/api/direct-messages/route.ts
new file mode 100644
index 0000000..674796e
--- /dev/null
+++ b/app/api/direct-messages/route.ts
@@ -0,0 +1,83 @@
+import { currentProfile } from "@/lib/current-profile";
+import { db } from "@/lib/db";
+import { DirectMessage } from "@prisma/client";
+
+import { NextResponse } from "next/server";
+
+const MESSAGES_BATCH = 10;
+
+export async function GET(
+ req: Request
+) {
+ try {
+ const profile = await currentProfile();
+ const { searchParams } = new URL(req.url);
+
+ const cursor = searchParams.get("cursor");
+ const conversationId = searchParams.get("conversationId");
+
+ if (!profile) {
+ return new NextResponse("Unauthorized", { status: 401 });
+ }
+
+ if (!conversationId) {
+ return new NextResponse("Conversation ID missing", { status: 400 });
+ }
+
+ let messages: DirectMessage[] = [];
+
+ if (cursor) {
+ messages = await db.directMessage.findMany({
+ take: MESSAGES_BATCH,
+ skip: 1,
+ cursor: {
+ id: cursor,
+ },
+ where: {
+ conversationId,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true,
+ }
+ }
+ },
+ orderBy: {
+ createdAt: "desc",
+ },
+ });
+ } else {
+ messages = await db.directMessage.findMany({
+ take: MESSAGES_BATCH,
+ where: {
+ conversationId,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true,
+ }
+ }
+ },
+ orderBy: {
+ createdAt: "desc",
+ },
+ });
+ }
+
+ let nextCursor = null;
+
+ if (messages.length === MESSAGES_BATCH) {
+ nextCursor = messages[MESSAGES_BATCH - 1].id;
+ }
+
+ return NextResponse.json({
+ items: messages,
+ nextCursor,
+ });
+ } catch (error) {
+ console.log("[DIRECT_MESSAGES_GET]", error);
+ return new NextResponse("Internal Error", { status: 500 })
+ }
+}
\ No newline at end of file
diff --git a/pages/api/socket/direct-messages/[directmessageId].ts b/pages/api/socket/direct-messages/[directmessageId].ts
new file mode 100644
index 0000000..4c32321
--- /dev/null
+++ b/pages/api/socket/direct-messages/[directmessageId].ts
@@ -0,0 +1,145 @@
+import { currentProfilePages } from "@/lib/current-profile-pages";
+import { db } from "@/lib/db";
+import { NextApiResponseServerIo } from "@/types";
+import { MemberRole } from "@prisma/client";
+import { NextApiRequest } from "next";
+
+export default async function handler(req: NextApiRequest, res: NextApiResponseServerIo) {
+ if (req.method !== "DELETE" && req.method !== "PATCH") {
+ return res.status(405).json({ error: "Method not allowed" });
+ }
+
+ try {
+ const profile = await currentProfilePages(req);
+ const { directMessageId, conversationId } = req.query;
+ const { content } = req.body;
+
+ if (!profile) {
+ return res.status(401).json({ error: "Unauthorized" });
+ }
+
+ if (!conversationId) {
+ return res.status(400).json({ error: "Conversation ID Missing" });
+ }
+
+ const conversation = await db.conversation.findFirst({
+ where: {
+ id: conversationId as string,
+ OR: [
+ {
+ memberOne: {
+ profileId: profile.id
+ }
+ },
+ {
+ memberTwo: {
+ profileId: profile.id
+ }
+ },
+ ]
+ },
+ include: {
+ memberOne: {
+ include: {
+ profile: true
+ }
+ },
+ memberTwo: {
+ include: {
+ profile: true
+ }
+ }
+ }
+ });
+
+ if (!conversation) {
+ return res.status(404).json({ error: "Conversation not found" });
+ }
+
+ const member = conversation.memberOne.profileId === profile.id ?
+ conversation.memberOne : conversation.memberTwo;
+
+ if (!member) {
+ return res.status(404).json({ error: "Member not found" });
+ }
+
+ let directMessage = await db.directMessage.findFirst({
+ where: {
+ id: directMessageId as string,
+ conversationId: conversationId as string,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true,
+ }
+ }
+ }
+ });
+
+ if (!directMessage || directMessage.deleted) {
+ return res.status(404).json({ error: "Message not found" });
+ }
+
+ const isMessageOwner = directMessage.memberId === member.id;
+ const isAdmin = member.role === MemberRole.ADMIN;
+ const isModerator = member.role === MemberRole.MODERATOR;
+ const canModify = isMessageOwner || isAdmin || isModerator;
+
+ if (!canModify) {
+ return res.status(401).json({ error: "Unauthorized" });
+ }
+
+ if (req.method === "DELETE") {
+ directMessage = await db.directMessage.update({
+ where: {
+ id: directMessageId as string,
+ },
+ data: {
+ fileUrl: null,
+ content: "This message has been deleted.",
+ deleted: true,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true,
+ }
+ }
+ }
+ });
+ }
+
+ if (req.method === "PATCH") {
+ if (!isMessageOwner) {
+ return res.status(401).json({ error: "Unauthorized" });
+ }
+
+ directMessage = await db.directMessage.update({
+ where: {
+ id: directMessageId as string,
+ },
+ data: {
+ content,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true,
+ }
+ }
+ }
+ });
+ }
+
+ const updateKey = `chat:${conversationId}:messages:update`;
+
+ res?.socket?.server?.io?.to(updateKey).emit(updateKey, directMessage);
+
+ return res.status(200).json(directMessage);
+
+ } catch (error) {
+ console.error(error);
+ return res.status(500).json({ error: "Internal Error" });
+ }
+}
\ No newline at end of file
diff --git a/pages/api/socket/direct-messages/index.ts b/pages/api/socket/direct-messages/index.ts
new file mode 100644
index 0000000..7dfb852
--- /dev/null
+++ b/pages/api/socket/direct-messages/index.ts
@@ -0,0 +1,95 @@
+import { currentProfilePages } from "@/lib/current-profile-pages";
+import { NextApiResponseServerIo } from "@/types";
+import { NextApiRequest } from "next";
+import { db } from "@/lib/db";
+
+export default async function handler(req: NextApiRequest, res: NextApiResponseServerIo) {
+ if (req.method !== "POST") {
+ return res.status(405).json({ error: "Method not allowed" });
+ }
+
+ try {
+ const profile = await currentProfilePages(req);
+ const { content, fileUrl } = req.body;
+ const { conversationId } = req.query;
+
+ if (!profile) {
+ return res.status(401).json({ error: "Unauthorized" });
+ }
+
+ if (!conversationId) {
+ return res.status(400).json({ error: "Conversation ID Missing" });
+ }
+
+ if (!content) {
+ return res.status(400).json({ error: "Content Missing" });
+ }
+
+ const conversation = await db.conversation.findFirst({
+ where: {
+ id: conversationId as string,
+ OR: [
+ {
+ memberOne: {
+ profileId: profile.id
+ }
+ },
+ {
+ memberTwo: {
+ profileId: profile.id
+ }
+ },
+ ]
+ },
+ include: {
+ memberOne: {
+ include: {
+ profile: true
+ }
+ },
+ memberTwo: {
+ include: {
+ profile: true
+ }
+ }
+ }
+ });
+
+ if (!conversation) {
+ return res.status(404).json({ error: "Conversation not found" });
+ }
+
+ const member = conversation.memberOne.profileId === profile.id ?
+ conversation.memberOne : conversation.memberTwo;
+
+ if (!member) {
+ return res.status(401).json({ error: "Member not found" });
+ }
+
+ const message = await db.directMessage.create({
+ data: {
+ content,
+ fileUrl,
+ conversationId: conversationId as string,
+ memberId: member.id,
+ },
+ include: {
+ member: {
+ include: {
+ profile: true
+ }
+ }
+ }
+ });
+
+ const channelKey = `chat:${conversationId}:messages`;
+
+ res?.socket?.server?.io?.emit(channelKey, message);
+
+ return res.status(200).json({ message });
+ }
+ catch (error) {
+ console.log("[DIRECT_MESSAGES_POST]", error);
+ return res.status(500).json({ message: "Internal Error" });
+ }
+}
\ No newline at end of file