1
+ use std:: { pin:: Pin , time:: Duration } ;
2
+
3
+ use crossterm:: event:: { Event as CrosstermEvent , KeyCode , KeyEvent , KeyEventKind } ;
4
+ use futures_util:: { FutureExt , Stream , StreamExt } ;
5
+ use tokio:: time:: { Instant , Sleep } ;
6
+
1
7
use crate :: {
2
8
auth:: {
3
9
self ,
@@ -18,8 +24,6 @@ use crate::{
18
24
theme:: Theme ,
19
25
} ,
20
26
} ;
21
- use crossterm:: event:: { Event as CrosstermEvent , KeyCode , KeyEvent , KeyEventKind } ;
22
- use futures_util:: { FutureExt , Stream , StreamExt } ;
23
27
24
28
mod direction;
25
29
pub use direction:: { Direction , IndexOutOfRange } ;
@@ -58,10 +62,17 @@ pub struct Application {
58
62
jobs : Jobs ,
59
63
state : State ,
60
64
theme : Theme ,
65
+ idle_timer : Pin < Box < Sleep > > ,
66
+
61
67
should_render : bool ,
62
68
should_quit : bool ,
63
69
}
64
70
71
+ #[ derive( PartialEq , Eq ) ]
72
+ pub enum EventLoopControlFlow {
73
+ Quit ,
74
+ }
75
+
65
76
impl Application {
66
77
pub fn new ( terminal : Terminal , client : Client ) -> Self {
67
78
let state = State {
@@ -82,6 +93,7 @@ impl Application {
82
93
jobs : Jobs :: new ( ) ,
83
94
state,
84
95
theme : Theme :: new ( ) ,
96
+ idle_timer : Box :: pin ( tokio:: time:: sleep ( Duration :: from_millis ( 250 ) ) ) ,
85
97
should_quit : false ,
86
98
should_render : false ,
87
99
}
@@ -112,8 +124,6 @@ impl Application {
112
124
{
113
125
self . terminal . init ( ) ?;
114
126
115
- self . render ( ) ;
116
-
117
127
self . event_loop ( input) . await ;
118
128
119
129
self . terminal . exit ( ) ?;
@@ -122,6 +132,19 @@ impl Application {
122
132
}
123
133
124
134
async fn event_loop < S > ( & mut self , input : & mut S )
135
+ where
136
+ S : Stream < Item = std:: io:: Result < CrosstermEvent > > + Unpin ,
137
+ {
138
+ self . render ( ) ;
139
+
140
+ loop {
141
+ if self . event_loop_until_idle ( input) . await == EventLoopControlFlow :: Quit {
142
+ break ;
143
+ }
144
+ }
145
+ }
146
+
147
+ pub async fn event_loop_until_idle < S > ( & mut self , input : & mut S ) -> EventLoopControlFlow
125
148
where
126
149
S : Stream < Item = std:: io:: Result < CrosstermEvent > > + Unpin ,
127
150
{
@@ -135,6 +158,9 @@ impl Application {
135
158
Some ( command) = self . jobs. futures. next( ) => {
136
159
Some ( command. unwrap( ) )
137
160
}
161
+ _ = & mut self . idle_timer => {
162
+ Some ( Command :: Idle )
163
+ }
138
164
} ;
139
165
140
166
if let Some ( command) = command {
@@ -148,11 +174,12 @@ impl Application {
148
174
}
149
175
150
176
if self . should_quit {
151
- break ;
177
+ break EventLoopControlFlow :: Quit ;
152
178
}
153
179
}
154
180
}
155
181
182
+ #[ tracing:: instrument( skip_all, fields( %command) ) ]
156
183
fn apply ( & mut self , command : Command ) {
157
184
tracing:: debug!( "Apply {command:?}" ) ;
158
185
@@ -166,6 +193,9 @@ impl Application {
166
193
Command :: ResizeTerminal { .. } => {
167
194
self . should_render = true ;
168
195
}
196
+ Command :: Idle => {
197
+ self . handle_idle ( ) ;
198
+ }
169
199
Command :: Authenticate ( method) => self . authenticate ( method) ,
170
200
Command :: DeviceAuthorizationFlow ( device_authorization) => {
171
201
self . device_authorize_flow ( device_authorization)
@@ -429,6 +459,7 @@ impl Application {
429
459
. device_authorize_request ( )
430
460
. await
431
461
. unwrap ( ) ;
462
+
432
463
Ok ( Command :: DeviceAuthorizationFlow ( res) )
433
464
}
434
465
. boxed ( ) ;
@@ -472,3 +503,29 @@ impl Application {
472
503
self . set_auth ( auth) ;
473
504
}
474
505
}
506
+
507
+ impl Application {
508
+ fn handle_idle ( & mut self ) {
509
+ self . reset_idle_timer ( ) ;
510
+
511
+ #[ cfg( feature = "integration" ) ]
512
+ {
513
+ self . should_render = true ;
514
+ self . should_quit = true ;
515
+ }
516
+ }
517
+
518
+ fn reset_idle_timer ( & mut self ) {
519
+ // https://github.com/tokio-rs/tokio/blob/e53b92a9939565edb33575fff296804279e5e419/tokio/src/time/instant.rs#L62
520
+ self . idle_timer
521
+ . as_mut ( )
522
+ . reset ( Instant :: now ( ) + Duration :: from_secs ( 86400 * 365 * 30 ) ) ;
523
+ }
524
+ }
525
+
526
+ #[ cfg( feature = "integration" ) ]
527
+ impl Application {
528
+ pub fn assert_buffer ( & self , expected : & ratatui:: buffer:: Buffer ) {
529
+ self . terminal . assert_buffer ( expected)
530
+ }
531
+ }
0 commit comments