robespierre_events/
typing.rs1use 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#[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}