fix(chat): show username instead of invalid date, Anonymous instead of dash#45
fix(chat): show username instead of invalid date, Anonymous instead of dash#45AKSHEXXXX wants to merge 2 commits intoZenYukti:mainfrom
Conversation
…ad of dash - When message timestamp is invalid, show 'You' for own messages and sender nickname for others (instead of 'Invalid Date') - Replace '—' fallback with 'Anonymous' when nickname is missing - Own messages remain on the right with 'You'; others on the left with their username Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
This PR fixes two display issues in the ChatRoom component: showing "You" / sender nickname instead of "Invalid Date" when a message timestamp cannot be parsed, and showing "Anonymous" instead of an empty string when a sender's nickname is missing. It also introduces client-side message validation (character counter, warning messages, send-button gating) by importing from a new messageValidation utility.
Changes:
- Invalid timestamp fallback: Timestamp
<span>now shows "You" or the sender's nickname when the date cannot be parsed, rather than "Invalid Date". - Missing nickname fallback: The sender label now shows "Anonymous" instead of rendering nothing when
msg.nicknameis falsy. - New message validation UI: Imports
getMessageStats,getWarningMessage,isSendButtonDisabled, andvalidateMessagefrom../lib/messageValidationto add a character counter, warning message, server-error display, and smarter send-button disabling.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import { | ||
| getMessageStats, | ||
| getWarningMessage, | ||
| isSendButtonDisabled, | ||
| validateMessage, | ||
| } from '../lib/messageValidation'; | ||
|
|
There was a problem hiding this comment.
The file frontend/src/lib/messageValidation.ts (or .js) is imported on lines 3–8 but does not exist in the repository. All four functions used — getMessageStats, getWarningMessage, isSendButtonDisabled, and validateMessage — are sourced from this missing module. This will cause a compile-time error (TypeScript/Vite will fail to resolve the module) and the chat UI will not load at all. The missing file must be created and exported before this PR can be merged.
| import { | |
| getMessageStats, | |
| getWarningMessage, | |
| isSendButtonDisabled, | |
| validateMessage, | |
| } from '../lib/messageValidation'; | |
| const MAX_MESSAGE_LENGTH = 1000; | |
| interface MessageStats { | |
| charCount: number; | |
| wordCount: number; | |
| maxChars: number; | |
| } | |
| function getMessageStats(message: string): MessageStats { | |
| const trimmed = message ?? ''; | |
| const charCount = trimmed.length; | |
| const wordCount = trimmed.trim() === '' ? 0 : trimmed.trim().split(/\s+/).length; | |
| return { | |
| charCount, | |
| wordCount, | |
| maxChars: MAX_MESSAGE_LENGTH, | |
| }; | |
| } | |
| function getWarningMessage(stats: MessageStats): string { | |
| if (stats.charCount === 0) { | |
| return ''; | |
| } | |
| const threshold = Math.floor(stats.maxChars * 0.9); | |
| if (stats.charCount > stats.maxChars) { | |
| return `Your message is too long. The maximum length is ${stats.maxChars} characters.`; | |
| } | |
| if (stats.charCount >= threshold) { | |
| return `You are approaching the maximum message length of ${stats.maxChars} characters.`; | |
| } | |
| return ''; | |
| } | |
| function validateMessage(message: string): string { | |
| const trimmed = (message ?? '').trim(); | |
| if (trimmed.length === 0) { | |
| return 'Message cannot be empty.'; | |
| } | |
| if (trimmed.length > MAX_MESSAGE_LENGTH) { | |
| return `Message cannot be longer than ${MAX_MESSAGE_LENGTH} characters.`; | |
| } | |
| return ''; | |
| } | |
| function isSendButtonDisabled(message: string, validationError: string): boolean { | |
| const trimmed = (message ?? '').trim(); | |
| if (validationError) { | |
| return true; | |
| } | |
| return trimmed.length === 0; | |
| } |
| const d = new Date(msg.timestamp); | ||
| const isValid = !Number.isNaN(d.getTime()); | ||
| if (isValid) return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | ||
| return msg.userId === currentUser.id ? 'You' : (msg.nickname || 'Anonymous'); |
There was a problem hiding this comment.
When msg.timestamp is invalid, the timestamp <span> (line 246) falls back to showing 'You' (for own messages) or msg.nickname || 'Anonymous' (for others). However, the sender-name <span> immediately above (line 231) already displays exactly the same values. This means both spans would render the same text side by side — e.g., "You · You" or "Alice · Alice" — which is confusing and provides no useful information in the timestamp position. A more appropriate fallback would be an empty string, a dash, or simply omitting the timestamp span entirely when the date is invalid.
| return msg.userId === currentUser.id ? 'You' : (msg.nickname || 'Anonymous'); | |
| return '-'; |
| maxLength={500} | ||
| /> | ||
| <button | ||
| type="submit" | ||
| disabled={!isConnected || !inputValue.trim()} | ||
| disabled={!isConnected || sendDisabled} | ||
| className="btn-primary px-6 disabled:opacity-50 disabled:cursor-not-allowed" | ||
| > | ||
| Send | ||
| </button> | ||
| </div> | ||
| <p className="text-xs text-gray-500 mt-2"> | ||
| 💡 Be kind and supportive. All messages are monitored for safety. | ||
| </p> | ||
| <div className="mt-2 flex items-center justify-between"> | ||
| <p className="text-xs text-gray-500"> | ||
| 💡 Be kind and supportive. All messages are monitored for safety. | ||
| </p> | ||
| <span | ||
| className={`text-xs tabular-nums ${ | ||
| stats.isNearLimit ? (stats.percentUsed >= 95 ? 'text-red-600 font-medium' : 'text-amber-600') : 'text-gray-500' | ||
| }`} | ||
| > | ||
| {stats.characterCount} / {stats.maxLength} |
There was a problem hiding this comment.
The <input> element has a hardcoded maxLength={500} attribute (line 289), while the character counter displays stats.maxLength (line 308) sourced from the missing messageValidation module. If messageValidation.getMessageStats uses a different maximum length, the browser-enforced maxLength on the input and the displayed counter will be inconsistent — the user could see a counter limit that doesn't match the actual character cap. Once the messageValidation module is created, ensure that stats.maxLength and the maxLength attribute agree, or derive the maxLength prop dynamically from stats.maxLength.
| }`} | ||
| > | ||
| {msg.userId === currentUser.id ? 'You' : msg.nickname} | ||
| {msg.userId === currentUser.id ? 'You' : (msg.nickname || 'Anonymous')} |
There was a problem hiding this comment.
The PR title and description claim the original code showed a "—" (dash) when the sender's nickname was missing, but the original expression msg.userId === currentUser.id ? 'You' : msg.nickname would render nothing (empty) when msg.nickname is falsy — not a dash. The PR description is slightly inaccurate in this claim, though the fix itself (using 'Anonymous' as the fallback) is still a valid improvement.
ChatRoom imports getMessageStats, getWarningMessage, isSendButtonDisabled, and validateMessage from this module. Adding the file so the project builds. Made-with: Cursor
Summary
Fixes chat message display when timestamp is invalid or nickname is missing.
Changes
Testing
Made with Cursor