fractal/session/view/content/room_history/message_row/
message_state_stack.rs

1use adw::subclass::prelude::*;
2use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
3
4use crate::session::model::MessageState;
5
6/// The number of seconds for which we show the icon acknowledging that the
7/// message was sent.
8const SENT_VISIBLE_SECONDS: u32 = 3;
9
10mod imp {
11    use std::cell::Cell;
12
13    use glib::subclass::InitializingObject;
14
15    use super::*;
16
17    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
18    #[template(
19        resource = "/org/gnome/Fractal/ui/session/view/content/room_history/message_row/message_state_stack.ui"
20    )]
21    #[properties(wrapper_type = super::MessageStateStack)]
22    pub struct MessageStateStack {
23        /// The state that is currently displayed.
24        #[property(get, set = Self::set_state, explicit_notify, builder(MessageState::default()))]
25        state: Cell<MessageState>,
26        #[template_child]
27        stack: TemplateChild<gtk::Stack>,
28    }
29
30    #[glib::object_subclass]
31    impl ObjectSubclass for MessageStateStack {
32        const NAME: &'static str = "MessageStateStack";
33        type Type = super::MessageStateStack;
34        type ParentType = adw::Bin;
35
36        fn class_init(klass: &mut Self::Class) {
37            Self::bind_template(klass);
38        }
39
40        fn instance_init(obj: &InitializingObject<Self>) {
41            obj.init_template();
42        }
43    }
44
45    #[glib::derived_properties]
46    impl ObjectImpl for MessageStateStack {}
47
48    impl WidgetImpl for MessageStateStack {}
49    impl BinImpl for MessageStateStack {}
50
51    impl MessageStateStack {
52        /// Set the state to display.
53        fn set_state(&self, state: MessageState) {
54            let prev_state = self.state.get();
55
56            if prev_state == state {
57                return;
58            }
59
60            let stack = &self.stack;
61
62            let name = match state {
63                MessageState::None => {
64                    if matches!(
65                        prev_state,
66                        MessageState::Sending
67                            | MessageState::RecoverableError
68                            | MessageState::PermanentError
69                    ) {
70                        // Show the sent icon for a few seconds.
71                        glib::timeout_add_seconds_local_once(
72                            SENT_VISIBLE_SECONDS,
73                            clone!(
74                                #[weak]
75                                stack,
76                                move || {
77                                    stack.set_visible_child_name("none");
78                                }
79                            ),
80                        );
81
82                        "sent"
83                    } else {
84                        "none"
85                    }
86                }
87                MessageState::Sending => "sending",
88                MessageState::RecoverableError => "warning",
89                MessageState::PermanentError => "error",
90                MessageState::Edited => {
91                    if matches!(
92                        prev_state,
93                        MessageState::Sending
94                            | MessageState::RecoverableError
95                            | MessageState::PermanentError
96                    ) {
97                        // Show the sent icon for a few seconds.
98                        glib::timeout_add_seconds_local_once(
99                            SENT_VISIBLE_SECONDS,
100                            clone!(
101                                #[weak]
102                                stack,
103                                move || {
104                                    stack.set_visible_child_name("edited");
105                                }
106                            ),
107                        );
108
109                        "sent"
110                    } else {
111                        "edited"
112                    }
113                }
114            };
115            stack.set_visible_child_name(name);
116
117            self.state.set(state);
118            self.obj().notify_state();
119        }
120    }
121}
122
123glib::wrapper! {
124    /// A stack to display the different message states.
125    pub struct MessageStateStack(ObjectSubclass<imp::MessageStateStack>)
126        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
127}
128
129impl MessageStateStack {
130    /// Create a new `MessageStateStack`.
131    pub fn new() -> Self {
132        glib::Object::new()
133    }
134}