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