@@ -35,6 +35,7 @@ function TestConfigurator({
3535 onShowLeaderboard,
3636 isLobby = false ,
3737 snippetError = null ,
38+ allowTimed = true ,
3839} ) {
3940
4041 const [ subjects , setSubjects ] = React . useState ( [ 'all' ] ) ;
@@ -68,6 +69,21 @@ function TestConfigurator({
6869 fetchDifficulties ( ) ;
6970 } , [ snippetCategory , snippetSubject ] ) ;
7071
72+ // If timed mode is disallowed (e.g., private lobbies), coerce to snippet mode
73+ React . useEffect ( ( ) => {
74+ if ( ! allowTimed && testMode === 'timed' ) {
75+ // Update race state first so the server/client agree on mode
76+ setRaceState ( prev => ( {
77+ ...prev ,
78+ timedTest : { ...prev . timedTest , enabled : false } ,
79+ settings : { ...( prev . settings || { } ) , testMode : 'snippet' }
80+ } ) ) ;
81+ setTestMode ( 'snippet' ) ;
82+ // Load a snippet immediately to reflect the change
83+ setTimeout ( ( ) => { loadNewSnippet && loadNewSnippet ( ) ; } , 0 ) ;
84+ }
85+ } , [ allowTimed , testMode , setRaceState , setTestMode , loadNewSnippet ] ) ;
86+
7187 // Fetch subjects on mount
7288 React . useEffect ( ( ) => {
7389 const fetchSubjects = async ( ) => {
@@ -272,7 +288,7 @@ function TestConfigurator({
272288 { /* Mode Selection Group */ }
273289 < div className = "config-section mode-selection" >
274290 { renderButton ( 'snippet' , testMode , setTestMode , 'Snippets' , QuoteIcon ) }
275- { renderButton ( 'timed' , testMode , setTestMode , 'Timed' , ClockIcon ) }
291+ { allowTimed && renderButton ( 'timed' , testMode , setTestMode , 'Timed' , ClockIcon ) }
276292 </ div >
277293
278294 { /* Separator */ }
@@ -372,13 +388,15 @@ function TestConfigurator({
372388 </ div >
373389
374390 { /* Timed Mode Duration Wrapper */ }
375- < TutorialAnchor anchorId = "timed-options" >
376- < div className = { `options-wrapper timed-options ${ testMode === 'timed' ? 'visible' : '' } ` } >
377- < div className = "config-section duration-selection-inner" >
378- { DURATIONS . map ( duration => renderButton ( duration , testDuration , setTestDuration , `${ duration } s` ) ) }
391+ { allowTimed && (
392+ < TutorialAnchor anchorId = "timed-options" >
393+ < div className = { `options-wrapper timed-options ${ testMode === 'timed' ? 'visible' : '' } ` } >
394+ < div className = "config-section duration-selection-inner" >
395+ { DURATIONS . map ( duration => renderButton ( duration , testDuration , setTestDuration , `${ duration } s` ) ) }
396+ </ div >
379397 </ div >
380- </ div >
381- </ TutorialAnchor >
398+ </ TutorialAnchor >
399+ ) }
382400
383401 </ div > { /* End Conditional Options Container */ }
384402
@@ -409,6 +427,7 @@ TestConfigurator.propTypes = {
409427 setRaceState : PropTypes . func . isRequired ,
410428 loadNewSnippet : PropTypes . func ,
411429 onShowLeaderboard : PropTypes . func . isRequired ,
430+ allowTimed : PropTypes . bool ,
412431} ;
413432
414433export default TestConfigurator ;
0 commit comments