This commit is contained in:
Max Brunsfeld 2021-08-25 15:22:14 -07:00
parent 8f86fa1ccd
commit b923f65a63
8 changed files with 114 additions and 21 deletions

10
Cargo.lock generated
View file

@ -5142,6 +5142,15 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "time"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0a10c9a9fb3a5dce8c2239ed670f1a2569fcf42da035f5face1b19860d52b0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.1.1" version = "0.1.1"
@ -5832,6 +5841,7 @@ dependencies = [
"smol", "smol",
"surf", "surf",
"tempdir", "tempdir",
"time 0.3.2",
"tiny_http", "tiny_http",
"toml 0.5.8", "toml 0.5.8",
"tree-sitter", "tree-sitter",

View file

@ -48,6 +48,7 @@ smallvec = { version = "1.6", features = ["union"] }
smol = "1.2.5" smol = "1.2.5"
surf = "2.2" surf = "2.2"
tempdir = { version = "0.3.7", optional = true } tempdir = { version = "0.3.7", optional = true }
time = { version = "0.3", features = ["local-offset"] }
tiny_http = "0.8" tiny_http = "0.8"
toml = "0.5" toml = "0.5"
tree-sitter = "0.19.5" tree-sitter = "0.19.5"

View file

@ -22,8 +22,14 @@ color = "$text.2"
[workspace.active_sidebar_icon] [workspace.active_sidebar_icon]
color = "$text.0" color = "$text.0"
[chat_panel]
padding = { top = 10.0, bottom = 10.0, left = 10.0, right = 10.0 }
[chat_panel.message] [chat_panel.message]
body = "$text.0" body = "$text.0"
sender.margin.right = 10.0
sender.text = { color = "#ff0000", weight = "bold", italic = true }
timestamp.text = "$text.2"
[selector] [selector]
background = "$surface.2" background = "$surface.2"

View file

@ -14,6 +14,7 @@ use std::{
ops::Range, ops::Range,
sync::Arc, sync::Arc,
}; };
use time::OffsetDateTime;
use zrpc::{ use zrpc::{
proto::{self, ChannelMessageSent}, proto::{self, ChannelMessageSent},
TypedEnvelope, TypedEnvelope,
@ -47,6 +48,7 @@ pub struct Channel {
pub struct ChannelMessage { pub struct ChannelMessage {
pub id: u64, pub id: u64,
pub body: String, pub body: String,
pub timestamp: OffsetDateTime,
pub sender: Arc<User>, pub sender: Arc<User>,
} }
@ -261,14 +263,17 @@ impl Channel {
this.insert_message( this.insert_message(
ChannelMessage { ChannelMessage {
id: response.message_id, id: response.message_id,
timestamp: OffsetDateTime::from_unix_timestamp(
response.timestamp as i64,
)?,
body, body,
sender, sender,
}, },
cx, cx,
); );
} }
}); Ok(())
Ok(()) })
} }
.log_err() .log_err()
}) })
@ -363,6 +368,7 @@ impl ChannelMessage {
Ok(ChannelMessage { Ok(ChannelMessage {
id: message.id, id: message.id,
body: message.body, body: message.body,
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)?,
sender, sender,
}) })
} }

View file

