Closes FUNCTIONAL_AUDIT.md §4 #1: WebRTC 1:1 calls had working
signaling but no NAT traversal, so calls between two peers behind
symmetric NAT (corporate firewalls, mobile carrier CGNAT, Incus
container default networking) failed silently after the SDP exchange.
Backend:
- GET /api/v1/config/webrtc (public) returns {iceServers: [...]}
built from WEBRTC_STUN_URLS / WEBRTC_TURN_URLS / *_USERNAME /
*_CREDENTIAL env vars. Half-config (URLs without creds, or vice
versa) deliberately omits the TURN block — a half-configured TURN
surfaces auth errors at call time instead of falling back cleanly
to STUN-only.
- 4 handler tests cover the matrix.
Frontend:
- services/api/webrtcConfig.ts caches the config for the page
lifetime and falls back to the historical hardcoded Google STUN
if the fetch fails.
- useWebRTC fetches at mount, hands iceServers synchronously to
every RTCPeerConnection, exposes a {hasTurn, loaded} hint.
- CallButton tooltip warns up-front when TURN isn't configured
instead of letting calls time out silently.
Ops:
- infra/coturn/turnserver.conf — annotated template with the SSRF-
safe denied-peer-ip ranges, prometheus exporter, TLS for TURNS,
static lt-cred-mech (REST-secret rotation deferred to v1.1).
- infra/coturn/README.md — Incus deploy walkthrough, smoke test
via turnutils_uclient, capacity rules of thumb.
- docs/ENV_VARIABLES.md gains a 13bis. WebRTC ICE servers section.
Coturn deployment itself is a separate ops action — this commit lands
the plumbing so the deploy can light up the path with zero code
changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 @critical failures in 41-chat-deep.spec.ts shared one root cause:
`firstConversationRow` searched for `button[type="button"]` inside
the sidebar container, which also matched the "New Channel" CTA
button at the sidebar footer. When the listener test user had no
conversations seeded, `waitForConversationOrEmpty` raced and
returned 'has-conversations' because the CTA button matched the
conversation-row locator — `selectFirstConversation` then clicked
the CTA, opened CreateRoomDialog, and the subsequent
`expect(input).toBeEnabled()` failed because clicking the CTA
never set `currentConversationId`.
Fix:
* `data-testid="chat-conversation-item"` on ConversationItem
(+ `data-conversation-id` for callers that need the id).
* `data-testid="chat-new-channel-cta"` on the New Channel
footer button.
* `firstConversationRow` / `waitForConversationOrEmpty` /
`createRoom` rewired to target by testid. No more overlap.
* Shared helper `tests/e2e/helpers/conversation.ts` with a
minimal `navigateToConversation(page)` — picks the first
existing conversation if any, else creates a disposable one,
returns when the message input is enabled. Signature is
deliberately minimal (no options) to avoid the second-API-
surface trap. Future callers that need specialised behavior
set up store state directly instead of extending this helper.
Results:
* 22 failed → 20 passed / 3 failed / 10 skipped (graceful skips
when test user lacks seed data).
* The 3 remaining failures are distinct root causes:
- `:220` chat page debug text leak (suspected [object Object]
or undefined rendering somewhere in chat UI — real bug,
tracked separately)
- `:339` / `:347` createRoom DOM-detach race: the "Create
room" button gets detached mid-click, suggesting the dialog
is re-rendering during the click handler. Likely a fix in
the dialog lifecycle rather than the test. Tracked
separately.
29-chat-functional.spec.ts (2 failures on send-message) not
touched by this fix — those tests don't hit the row-vs-CTA
ambiguity, they fail further downstream when the backend doesn't
echo sent messages. Same class as #7 (backend-side chat
processing incomplete in test env).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update auth, playlists, tracks, search, profile, dashboard, player,
settings, and social features. Add e2e audit specs for all major pages.
Update ESLint config, vitest config, and route configuration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix mocking issues, add missing test cases, and align tests with
current component APIs for analytics, chat, marketplace, player,
playlists, settings, tracks, and auth features.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLN-04: Replaced any with unknown, proper interfaces, or concrete
types across 17 files. Focus: error handlers, API responses,
WebSocket data, and function parameters.
- Add setMessageRead to chatStore, handle MessageRead in useChat
- Send MarkAsRead when receiving messages and when loading history
- Add read_at to ChatMessage, tooltip 'Vu à HH:mm' for read status
- Typing, Delivered, MessageRead already wired in useChat
- Add status and delivered_at to ChatMessage
- Handle MessageDelivered WebSocket event
- Send Delivered when receiving NewMessage or loading history
- Display ✓ (sent) / ✓✓ (delivered) in ChatMessageComponent
- Fix setTimeout memory leak in ChatRoom.tsx by storing timeout in
useRef and cleaning up on unmount
- Add tests for Accordion, Collapsible, FloatingInput, AnimatedNumber,
and FAB components (5 new test files, all passing)
- Fix socialService methods (deleteComment, markRead, markAllRead) to
return values matching test expectations
- Fix MSW handlers for chat/token and notification endpoints to use
proper { success: true, data: ... } envelope format
- Fix invalid CSS selector in TrackList.test.tsx that caused JSDOM crash
- Document excluded test files with TODO tickets in vitest.config.ts
Co-authored-by: Cursor <cursoragent@cursor.com>
Unsaved changes:
- New useUnsavedChanges hook: browser beforeunload warning
- New useFormDirtyState hook: isDirty/markDirty/markClean tracking
- SettingsPage: wired up dirty tracking with markClean on save
Chat date separators:
- DateSeparator component with centered date label and hr lines
- Inserted between messages from different days
- Formats: Today, Yesterday, or full date (e.g. "Monday, February 10")
Co-authored-by: Cursor <cursoragent@cursor.com>
Chat status indicators:
- ChatMessage: Avatar with online status on incoming messages
- VirtualizedChatMessageItem: proper Avatar component with status
- ChatInterfaceMessages: added status="online" to existing avatars
- ConversationItem: Avatar with status for DM conversations
Notification polish:
- AnimatePresence + motion.div on dropdown (scale+fade, 150ms)
- Date grouping: Today, Yesterday, This Week, Earlier
- Sticky section headers with backdrop-blur
Feature discovery (new FeatureHighlight component):
- One-time spotlight tooltip with localStorage persistence
- Applied to: search bar (Ctrl+K), keyboard shortcuts (?), track context menu
- framer-motion animation with 0.5s delay
Co-authored-by: Cursor <cursoragent@cursor.com>
Tooltip adoption (18 conversions across 11 files):
- Player controls: shuffle, repeat, mute, expand, close, lyrics, auto-scroll
- Navbar: theme toggle
- File browser: download, add tag, AI auto-tag, watermark, process with AI
- Notifications: mark as read
- Share links: open link, revoke link
- Chat: scroll to bottom
Search polish:
- New highlightMatch utility — wraps matching text in <mark> with primary color
- Applied to track titles, artist names, playlist names in SearchPageResults
- Applied to suggestion dropdown titles and subtitles
- Replaced spinner loading state with content-aware SearchPageSkeleton
- Skeleton matches actual results layout (tab bar, track cards, artist circles)
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace legacy text-kodo-cyan/border-kodo-cyan/bg-kodo-cyan with semantic
text-primary/border-primary/bg-primary across 51 components.
The brand primary color now uses the design system token, enabling proper
theme adaptation. Covers UI primitives, search, dashboard, chat, playlists,
settings, social, marketplace, and auth components.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace legacy hardcoded bg-kodo-steel (RGB 59,69,84, theme-unaware)
with semantic bg-muted token across 46 user-facing components.
This completes the kodo-steel elimination from source files: text, border,
and background variants are now all on semantic design system tokens.
Co-authored-by: Cursor <cursoragent@cursor.com>