Skip to content

Commit

Permalink
message box added
Browse files Browse the repository at this point in the history
  • Loading branch information
stephan-rz committed May 18, 2024
1 parent 34aeb3e commit 2704ecc
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/app/(Main)/conversations/[conversationId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const ConversationId = async ({params}: {params: IParams}) => {
<div className="lg:pl-80 h-full">
<div className="h-full flex flex-col">
<ChatHeader conversation={conversation}/>
<ChatBody />
<ChatBody initialMessages={messages}/>
<ChatForm />
</div>
</div>
Expand Down
18 changes: 18 additions & 0 deletions src/app/api/conversations/[conversationId]/seen/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextResponse } from "next/server";

interface IParams {
conversationId?: string;
}


export async function POST(
request: Request,
{ params }: { params: IParams }
) {
try {

} catch (error) {
console.log(error, 'ERROR_MESSAGES_SEEN');
return new NextResponse("Internal Error", { status: 500 })
}
}
69 changes: 69 additions & 0 deletions src/app/api/messages/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { currentUser } from "@/lib/current-user";
import { db } from "@/lib/db";
import { NextResponse } from "next/server";

export async function POST(
request: Request
) {
const user = await currentUser();
if (!user) return new NextResponse('Unauthorized', { status: 401 })

try {
const body = await request.json();
const { message, image, conversationId } = body;

const newMessage = await db.message.create({
data: {
body: message,
image,
conversation: {
connect: {
id: conversationId
}
},
sender: {
connect: {
id: user.id
}
},
seen: {
connect: {
id: user.id
}
}
},
include: {
seen: true,
sender: true
}
})

const updatedConversation = await db.conversation.update({
where: {
id: conversationId
},
data: {
lastMessageAt: new Date(),
messages: {
connect: {
id: newMessage.id
}
}
},
include: {
users: true,
messages: {
include: {
seen: true
}
}
}
});


return NextResponse.json(newMessage)
} catch (error) {
console.log(error, 'ERROR_MESSAGES');
return new NextResponse('Internal Error', { status: 500 })
}
}
32 changes: 30 additions & 2 deletions src/components/conversations/chat-body.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
"use client"

const ChatBody = () => {
import useConversation from "@/hooks/use-conversation"
import { FullMessageType } from "@/types"
import { useEffect, useRef, useState } from "react"
import MessageBox from "./message-box"
import axios from "axios"

interface ChatBodyProps {
initialMessages: FullMessageType[] | null
}

const ChatBody = ({ initialMessages }: ChatBodyProps) => {
const [messages, setMessages] = useState(initialMessages);
const bottomRef = useRef<HTMLDivElement>(null);

const { conversationId } = useConversation();

useEffect(() => {
axios.post(`/api/conversations/${conversationId}/seen`)
}, [conversationId])

return (
<div className="flex-1 overflow-y-auto">Body</div>
<div className="flex-1 overflow-y-auto">
{messages?.map((message, i) => (
<MessageBox
isLast={i === messages.length - 1}
key={message.id}
data={message}
/>
))}
<div ref={bottomRef} className="pt-24" />
</div>
)
}

Expand Down
24 changes: 20 additions & 4 deletions src/components/conversations/chat-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import useConversation from "@/hooks/use-conversation"
import { MessageSchema } from "@/schemas/message";
import { zodResolver } from "@hookform/resolvers/zod";
import { Input } from "@nextui-org/react";
import { Image as ImageIcon } from "lucide-react";
import { Button, Input } from "@nextui-org/react";
import axios from "axios";
import { Image as ImageIcon, SendHorizonal } from "lucide-react";
import { useForm } from "react-hook-form";

const ChatForm = () => {
Expand All @@ -18,19 +19,34 @@ const ChatForm = () => {
})

const onSubmit = (values: MessageSchema) => {
console.log(values)
form.reset()
axios.post('/api/messages', {
...values,
conversationId
})
}

return (
<div className="p-4 bg-zinc-800 border-t border-zinc-800 flex items-center gap-2 lg:gap-4 w-full">
<ImageIcon size={24} />
<form onSubmit={form.handleSubmit(onSubmit)}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full flex items-center gap-2 text-sm">
<Input
{...form.register("message")}
placeholder="Write a message"
required
variant="faded"
size="lg"
classNames={{
input: [
"text-white",
"placeholder:text-white-700/50",
"text-sm"
],
}}
/>
<Button type="submit" isIconOnly className="bg-transparent hover:bg-primary">
<SendHorizonal/>
</Button>
</form>
</div>
)
Expand Down
83 changes: 83 additions & 0 deletions src/components/conversations/message-box.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use client"

import { useCurrentUser } from "@/hooks/use-current-user";
import { cn } from "@/lib/utils";
import { FullMessageType } from "@/types"
import { Avatar } from "@nextui-org/react";
import { format } from "date-fns";
import Image from "next/image";

interface MessageBoxProps {
data: FullMessageType;
isLast?: boolean;
}

const MessageBox = ({
data,
isLast
}: MessageBoxProps) => {
const session = useCurrentUser();

const isOwn = session?.email === data.sender.email;
const seenList = (data.seen || [])
.filter((user) => user.email !== data.sender.email)
.map((user) => user.name)
.join(', ');

const container = cn(
"flex gap-3 p-4",
isOwn && "justify-end"
);

const avatar = cn(isOwn && "order-2")

const body = cn(
"flex flex-col gap-2",
isOwn && "items-end"
)

const message = cn(
"text-sm w-fit overflow-hidden",
isOwn ? "bg-primary text-white" : "bg-zinc-800 text-white",
data.image ? "rounded-md p-0" : "rounded-full py-2 px-3"
)

return (
<div className={container}>
<div className={avatar}>
<div className="relative">
<Avatar src={data?.sender.image as string} alt="avatar" showFallback />
<span className="absolute block rounded-full bg-green-500 ring-2 ring-white top-[2px] right-[2px] h-2 w-2 md:h-2 md:w-2" />
</div>
</div>
<div className={body}>
<div className="flex items-center gap-2">
<div className="text-sm truncate w-[150px] text-white/80">
{data.sender.name}
</div>
<div className="text-sm text-white/50">
{format(new Date(data.createdAt), 'p')}
</div>
</div>
<div className={message}>
{data.image ? (
<Image
alt="image"
height={288}
width={288}
src={data.image}
className="object-cover cursor-pointer hover:scale-110 transition translate"
/>
): (
<div>
{data.body}
</div>
)}
</div>

</div>
</div>
)
}

export default MessageBox

0 comments on commit 2704ecc

Please sign in to comment.