Commit Graph

645 Commits

Author SHA1 Message Date
Nuwan c344decea4 fix(ui): persist VST plugin selection across page reloads
- Use FTUESave(true) instead of TrackSaveAssignments() to properly
  persist VST assignments to the profile file
- Always call VSTLoad() when modal opens if VST not already loaded,
  removing unreliable hasVstAssignment() check
- Pass correct trackIndex to JKSessionPluginModal for multi-track support
- Add trackIndex prop to track data in useMixerHelper for VST operations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-18 17:58:52 +05:30
Nuwan 78cdafbb8a fix(ui): implement independent Audio Input and Session Mix fader controls
- Add mixType prop to JKSessionAudioInputs to select master/personal mixer
- Store both masterMixers and personalMixers in myTracks for each track
- Fix async state issue in fillTrackVolumeObject by returning volumeObj directly
- Change SessionTrackGain to use useLayoutEffect for synchronous visual updates
- Fix controlGroup to null for individual track controls (fixes persistence)
- Prevent fader reset when mixer temporarily becomes undefined during re-renders

Audio Inputs now uses master mixer, Session Mix uses personal mixer,
matching the legacy app behavior. Both faders persist independently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 15:56:46 +05:30
Nuwan aa731c96d0 enhancements to UI and functional issues 2026-02-15 14:10:34 +05:30
Nuwan ae7a745659 fix(ui): handle both string and numeric instrument_id formats
On page load, instrument_id from server is already a string ("piano").
After user selection, it's a numeric client_id (61) that needs conversion.
Now checks type before converting to handle both cases correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-15 10:43:35 +05:30
Nuwan 2b4bd8b934 fix(ui): convert instrument_id to server format for icon lookup
instrument_id is stored as numeric client_id (e.g., 61 for Piano)
but the icon map uses server format strings ("piano"). Added conversion
using convertClientInstrumentToServer before passing to icon lookup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-15 10:37:23 +05:30
Nuwan 808cb27390 fix(ui): instrument selection modal now updates UI immediately
- Add updateParticipantTrackInstrument reducer to update sessionData.participants
- Dispatch both participant track and userTracks updates on instrument save
- Fix syncTracksToServer call to pass clientId instead of jamClient object
- UI now reflects instrument changes without needing page refresh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 22:37:35 +05:30
Nuwan 2cf1c2cf98 fix(22-01): improve callback cleanup reliability with useRef
- Add registeredCallbacksRef alongside registeredCallbacks state
- Update unregisterMessageCallbacks to read from ref for cleanup
- Store callbacks to both state and ref during registration
- Ensures cleanup works reliably on all exit paths (browser close, navigation)
2026-02-08 21:51:04 +05:30
Nuwan 5407f19592 feat(21-01): add clearAllMessages action and dispatch on session leave
- Add clearAllMessages reducer to sessionChatSlice.js
- Export clearAllMessages action
- Dispatch clearAllMessages in handleLeaveSubmit (explicit leave)
- Dispatch clearAllMessages in unmount cleanup useEffect

Fixes CHAT-03: Prevents stale chat data accumulation across sessions
2026-02-08 20:52:17 +05:30
Nuwan f1189af677 feat(21-01): add MAX_MESSAGES limit to sessionChatSlice.js
- Add MAX_MESSAGES = 500 constant to bound chat message storage
- Apply slice(-MAX_MESSAGES) in addMessageFromWebSocket reducer
- Apply slice(-MAX_MESSAGES) in fetchChatHistory.fulfilled reducer
- Apply slice(-MAX_MESSAGES) in uploadAttachment.fulfilled reducer

Fixes CHAT-02: Prevents unbounded memory growth from chat activity
2026-02-08 20:51:14 +05:30
Nuwan 6a7ee474d2 feat(20-01): integrate removeVuState with mixer lifecycle
- Destructure removeVuState from useVuContext alongside updateVU3
- Add previousMixerIdsRef to track mixer IDs between renders
- Add cleanup useEffect that detects removed mixers
- Call removeVuState when mixer is removed from allMixers
- Only run cleanup when mixers are ready (isReadyRedux is true)

