chat_panel.rs

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