diff --git a/jam-ui/src/components/client/JKSessionScreen.js b/jam-ui/src/components/client/JKSessionScreen.js index e26c719f4..3922aab84 100644 --- a/jam-ui/src/components/client/JKSessionScreen.js +++ b/jam-ui/src/components/client/JKSessionScreen.js @@ -55,7 +55,8 @@ import { selectBackingTrackData, selectJamTrackData } from '../../store/features/activeSessionSlice'; -import { addMessageFromWebSocket } from '../../store/features/sessionChatSlice'; +import { addMessageFromWebSocket, uploadAttachment, selectIsUploading, selectUploadError, selectUploadFileName, clearUploadError } from '../../store/features/sessionChatSlice'; +import { validateFile } from '../../services/attachmentValidation'; import { CLIENT_ROLE, RECORD_TYPE_AUDIO, RECORD_TYPE_BOTH } from '../../helpers/globals'; import { MessageType } from '../../helpers/MessageFactory.js'; @@ -198,6 +199,14 @@ const JKSessionScreen = () => { const backingTrackData = useSelector(selectBackingTrackData); const showBackingTrackPlayer = Boolean(backingTrackData); + // Redux upload state for attachments + const isUploading = useSelector(selectIsUploading); + const uploadError = useSelector(selectUploadError); + const uploadFileName = useSelector(selectUploadFileName); + + // File input ref for attach button + const attachFileInputRef = useRef(null); + // Stable callback for backing track popup close const handleBackingTrackClose = useCallback(() => { console.log('JKSessionScreen: Backing Track Popup closing'); @@ -987,6 +996,50 @@ const JKSessionScreen = () => { } }; + // Attach button handlers + const handleAttachClick = useCallback(() => { + attachFileInputRef.current?.click(); + }, []); + + const handleFileSelect = useCallback((e) => { + const file = e.target.files?.[0]; + if (!file) return; + + // Reset input for re-selection of same file + e.target.value = ''; + + // Validate file before upload + const validation = validateFile(file); + + if (!validation.valid) { + toast.error(validation.error); + return; + } + + // Show warnings if any (e.g., backend may not fully support this type) + validation.warnings?.forEach(warning => { + console.warn('Attachment warning:', warning); + }); + + // Open chat window if not already open + dispatch(openModal('chat')); + + // Dispatch upload + dispatch(uploadAttachment({ + file, + sessionId, + clientId: server?.clientId + })); + }, [dispatch, sessionId, server?.clientId]); + + // Show error toast when upload fails + useEffect(() => { + if (uploadError) { + toast.error(uploadError); + dispatch(clearUploadError()); + } + }, [uploadError, dispatch]); + const handleBackingTrackSelected = async (result) => { console.log('JKSessionScreen: handleBackingTrackSelected called with:', result); console.log('JKSessionScreen: Current state - showBackingTrackPopup:', showBackingTrackPopup, 'popupGuard:', popupGuard); @@ -1175,9 +1228,23 @@ const JKSessionScreen = () => { Broadcast dispatch(openModal('jamTrack'))} onMetronomeSelected={handleMetronomeSelected} /> - + {isUploading ? 'Uploading...' : 'Attach'} +