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, Panel, PanelSide};
  8
  9#[derive(Element)]
 10pub struct ChatPanel<V: 'static> {
 11    view_type: PhantomData<V>,
 12    scroll_state: ScrollState,
 13    current_side: PanelSide,
 14    messages: Vec<ChatMessage>,
 15}
 16
 17impl<V: 'static> ChatPanel<V> {
 18    pub fn new(scroll_state: ScrollState) -> Self {
 19        Self {
 20            view_type: PhantomData,
 21            scroll_state,
 22            current_side: PanelSide::default(),
 23            messages: Vec::new(),
 24        }
 25    }
 26
 27    pub fn side(mut self, side: PanelSide) -> Self {
 28        self.current_side = side;
 29        self
 30    }
 31
 32    pub fn with_messages(mut self, messages: Vec<ChatMessage>) -> Self {
 33        self.messages = messages;
 34        self
 35    }
 36
 37    fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
 38        let theme = theme(cx);
 39
 40        struct PanelPayload {
 41            pub scroll_state: ScrollState,
 42            pub messages: Vec<ChatMessage>,
 43        }
 44
 45        Panel::new(
 46            self.scroll_state.clone(),
 47            |_, payload| {
 48                let payload = payload.downcast_ref::<PanelPayload>().unwrap();
 49
 50                vec![div()
 51                    .flex()
 52                    .flex_col()
 53                    .h_full()
 54                    .px_2()
 55                    .gap_2()
 56                    // Header
 57                    .child(
 58                        div()
 59                            .flex()
 60                            .justify_between()
 61                            .gap_2()
 62                            .child(div().flex().child(Label::new("#design")))
 63                            .child(
 64                                div()
 65                                    .flex()
 66                                    .items_center()
 67                                    .gap_px()
 68                                    .child(IconButton::new(Icon::File))
 69                                    .child(IconButton::new(Icon::AudioOn)),
 70                            ),
 71                    )
 72                    // Chat Body
 73                    .child(
 74                        div()
 75                            .w_full()
 76                            .flex()
 77                            .flex_col()
 78                            .gap_3()
 79                            .overflow_y_scroll(payload.scroll_state.clone())
 80                            .children(payload.messages.clone()),
 81                    )
 82                    // Composer
 83                    .child(div().flex().gap_2().child(Input::new("Message #design")))
 84                    .into_any()]
 85            },
 86            Box::new(PanelPayload {
 87                scroll_state: self.scroll_state.clone(),
 88                messages: self.messages.clone(),
 89            }),
 90        )
 91        .side(self.current_side)
 92    }
 93}
 94
 95#[derive(Element, Clone)]
 96pub struct ChatMessage {
 97    author: String,
 98    text: String,
 99    sent_at: NaiveDateTime,
100}
101
102impl ChatMessage {
103    pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
104        Self {
105            author,
106            text,
107            sent_at,
108        }
109    }
110
111    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
112        div()
113            .flex()
114            .flex_col()
115            .child(
116                div()
117                    .flex()
118                    .gap_2()
119                    .child(Label::new(self.author.clone()))
120                    .child(
121                        Label::new(self.sent_at.format("%m/%d/%Y").to_string())
122                            .color(LabelColor::Muted),
123                    ),
124            )
125            .child(div().child(Label::new(self.text.clone())))
126    }
127}