This prevents unbounded growth of vuStates object as tracks join/leave.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 19:27:48 +05:30
Nuwan 3624c09102 feat(20-01): add removeVuState function to useVuHelpers
- Add removeVuState callback to clean up vuStates entries
- Export removeVuState alongside updateVuState
- Uses functional setState for React state batching compatibility
- Maintains immutability by creating shallow copy before deletion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 19:26:50 +05:30
Nuwan a5b5c7f550 save test data 2026-02-08 13:33:58 +05:30
Nuwan 4d459fc2ac test(18-01): add INT-03 Cancel closes modal without API call test
- Add test verifying Cancel button closes modal without making API call
- Fill description field before cancel to verify changes are discarded
- Use page.route() to track any PUT requests (should be none)
- Verify modal closes by checking privacy select visibility
2026-02-08 12:44:10 +05:30
Nuwan b81d163650 test(18-01): add INT-02 Save settings API verification test
- Add test for save button making PUT /sessions/{id} API call
- Verify payload includes description and privacy fields (musician_access, approval_required)
- Use page.route() to intercept and capture API request
- Verify modal closes after successful save
- Fix locators to use privacy select instead of modal header text (avoids toast collision)
2026-02-08 12:41:10 +05:30
Nuwan c137cb1ee8 test(18-01): add INT-01 Settings button opens modal test
- Create session-settings test directory
- Add Playwright test for Settings button opening modal
- Verify modal form elements: privacy select, description, Save/Cancel buttons
- Follow existing test patterns from chat-button.spec.ts
2026-02-08 12:37:37 +05:30
Nuwan c3d4ffd06a test(17-01): add tests for UNIT-01, UNIT-02, UNIT-03 requirements
UNIT-01: Modal renders with currentSession props
- Test privacy value displayed from currentSession
- Test description value displayed from currentSession
- Test modal title renders

UNIT-02: Save functionality
- Test onSave called with correct payload on save click
- Test onSave called with updated values after user changes

UNIT-03: Loading state
- Test save button disabled when loading
- Test cancel button disabled when loading
- Test description textarea disabled when loading
- Test "Saving..." text shown when loading
2026-02-08 12:20:39 +05:30
Nuwan b7cec1ff34 test(17-01): add JKSessionSettingsModal test file with setup
- Create __tests__ directory for client components
- Add test file with imports and mocks for react-i18next
- Create renderModal helper function with default props
- Add placeholder test verifying setup works
2026-02-08 12:20:04 +05:30
Nuwan 86420c85de feat: wire up Resync button in session toolbar
Connect the existing Resync button to the resyncAudio hook from useGearUtils.
Calls jamClient.SessionAudioResync() via native C++ bridge to perform audio
resync, with loading state and error handling via toast notifications.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 02:38:21 +05:30
Nuwan 1278002a07 right align unread message count on the chat button 2026-02-07 02:20:07 +05:30
Nuwan 7110e7cdf9 fix(16): persist unread count across page reloads
Problem: After page reload, the unread badge on chat button disappeared
even though there were unread messages.

Root cause:
1. unreadCounts was reset to {} on page reload
2. fetchChatHistory was only called when chat window opened
3. By that time, openChatWindow already reset the count to 0

Fix:
1. Fetch chat history when session joins (not just when chat opens)
2. In fetchChatHistory.fulfilled, calculate unread count based on
   lastReadAt timestamp from localStorage
3. Only calculate unread if chat window is NOT open for that channel

This ensures the badge shows correct unread count after page reload.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 02:07:01 +05:30
Nuwan 20d0259433 fix(16): fix attachment deduplication in uploadAttachment.fulfilled
The previous fix only added deduplication by attachmentId in addMessageFromWebSocket
and fetchChatHistory.fulfilled, but missed the uploadAttachment.fulfilled handler.

Race condition: WebSocket message can arrive BEFORE the upload API returns.
When this happens:
1. WebSocket delivers message with id='456', attachmentId=123
2. addMessageFromWebSocket adds it to state
3. Upload API returns
4. uploadAttachment.fulfilled only checked for id='attachment-123' → not found
5. Duplicate message added

Fix: Also check by attachmentId in uploadAttachment.fulfilled handler.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 01:44:23 +05:30
Nuwan c1ed8470ae fix(16): improve attachment deduplication by attachmentId
The optimistic message uses 'attachment-{notation.id}' as ID while
REST API and WebSocket use the chat message ID. This caused duplicates
when fetchChatHistory ran after an optimistic upload.

