Skip to content

Commit 8a79b9b

Browse files
Merge pull request lugnitdgp#23 from skndash96/forward-msg
Added Forward message feature
2 parents 7149207 + 4b21c77 commit 8a79b9b

File tree

11 files changed

+199
-23
lines changed

11 files changed

+199
-23
lines changed

chatopia/app/api/conversations/route.ts

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import getCurrentUser from "@/app/actions/getCurrentUser";
22
import { NextResponse } from "next/server";
33
import prisma from "@/app/libs/prismadb";
4+
import getConversations from "@/app/actions/getConversations";
5+
6+
export async function GET(request: Request) {
7+
try {
8+
const conversations = await getConversations();
9+
10+
return NextResponse.json(conversations, { status: 200 });
11+
} catch (e) {
12+
console.log(e, "ERROR_CONVERSATIONS_GET");
13+
return new NextResponse("Internal Error", { status: 500});
14+
}
15+
}
416

517
export async function POST(request : Request){
618
try{

chatopia/app/api/messages/route.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export async function POST(
1212
message,
1313
file,
1414
fileType,
15-
conversationId
15+
conversationId,
16+
isForwarded
1617
} = body;
1718

1819
// If user is not logged in, return unauthorized
@@ -26,6 +27,7 @@ export async function POST(
2627
body: message,
2728
file: file,
2829
fileType,
30+
isForwarded: !!isForwarded,
2931
conversation: {
3032
connect: { id: conversationId }
3133
},

chatopia/app/components/inputs/Select.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ interface SelectProps {
99
onChange: (value: Record<string, any>) => void;
1010
options: Record<string, any>[];
1111
disabled?: boolean;
12+
isMulti?: boolean;
1213
}
1314

1415
const Select: React.FC<SelectProps> = ({
15-
label,value,onChange,options,disabled
16+
label,value,onChange,options,disabled,isMulti
1617
}) => {
1718
return (
1819
<div className={styles.wrapper}>
@@ -26,7 +27,7 @@ const Select: React.FC<SelectProps> = ({
2627
isDisabled={disabled}
2728
value={value}
2829
onChange={onChange}
29-
isMulti
30+
isMulti={isMulti || true}
3031
options={options}
3132
menuPortalTarget={document.body}
3233
styles={{

chatopia/app/conversations/[conversationId]/components/Form.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import React, { useState , useRef, useEffect } from "react";
1717
import Modal from "@/app/components/Modal";
1818
import {socket} from "@/socket";
1919

20-
import { FileUpload } from 'primereact/fileupload';
21-
2220
const Form = () => {
2321
const { conversationId } = useConversation();
2422
const [isEmojiVisible, setIsEmojiVisible] = useState(false);
@@ -35,7 +33,7 @@ const Form = () => {
3533
} = useForm<FieldValues>({
3634
defaultValues: {
3735
message: '',
38-
image: ''
36+
file: ''
3937
}
4038
});
4139

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import Button from "@/app/components/Button";
2+
import Select from "@/app/components/inputs/Select";
3+
import Modal from "@/app/components/Modal";
4+
import { FullConversationType, FullMessageType } from "@/app/types";
5+
import { socket } from "@/socket";
6+
import { Conversation } from "@prisma/client";
7+
import axios from "axios";
8+
import { useSession } from "next-auth/react";
9+
import { useEffect, useState } from "react";
10+
import toast from "react-hot-toast";
11+
12+
interface ForwardModalProps {
13+
data: FullMessageType,
14+
isOpen: boolean,
15+
setIsOpen: (status: boolean) => void
16+
};
17+
18+
const ForwardModal: React.FC<ForwardModalProps> = ({
19+
data: { file, fileType, body, id }, isOpen, setIsOpen
20+
}) => {
21+
const { data: session, status } = useSession();
22+
const [isLoading, setIsLoading] = useState<boolean>(false);
23+
const [to, setTo] = useState<string[]>([]);
24+
const [conversations, setConversations] = useState<Conversation[]>([]);
25+
26+
const handleForward = async () => {
27+
if (to.length === 0) {
28+
toast.error("Please select atleast one conversation");
29+
return;
30+
}
31+
32+
setIsLoading(true);
33+
34+
await Promise.all(to.map(conversationId => {
35+
return new Promise(resolve => {
36+
axios.post("/api/messages", {
37+
message: body,
38+
conversationId,
39+
isForwarded: true,
40+
file,
41+
fileType
42+
})
43+
.then(response => {
44+
socket.emit('send_message', response.data);
45+
socket.emit('update_conversation', response.data);
46+
})
47+
.catch(error => {
48+
console.error(error);
49+
toast.error("Failed to forward");
50+
})
51+
.finally((() => resolve(1)));
52+
});
53+
}));
54+
55+
toast.success("Forwarded");
56+
setIsLoading(false);
57+
setTo([]);
58+
setIsOpen(false);
59+
};
60+
61+
useEffect(() => {
62+
if (!session?.user) {
63+
setConversations([]);
64+
return;
65+
}
66+
67+
axios.get("/api/conversations")
68+
.then(res => {
69+
const conversations = res.data;
70+
71+
const mapped = conversations.map((c: FullConversationType) => {
72+
if (c.isGroup) {
73+
return {
74+
label: c.name,
75+
value: c.id
76+
};
77+
} else {
78+
const otherUserConversation = c.userConversations.find((uc:any) => uc.user.email !== session?.user?.email);
79+
80+
if (!otherUserConversation) return {
81+
label: c.userConversations[0]?.user?.name || c.id,
82+
value: c.id
83+
};
84+
85+
return {
86+
label: otherUserConversation.user.name,
87+
value: c.id
88+
};
89+
}
90+
});
91+
92+
setConversations(mapped);
93+
})
94+
.catch(error => {
95+
console.error(error);
96+
toast.error("Something went wrong");
97+
});
98+
}, [session]);
99+
100+
return (
101+
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
102+
<p>
103+
{body?.slice(0,50)}
104+
{file?.split("/").pop()}
105+
</p>
106+
<br/>
107+
<Select
108+
options={conversations}
109+
label="Forward to"
110+
onChange={(records: any) => setTo(records.map((r: any) => r.value))}
111+
/>
112+
<br/>
113+
<Button onClick={handleForward} disabled={!to.length || isLoading}>
114+
Forward
115+
</Button>
116+
</Modal>
117+
);
118+
};
119+
120+
export default ForwardModal;

chatopia/app/conversations/[conversationId]/components/MessageBox.module.css

+30-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
}
4444

4545
.msg{
46-
overflow: hidden;
46+
position: relative;
4747
width: fit-content;
4848
font-size: 0.875rem;
4949
line-height: 1.25rem;
@@ -98,6 +98,35 @@
9898
border-radius: 9999px;
9999
}
100100

101+
.fwdBox{
102+
position: absolute;
103+
bottom: 0;
104+
right: 0;
105+
transform: translateY(100%);
106+
display: none;
107+
z-index: 2;
108+
}
109+
110+
.msg:hover .fwdBox, .fwdBox:hover {
111+
display: block;
112+
}
113+
114+
.fwdBtn{
115+
border: none;
116+
outline: none;
117+
padding: .5rem;
118+
cursor: pointer;
119+
background: #000;
120+
color: #fff;
121+
border-radius: 0.375rem;
122+
}
123+
124+
.fwdMsg{
125+
margin-top: -.1rem;
126+
opacity: .7;
127+
font-size: .75rem;
128+
}
129+
101130
.seenStatus{
102131
font-size: 0.75rem;
103132
line-height: 1rem;

chatopia/app/conversations/[conversationId]/components/MessageBox.tsx

+16-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { FullMessageType } from "@/app/types";
55
import { useSession } from "next-auth/react";
66
import { format } from "date-fns";
77
import { useState } from "react";
8-
import Image from "next/image";
98
import ImageModal from "./ImageModal";
109
import clsx from "clsx";
1110
import styles from "./MessageBox.module.css"
1211
import { IoDocument, IoImage } from "react-icons/io5";
1312
import Link from "next/link";
13+
import ForwardModal from "./ForwardModal";
1414

1515
interface MessageBoxProps {
1616
data: FullMessageType;
@@ -19,7 +19,7 @@ interface MessageBoxProps {
1919

2020
const MessageBox: React.FC<MessageBoxProps> = ({ data, isLast }) => {
2121
const session = useSession();
22-
const [imageModalOpen, setImageModalOpen] = useState(false);
22+
const [fwdModalOpen, setFwdModalOpen] = useState<boolean>(false);
2323
const [imageOpen, setImageOpen] = useState<boolean>(false);
2424

2525
const isOwn = session?.data?.user?.email === data?.sender?.email;
@@ -79,7 +79,21 @@ const MessageBox: React.FC<MessageBoxProps> = ({ data, isLast }) => {
7979
</Link>
8080
)
8181
)}
82+
83+
<div className={clsx(styles.fwdBox)}>
84+
<button className={clsx(styles.fwdBtn)} onClick={() => setFwdModalOpen(true)}>
85+
Forward
86+
</button>
87+
88+
<ForwardModal setIsOpen={setFwdModalOpen} isOpen={fwdModalOpen} data={data} />
89+
</div>
8290
</div>
91+
92+
{data.isForwarded && (
93+
<div className={clsx(styles.fwdMsg)}>
94+
<span>forwarded</span>
95+
</div>
96+
)}
8397
{isLast && isOwn && seenList.length > 0 && (
8498
<div className={styles.seenStaus}>
8599
{`Seen by ${seenList}`}

chatopia/app/conversations/components/ConversationBox.tsx

+2-13
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,10 @@ const ConversationBox: React.FC<ConversationBoxProps> = ({
7272
}, [userEmail, lastMessage]);
7373

7474
const lastMessageText = useMemo(() => {
75-
// If last message of conversation is an image
76-
if (lastMessage?.image) {
77-
return 'Sent an image';
78-
}
79-
80-
// If last message of conversation is a text message
81-
if (lastMessage?.body) {
82-
return lastMessage.body;
83-
}
84-
85-
// If no last message exists
86-
return "Started a conversation";
75+
return (lastMessage?.body) || (lastMessage?.file ? "Sent a file" : "Started a conversation");
8776
}, [lastMessage]);
8877

89-
console.log("active list:" , activeList);
78+
console.log("active list:", activeList);
9079
// const activeEmails = activeList.map((activeUser)=>activeUser.email );
9180
// console.log("active emails:" , activeEmails);
9281
const isActive = activeList.includes(otherUser.email!);

chatopia/app/conversations/components/ConversationList.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ const ConversationList: React.FC<ConversationListProps> = ({
8585
// To update the conversation list when a new conversation is created
8686
const newConversationHandler = (conversation: FullConversationType) => {
8787
// Check if the conversation to be added already exists in the conversation list
88+
if (!conversation.userConversations.find(uc => uc.user.email === userEmail)) {
89+
return;
90+
}
91+
8892
setItems((current) => {
8993
if (find(current, { id: conversation.id })) {
9094
return current;

chatopia/app/conversations/components/GroupChatModal.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { toast } from "react-hot-toast";
1313
import styles from "./GroupChatModal.module.css";
1414
import Image from "next/image";
1515
import { CldUploadButton } from "next-cloudinary";
16+
import { socket } from "@/socket";
1617

1718
interface GroupChatModalProps {
1819
isOpen?: boolean;
@@ -52,7 +53,12 @@ const GroupChatModal: React.FC<GroupChatModalProps> = ({
5253
...data,
5354
isGroup: true
5455
})
55-
.then(() => {
56+
.then(response => {
57+
if (response.data.isGroup) {
58+
alert(response.data.id);
59+
socket.emit('new_conversation', response.data);
60+
}
61+
5662
router.refresh();
5763
onClose();
5864
})

chatopia/prisma/schema.prisma

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ model Message {
7373
body String?
7474
file String?
7575
fileType String?
76+
isForwarded Boolean?
7677
createdAt DateTime @default(now())
7778
7879
conversationId String @db.Uuid

0 commit comments

Comments
 (0)