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