112112 < div id ="finalTime " class ="score-value "> 0:00.00</ div >
113113 < div id ="missionPerformance " class ="performance-rating "> Calculating...</ div >
114114 < div id ="leaderboardSubmit " class ="leaderboard-submit hidden ">
115- <!-- Shown when signed in -->
116- < div class ="submit-signed-in " style ="display:none; ">
117- < div class ="submit-row ">
118- < input type ="text " id ="playerNameInput " placeholder ="Enter your name " maxlength ="16 "
119- class ="player-name-input " autocomplete ="off " spellcheck ="false ">
120- < button id ="submitScoreBtn " class ="btn-submit-score " onclick ="submitScore() ">
121- < i class ="iconoir-send "> </ i >
122- </ button >
123- </ div >
124- </ div >
125115 <!-- Shown when anonymous -->
126116 < div class ="submit-anon ">
127117 < button class ="btn-sign-in-to-submit " onclick ="openAuthModal() ">
158148 < div id ="victoryTime " class ="score-value "> 5:00.00</ div >
159149 < div id ="victoryPerformance " class ="performance-rating perfect "> MAX SURVIVAL</ div >
160150 < div id ="victoryLeaderboardSubmit " class ="leaderboard-submit hidden ">
161- <!-- Shown when signed in -->
162- < div class ="submit-signed-in " style ="display:none; ">
163- < div class ="submit-row ">
164- < input type ="text " id ="victoryPlayerNameInput " placeholder ="Enter your name " maxlength ="16 "
165- class ="player-name-input " autocomplete ="off " spellcheck ="false ">
166- < button id ="victorySubmitScoreBtn " class ="btn-submit-score " onclick ="submitScore() ">
167- < i class ="iconoir-send "> </ i >
168- </ button >
169- </ div >
170- </ div >
171151 <!-- Shown when anonymous -->
172152 < div class ="submit-anon ">
173153 < button class ="btn-sign-in-to-submit " onclick ="openAuthModal() ">
@@ -633,43 +613,37 @@ <h2 class="mobile-menu-title">REFLECTIONS</h2>
633613 }
634614
635615 // Score submission to Firebase leaderboard
616+ // Auto-submit score to leaderboard. Called when:
617+ // 1. Game over modal appears and user is signed in
618+ // 2. User signs in from auth modal while game over modal is showing
619+ window . submitScore = submitScore ;
636620 async function submitScore ( ) {
637- if ( ! window . game || ! window . leaderboardService || ! window . firebaseAuth ) {
638- showToast ( 'Leaderboard not available. Please refresh.' ) ;
639- return ;
640- }
621+ if ( ! window . game || ! window . leaderboardService || ! window . firebaseAuth ) return ;
641622
642623 const auth = window . firebaseAuth ;
643624 const service = window . leaderboardService ;
644625 const game = window . game ;
645626
646- // Require sign-in — anonymous users must sign in first
647- if ( auth . isAnonymous ( ) ) {
648- openAuthModal ( ) ;
649- return ;
650- }
627+ // Must be signed in and game must be over
628+ if ( auth . isAnonymous ( ) || ! game . gameOver || game . gameTime <= 0 ) return ;
651629
652- // Get name from whichever modal is visible
653- const nameInput = document . getElementById ( 'playerNameInput' ) ;
654- const victoryNameInput = document . getElementById ( 'victoryPlayerNameInput' ) ;
655- const activeInput = ! document . getElementById ( 'victoryModal' ) . classList . contains ( 'hidden' )
656- ? victoryNameInput : nameInput ;
657- const statusEl = ! document . getElementById ( 'victoryModal' ) . classList . contains ( 'hidden' )
630+ // Find the visible status element
631+ const isVictory = ! document . getElementById ( 'victoryModal' ) . classList . contains ( 'hidden' ) ;
632+ const statusEl = isVictory
658633 ? document . getElementById ( 'victoryScoreSubmitStatus' )
659634 : document . getElementById ( 'scoreSubmitStatus' ) ;
660635
661- const name = ( activeInput ? activeInput . value : '' ) . trim ( ) ;
662- if ( name ) {
663- auth . setDisplayName ( name ) ;
664- }
665-
666- // Disable buttons during submission
667- const btns = document . querySelectorAll ( '.btn-submit-score' ) ;
668- btns . forEach ( b => b . disabled = true ) ;
636+ // Don't re-submit if already submitted this game
637+ if ( statusEl && statusEl . dataset . submitted === 'true' ) return ;
669638
670639 try {
671- statusEl . textContent = 'Submitting...' ;
672- statusEl . style . color = 'var(--text-secondary)' ;
640+ if ( statusEl ) {
641+ statusEl . textContent = 'Submitting score...' ;
642+ statusEl . style . color = 'var(--text-secondary)' ;
643+ }
644+
645+ // Hide the sign-in prompt since we're signed in
646+ document . querySelectorAll ( '.submit-anon' ) . forEach ( el => el . style . display = 'none' ) ;
673647
674648 const mode = game . isDailyChallenge ? 'daily' : 'main' ;
675649 const DailyChallenge = ( await import ( './js/validation/DailyChallenge.js' ) ) . DailyChallenge ;
@@ -686,56 +660,50 @@ <h2 class="mobile-menu-title">REFLECTIONS</h2>
686660 spawnerCount : game . spawners . length ,
687661 } ) ;
688662
689- if ( result . isNewBest ) {
690- statusEl . textContent = 'New personal best!' ;
691- statusEl . style . color = '#FFD700' ;
692-
693- // Video upload for main game top 10
694- if ( mode === 'main' && window . videoUploader ) {
695- const isTop = await service . isTop10 ( game . gameTime ) ;
696- if ( isTop ) {
697- const recorder = game . replayRecorder ;
698- let videoBlob = recorder . videoBlob ;
699-
700- // Generate MP4 if no video but canvas replay exists
701- if ( ! videoBlob && recorder . canGenerateMP4 ( ) ) {
702- statusEl . textContent = 'Top 10! Generating replay...' ;
703- await game . generateReplayMP4 ( ) ;
704- videoBlob = recorder . videoBlob ;
705- }
706-
707- if ( videoBlob ) {
708- statusEl . textContent = 'Top 10! Uploading replay...' ;
709- await window . videoUploader . uploadReplayVideo (
710- videoBlob , game . gameTime , result . docId ,
711- ( percent ) => {
712- statusEl . textContent = `Uploading replay... ${ percent } %` ;
713- }
714- ) ;
715- statusEl . textContent = 'Top 10! Replay uploaded!' ;
716- } else {
717- statusEl . textContent = 'Top 10! Score submitted!' ;
663+ if ( statusEl ) {
664+ statusEl . dataset . submitted = 'true' ;
665+
666+ if ( result . isNewBest ) {
667+ statusEl . textContent = 'Score submitted - new personal best!' ;
668+ statusEl . style . color = '#FFD700' ;
669+
670+ // Video upload for main game top 10
671+ if ( mode === 'main' && window . videoUploader ) {
672+ const isTop = await service . isTop10 ( game . gameTime ) ;
673+ if ( isTop ) {
674+ const recorder = game . replayRecorder ;
675+ let videoBlob = recorder . videoBlob ;
676+
677+ if ( ! videoBlob && recorder . canGenerateMP4 ( ) ) {
678+ statusEl . textContent = 'Top 10! Generating replay...' ;
679+ await game . generateReplayMP4 ( ) ;
680+ videoBlob = recorder . videoBlob ;
681+ }
682+
683+ if ( videoBlob ) {
684+ statusEl . textContent = 'Top 10! Uploading replay...' ;
685+ await window . videoUploader . uploadReplayVideo (
686+ videoBlob , game . gameTime , result . docId ,
687+ ( percent ) => {
688+ statusEl . textContent = `Uploading replay... ${ percent } %` ;
689+ }
690+ ) ;
691+ statusEl . textContent = 'Top 10! Replay uploaded!' ;
692+ }
718693 }
719694 }
695+ } else {
696+ statusEl . textContent = 'Score submitted' ;
697+ statusEl . style . color = 'var(--text-secondary)' ;
720698 }
721- } else {
722- statusEl . textContent = 'Score submitted (not a new best)' ;
723- statusEl . style . color = 'var(--text-secondary)' ;
724- }
725-
726- // Hide the input, show submitted state
727- const submitContainer = activeInput . closest ( '.leaderboard-submit' ) ;
728- if ( submitContainer ) {
729- const submitRow = submitContainer . querySelector ( '.submit-row' ) ;
730- if ( submitRow ) submitRow . style . display = 'none' ;
731699 }
732700
733701 } catch ( error ) {
734702 console . error ( 'Score submission failed:' , error ) ;
735- statusEl . textContent = 'Submission failed. Try again.' ;
736- statusEl . style . color = '#E84E6A ' ;
737- } finally {
738- btns . forEach ( b => b . disabled = false ) ;
703+ if ( statusEl ) {
704+ statusEl . textContent = 'Submission failed ' ;
705+ statusEl . style . color = '#E84E6A' ;
706+ }
739707 }
740708 }
741709
0 commit comments