Now deduplication checks both message ID and attachmentId for attachment
messages, preventing duplicates regardless of ID format.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 01:38:50 +05:30
Nuwan 4bb3fa2065 test(16-01): add error handling integration tests
- Create error-handling.spec.ts with 5 test scenarios
- Test REQ-5.1: File size exceeded validation
- Test REQ-5.4: Upload success toast with auto-dismiss
- Test REQ-5.3: Network error handling with retry capability
- Test REQ-5.5: S3 404 error handling (missing file)
- Test edge case: Prevent rapid clicks during upload
- Uses test-helpers.ts for login and session creation
2026-02-06 18:45:36 +05:30
Nuwan 1d026c3d30 fix(16-01): add S3 404 error handling with toast notification
- Add react-toastify import for toast notifications
- Show toast.error('File no longer available') when attachment fetch fails
- Handles S3 404 errors gracefully per REQ-5.5
- Replaces silent failure with user-friendly error message
2026-02-06 18:44:14 +05:30
Nuwan 744c735817 fix(16-01): update network error message per REQ-5.3
- Change uploadAttachment error message to 'Upload failed. Please try again.'
- Provides actionable feedback to retry on network errors
- Matches exact wording from REQ-5.3
2026-02-06 18:43:53 +05:30
Nuwan 1ecc1d7987 fix(16-01): improve validation error messages per requirements
- Update validateFileSize error: 'File size exceeds 10 MB limit' (REQ-5.1)
- Update validateFileType error: 'File type not supported. Allowed: [list]' (REQ-5.2)
- Hardcode error messages for clarity per requirements
- Update unit tests to match new error messages
- All 37 unit tests passing
2026-02-06 18:43:37 +05:30
Nuwan 354492bc1a feat(16-01): add upload success toast notification
- Add selectUploadStatus selector to track upload state transitions
- Track previous upload status with useRef
- Show toast.success when upload completes (uploading -> idle)
- Auto-dismiss after 3 seconds per REQ-5.4
- Success toast provides clear feedback on successful file uploads
2026-02-06 18:42:35 +05:30
Nuwan 85fd4916e2 style(chat): update chat button to match nav button style
Changed JKSessionChatButton from bare icon to proper Button component:
- Uses Button from reactstrap with btn-custom-outline class
- Matches other nav buttons (Invite, Volume, Video, etc.)
- Icon sized at 16x16px with 0.2rem right margin
- "Chat" text label added
- Unread badge repositioned for button layout
- Reduced opacity when window is open (visual feedback)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 18:23:59 +05:30
Nuwan e24c655905 fix(15): use correct parameter name for chat API (music_session)
Backend's check_session filter expects 'music_session' parameter:
  @music_session = ActiveMusicSession.find(params[:music_session])

But we were sending 'session_id', causing ActiveMusicSession.find(nil)
to fail.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:34:36 +05:30
Nuwan fce74152f8 fix(15): prevent infinite loop in chat history fetch
The useEffect condition allowed fetching when fetchStatus was 'failed',
causing infinite retry loops when the API returned an error.

Changed condition from:
  fetchStatus !== 'loading' && fetchStatus !== 'succeeded'
To:
  fetchStatus === 'idle'

This ensures we only fetch once per channel on initial load, and don't
retry automatically on failure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:32:52 +05:30
Nuwan 5df60ad6cf fix(15): pass sessionId to fetchChatHistory for session messages
The fetchChatHistory thunk needs sessionId to determine channel type:
- With sessionId: fetches 'session' channel messages
- Without sessionId: fetches 'global' channel messages

The useEffect was only passing `channel` (which is the sessionId for
session chats), but not the `sessionId` parameter. This caused the
API to fetch global chat instead of session chat, so session messages
disappeared after page reload.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:04:41 +05:30
Nuwan da4e864ab7 fix(15): correct API response field name (chats not messages)
The chat history API returns { chats: [...], next: ... } but the
fetchChatHistory.fulfilled handler was expecting { messages: [...] }.

This caused a TypeError when opening the chat window because
`messages` was undefined.

Fixed:
- sessionChatSlice.js: Extract `chats` from payload, default to []
- Updated all test payloads to use `chats` field name

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:45:06 +05:30
Nuwan bfee9acdfb fix(15): resolve UAT issues - uploader message visibility and chat history
Issues found during UAT:
1. Uploader doesn't see their own attachment message
2. Chat history doesn't load on page refresh/rejoin

