chat_panel.rs

  1use std::marker::PhantomData;
  2
  3use chrono::NaiveDateTime;
  4
  5use crate::prelude::*;
  6use crate::{Icon, IconButton, Input, Label, LabelColor};
  7
  8#[derive(Element)]
  9pub struct ChatPanel<S: 'static + Send + Sync> {
 10    element_id: ElementId,
 11    messages: Vec<ChatMessage<S>>,
 12}
 13
 14impl<S: 'static + Send + Sync> ChatPanel<S> {
 15    pub fn new(element_id: impl Into<ElementId>) -> Self {
 16        Self {
 17            element_id: element_id.into(),
 18            messages: Vec::new(),
 19        }
 20    }
 21
 22    pub fn messages(mut self, messages: Vec<ChatMessage<S>>) -> Self {
 23        self.messages = messages;
 24        self
 25    }
 26
 27    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 28        div()
 29            .id(self.element_id.clone())
 30            .flex()
 31            .flex_col()
 32            .justify_between()
 33            .h_full()
 34            .px_2()
 35            .gap_2()
 36            // Header
 37            .child(
 38                div()
 39                    .flex()
 40                    .justify_between()
 41                    .py_2()
 42                    .child(div().flex().child(Label::new("#design")))
 43                    .child(
 44                        div()
 45                            .flex()
 46                            .items_center()
 47                            .gap_px()
 48                            .child(IconButton::new("file", Icon::File))
 49                            .child(IconButton::new("audio_on", Icon::AudioOn)),
 50                    ),
 51            )
 52            .child(
 53                div()
 54                    .flex()
 55                    .flex_col()
 56                    // Chat Body
 57                    .child(
 58                        div()
 59                            .id("chat-body")
 60                            .w_full()
 61                            .flex()
 62                            .flex_col()
 63                            .gap_3()
 64                            .overflow_y_scroll()
 65                            .children(self.messages.drain(..)),
 66                    )
 67                    // Composer
 68                    .child(div().flex().my_2().child(Input::new("Message #design"))),
 69            )
 70    }
 71}
 72
 73#[derive(Element)]
 74pub struct ChatMessage<S: 'static + Send + Sync> {
 75    state_type: PhantomData<S>,
 76    author: String,
 77    text: String,
 78    sent_at: NaiveDateTime,
 79}
 80
 81impl<S: 'static + Send + Sync> ChatMessage<S> {
 82    pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
 83        Self {
 84            state_type: PhantomData,
 85            author,
 86            text,
 87            sent_at,
 88        }
 89    }
 90
 91    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 92        div()
 93            .flex()
 94            .flex_col()
 95            .child(
 96                div()
 97                    .flex()
 98                    .gap_2()
 99                    .child(Label::new(self.author.clone()))
100                    .child(
101                        Label::new(self.sent_at.format("%m/%d/%Y").to_string())
102                            .color(LabelColor::Muted),
103                    ),
104            )
105            .child(div().child(Label::new(self.text.clone())))
106    }
107}
108
109#[cfg(feature = "stories")]
110pub use stories::*;
111
112#[cfg(feature = "stories")]
113mod stories {
114    use chrono::DateTime;
115
116    use crate::{Panel, Story};
117
118    use super::*;
119
120    #[derive(Element)]
121    pub struct ChatPanelStory<S: 'static + Send + Sync> {
122        state_type: PhantomData<S>,
123    }
124
125    impl<S: 'static + Send + Sync> ChatPanelStory<S> {
126        pub fn new() -> Self {
127            Self {
128                state_type: PhantomData,
129            }
130        }
131
132        fn render(
133            &mut self,
134            _view: &mut S,
135            cx: &mut ViewContext<S>,
136        ) -> impl Element<ViewState = S> {
137            Story::container(cx)
138                .child(Story::title_for::<_, ChatPanel<S>>(cx))
139                .child(Story::label(cx, "Default"))
140                .child(
141                    Panel::new("chat-panel-1-outer", cx)
142                        .child(ChatPanel::new("chat-panel-1-inner")),
143                )
144                .child(Story::label(cx, "With Mesages"))
145                .child(Panel::new("chat-panel-2-outer", cx).child(
146                    ChatPanel::new("chat-panel-2-inner").messages(vec![
147                        ChatMessage::new(
148                            "osiewicz".to_string(),
149                            "is this thing on?".to_string(),
150                            DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
151                                .unwrap()
152                                .naive_local(),
153                        ),
154                        ChatMessage::new(
155                            "maxdeviant".to_string(),
156                            "Reading you loud and clear!".to_string(),
157                            DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
158                                .unwrap()
159                                .naive_local(),
160                        ),
161                    ]),
162                ))
163        }
164    }
165}