-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add collab page #183
Merged
Merged
Add collab page #183
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
0363b51
Create basic collab room skeleton with code editor
dylkaw 46682c7
Add chat placeholder
dylkaw 78b8180
Add Question placeholder
dylkaw 392d1ab
Merge branch 'main' into feat/collab-service/collab-page
dylkaw 4b8b23e
Add routing to collab room
dylkaw 5428fb8
Fix ESLint issues
dylkaw 17c23e5
Fix prettier issues
dylkaw 332084b
Standardise route with /app
dylkaw dec0816
Merge branch 'main' into feat/collab-service/collab-page
SelwynAng 2672ed0
Link up frontend with collab service to redirect to common room
SelwynAng d04771c
Set up real time communication chat between users in same room
SelwynAng 718cf32
Run yarn prettier fix
SelwynAng 3e36b54
Reorganise utility functions for collab service api
SelwynAng ab06445
Resolve scroll down bug
SelwynAng 03858fa
Allow matching service to fetch random question from question service
SelwynAng fd6e676
Enable fetching of question details to the frontend
SelwynAng f6381fa
Edit collab backend such that room requires question id
SelwynAng 6f8b7f1
Remove excess console log statements
SelwynAng 5b01757
Shift create room logic from frontend to matching service
SelwynAng 608f28d
Resolve merge conflict
SelwynAng 6050f39
Integrate collaborative monaco editor
SelwynAng c3f9789
Add language switching
dylkaw 2c6698e
Resolve lint and prettier issues
dylkaw 2bd1ee8
Resolve type issue
dylkaw b9b5bce
Chat: Fix bug where same user won't see message sent from another window
wr1159 b1ef5d6
Merge branch 'feat/collab-service/collab-page' of github.com:CS3219-A…
wr1159 70948a0
Enable toggling of theme for code editor
SelwynAng 759666a
Remove debug statement
SelwynAng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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
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
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
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
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 |
---|---|---|
@@ -1,16 +1,17 @@ | ||
import dynamic from "next/dynamic"; | ||
import AuthPageWrapper from "@/components/auth/auth-page-wrapper"; | ||
import CollabRoom from "@/components/collab/collab-room"; | ||
import { Suspense } from "react"; | ||
|
||
const MonacoEditor = dynamic( | ||
() => import("@/components/collab/monaco-editor"), | ||
{ | ||
ssr: false, | ||
} | ||
); | ||
|
||
export default function CollabRoom({ | ||
export default function CollabPage({ | ||
params, | ||
}: { | ||
params: { room_id: string }; | ||
}) { | ||
return <MonacoEditor roomId={params.room_id} />; | ||
return ( | ||
<AuthPageWrapper requireLoggedIn> | ||
<Suspense> | ||
<CollabRoom roomId={params.room_id} /> | ||
</Suspense> | ||
</AuthPageWrapper> | ||
); | ||
} |
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,184 @@ | ||
"use client"; | ||
|
||
import React, { useState, useEffect, useRef } from "react"; | ||
import { Button } from "@/components/ui/button"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | ||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | ||
import { ScrollArea } from "@/components/ui/scroll-area"; | ||
import { Send } from "lucide-react"; | ||
import { io, Socket } from "socket.io-client"; | ||
import { useAuth } from "@/app/auth/auth-context"; | ||
import LoadingScreen from "@/components/common/loading-screen"; | ||
|
||
interface Message { | ||
id: string; | ||
userId: string; | ||
text: string; | ||
timestamp: Date; | ||
} | ||
|
||
export default function Chat({ roomId }: { roomId: string }) { | ||
const auth = useAuth(); | ||
const own_user_id = auth?.user?.id; | ||
const [socket, setSocket] = useState<Socket | null>(null); | ||
const [chatTarget, setChatTarget] = useState<string>("partner"); | ||
const [newMessage, setNewMessage] = useState<string>(""); | ||
const [partnerMessages, setPartnerMessages] = useState<Message[]>([]); | ||
const [aiMessages, setAiMessages] = useState<Message[]>([]); | ||
const [isConnected, setIsConnected] = useState(false); | ||
const lastMessageRef = useRef<HTMLDivElement | null>(null); | ||
|
||
useEffect(() => { | ||
if (!auth?.user?.id) return; // Avoid connecting if user is not authenticated | ||
|
||
const socketInstance = io( | ||
process.env.NEXT_PUBLIC_COLLAB_SERVICE_URL || "http://localhost:3002", | ||
{ | ||
auth: { userId: own_user_id }, | ||
} | ||
); | ||
|
||
socketInstance.on("connect", () => { | ||
console.log("Connected to Socket.IO"); | ||
setIsConnected(true); | ||
socketInstance.emit("joinRoom", roomId); | ||
}); | ||
|
||
socketInstance.on("disconnect", () => { | ||
console.log("Disconnected from Socket.IO"); | ||
setIsConnected(false); | ||
}); | ||
|
||
socketInstance.on("chatMessage", (message: Message) => { | ||
setPartnerMessages((prev) => [...prev, message]); | ||
}); | ||
|
||
setSocket(socketInstance); | ||
|
||
return () => { | ||
socketInstance.disconnect(); | ||
}; | ||
}, [roomId, own_user_id, auth?.user?.id]); | ||
|
||
useEffect(() => { | ||
const scrollWithDelay = () => { | ||
setTimeout(() => { | ||
if (lastMessageRef.current) { | ||
lastMessageRef.current.scrollIntoView({ behavior: "smooth" }); | ||
} | ||
}, 100); // Delay to ensure the DOM is fully rendered | ||
}; | ||
|
||
scrollWithDelay(); | ||
}, [partnerMessages, aiMessages, chatTarget]); | ||
|
||
const sendMessage = () => { | ||
if (!newMessage.trim() || !socket || !isConnected || !own_user_id) return; | ||
|
||
const message = { | ||
id: crypto.randomUUID(), | ||
userId: own_user_id, | ||
text: newMessage, | ||
timestamp: new Date(), | ||
}; | ||
|
||
if (chatTarget === "partner") { | ||
socket.emit("sendMessage", { | ||
roomId, | ||
userId: own_user_id, | ||
text: newMessage, | ||
}); | ||
} else { | ||
setAiMessages((prev) => [...prev, message]); | ||
} | ||
|
||
setNewMessage(""); | ||
}; | ||
|
||
const formatTimestamp = (date: Date) => { | ||
return new Date(date).toLocaleTimeString([], { | ||
hour: "2-digit", | ||
minute: "2-digit", | ||
}); | ||
}; | ||
|
||
const renderMessage = (message: Message, isOwnMessage: boolean) => ( | ||
<div | ||
key={message.id} | ||
className={`p-2 rounded-lg mb-2 max-w-[80%] ${ | ||
isOwnMessage | ||
? "ml-auto bg-blue-500 text-white" | ||
: "bg-gray-100 dark:bg-gray-800" | ||
}`} | ||
> | ||
<div className="text-sm">{message.text}</div> | ||
<div | ||
className={`text-xs ${isOwnMessage ? "text-blue-100" : "text-gray-500"}`} | ||
> | ||
{formatTimestamp(message.timestamp)} | ||
</div> | ||
</div> | ||
); | ||
|
||
if (!own_user_id) { | ||
return <LoadingScreen />; | ||
} | ||
|
||
return ( | ||
<Card className="flex flex-col"> | ||
<CardHeader> | ||
<CardTitle className="flex justify-between items-center"> | ||
Chat | ||
<span | ||
className={`h-2 w-2 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}`} | ||
/> | ||
</CardTitle> | ||
</CardHeader> | ||
<CardContent className="flex-1 flex flex-col"> | ||
<Tabs | ||
value={chatTarget} | ||
onValueChange={setChatTarget} | ||
className="flex-col" | ||
> | ||
<TabsList className="flex-shrink-0 mb-2"> | ||
<TabsTrigger value="partner">Partner Chat</TabsTrigger> | ||
<TabsTrigger value="ai">AI Chat</TabsTrigger> | ||
</TabsList> | ||
<TabsContent value="partner" className="h-full"> | ||
<ScrollArea className="h-[calc(70vh-280px)]"> | ||
<div className="pr-4 space-y-2"> | ||
{partnerMessages.map((msg) => | ||
renderMessage(msg, msg.userId === own_user_id) | ||
)} | ||
<div ref={lastMessageRef} /> | ||
</div> | ||
</ScrollArea> | ||
</TabsContent> | ||
<TabsContent value="ai" className="h-full"> | ||
<ScrollArea className="h-[calc(70vh-280px)]"> | ||
<div className="pr-4 space-y-2"> | ||
{aiMessages.map((msg) => | ||
renderMessage(msg, msg.userId === own_user_id) | ||
)} | ||
<div ref={lastMessageRef} /> | ||
</div> | ||
</ScrollArea> | ||
</TabsContent> | ||
</Tabs> | ||
<div className="flex space-x-2 mt-4 pt-4 border-t"> | ||
<Input | ||
value={newMessage} | ||
onChange={(e) => setNewMessage(e.target.value)} | ||
placeholder={`Message ${chatTarget === "partner" ? "your partner" : "AI assistant"}...`} | ||
onKeyDown={(e) => e.key === "Enter" && sendMessage()} | ||
disabled={!isConnected} | ||
/> | ||
<Button onClick={sendMessage} disabled={!isConnected}> | ||
<Send className="h-4 w-4" /> | ||
</Button> | ||
</div> | ||
</CardContent> | ||
</Card> | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this if we're implementing the past rooms history feature