Skip to content

Conversation

@kmaxii
Copy link
Contributor

@kmaxii kmaxii commented Feb 8, 2026

image image image

Summary by CodeRabbit

  • New Features

    • Added audio player for voice actor recordings with dynamic track loading.
  • Improvements

    • Redesigned comments interface with modernized card-based layout.
    • Reorganized comment form with dedicated contributor and guest submission flows.
    • Added prominent hero section displaying recording information and audio controls.
    • Enhanced responsive design and visual styling across all screen sizes.

@coderabbitai
Copy link

coderabbitai bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

The pull request restructures the comments section frontend and backend. Backend controller adds null-safety checks for NPC and voice actor retrieval, plus assets for an audio player. The view reorganizes with a hero section containing audio player, new card-based comment layout, and restructured form. Comprehensive CSS refactoring introduces theme variables and component styling. JavaScript updates selectors to match the new DOM structure.

Changes

Cohort / File(s) Summary
Backend Defensive Checks
Controllers/Website/Comments.php
Added null/false checks before accessing nested NPC and voice actor properties; introduced intermediate variables for safer property access. Asset loading for audio-player CSS and JS files.
View Restructuring
Views/comments.phtml
Replaced flat layout with hero section containing title and audio player. Refactored form from nested structure to card-based layout with conditional contributor/guest flows. Restructured comments list from table to card-based rendering with updated badges and admin controls. Added action buttons section and back button container.
CSS Theme System & Components
css/comments.css
Comprehensive refactor introducing CSS variable theme system. Replaced legacy selectors with modular component classes (comments-hero, comments-form-card, comment-card, comment-badges, etc.). Added gradient decorations, responsive layouts for tablet/mobile, accessibility focus states, and animation transitions. Removed ad-hoc styling in favor of systematic naming and spacing tokens.
Responsive Margin Adjustment
css/npc.css
Updated .npc-hero negative margin on small viewports from fixed −1.5rem to dynamic calc(−1 * var(--container-padding)) for viewport-aware alignment.
DOM & Event Handler Updates
js/comments.js
Updated JavaScript selectors and DOM manipulation to target new card-based structure. Changed form toggle from form to .comments-form-card. Updated comment rendering to use new class names (comment-card, comment-header, comment-author-info, comment-badges). Adjusted delete handler to use .closest('.comment-card').

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A Carrot-Colored Refactor

With cards so neat and hero fair,
The comments now have structure rare,
Theme variables in colors bright,
Audio players, responsive sight,
Old tables hopped away with care! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "fix: improve comment page" is vague and lacks specificity. While it broadly relates to the changeset, it doesn't clearly communicate the main purpose of the changes (architectural refactor with hero layout, new component styling, and structural improvements). Use a more descriptive title that captures the primary change, such as "refactor: restructure comments page with hero layout and modular components" or "refactor: redesign comments UI with new hero section and card-based layout."
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/comments-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
js/comments.js (2)

100-110: ⚠️ Potential issue | 🟡 Minor

Client-side XSS via unsanitized string interpolation into HTML template.

Guest name ($("#name").val()) and comment content (.val().replace(/\n/g, '<br>')) are inserted into the HTML template via .replace() without escaping. A user entering <img src=x onerror=alert(1)> in the name or comment field would get script execution when the template is prepended to the DOM.

While this is "self-XSS" (only affects the poster's own session until page reload), it's still good practice to sanitize. Consider escaping HTML entities before interpolation:

Proposed helper + usage
function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}

Then use it when building the comment from guest input:

-                name = $("#name").val();
+                name = escapeHtml($("#name").val());
...
-                content = $("#content-guest").val().replace(/\n/g, '<br>');
+                content = escapeHtml($("#content-guest").val()).replace(/\n/g, '<br>');

125-142: ⚠️ Potential issue | 🟠 Major

deleteComment uses event.target which may not carry data-comment-id.

If the user clicks the × text node or a child element inside .delete-comment-button, event.target may not be the button itself. Line 130 correctly uses .closest('.comment-card'), but line 133 still reads $(event.target).attr('data-comment-id'), which would return undefined if event.target is a child node.

Use .closest() consistently:

