-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
550 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,6 @@ DATABASE_URL="mongodb+srv://quyduc:[email protected] | |
NEXTAUTH_SECRET = "NEXTAUTH_SECRET" | ||
|
||
GOOGLE_CLIENT_ID=409276087119-9hmgc0ad6augp7gkeaqmq2r1ru92afo0.apps.googleusercontent.com | ||
GOOGLE_CLIENT_SECRET=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 | ||
GOOGLE_CLIENT_SECRET=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 | ||
|
||
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=dgp72bk0u |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import prisma from '@/app/libs/prismadb' | ||
import getCurrentUser from './getCurrentUser' | ||
|
||
const getConversationById = async (conversationId: string) => { | ||
try { | ||
const currentUser = await getCurrentUser() | ||
|
||
if (!currentUser?.email) { | ||
return null | ||
} | ||
|
||
const conversation = await prisma.conversation.findUnique({ | ||
where: { | ||
id: conversationId, | ||
}, | ||
include: { | ||
users: true, | ||
}, | ||
}) | ||
return conversation | ||
} catch (error: any) { | ||
return null | ||
} | ||
} | ||
|
||
export default getConversationById |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import prisma from '@/app/libs/prismadb' | ||
|
||
const getMessages = async (conversationId: string) => { | ||
try { | ||
const messages = await prisma.message.findMany({ | ||
where: { | ||
conversationId: conversationId, | ||
}, | ||
include: { | ||
sender: true, | ||
seen: true, | ||
}, | ||
orderBy: { | ||
createdAt: 'asc', | ||
}, | ||
}) | ||
return messages | ||
} catch (error: any) { | ||
return [] | ||
} | ||
} | ||
|
||
export default getMessages |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import getCurrentUser from '@/app/actions/getCurrentUser' | ||
import prisma from '@/app/libs/prismadb' | ||
import { NextResponse } from 'next/server' | ||
|
||
interface IParams { | ||
conversationId?: string | ||
} | ||
|
||
export async function POST(request: Request, { params }: { params: IParams }) { | ||
try { | ||
const currentUser = await getCurrentUser() | ||
const { conversationId } = params | ||
|
||
if (!currentUser?.id || !currentUser?.email) { | ||
return new NextResponse('Unauthorized', { status: 401 }) | ||
} | ||
|
||
// find the existing conversation | ||
const conversation = await prisma.conversation.findUnique({ | ||
where: { | ||
id: conversationId, | ||
}, | ||
include: { | ||
messages: { | ||
include: { | ||
seen: true, | ||
}, | ||
}, | ||
users: true, | ||
}, | ||
}) | ||
|
||
if (!conversation) return new NextResponse('Invalid Id', { status: 400 }) | ||
|
||
// find last message | ||
const lastMessage = conversation.messages[conversation.messages.length - 1] | ||
if (!lastMessage) return NextResponse.json(conversation) | ||
|
||
// update seen of last message | ||
const updatedMessage = await prisma.message.update({ | ||
where: { | ||
id: lastMessage.id, | ||
}, | ||
include: { | ||
sender: true, | ||
seen: true, | ||
}, | ||
data: { | ||
seen: { | ||
connect: { | ||
id: currentUser.id, | ||
}, | ||
}, | ||
}, | ||
}) | ||
return NextResponse.json(updatedMessage) | ||
} catch (error: any) { | ||
console.log(error, 'ERROR_MESSAGES_SEEN') | ||
return new NextResponse('Internal Error', { status: 500 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import getCurrentUser from '@/app/actions/getCurrentUser' | ||
import prisma from '@/app/libs/prismadb' | ||
import { NextResponse } from 'next/server' | ||
|
||
export async function POST(request: Request) { | ||
try { | ||
const currentUser = await getCurrentUser() | ||
const body = await request.json() | ||
const { message, image, conversationId } = body | ||
|
||
if (!currentUser?.id || !currentUser?.email) { | ||
return new NextResponse('Unauthorized', { status: 401 }) | ||
} | ||
|
||
const newMessage = await prisma.message.create({ | ||
data: { | ||
body: message, | ||
image: image, | ||
conversation: { | ||
connect: { | ||
id: conversationId, | ||
}, | ||
}, | ||
sender: { | ||
connect: { | ||
id: currentUser.id, | ||
}, | ||
}, | ||
seen: { | ||
connect: { | ||
id: currentUser.id, | ||
}, | ||
}, | ||
}, | ||
include: { | ||
seen: true, | ||
sender: true, | ||
}, | ||
}) | ||
|
||
const updatedConversation = await prisma.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: any) { | ||
console.log(error, 'ERROR_MESSAGES') | ||
return new NextResponse('InternalError', { status: 500 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
'use client' | ||
|
||
import useConversation from '@/app/hooks/useConversation' | ||
import { FullMessageType } from '@/app/types' | ||
import axios from 'axios' | ||
import { useEffect, useRef, useState } from 'react' | ||
import MessageBox from './MessageBox' | ||
|
||
interface BodyProps { | ||
initialMessages: FullMessageType[] | ||
} | ||
|
||
const Body: React.FC<BodyProps> = ({ initialMessages }) => { | ||
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"> | ||
{messages.map((message, i) => ( | ||
<MessageBox | ||
isLast={i === messages.length - 1} | ||
key={message.id} | ||
data={message} | ||
/> | ||
))} | ||
<div ref={bottomRef} className="pt-24" /> | ||
</div> | ||
) | ||
} | ||
|
||
export default Body |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
'use client' | ||
|
||
import useConversation from '@/app/hooks/useConversation' | ||
import axios from 'axios' | ||
import { CldUploadButton } from 'next-cloudinary' | ||
import { FieldValues, SubmitHandler, useForm } from 'react-hook-form' | ||
import { HiPaperAirplane, HiPhoto } from 'react-icons/hi2' | ||
import MessageInput from './MessageInput' | ||
|
||
const Form = () => { | ||
const { conversationId } = useConversation() | ||
|
||
const { | ||
register, | ||
handleSubmit, | ||
setValue, | ||
formState: { errors }, | ||
} = useForm<FieldValues>({ | ||
defaultValues: { | ||
messages: '', | ||
}, | ||
}) | ||
|
||
const onSubmit: SubmitHandler<FieldValues> = (data) => { | ||
setValue('message', '', { shouldValidate: true }) | ||
axios.post('/api/messages', { | ||
...data, | ||
conversationId, | ||
}) | ||
} | ||
|
||
const handleUpload = (result: any) => { | ||
axios.post('/api/messages', { | ||
image: result?.info?.secure_url, | ||
conversationId, | ||
}) | ||
} | ||
|
||
return ( | ||
<div | ||
className="py-4 px-4 bg-white border-t flex items-center gap-2 | ||
lg:gap-4 w-full" | ||
> | ||
<CldUploadButton | ||
options={{ maxFiles: 1 }} | ||
onUpload={handleUpload} | ||
uploadPreset="lfdklgnr" | ||
> | ||
<HiPhoto size={30} className="text-sky-500" /> | ||
</CldUploadButton> | ||
|
||
<form | ||
onSubmit={handleSubmit(onSubmit)} | ||
className="flex items-center gap-2 lg:gap-4 w-full" | ||
> | ||
<MessageInput | ||
id="message" | ||
register={register} | ||
errors={errors} | ||
required | ||
placeholder="Write a message" | ||
/> | ||
<button | ||
type="submit" | ||
className="rounded-full p-2 bg-sky-500 cursor-pointer hover:bg-sky-600 transition" | ||
> | ||
<HiPaperAirplane size={18} className="text-white" /> | ||
</button> | ||
</form> | ||
</div> | ||
) | ||
} | ||
|
||
export default Form |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
'use client' | ||
|
||
import Avatar from '@/app/components/Avatar' | ||
import useOtherUser from '@/app/hooks/useOtherUser' | ||
import { Conversation, User } from '@prisma/client' | ||
import Link from 'next/link' | ||
import { useMemo, useState } from 'react' | ||
import { HiChevronLeft, HiEllipsisHorizontal } from 'react-icons/hi2' | ||
|
||
interface HeaderProps { | ||
conversation: Conversation & { | ||
user: User[] | ||
} | ||
} | ||
|
||
const Header: React.FC<HeaderProps> = ({ conversation }) => { | ||
const otherUser = useOtherUser(conversation) | ||
const [drawerOpen, setDrawerOpen] = useState(false) | ||
|
||
const statusText = useMemo(() => { | ||
if (conversation.isGroup) { | ||
return `${conversation.user.length} members` | ||
} | ||
|
||
return 'Active' | ||
}, [conversation]) | ||
|
||
return ( | ||
<> | ||
<div | ||
className="bg-white w-full flex border-b-[1px] sm:px-4 py-3 px-4 | ||
lg:px-6 justify-between items-center shadow-sm" | ||
> | ||
<div className="flex gap-3 items-center"> | ||
<Link | ||
className="lg:hidden block text-sky-500 hover:text-sky-600 | ||
transition cursor-pointer" | ||
href="/conversations" | ||
> | ||
<HiChevronLeft size={32} /> | ||
</Link> | ||
<Avatar user={otherUser} /> | ||
<div className="flex flex-col"> | ||
<div>{conversation.name || otherUser.name}</div> | ||
<div className="text-sm font-light text-neutral-500"> | ||
{statusText} | ||
</div> | ||
</div> | ||
</div> | ||
<HiEllipsisHorizontal | ||
size={32} | ||
onClick={() => {}} | ||
className="text-sky-500 cursor-pointer hover:text-sky-600 transition" | ||
/> | ||
</div> | ||
</> | ||
) | ||
} | ||
|
||
export default Header |
Oops, something went wrong.