From 40e48144d3c13f37d3efd7996f076d8c5703b6e7 Mon Sep 17 00:00:00 2001 From: Phineas Truong <56217067+phintruong@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:21:13 -0400 Subject: [PATCH 1/5] need testing --- src/app/room/classChat/index.tsx | 18 +++ src/app/room/classChat/post/QuestionPost.tsx | 19 ++- src/app/room/classChat/post/index.tsx | 3 + src/socket/handlers/questionHandlers.ts | 118 +++++++++++++++++++ src/socket/index.ts | 2 + src/socket/types.ts | 11 ++ 6 files changed, 169 insertions(+), 2 deletions(-) diff --git a/src/app/room/classChat/index.tsx b/src/app/room/classChat/index.tsx index 5ba99a5..d5d35d3 100644 --- a/src/app/room/classChat/index.tsx +++ b/src/app/room/classChat/index.tsx @@ -225,6 +225,12 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { ); }; + const onQuestionUnresolved = (payload: { id: string }) => { + setQuestions((prev) => + prev.map((q) => (q.id === payload.id ? { ...q, isResolved: false } : q)) + ); + }; + const onAnswerCreated = (payload: { id: string; questionId: string; @@ -386,6 +392,7 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { socket.on("question:created", onQuestionCreated); socket.on("question:updated", onQuestionUpdated); socket.on("question:resolved", onQuestionResolved); + socket.on("question:unresolved", onQuestionUnresolved); socket.on("question:deleted", onQuestionDeleted); socket.on("answer:created", onAnswerCreated); socket.on("answer:updated", onAnswerUpdated); @@ -400,6 +407,7 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { socket.off("question:created", onQuestionCreated); socket.off("question:updated", onQuestionUpdated); socket.off("question:resolved", onQuestionResolved); + socket.off("question:unresolved", onQuestionUnresolved); socket.off("question:deleted", onQuestionDeleted); socket.off("answer:created", onAnswerCreated); socket.off("answer:updated", onAnswerUpdated); @@ -454,6 +462,13 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { setQuestions((prev) => prev.map((q) => (q.id === questionId ? { ...q, isResolved: true } : q))); }; + const handleUnresolve = (questionId: string) => { + if (!socket) return; + socket.emit("question:unresolve", { questionId }); + // Optimistic update + setQuestions((prev) => prev.map((q) => (q.id === questionId ? { ...q, isResolved: false } : q))); + }; + const handleSubmitAnswer = (questionId: string, content: string) => { if (!socket) return; socket.emit("answer:create", { questionId, content, isAnonymous: globalIsAnonymous }); @@ -565,6 +580,9 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { ? () => handleResolve(q.id) : undefined } + onUnresolve={ + isInstructor ? () => handleUnresolve(q.id) : undefined + } canAnswer={canAnswerGlobal || q.user?.id === userId} onSubmitAnswer={(content) => handleSubmitAnswer(q.id, content)} onAnswerUpvote={handleAnswerUpvote} diff --git a/src/app/room/classChat/post/QuestionPost.tsx b/src/app/room/classChat/post/QuestionPost.tsx index 2b383dd..50aeba5 100644 --- a/src/app/room/classChat/post/QuestionPost.tsx +++ b/src/app/room/classChat/post/QuestionPost.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; -import { MessageCircle, CheckCircle2, Trash2, ChevronDown, ChevronUp } from "lucide-react"; +import { MessageCircle, CheckCircle2, Undo2, Trash2, ChevronDown, ChevronUp } from "lucide-react"; import { Question, Post } from "@/utils/types"; import { UpvoteButton, renderUsername } from "./PostUtils"; @@ -111,6 +111,7 @@ export default function QuestionPost({ canAnswer = true, onUpvote, onResolve, + onUnresolve, onDelete, onSubmitAnswer, children, @@ -122,6 +123,7 @@ export default function QuestionPost({ canAnswer?: boolean; onUpvote?: () => void; onResolve?: () => void; + onUnresolve?: () => void; onDelete?: () => void; onSubmitAnswer?: (content: string) => void; children?: React.ReactNode; @@ -159,7 +161,7 @@ export default function QuestionPost({ const showThread = threadState !== "collapsed" && (visibleReplies.length > 0 || isReplying); return ( -
+
{/* Question body */}
{post.content}
@@ -248,6 +250,19 @@ export default function QuestionPost({ )} + {onUnresolve && resolved && ( + + )} + {onDelete && ( )} + + + AskEasy logo +
)} diff --git a/src/app/room/page.tsx b/src/app/room/page.tsx index 2fa1ba9..38d1318 100644 --- a/src/app/room/page.tsx +++ b/src/app/room/page.tsx @@ -296,7 +296,7 @@ function RoomInner() { return ( -
+
{isSlidesVisible ? (
From 75d6c41ae7658fabe4e03b4c244b6bafc7683f36 Mon Sep 17 00:00:00 2001 From: Marwan Date: Sat, 4 Apr 2026 01:58:07 -0400 Subject: [PATCH 4/5] lint --- src/app/room/classChat/ChatHeader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/room/classChat/ChatHeader.tsx b/src/app/room/classChat/ChatHeader.tsx index e911e62..9fd8ece 100644 --- a/src/app/room/classChat/ChatHeader.tsx +++ b/src/app/room/classChat/ChatHeader.tsx @@ -157,7 +157,6 @@ export default function ChatHeader({ )} )} - Date: Sat, 4 Apr 2026 02:02:11 -0400 Subject: [PATCH 5/5] typecheck --- src/types/images.d.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/types/images.d.ts diff --git a/src/types/images.d.ts b/src/types/images.d.ts new file mode 100644 index 0000000..b1d1ec0 --- /dev/null +++ b/src/types/images.d.ts @@ -0,0 +1,29 @@ +declare module "*.png" { + import type { StaticImageData } from "next/image"; + const content: StaticImageData; + export default content; +} + +declare module "*.jpg" { + import type { StaticImageData } from "next/image"; + const content: StaticImageData; + export default content; +} + +declare module "*.jpeg" { + import type { StaticImageData } from "next/image"; + const content: StaticImageData; + export default content; +} + +declare module "*.webp" { + import type { StaticImageData } from "next/image"; + const content: StaticImageData; + export default content; +} + +declare module "*.svg" { + import type { StaticImageData } from "next/image"; + const content: StaticImageData; + export default content; +}