11use std:: ops:: Range ;
2+ use std:: time:: { Duration , Instant } ;
23
34use crate :: discord:: ids:: {
45 Id ,
@@ -46,6 +47,10 @@ pub enum DmComposerLock {
4647 NeverMessaged ,
4748}
4849
50+ /// Discord keeps a typing indicator alive for about ten seconds, so resend a
51+ /// little sooner while the user keeps typing.
52+ const COMPOSER_TYPING_INTERVAL : Duration = Duration :: from_secs ( 8 ) ;
53+
4954#[ derive( Debug , Default ) ]
5055pub ( in crate :: tui:: state) struct ComposerUiState {
5156 pub ( in crate :: tui:: state) composer_input : TextInputState ,
@@ -71,6 +76,9 @@ pub(in crate::tui::state) struct ComposerUiState {
7176 /// Mutually exclusive with those pickers, enforced in
7277 /// `refresh_active_mention_query`.
7378 pub ( in crate :: tui:: state) emoji_completion : EmojiCompletionState ,
79+ /// Channel and time of the last typing indicator sent while composing, used
80+ /// to throttle resends.
81+ pub ( in crate :: tui:: state) last_typing_sent : Option < ( Id < ChannelMarker > , Instant ) > ,
7482}
7583
7684#[ derive( Debug , Default ) ]
@@ -462,6 +470,32 @@ impl DashboardState {
462470 self . insert_composer_text_at_cursor ( & text) ;
463471 }
464472
473+ pub fn note_composer_typing ( & mut self ) -> Option < AppCommand > {
474+ self . note_composer_typing_at ( Instant :: now ( ) )
475+ }
476+
477+ pub ( in crate :: tui:: state) fn note_composer_typing_at (
478+ & mut self ,
479+ now : Instant ,
480+ ) -> Option < AppCommand > {
481+ // Discord does not broadcast typing while editing an existing message.
482+ if self . composer . edit_target_message . is_some ( ) {
483+ return None ;
484+ }
485+ let channel_id = self . selected_channel_id ( ) ?;
486+ let due = match self . composer . last_typing_sent {
487+ Some ( ( last_channel, at) ) if last_channel == channel_id => {
488+ now. saturating_duration_since ( at) >= COMPOSER_TYPING_INTERVAL
489+ }
490+ _ => true ,
491+ } ;
492+ if !due {
493+ return None ;
494+ }
495+ self . composer . last_typing_sent = Some ( ( channel_id, now) ) ;
496+ Some ( AppCommand :: TriggerTyping { channel_id } )
497+ }
498+
465499 pub fn insert_composer_text_at_cursor ( & mut self , value : & str ) {
466500 if value. is_empty ( ) {
467501 return ;
0 commit comments