Root causes:
1. Backend's server_publish_to_session excludes sender from WebSocket
   broadcast (exclude_client_id: sender[:client_id])
2. fetchChatHistory was imported but never called in JKChatMessageList

Fixes:
- Add optimistic message in uploadAttachment.fulfilled for uploader
  Since sender is excluded from WebSocket, we add the message locally
  using the MusicNotation response + user info
- Add useEffect in JKChatMessageList to dispatch fetchChatHistory
  when channel becomes active

Technical details:
- Pass userId/userName to uploadAttachment thunk for message construction
- Use 'attachment-{notation.id}' as message ID to avoid collision
- Fetch history when fetchStatus is not 'loading' or 'succeeded'

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:36:04 +05:30
Nuwan e9dd992e29 test(15-01): add real-time attachment sync integration tests
- Create test/attachments/real-time-sync.spec.ts
- Test WebSocket message receipt for attachments
- Test deduplication of duplicate messages
- Test REST API and WebSocket message deduplication
- Test attachment display in chat
- Uses Redux dispatch simulation approach for CI compatibility
2026-02-06 02:20:47 +05:30
Nuwan 86206e1992 refactor(15-01): clean up debug logging in WebSocket handler
- Remove console.log for received WebSocket messages
- Remove console.log for Redux dispatch
- Remove console.log for callback registration
- WebSocket handler verified: extracts all attachment fields correctly
2026-02-06 02:19:31 +05:30
Nuwan 977d1a9b95 feat(14-03): transform REST API music_notation to flat attachment fields
- Transform nested music_notation object from REST API to flat attachment fields
- Map music_notation.id → attachmentId, file_name → attachmentName, attachment_type → attachmentType
- Include purpose field from API response root
- Set attachmentSize to null (not available from REST API, only WebSocket)
- Matches WebSocket message format for consistent JKChatMessage rendering

