Skip to main content

robespierre_events/
typing.rs

1use std::{
2    collections::{hash_map::Entry, HashMap},
3    time::Duration,
4};
5
6use futures::Future;
7use robespierre_models::id::ChannelId;
8use tokio::time::Instant;
9
10use crate::{ConnectionMessage, ConnectionMessanger};
11
12/// A RAII-style typing session, which when dropped sends a StopTyping message to the [`ConnectionManager`].
13#[derive(Clone, Debug)]
14#[must_use = "Has to be dropped when the typing session ends"]
15pub struct TypingSession {
16    channel_id: ChannelId,
17    messanger: ConnectionMessanger,
18}
19
20impl TypingSession {
21    pub fn new(channel_id: ChannelId, messanger: ConnectionMessanger) -> Self {
22        Self {
23            channel_id,
24            messanger,
25        }
26    }
27}
28
29impl Drop for TypingSession {
30    fn drop(&mut self) {
31        self.messanger.send(ConnectionMessage::StopTyping {
32            channel: self.channel_id,
33        })
34    }
35}
36
37pub struct TypingSessionManager {
38    sessions: HashMap<ChannelId, usize>,
39    interval: tokio::time::Interval,
40}
41
42impl Default for TypingSessionManager {
43    fn default() -> Self {
44        Self {
45            sessions: HashMap::new(),
46            interval: tokio::time::interval(Duration::new(2, 500_000_000)),
47        }
48    }
49}
50
51impl TypingSessionManager {
52    pub fn start_typing(&mut self, channel: ChannelId) {
53        *self.sessions.entry(channel).or_insert(0) += 1;
54    }
55
56    pub fn stop_typing(&mut self, channel: ChannelId) -> bool {
57        match self.sessions.entry(channel) {
58            Entry::Occupied(mut entry) => {
59                *entry.get_mut() -= 1;
60                if *entry.get() == 0 {
61                    entry.remove();
62                    return true;
63                }
64            }
65            Entry::Vacant(_) => {
66                tracing::debug!("Trying to stop typing, but no session for the channel");
67            }
68        }
69
70        false
71    }
72
73    pub fn current_sessions(&self) -> ChannelIdIter {
74        ChannelIdIter(self.sessions.keys())
75    }
76
77    pub fn tick(&mut self) -> impl Future<Output = Instant> + '_ {
78        self.interval.tick()
79    }
80}
81
82pub struct ChannelIdIter<'a>(std::collections::hash_map::Keys<'a, ChannelId, usize>);
83
84impl<'a> Iterator for ChannelIdIter<'a> {
85    type Item = &'a ChannelId;
86
87    fn next(&mut self) -> Option<Self::Item> {
88        self.0.next()
89    }
90
91    fn size_hint(&self) -> (usize, Option<usize>) {
92        self.0.size_hint()
93    }
94}