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