Tests:
- Add test for REST API attachment transformation
- Fix pre-existing test bugs: incorrect sendMessage.fulfilled payload format
- Fix pre-existing test bug: fetchChatHistory deduplication test used wrong format
- All 89 tests now pass (previously 3 failures)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 01:52:40 +05:30
Nuwan 1143048ec3 feat(14-02): add responsive styling with filename truncation
- Add flexWrap to header (name + timestamp wrap on narrow windows)
- Change filename maxWidth from 200px to 100% (takes available width)
- Add flexShrink: 0 to file size and paperclip icon (never shrink)
- Add fontSize: 13px to file size for consistent sizing
- Remove marginRight from paperclip icon (gap handles spacing)
- Ensure minWidth: 0 on content container for text-overflow to work
- Add comment clarifying header layout
- Layout responsive at different window sizes with proper truncation
2026-02-05 19:49:01 +05:30
Nuwan 91cac19a52 feat(14-02): add clickable attachment links with signed URL fetching
- Import getMusicNotationUrl REST helper and useState/useCallback hooks
- Add handleAttachmentClick handler that fetches signed S3 URL and opens in new tab
- Implement loading state (isLoadingUrl) to prevent rapid clicks
- Make filename a clickable link with underline and blue color
- Add error handling that logs to console without crashing
- Show 'wait' cursor during URL fetch
- Browser handles file display based on Content-Type (PDF viewer, image display, audio player)
2026-02-05 19:48:11 +05:30
Nuwan b8bfd23a9c feat(14-01): render attachment messages with distinct styling and metadata
- Import formatFileSize from attachmentValidation service
- Add isAttachmentMessage helper to detect attachment messages
- Render attachment messages with light blue background (#e3f2fd)
- Display paperclip icon with accessible aria-label
- Show '[name] attached [filename]' format with file size if available
- Update PropTypes to include optional attachment fields
- Maintain React.memo for performance
2026-02-05 19:43:46 +05:30
Nuwan 6d1cf489d7 feat(14-01): extend WebSocket message transformation for attachment fields
- Add attachment fields to CHAT_MESSAGE handler transformation
- Include attachmentId, attachmentName, attachmentType, purpose, attachmentSize
- Fields null/undefined for regular text messages
- WebSocket payload maps to Redux format with attachment metadata
2026-02-05 19:43:36 +05:30
Nuwan b1ed2247ba fix(13-03): correct API endpoint URL for music notation upload
Remove duplicate /api/ prefix - REACT_APP_API_BASE_URL already includes /api.
Fixed: /api/api/music_notations -> /api/music_notations
2026-02-05 11:42:56 +05:30
Nuwan edf74f7248 fix(13-03): resolve ESLint no-unused-expressions errors
Replace optional chaining expressions with explicit if checks to satisfy ESLint configuration.
2026-02-05 11:38:28 +05:30
Nuwan f2c07faede feat(13-03): display upload progress in chat message list
- Import selectIsUploading and selectUploadFileName from sessionChatSlice
- Import JKChatUploadProgress component
- Add useSelector hooks for upload state (isUploading, uploadFileName)
- Render JKChatUploadProgress at end of message list when upload in progress
- Upload indicator appears at bottom of chat for easy visibility
- Conditional rendering: only shows when isUploading is true AND fileName exists
2026-02-05 11:06:50 +05:30
Nuwan cd2d79b190 feat(13-03): create upload progress component for chat window
- Create JKChatUploadProgress component with Spinner and filename display
- Styled as system message (gray background, italic text)
- Display format: 'Uploading [filename]...'
- Uses React.memo for performance optimization
- PropTypes validation for fileName (required)
- 42 lines including component, styles, and PropTypes
2026-02-05 11:06:44 +05:30
Nuwan 7455e2ec28 feat(13-03): wire up Attach button in session toolbar
- Add imports for uploadAttachment, validation, and upload state selectors
- Add attachFileInputRef for hidden file input element
- Add handleAttachClick to trigger file dialog
- Add handleFileSelect with validateFile pre-upload validation
- Add useEffect to display upload error toasts
- Add hidden file input with accept attribute for valid file types
- Update Attach button with onClick handler and disabled state during upload
- Button shows 'Uploading...' text when upload is in progress
- Chat window opens automatically when upload starts
- File validation prevents invalid uploads (size/type) with immediate error feedback
2026-02-05 11:06:39 +05:30
Nuwan 3b52b58cc5 feat(13-02): add Redux upload state and REST helpers
- Add uploadState to sessionChatSlice initialState
- Implement setUploadStatus and clearUploadError reducers
- Create uploadAttachment async thunk with error handling
- Add 5 upload selectors (status, error, progress, fileName, isUploading)
- Implement uploadMusicNotation using native fetch (NOT apiFetch)
- Implement getMusicNotationUrl using apiFetch
- Add comprehensive unit tests for all upload functionality

TDD: RED-GREEN cycle complete
Tests: 85/88 passing (3 pre-existing failures unrelated to upload)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 11:00:58 +05:30
Nuwan 878e9e44aa feat(13-01): implement attachment validation service
GREEN phase - TDD cycle
- validateFileSize: checks 10 MB limit, customizable
- validateFileType: extension whitelist validation
- getAttachmentType: audio vs notation detection
- validateFile: combined validation with backend warnings
- formatFileSize: human-readable display (B/KB/MB)
- All constants exported for component usage

Implementation follows REACT_INTEGRATION_DESIGN.md Section 7
All 37 tests passing (100% coverage)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 10:59:03 +05:30
Nuwan 1eef4d227e test(13-01): add failing tests for attachment validation service
RED phase - TDD cycle
- 15+ test cases covering all 5 functions
- validateFileSize: size limits and edge cases
- validateFileType: extension whitelist and case handling
- getAttachmentType: audio vs notation detection
- validateFile: combined validation with warnings
- formatFileSize: human-readable formatting
- Constants validation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 10:58:30 +05:30
Nuwan 961c0e36bb chore: update debug logging and test storage state
Changes:
- .gitignore: Add .run-claude.sh to ignored files
- JKSessionScreen.js: Add "-DEBUG-" prefix to console logs for easier filtering
- user1.json, user2.json: Update test authentication state
- 11-VERIFICATION.md: Add verification document for Phase 11

These are maintenance updates from chat feature development and testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 17:26:16 +05:30
Nuwan 010cf55302 fix: show alert instead of redirecting to non-existent /client route
When profile guard fails (single-player profile detected):
- Show JavaScript alert explaining the issue
- Leave session cleanly via handleLeaveSession()
- Redirect to dashboard (/) instead of /client (404)

Alert message explains:
- Audio profile not suitable for multi-user sessions
- Need proper audio interface or create private session

TODO: Replace alert() with proper modal dialog component
that offers options like legacy app (create private session,
go to audio settings, cancel).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 10:13:14 +05:30