@@ -14,7 +14,7 @@ use crate::views::selection::SelectionItem;
1414use crate :: service;
1515use crate :: utils;
1616use crate :: views:: loading_page:: { LoadingPage , LoadingPageState } ;
17- use crate :: views:: result_page:: { ResultPage , ResultPageState } ;
17+ use crate :: views:: result_page:: { ResultPageState , ResultView , ResultAction } ;
1818use crate :: views:: welcome_page:: { WelcomeView , WelcomeAction } ;
1919use crate :: views:: file_selection_page:: { FileSelectionView , FileSelectionAction } ;
2020use crate :: views:: selection:: { SelectionView , SelectionAction } ;
@@ -146,8 +146,6 @@ pub struct App {
146146 pub selected_submission_mode : Option < String > ,
147147
148148 pub app_state : AppState ,
149- pub final_status : Option < String > ,
150-
151149 pub should_quit : bool ,
152150 pub submission_task : Option < JoinHandle < Result < String , anyhow:: Error > > > ,
153151 pub leaderboards_task : Option < JoinHandle < Result < Vec < LeaderboardItem > , anyhow:: Error > > > ,
@@ -162,6 +160,7 @@ pub struct App {
162160 pub leaderboard_view : Option < LeaderboardSelectionView > ,
163161 pub gpu_view : Option < GpuSelectionView > ,
164162 pub submission_mode_view : Option < SubmissionModeSelectionView > ,
163+ pub result_view : Option < ResultView > ,
165164}
166165
167166impl App {
@@ -199,6 +198,7 @@ impl App {
199198 leaderboard_view : None ,
200199 gpu_view : None ,
201200 submission_mode_view : None ,
201+ result_view : None ,
202202 ..Default :: default ( )
203203 } ;
204204
@@ -278,7 +278,7 @@ impl App {
278278 return Ok ( true ) ;
279279 }
280280 WelcomeAction :: ViewHistory => {
281- self . set_error_and_quit ( "View History feature is not yet implemented" . to_string ( ) ) ;
281+ self . show_error ( "View History feature is not yet implemented" . to_string ( ) ) ;
282282 return Ok ( true ) ;
283283 }
284284 WelcomeAction :: Handled => return Ok ( true ) ,
@@ -293,7 +293,7 @@ impl App {
293293 self . filepath = filepath;
294294 self . app_state = AppState :: LeaderboardSelection ;
295295 if let Err ( e) = self . spawn_load_leaderboards ( ) {
296- self . set_error_and_quit ( format ! ( "Error starting leaderboard fetch: {}" , e) ) ;
296+ self . show_error ( format ! ( "Error starting leaderboard fetch: {}" , e) ) ;
297297 }
298298 return Ok ( true ) ;
299299 }
@@ -312,7 +312,7 @@ impl App {
312312 if self . selected_gpu . is_none ( ) {
313313 self . app_state = AppState :: GpuSelection ;
314314 if let Err ( e) = self . spawn_load_gpus ( ) {
315- self . set_error_and_quit ( format ! ( "Error starting GPU fetch: {}" , e) ) ;
315+ self . show_error ( format ! ( "Error starting GPU fetch: {}" , e) ) ;
316316 }
317317 } else {
318318 self . app_state = AppState :: SubmissionModeSelection ;
@@ -354,7 +354,7 @@ impl App {
354354 self . selected_submission_mode = Some ( view. items ( ) [ idx] . value . clone ( ) ) ;
355355 self . app_state = AppState :: WaitingForResult ;
356356 if let Err ( e) = self . spawn_submit_solution ( ) {
357- self . set_error_and_quit ( format ! ( "Error starting submission: {}" , e) ) ;
357+ self . show_error ( format ! ( "Error starting submission: {}" , e) ) ;
358358 }
359359 return Ok ( true ) ;
360360 }
@@ -363,15 +363,31 @@ impl App {
363363 }
364364 }
365365 }
366+ AppState :: ShowingResult => {
367+ if let Some ( view) = & mut self . result_view {
368+ match view. handle_key_event ( key) {
369+ ResultAction :: Quit => {
370+ self . should_quit = true ;
371+ return Ok ( true ) ;
372+ }
373+ ResultAction :: Handled => {
374+ // Update scroll state based on key
375+ view. update_scroll ( key, & mut self . result_page_state ) ;
376+ return Ok ( true ) ;
377+ }
378+ ResultAction :: NotHandled => return Ok ( false ) ,
379+ }
380+ }
381+ }
366382 _ => { }
367383 }
368384
369385 Ok ( false )
370386 }
371387
372- fn set_error_and_quit ( & mut self , error_message : String ) {
373- self . final_status = Some ( error_message) ;
374- self . should_quit = true ;
388+ fn show_error ( & mut self , error_message : String ) {
389+ self . result_view = Some ( ResultView :: new ( error_message) ) ;
390+ self . app_state = AppState :: ShowingResult ;
375391 }
376392
377393 pub fn spawn_load_leaderboards ( & mut self ) -> Result < ( ) > {
@@ -450,7 +466,7 @@ impl App {
450466 } else {
451467 self . app_state = AppState :: GpuSelection ;
452468 if let Err ( e) = self . spawn_load_gpus ( ) {
453- self . set_error_and_quit ( format ! ( "Error starting GPU fetch: {}" , e) ) ;
469+ self . show_error ( format ! ( "Error starting GPU fetch: {}" , e) ) ;
454470 return ;
455471 }
456472 }
@@ -466,9 +482,9 @@ impl App {
466482 }
467483 }
468484 Ok ( Err ( e) ) => {
469- self . set_error_and_quit ( format ! ( "Error fetching leaderboards: {}" , e) )
485+ self . show_error ( format ! ( "Error fetching leaderboards: {}" , e) )
470486 }
471- Err ( e) => self . set_error_and_quit ( format ! ( "Task join error: {}" , e) ) ,
487+ Err ( e) => self . show_error ( format ! ( "Task join error: {}" , e) ) ,
472488 }
473489 }
474490 }
@@ -512,8 +528,8 @@ impl App {
512528 view. state_mut ( ) . select ( Some ( 0 ) ) ;
513529 }
514530 }
515- Ok ( Err ( e) ) => self . set_error_and_quit ( format ! ( "Error fetching GPUs: {}" , e) ) ,
516- Err ( e) => self . set_error_and_quit ( format ! ( "Task join error: {}" , e) ) ,
531+ Ok ( Err ( e) ) => self . show_error ( format ! ( "Error fetching GPUs: {}" , e) ) ,
532+ Err ( e) => self . show_error ( format ! ( "Task join error: {}" , e) ) ,
517533 }
518534 }
519535 }
@@ -525,11 +541,29 @@ impl App {
525541 let task = self . submission_task . take ( ) . unwrap ( ) ;
526542 match task. await {
527543 Ok ( Ok ( status) ) => {
528- self . final_status = Some ( status) ;
529- self . should_quit = true ; // Quit after showing final status
544+ // Process the status text
545+ let trimmed = status. trim ( ) ;
546+ let content = if trimmed. starts_with ( '[' ) && trimmed. ends_with ( ']' ) && trimmed. len ( ) >= 2 {
547+ & trimmed[ 1 ..trimmed. len ( ) - 1 ]
548+ } else {
549+ trimmed
550+ } ;
551+ let content = content. replace ( "\\ n" , "\n " ) ;
552+
553+ // Create result view and transition to showing result
554+ self . result_view = Some ( ResultView :: new ( content) ) ;
555+ self . app_state = AppState :: ShowingResult ;
556+ }
557+ Ok ( Err ( e) ) => {
558+ // Show error in result view
559+ self . result_view = Some ( ResultView :: new ( format ! ( "Submission error: {}" , e) ) ) ;
560+ self . app_state = AppState :: ShowingResult ;
561+ }
562+ Err ( e) => {
563+ // Show task join error in result view
564+ self . result_view = Some ( ResultView :: new ( format ! ( "Task join error: {}" , e) ) ) ;
565+ self . app_state = AppState :: ShowingResult ;
530566 }
531- Ok ( Err ( e) ) => self . set_error_and_quit ( format ! ( "Submission error: {}" , e) ) ,
532- Err ( e) => self . set_error_and_quit ( format ! ( "Task join error: {}" , e) ) ,
533567 }
534568 }
535569 }
@@ -571,6 +605,11 @@ pub fn ui(app: &mut App, frame: &mut Frame) {
571605 & mut app. loading_page_state . clone ( ) ,
572606 )
573607 }
608+ AppState :: ShowingResult => {
609+ if let Some ( view) = & mut app. result_view {
610+ view. render ( frame, & mut app. result_page_state ) ;
611+ }
612+ }
574613 }
575614}
576615
@@ -669,39 +708,6 @@ pub async fn run_submit_tui(
669708 }
670709 }
671710
672- let mut result_text = "Submission cancelled." . to_string ( ) ;
673-
674- if let Some ( status) = app. final_status {
675- let trimmed = status. trim ( ) ;
676- let content = if trimmed. starts_with ( '[' ) && trimmed. ends_with ( ']' ) && trimmed. len ( ) >= 2 {
677- & trimmed[ 1 ..trimmed. len ( ) - 1 ]
678- } else {
679- trimmed
680- } ;
681-
682- let content = content. replace ( "\\ n" , "\n " ) ;
683-
684- result_text = content. to_string ( ) ;
685- }
686-
687- let state = & mut app. result_page_state ;
688-
689- let mut result_page = ResultPage :: new ( result_text. clone ( ) , state) ;
690- let mut last_draw = std:: time:: Instant :: now ( ) ;
691- while !state. ack {
692- // Force redraw every 100ms for smooth animation
693- let now = std:: time:: Instant :: now ( ) ;
694- if now. duration_since ( last_draw) >= std:: time:: Duration :: from_millis ( 100 ) {
695- terminal
696- . draw ( |frame : & mut Frame | {
697- frame. render_stateful_widget ( & result_page, frame. size ( ) , state) ;
698- } )
699- . unwrap ( ) ;
700- last_draw = now;
701- }
702- result_page. handle_key_event ( state) ;
703- }
704-
705711 // Restore terminal
706712 disable_raw_mode ( ) ?;
707713 crossterm:: execute!(
0 commit comments