pikadick/util/
loading_reaction.rs

1use serenity::{
2    http::Http,
3    model::prelude::*,
4};
5use std::sync::Arc;
6
7const LOADING_EMOJI: char = '⌛';
8const OK_EMOJI: char = '✅';
9const ERR_EMOJI: char = '❌';
10
11/// This type attaches to a message and displays a loading sign until `send_ok` or `send_err` are called,
12/// where it then displays a check or an X respectively.
13/// If neither are called, send_err is called automatically from the destructor.
14/// All functions are not async and can only be used from a tokio runtime context.
15/// Errors are silently ignored.
16pub struct LoadingReaction {
17    http: Arc<Http>,
18    channel_id: ChannelId,
19    msg_id: MessageId,
20
21    sent_reaction: bool,
22}
23
24impl std::fmt::Debug for LoadingReaction {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.debug_struct("LoadingReaction")
27            .field("channel_id", &self.channel_id)
28            .field("msg_id", &self.msg_id)
29            .field("sent_reaction", &self.sent_reaction)
30            .finish()
31    }
32}
33
34impl LoadingReaction {
35    /// Create a Loading Reaction attatched to a message.
36    pub fn new(http: Arc<Http>, msg: &Message) -> Self {
37        let channel_id = msg.channel_id;
38        let msg_id = msg.id;
39
40        let ret = LoadingReaction {
41            http,
42            channel_id,
43            msg_id,
44
45            sent_reaction: false,
46        };
47
48        ret.send_reaction(LOADING_EMOJI);
49
50        ret
51    }
52
53    /// Send a reaction.
54    pub fn send_reaction<T>(&self, reaction: T)
55    where
56        T: Into<ReactionType>,
57    {
58        {
59            let msg_id = self.msg_id;
60            let channel_id = self.channel_id;
61            let http = self.http.clone();
62            let reaction = reaction.into();
63
64            tokio::spawn(async move {
65                http.create_reaction(channel_id, msg_id, &reaction)
66                    .await
67                    .ok();
68            });
69        }
70    }
71
72    /// Send the `Ok` reaction
73    pub fn send_ok(&mut self) {
74        self.send_reaction(OK_EMOJI);
75        self.sent_reaction = true;
76    }
77
78    /// Send the `Fail` reaction
79    pub fn send_fail(&mut self) {
80        self.send_reaction(ERR_EMOJI);
81        self.sent_reaction = true;
82    }
83}
84
85impl Drop for LoadingReaction {
86    fn drop(&mut self) {
87        if !self.sent_reaction {
88            self.send_fail();
89        }
90    }
91}