Proposed fix
 function deleteComment(event) {
     if (!confirm('Do you really want to delete this comment?')) {
         return;
     }
 
-    $deletingComment = $(event.target).closest('.comment-card');
+    var $btn = $(event.target).closest('.delete-comment-button');
+    $deletingComment = $btn.closest('.comment-card');
 
     $.ajax({
-        url: "contents/npc/" + npcId + "/comments/" + $("form").attr('data-recording-id') + "/delete/" + $(event.target).attr('data-comment-id'),
+        url: "contents/npc/" + npcId + "/comments/" + $("form").attr('data-recording-id') + "/delete/" + $btn.attr('data-comment-id'),
         type: 'DELETE',
🤖 Fix all issues with AI agents
In `@Controllers/Website/Comments.php`:
- Around line 53-65: The code currently returns 404 when getVoiceActor() is
null, blocking comments for NPCs without an assigned actor; instead, remove the
early return and set self::$data['comments_voice_actor_id'] to a sentinel (e.g.,
0 or null) when $npc->getVoiceActor() yields null/false so the comments page can
load; locate the block around getNpc(self::$data['comments_recording']->npcId)
and the voice actor check, replace the return 404 path with assigning the
sentinel to self::$data['comments_voice_actor_id'] while preserving the normal
assignment when a User is returned.

In `@css/comments.css`:
- Around line 553-556: The .comment-avatar at the 40rem breakpoint is set to
50×50 but is still constrained by the global min-width: 60px and min-height:
60px; update the media query that sets .comment-avatar to width:50px and
height:50px to also include min-width:50px and min-height:50px (or
remove/override the larger min values) so the avatar can actually shrink; target
the .comment-avatar rule inside the 40rem media query to make this change.
- Around line 574-577: The responsive CSS selector is misspelled: replace
.comment-toggle-btn with the correct .comments-toggle-btn in the rule that
groups it with .comment-submit-btn (the rule that sets width: 100% for both);
update the selector so it reads .comments-toggle-btn, .comment-submit-btn {
width: 100%; } to match the HTML class used and ensure the style applies.

In `@Views/comments.phtml`:
- Around line 70-73: Fix the typo in the disclaimer text by replacing the
sentence "Your IP and e-mail addresses and will be stored, but will never be
publicly visible." with "Your IP and e-mail address will be stored, but will
never be publicly visible." — locate the exact string in the comments template
and update it accordingly.
🧹 Nitpick comments (2)
js/comments.js (1)

28-37: 1500 ms slide animation feels sluggish.

The slideDown(1500) and slideUp(1500) durations are 1.5 seconds each. This is noticeably slow for toggling a form and may frustrate users on repeated interactions. A value in the 300–500 ms range is more typical for slide toggles.

css/comments.css (1)

3-19: Duplicated CSS variables across comments.css and npc.css.

The :root variables here (lines 3–19) are identical in value to those in npc.css (lines 3–19), differing only in the --comments- vs --npc- prefix. Both files also share near-identical hero, card, back-button, and reduced-motion patterns.

Consider extracting shared tokens into a common base stylesheet (e.g., --vow-gradient-purple, --vow-card-shadow, etc.) and referencing them from both files. This would reduce the maintenance burden when theme values change.

Comment on lines +53 to +65
$npc = $cnm->getNpc(self::$data['comments_recording']->npcId);
if ($npc === null || $npc === false) {
//NPC associated with this recording was not found
return 404;
}

$voiceActor = $npc->getVoiceActor();
if ($voiceActor === null || $voiceActor === false) {
//Voice actor for this NPC was not found
return 404;
}

self::$data['comments_voice_actor_id'] = $voiceActor->getId();
Copy link

@coderabbitai coderabbitai bot Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for voice_actor_id usage to understand downstream impact
rg -n 'voice_actor_id' -C 3

Repository: Team-VoW/VoicesOfWynn-Website

Length of output: 6013


🏁 Script executed:

# Check the NPC class to understand getVoiceActor behavior and return types
rg -n 'getVoiceActor|class Npc' --type=php -A 10

Repository: Team-VoW/VoicesOfWynn-Website

Length of output: 10165


🏁 Script executed:

# Look for how comments view uses the voice actor ID
fd -e 'php' -e 'js' -e 'html' | xargs rg -l 'comments_voice_actor_id'

Repository: Team-VoW/VoicesOfWynn-Website

Length of output: 105


Returning 404 when voice actor is missing prevents viewing comments on unassigned NPCs.

NPCs can legitimately have no voice actor assigned—the database schema explicitly allows NULL values for voice_actor_id with ON DELETE SET NULL, and the NPC page itself already handles this case (showing "Nobody" in the cast list). The getVoiceActor() method returns ?User, confirming it can be null.

Returning 404 on line 62 blocks users from viewing or posting comments on recordings for NPCs without an assigned voice actor. Instead, use a sentinel value so the comments page loads:

Proposed fix
 		$voiceActor = $npc->getVoiceActor();
-		if ($voiceActor === null || $voiceActor === false) {
-			//Voice actor for this NPC was not found
-			return 404;
-		}
-
-		self::$data['comments_voice_actor_id'] = $voiceActor->getId();
+		self::$data['comments_voice_actor_id'] = ($voiceActor !== null && $voiceActor !== false)
+			? $voiceActor->getId()
+			: 0;

The author badge comparison in the view will still work correctly—passing 0 or null will simply prevent the badge from displaying when there is no voice actor.

🤖 Prompt for AI Agents
In `@Controllers/Website/Comments.php` around lines 53 - 65, The code currently
returns 404 when getVoiceActor() is null, blocking comments for NPCs without an
assigned actor; instead, remove the early return and set
self::$data['comments_voice_actor_id'] to a sentinel (e.g., 0 or null) when
$npc->getVoiceActor() yields null/false so the comments page can load; locate
the block around getNpc(self::$data['comments_recording']->npcId) and the voice
actor check, replace the return 404 path with assigning the sentinel to
self::$data['comments_voice_actor_id'] while preserving the normal assignment
when a User is returned.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, @kmaxii remove the 404 return for no-voice-actor scenario.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@ShadyMedic ShadyMedic self-requested a review February 12, 2026 19:55
Copy link
Member

@ShadyMedic ShadyMedic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix those small things and it's good to go.

Comment on lines +53 to +65
$npc = $cnm->getNpc(self::$data['comments_recording']->npcId);
if ($npc === null || $npc === false) {
//NPC associated with this recording was not found
return 404;
}

$voiceActor = $npc->getVoiceActor();
if ($voiceActor === null || $voiceActor === false) {
//Voice actor for this NPC was not found
return 404;
}

self::$data['comments_voice_actor_id'] = $voiceActor->getId();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, @kmaxii remove the 404 return for no-voice-actor scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants