@@ -389,6 +389,29 @@ html(lang="en")
389389 background : rgba (99 , 102 , 241 , 0.1 );
390390 }
391391
392+ /* Message Styles */
393+ .message {
394+ padding : 1rem 1.5rem ;
395+ border-radius : 8px ;
396+ margin-bottom : 1rem ;
397+ display : flex ;
398+ align-items : center ;
399+ gap : 0.75rem ;
400+ font-weight : 500 ;
401+ }
402+
403+ .message.success {
404+ background : rgba (16 , 185 , 129 , 0.1 );
405+ border-left : 4px solid var (--secondary );
406+ color : var (--secondary );
407+ }
408+
409+ .message.error {
410+ background : rgba (239 , 68 , 68 , 0.1 );
411+ border-left : 4px solid var (--error );
412+ color : var (--error );
413+ }
414+
392415 /* Raise Problem Section */
393416 .raise-problem-section {
394417 background : var (--dark );
@@ -848,6 +871,15 @@ body
848871 // Main Content
849872 main.community-main
850873 .container
874+ // Display success/error messages
875+ if success
876+ .message.success
877+ i.fas.fa-check-circle
878+ = success
879+ if error
880+ .message.error
881+ i.fas.fa-exclamation-triangle
882+ = error
851883 // Community Header
852884 .community-header
853885 h1= community .Name
@@ -1469,14 +1501,17 @@ body
14691501 form( action =` /com/${ community .gp_id } /settings` , method ="POST" )
14701502 .form-group
14711503 label
1504+ input( type ="hidden" , name ="isPublic" , value ="false" )
14721505 input( type ="checkbox" , name ="isPublic" , value ="true" , checked =community .isPublic )
14731506 | Public - Anyone can discover the community
14741507 .form-group
14751508 label
1509+ input( type ="hidden" , name ="allowPublicJoin" , value ="false" )
14761510 input( type ="checkbox" , name ="allowPublicJoin" , value ="true" , checked =(community .settings && community .settings .allowPublicJoin ))
14771511 | Allow public to join without invite
14781512 .form-group
14791513 label
1514+ input( type ="hidden" , name ="onlyWorkersCanSolve" , value ="false" )
14801515 input( type ="checkbox" , name ="onlyWorkersCanSolve" , value ="true" , checked =(community .settings && community .settings .onlyWorkersCanSolve ))
14811516 | Only workers can mark problems as solved
14821517 button.btn.btn-primary ( type ="submit" )
@@ -2095,6 +2130,25 @@ script.
20952130 return ' audio/webm' ;
20962131 }
20972132
2133+ // Timer function for recording
2134+ function updateTimer () {
2135+ if (! recordingStartTime) return ;
2136+ const elapsed = Math .floor ((Date .now () - recordingStartTime) / 1000 );
2137+ const minutes = Math .floor (elapsed / 60 );
2138+ const seconds = elapsed % 60 ;
2139+ recordingTimer .textContent = ` ${ minutes .toString ().padStart (2 , ' 0' )} :${ seconds .toString ().padStart (2 , ' 0' )} ` ;
2140+ }
2141+
2142+ // Convert blob to base64
2143+ function blobToBase64 (blob ) {
2144+ return new Promise ((resolve , reject ) => {
2145+ const reader = new FileReader ();
2146+ reader .onload = () => resolve (reader .result );
2147+ reader .onerror = reject;
2148+ reader .readAsDataURL (blob);
2149+ });
2150+ }
2151+
20982152 let mediaRecorder;
20992153 let audioChunks = [];
21002154 let recordingStartTime;
@@ -2123,15 +2177,31 @@ script.
21232177
21242178 // Start Recording with supported format
21252179 startBtn .addEventListener (' click' , async () => {
2180+ let stream;
21262181 try {
2127- const stream = await navigator .mediaDevices .getUserMedia ({
2182+ stream = await navigator .mediaDevices .getUserMedia ({
21282183 audio: {
21292184 echoCancellation: true ,
21302185 noiseSuppression: true ,
21312186 sampleRate: 44100 // Higher quality for MP3
21322187 }
21332188 });
2189+ } catch (error) {
2190+ console .error (' Error accessing microphone with advanced constraints:' , error);
21342191
2192+ // Try with simpler constraints
2193+ try {
2194+ stream = await navigator .mediaDevices .getUserMedia ({ audio: true });
2195+ console .log (' Microphone access granted with basic constraints' );
2196+ } catch (simpleError) {
2197+ console .error (' Error accessing microphone with basic constraints:' , simpleError);
2198+ alert (' Could not access microphone. Please check browser permissions and try again.' );
2199+ return ;
2200+ }
2201+ }
2202+
2203+ // If we got here, we have a stream - proceed with recording
2204+ try {
21352205 // Get supported MIME type
21362206 currentMimeType = getSupportedMimeType ();
21372207 console .log (' Using MIME type:' , currentMimeType);
@@ -2200,18 +2270,64 @@ script.
22002270 recordingActions .style .display = ' none' ;
22012271 audioPreview .style .display = ' none' ;
22022272
2203- } catch (error) {
2204- console .error (' Error accessing microphone:' , error);
2205-
2206- // Try with simpler constraints
2207- try {
2208- const stream = await navigator .mediaDevices .getUserMedia ({ audio: true });
2209- alert (' Microphone access granted. Please try recording again.' );
2210- stream .getTracks ().forEach (track => track .stop ());
2211- } catch (simpleError) {
2212- alert (' Could not access microphone. Please check browser permissions and try again.' );
2273+ } catch (recordError) {
2274+ console .error (' Error setting up recording:' , recordError);
2275+ alert (' Error setting up audio recording. Please try again.' );
2276+ // Stop the stream if recording setup failed
2277+ stream .getTracks ().forEach (track => track .stop ());
2278+ }
2279+ });
2280+
2281+ // Stop Recording
2282+ stopBtn .addEventListener (' click' , () => {
2283+ if (mediaRecorder && isRecording) {
2284+ mediaRecorder .stop ();
2285+ isRecording = false ;
2286+ if (timerInterval) {
2287+ clearInterval (timerInterval);
2288+ timerInterval = null ;
2289+ }
2290+ }
2291+ });
2292+
2293+ // Cancel Recording
2294+ cancelBtn .addEventListener (' click' , () => {
2295+ if (mediaRecorder && isRecording) {
2296+ mediaRecorder .stop ();
2297+ isRecording = false ;
2298+ if (timerInterval) {
2299+ clearInterval (timerInterval);
2300+ timerInterval = null ;
22132301 }
22142302 }
2303+
2304+ // Reset UI
2305+ startBtn .style .display = ' block' ;
2306+ stopBtn .style .display = ' none' ;
2307+ cancelBtn .style .display = ' none' ;
2308+ recordingStatus .style .display = ' none' ;
2309+ recordingActions .style .display = ' none' ;
2310+ audioPreview .style .display = ' none' ;
2311+
2312+ // Clear any saved audio
2313+ savedAudioBlob = null ;
2314+ audioChunks = [];
2315+ });
2316+
2317+ // Discard Recording
2318+ discardBtn .addEventListener (' click' , () => {
2319+ savedAudioBlob = null ;
2320+ audioDataInput .value = ' ' ;
2321+ audioMimeTypeInput .value = ' ' ;
2322+
2323+ // Reset UI
2324+ recordingActions .style .display = ' none' ;
2325+ audioPreview .style .display = ' none' ;
2326+ saveBtn .innerHTML = ' <i class="fas fa-save"></i> Save' ;
2327+ saveBtn .classList .remove (' btn-secondary' );
2328+ saveBtn .classList .add (' btn-success' );
2329+ saveBtn .disabled = false ;
2330+ discardBtn .innerHTML = ' <i class="fas fa-trash"></i> Discard' ;
22152331 });
22162332
22172333 // Save Recording - Convert to Base64
0 commit comments