From 7141763bc61209878689197b0477c9e5452a57f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Sat, 7 Feb 2026 18:23:46 +0300 Subject: [PATCH] refactor: Optimize comment deletion handling with optimistic UI updates --- .../[projectId]/videos/[videoId]/page.tsx | 55 +++++++++++------- app/watch/[videoId]/page.tsx | 58 ++++++++++++------- 2 files changed, 70 insertions(+), 43 deletions(-) diff --git a/app/(dashboard)/projects/[projectId]/videos/[videoId]/page.tsx b/app/(dashboard)/projects/[projectId]/videos/[videoId]/page.tsx index 74bfaff..b60367a 100644 --- a/app/(dashboard)/projects/[projectId]/videos/[videoId]/page.tsx +++ b/app/(dashboard)/projects/[projectId]/videos/[videoId]/page.tsx @@ -1132,41 +1132,54 @@ export default function VideoPage() { // Delete a comment const handleDeleteComment = useCallback(async (commentId: string) => { setDeletingCommentId(commentId); + + // Optimistically remove from UI + const previousVideo = video; + setVideo((prev) => { + if (!prev) return prev; + return { + ...prev, + versions: prev.versions.map((v) => + v.id === activeVersionId + ? { + ...v, + comments: v.comments + .filter((c) => c.id !== commentId) + .map((c) => ({ + ...c, + replies: c.replies.filter((r) => r.id !== commentId), + })), + } + : v + ), + }; + }); + try { const res = await fetch(`/api/comments/${commentId}`, { method: 'DELETE' }); - if (res.ok) { - setVideo((prev) => { - if (!prev) return prev; - return { - ...prev, - versions: prev.versions.map((v) => - v.id === activeVersionId - ? { - ...v, - comments: v.comments - .filter((c) => c.id !== commentId) - .map((c) => ({ - ...c, - replies: c.replies.filter((r) => r.id !== commentId), - })), - } - : v - ), - }; - }); + if (!res.ok) { + setVideo(previousVideo); } } catch (err) { console.error('Failed to delete comment:', err); + setVideo(previousVideo); } finally { setDeletingCommentId(null); } - }, [activeVersionId]); + }, [activeVersionId, video]); // Poll for new comments every 10 seconds useEffect(() => { if (!activeVersion) return; const interval = setInterval(async () => { try { + const hasPendingComments = activeVersion.comments.some( + (c) => c.id.startsWith('temp-') + ) || activeVersion.comments.some( + (c) => c.replies.some((r) => r.id.startsWith('temp-')) + ); + if (hasPendingComments) return; + const res = await fetch(`/api/projects/${projectId}/videos/${videoId}`); if (res.ok) { const data = await res.json(); diff --git a/app/watch/[videoId]/page.tsx b/app/watch/[videoId]/page.tsx index a681b94..76cbe0c 100644 --- a/app/watch/[videoId]/page.tsx +++ b/app/watch/[videoId]/page.tsx @@ -1085,41 +1085,55 @@ export default function WatchPage() { // Delete a comment const handleDeleteComment = useCallback(async (commentId: string) => { setDeletingCommentId(commentId); + + // Optimistically remove from UI + const previousVideo = video; + setVideo((prev) => { + if (!prev) return prev; + return { + ...prev, + versions: prev.versions.map((v) => + v.id === activeVersionId + ? { + ...v, + comments: v.comments + .filter((c) => c.id !== commentId) + .map((c) => ({ + ...c, + replies: c.replies.filter((r) => r.id !== commentId), + })), + } + : v + ), + }; + }); + try { const res = await fetch(`/api/comments/${commentId}`, { method: 'DELETE' }); - if (res.ok) { - setVideo((prev) => { - if (!prev) return prev; - return { - ...prev, - versions: prev.versions.map((v) => - v.id === activeVersionId - ? { - ...v, - comments: v.comments - .filter((c) => c.id !== commentId) - .map((c) => ({ - ...c, - replies: c.replies.filter((r) => r.id !== commentId), - })), - } - : v - ), - }; - }); + if (!res.ok) { + setVideo(previousVideo); } } catch (err) { console.error('Failed to delete comment:', err); + setVideo(previousVideo); } finally { setDeletingCommentId(null); } - }, [activeVersionId]); + }, [activeVersionId, video]); // Poll for new comments every 10 seconds useEffect(() => { if (!video) return; const interval = setInterval(async () => { try { + const activeVersion = video.versions.find((v) => v.id === activeVersionId); + const hasPendingComments = activeVersion?.comments.some( + (c) => c.id.startsWith('temp-') + ) || activeVersion?.comments.some( + (c) => c.replies.some((r) => r.id.startsWith('temp-')) + ); + if (hasPendingComments) return; + const res = await fetch(`/api/watch/${videoId}`); if (res.ok) { const data = await res.json(); @@ -1128,7 +1142,7 @@ export default function WatchPage() { } catch { /* silent */ } }, 10000); return () => clearInterval(interval); - }, [video, videoId]); + }, [video, videoId, activeVersionId]); const getEmbedUrl = (version: Version) => { if (version.providerId === 'youtube') {