@ -9,6 +9,7 @@ use gpui::{
Subscription, View, ViewContext, ViewHandle, Subscription, View, ViewContext, ViewHandle,
}; };
use postage::watch; use postage::watch;
use time::{OffsetDateTime, UtcOffset};
pub struct ChatPanel { pub struct ChatPanel {
channel_list: ModelHandle<ChannelList>, channel_list: ModelHandle<ChannelList>,
@ -75,12 +76,13 @@ impl ChatPanel {
fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) { fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) {
if self.active_channel.as_ref().map(|e| &e.0) != Some(&channel) { if self.active_channel.as_ref().map(|e| &e.0) != Some(&channel) {
let subscription = cx.subscribe(&channel, Self::channel_did_change); let subscription = cx.subscribe(&channel, Self::channel_did_change);
let now = OffsetDateTime::now_utc();
self.messages = ListState::new( self.messages = ListState::new(
channel channel
.read(cx) .read(cx)
.messages() .messages()
.cursor::<(), ()>() .cursor::<(), ()>()
.map(|m| self.render_message(m)) .map(|m| self.render_message(m, now))
.collect(), .collect(),
Orientation::Bottom, Orientation::Bottom,
); );
@ -99,12 +101,13 @@ impl ChatPanel {
old_range, old_range,
new_count, new_count,
} => { } => {
let now = OffsetDateTime::now_utc();
self.messages.splice( self.messages.splice(
old_range.clone(), old_range.clone(),
channel channel
.read(cx) .read(cx)
.messages_in_range(old_range.start..(old_range.start + new_count)) .messages_in_range(old_range.start..(old_range.start + new_count))
.map(|message| self.render_message(message)), .map(|message| self.render_message(message, now)),
); );
} }
} }
@ -115,15 +118,50 @@ impl ChatPanel {
Expanded::new(1., List::new(self.messages.clone()).boxed()).boxed() Expanded::new(1., List::new(self.messages.clone()).boxed()).boxed()
} }
fn render_message(&self, message: &ChannelMessage) -> ElementBox { fn render_message(&self, message: &ChannelMessage, now: OffsetDateTime) -> ElementBox {
let settings = self.settings.borrow(); let settings = self.settings.borrow();
Text::new( let theme = &settings.theme.chat_panel.message;
message.body.clone(), Flex::column()
settings.ui_font_family, .with_child(
settings.ui_font_size, Flex::row()
) .with_child(
.with_style(&settings.theme.chat_panel.message.body) Container::new(
.boxed() Label::new(
message.sender.github_login.clone(),
settings.ui_font_family,
settings.ui_font_size,
)
.with_style(&theme.sender.label)
.boxed(),
)
.with_style(&theme.sender.container)
.boxed(),
)
.with_child(
Container::new(
Label::new(
format_timestamp(message.timestamp, now),
settings.ui_font_family,
settings.ui_font_size,
)
.with_style(&theme.timestamp.label)
.boxed(),
)
.with_style(&theme.timestamp.container)
.boxed(),
)
.boxed(),
)
.with_child(
Text::new(
message.body.clone(),
settings.ui_font_family,
settings.ui_font_size,
)
.with_style(&theme.body)
.boxed(),
)
.boxed()
} }
fn render_input_box(&self) -> ElementBox { fn render_input_box(&self) -> ElementBox {
@ -157,9 +195,36 @@ impl View for ChatPanel {
} }
fn render(&self, _: &RenderContext<Self>) -> ElementBox { fn render(&self, _: &RenderContext<Self>) -> ElementBox {
Flex::column() let theme = &self.settings.borrow().theme;
.with_child(self.render_active_channel_messages()) Container::new(
.with_child(self.render_input_box()) Flex::column()
.boxed() .with_child(self.render_active_channel_messages())
.with_child(self.render_input_box())
.boxed(),
)
.with_style(&theme.chat_panel.container)
.boxed()
}
}
fn format_timestamp(mut timestamp: OffsetDateTime, mut now: OffsetDateTime) -> String {
let local_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
timestamp = timestamp.to_offset(local_offset);
now = now.to_offset(local_offset);
let today = now.date();
let date = timestamp.date();
let mut hour = timestamp.hour();
let mut part = "am";
if hour > 12 {
hour -= 12;
part = "pm";
}
if date == today {
format!("{}:{}{}", hour, timestamp.minute(), part)
} else if date.next_day() == Some(today) {
format!("yesterday at {}:{}{}", hour, timestamp.minute(), part)
} else {
format!("{}/{}/{}", date.month(), date.day(), date.year())
} }
} }

View file

@ -55,12 +55,16 @@ pub struct SidebarIcon {
#[derive(Debug, Default, Deserialize)] #[derive(Debug, Default, Deserialize)]
pub struct ChatPanel { pub struct ChatPanel {
#[serde(flatten)]
pub container: ContainerStyle,
pub message: ChatMessage, pub message: ChatMessage,
} }
#[derive(Debug, Default, Deserialize)] #[derive(Debug, Default, Deserialize)]
pub struct ChatMessage { pub struct ChatMessage {
pub body: TextStyle, pub body: TextStyle,
pub sender: ContainedLabel,
pub timestamp: ContainedLabel,
} }
#[derive(Debug, Default, Deserialize)] #[derive(Debug, Default, Deserialize)]
@ -70,12 +74,12 @@ pub struct Selector {
#[serde(flatten)] #[serde(flatten)]
pub label: LabelStyle, pub label: LabelStyle,
pub item: SelectorItem, pub item: ContainedLabel,
pub active_item: SelectorItem, pub active_item: ContainedLabel,
} }
#[derive(Debug, Default, Deserialize)] #[derive(Debug, Default, Deserialize)]
pub struct SelectorItem { pub struct ContainedLabel {
#[serde(flatten)] #[serde(flatten)]
pub container: ContainerStyle, pub container: ContainerStyle,
#[serde(flatten)] #[serde(flatten)]

View file

@ -99,6 +99,7 @@ impl ThemeSelector {
Ok(theme) => { Ok(theme) => {
cx.notify_all(); cx.notify_all();
action.0.settings_tx.lock().borrow_mut().theme = theme; action.0.settings_tx.lock().borrow_mut().theme = theme;
log::info!("reloaded theme {}", current_theme_name);
} }
Err(error) => { Err(error) => {
log::error!("failed to load theme {}: {:?}", current_theme_name, error) log::error!("failed to load theme {}: {:?}", current_theme_name, error)

View file

@ -958,7 +958,7 @@ impl View for Workspace {
if let Some(panel) = self.left_sidebar.active_item() { if let Some(panel) = self.left_sidebar.active_item() {
content.add_child( content.add_child(
ConstrainedBox::new(ChildView::new(panel.id()).boxed()) ConstrainedBox::new(ChildView::new(panel.id()).boxed())
.with_width(200.0) .with_width(300.0)
.named("left panel"), .named("left panel"),
); );
} }
@ -966,7 +966,7 @@ impl View for Workspace {
if let Some(panel) = self.right_sidebar.active_item() { if let Some(panel) = self.right_sidebar.active_item() {
content.add_child( content.add_child(
ConstrainedBox::new(ChildView::new(panel.id()).boxed()) ConstrainedBox::new(ChildView::new(panel.id()).boxed())
.with_width(200.0) .with_width(300.0)
.named("right panel"), .named("right panel"),
); );
} }