Compare commits
7 Commits
acp-rewind
...
collab-v0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4edd0365a1 | ||
|
|
cc4fb1c1b5 | ||
|
|
fc3d754aea | ||
|
|
643f3db2b2 | ||
|
|
b90c04009f | ||
|
|
11f7a2cb0e | ||
|
|
8bdc59703a |
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1467,7 +1467,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.22.1"
|
||||
version = "0.22.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -10063,7 +10063,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.107.0"
|
||||
version = "0.107.1"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
||||
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
|
||||
default-run = "collab"
|
||||
edition = "2021"
|
||||
name = "collab"
|
||||
version = "0.22.1"
|
||||
version = "0.22.2"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -9,13 +9,3 @@ pub mod projects;
|
||||
pub mod rooms;
|
||||
pub mod servers;
|
||||
pub mod users;
|
||||
|
||||
fn max_assign<T: Ord>(max: &mut Option<T>, val: T) {
|
||||
if let Some(max_val) = max {
|
||||
if val > *max_val {
|
||||
*max = Some(val);
|
||||
}
|
||||
} else {
|
||||
*max = Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,17 +89,14 @@ impl Database {
|
||||
|
||||
let mut rows = channel_message::Entity::find()
|
||||
.filter(condition)
|
||||
.order_by_asc(channel_message::Column::Id)
|
||||
.limit(count as u64)
|
||||
.stream(&*tx)
|
||||
.await?;
|
||||
|
||||
let mut max_id = None;
|
||||
let mut messages = Vec::new();
|
||||
while let Some(row) = rows.next().await {
|
||||
let row = row?;
|
||||
|
||||
max_assign(&mut max_id, row.id);
|
||||
|
||||
let nonce = row.nonce.as_u64_pair();
|
||||
messages.push(proto::ChannelMessage {
|
||||
id: row.id.to_proto(),
|
||||
@@ -113,50 +110,6 @@ impl Database {
|
||||
});
|
||||
}
|
||||
drop(rows);
|
||||
|
||||
if let Some(max_id) = max_id {
|
||||
let has_older_message = observed_channel_messages::Entity::find()
|
||||
.filter(
|
||||
observed_channel_messages::Column::UserId
|
||||
.eq(user_id)
|
||||
.and(observed_channel_messages::Column::ChannelId.eq(channel_id))
|
||||
.and(observed_channel_messages::Column::ChannelMessageId.lt(max_id)),
|
||||
)
|
||||
.one(&*tx)
|
||||
.await?
|
||||
.is_some();
|
||||
|
||||
if has_older_message {
|
||||
observed_channel_messages::Entity::update(
|
||||
observed_channel_messages::ActiveModel {
|
||||
user_id: ActiveValue::Unchanged(user_id),
|
||||
channel_id: ActiveValue::Unchanged(channel_id),
|
||||
channel_message_id: ActiveValue::Set(max_id),
|
||||
},
|
||||
)
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
} else {
|
||||
observed_channel_messages::Entity::insert(
|
||||
observed_channel_messages::ActiveModel {
|
||||
user_id: ActiveValue::Set(user_id),
|
||||
channel_id: ActiveValue::Set(channel_id),
|
||||
channel_message_id: ActiveValue::Set(max_id),
|
||||
},
|
||||
)
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
observed_channel_messages::Column::UserId,
|
||||
observed_channel_messages::Column::ChannelId,
|
||||
])
|
||||
.update_columns([observed_channel_messages::Column::ChannelMessageId])
|
||||
.to_owned(),
|
||||
)
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(messages)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -130,6 +130,7 @@ impl ChatPanel {
|
||||
fs,
|
||||
client,
|
||||
channel_store,
|
||||
|
||||
active_chat: Default::default(),
|
||||
pending_serialization: Task::ready(None),
|
||||
message_list,
|
||||
@@ -328,12 +329,26 @@ impl ChatPanel {
|
||||
}
|
||||
|
||||
fn render_message(&self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
let message = self.active_chat.as_ref().unwrap().0.read(cx).message(ix);
|
||||
let (message, is_continuation, is_last) = {
|
||||
let active_chat = self.active_chat.as_ref().unwrap().0.read(cx);
|
||||
let last_message = active_chat.message(ix.saturating_sub(1));
|
||||
let this_message = active_chat.message(ix);
|
||||
let is_continuation = last_message.id != this_message.id
|
||||
&& this_message.sender.id == last_message.sender.id;
|
||||
|
||||
(
|
||||
active_chat.message(ix),
|
||||
is_continuation,
|
||||
active_chat.message_count() == ix + 1,
|
||||
)
|
||||
};
|
||||
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let theme = theme::current(cx);
|
||||
let style = if message.is_pending() {
|
||||
&theme.chat_panel.pending_message
|
||||
} else if is_continuation {
|
||||
&theme.chat_panel.continuation_message
|
||||
} else {
|
||||
&theme.chat_panel.message
|
||||
};
|
||||
@@ -349,49 +364,103 @@ impl ChatPanel {
|
||||
enum DeleteMessage {}
|
||||
|
||||
let body = message.body.clone();
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Label::new(
|
||||
message.sender.github_login.clone(),
|
||||
style.sender.text.clone(),
|
||||
if is_continuation {
|
||||
Flex::row()
|
||||
.with_child(Text::new(body, style.body.clone()))
|
||||
.with_children(message_id_to_remove.map(|id| {
|
||||
MouseEventHandler::new::<DeleteMessage, _>(id as usize, cx, |mouse_state, _| {
|
||||
let button_style = theme.chat_panel.icon_button.style_for(mouse_state);
|
||||
render_icon_button(button_style, "icons/x.svg")
|
||||
.aligned()
|
||||
.into_any()
|
||||
})
|
||||
.with_padding(Padding::uniform(2.))
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
this.remove_message(id, cx);
|
||||
})
|
||||
.flex_float()
|
||||
}))
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.with_margin_bottom(if is_last {
|
||||
theme.chat_panel.last_message_bottom_spacing
|
||||
} else {
|
||||
0.
|
||||
})
|
||||
.into_any()
|
||||
} else {
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
message
|
||||
.sender
|
||||
.avatar
|
||||
.clone()
|
||||
.map(|avatar| {
|
||||
Image::from_data(avatar)
|
||||
.with_style(theme.collab_panel.channel_avatar)
|
||||
.into_any()
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
Empty::new()
|
||||
.constrained()
|
||||
.with_width(
|
||||
theme.collab_panel.channel_avatar.width.unwrap_or(12.),
|
||||
)
|
||||
.into_any()
|
||||
})
|
||||
.contained()
|
||||
.with_margin_right(4.),
|
||||
)
|
||||
.contained()
|
||||
.with_style(style.sender.container),
|
||||
)
|
||||
.with_child(
|
||||
Label::new(
|
||||
format_timestamp(message.timestamp, now, self.local_timezone),
|
||||
style.timestamp.text.clone(),
|
||||
.with_child(
|
||||
Label::new(
|
||||
message.sender.github_login.clone(),
|
||||
style.sender.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(style.sender.container),
|
||||
)
|
||||
.contained()
|
||||
.with_style(style.timestamp.container),
|
||||
)
|
||||
.with_children(message_id_to_remove.map(|id| {
|
||||
MouseEventHandler::new::<DeleteMessage, _>(
|
||||
id as usize,
|
||||
cx,
|
||||
|mouse_state, _| {
|
||||
let button_style =
|
||||
theme.chat_panel.icon_button.style_for(mouse_state);
|
||||
render_icon_button(button_style, "icons/x.svg")
|
||||
.aligned()
|
||||
.into_any()
|
||||
},
|
||||
.with_child(
|
||||
Label::new(
|
||||
format_timestamp(message.timestamp, now, self.local_timezone),
|
||||
style.timestamp.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(style.timestamp.container),
|
||||
)
|
||||
.with_padding(Padding::uniform(2.))
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
this.remove_message(id, cx);
|
||||
})
|
||||
.flex_float()
|
||||
})),
|
||||
)
|
||||
.with_child(Text::new(body, style.body.clone()))
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.into_any()
|
||||
.with_children(message_id_to_remove.map(|id| {
|
||||
MouseEventHandler::new::<DeleteMessage, _>(
|
||||
id as usize,
|
||||
cx,
|
||||
|mouse_state, _| {
|
||||
let button_style =
|
||||
theme.chat_panel.icon_button.style_for(mouse_state);
|
||||
render_icon_button(button_style, "icons/x.svg")
|
||||
.aligned()
|
||||
.into_any()
|
||||
},
|
||||
)
|
||||
.with_padding(Padding::uniform(2.))
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
this.remove_message(id, cx);
|
||||
})
|
||||
.flex_float()
|
||||
}))
|
||||
.align_children_center(),
|
||||
)
|
||||
.with_child(Text::new(body, style.body.clone()))
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.with_margin_bottom(if is_last {
|
||||
theme.chat_panel.last_message_bottom_spacing
|
||||
} else {
|
||||
0.
|
||||
})
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
fn render_input_box(&self, theme: &Arc<Theme>, cx: &AppContext) -> AnyElement<Self> {
|
||||
|
||||
@@ -1937,6 +1937,8 @@ impl CollabPanel {
|
||||
is_dragged_over = true;
|
||||
}
|
||||
|
||||
let has_messages_notification = channel.unseen_message_id.is_some();
|
||||
|
||||
MouseEventHandler::new::<Channel, _>(ix, cx, |state, cx| {
|
||||
let row_hovered = state.hovered();
|
||||
|
||||
@@ -2022,24 +2024,33 @@ impl CollabPanel {
|
||||
.flex(1., true)
|
||||
})
|
||||
.with_child(
|
||||
MouseEventHandler::new::<ChannelNote, _>(ix, cx, move |_, _| {
|
||||
MouseEventHandler::new::<ChannelNote, _>(ix, cx, move |mouse_state, _| {
|
||||
let container_style = collab_theme
|
||||
.disclosure
|
||||
.button
|
||||
.style_for(mouse_state)
|
||||
.container;
|
||||
|
||||
if channel.unseen_message_id.is_some() {
|
||||
Svg::new("icons/conversations.svg")
|
||||
.with_color(collab_theme.channel_note_active_color)
|
||||
.constrained()
|
||||
.with_width(collab_theme.channel_hash.width)
|
||||
.contained()
|
||||
.with_style(container_style)
|
||||
.with_uniform_padding(4.)
|
||||
.into_any()
|
||||
} else if row_hovered {
|
||||
Svg::new("icons/conversations.svg")
|
||||
.with_color(collab_theme.channel_hash.color)
|
||||
.constrained()
|
||||
.with_width(collab_theme.channel_hash.width)
|
||||
.contained()
|
||||
.with_style(container_style)
|
||||
.with_uniform_padding(4.)
|
||||
.into_any()
|
||||
} else {
|
||||
Empty::new()
|
||||
.constrained()
|
||||
.with_width(collab_theme.channel_hash.width)
|
||||
.into_any()
|
||||
Empty::new().into_any()
|
||||
}
|
||||
})
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
@@ -2056,7 +2067,12 @@ impl CollabPanel {
|
||||
.with_margin_right(4.),
|
||||
)
|
||||
.with_child(
|
||||
MouseEventHandler::new::<ChannelCall, _>(ix, cx, move |_, cx| {
|
||||
MouseEventHandler::new::<ChannelCall, _>(ix, cx, move |mouse_state, cx| {
|
||||
let container_style = collab_theme
|
||||
.disclosure
|
||||
.button
|
||||
.style_for(mouse_state)
|
||||
.container;
|
||||
if row_hovered || channel.unseen_note_version.is_some() {
|
||||
Svg::new("icons/file.svg")
|
||||
.with_color(if channel.unseen_note_version.is_some() {
|
||||
@@ -2067,6 +2083,8 @@ impl CollabPanel {
|
||||
.constrained()
|
||||
.with_width(collab_theme.channel_hash.width)
|
||||
.contained()
|
||||
.with_style(container_style)
|
||||
.with_uniform_padding(4.)
|
||||
.with_margin_right(collab_theme.channel_hash.container.margin.left)
|
||||
.with_tooltip::<NotesTooltip>(
|
||||
ix as usize,
|
||||
@@ -2076,23 +2094,20 @@ impl CollabPanel {
|
||||
cx,
|
||||
)
|
||||
.into_any()
|
||||
} else {
|
||||
} else if has_messages_notification {
|
||||
Empty::new()
|
||||
.constrained()
|
||||
.with_width(collab_theme.channel_hash.width)
|
||||
.contained()
|
||||
.with_uniform_padding(4.)
|
||||
.with_margin_right(collab_theme.channel_hash.container.margin.left)
|
||||
.into_any()
|
||||
} else {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
})
|
||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||
let participants =
|
||||
this.channel_store.read(cx).channel_participants(channel_id);
|
||||
if is_active || participants.is_empty() {
|
||||
this.open_channel_notes(&OpenChannelNotes { channel_id }, cx);
|
||||
} else {
|
||||
this.join_channel(channel_id, cx);
|
||||
};
|
||||
this.open_channel_notes(&OpenChannelNotes { channel_id }, cx);
|
||||
}),
|
||||
)
|
||||
.align_children_center()
|
||||
|
||||
@@ -635,6 +635,8 @@ pub struct ChatPanel {
|
||||
pub channel_select: ChannelSelect,
|
||||
pub input_editor: FieldEditor,
|
||||
pub message: ChatMessage,
|
||||
pub continuation_message: ChatMessage,
|
||||
pub last_message_bottom_spacing: f32,
|
||||
pub pending_message: ChatMessage,
|
||||
pub sign_in_prompt: Interactive<TextStyle>,
|
||||
pub icon_button: Interactive<IconButton>,
|
||||
|
||||
@@ -78,10 +78,14 @@ fn increment(vim: &mut Vim, mut delta: i32, step: i32, cx: &mut WindowContext) {
|
||||
2 => format!("{:b}", result),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if selection.is_empty() {
|
||||
new_anchors.push((false, snapshot.anchor_after(range.end)))
|
||||
}
|
||||
edits.push((range, replace));
|
||||
edits.push((range.clone(), replace));
|
||||
}
|
||||
if selection.is_empty() {
|
||||
new_anchors.push((false, snapshot.anchor_after(range.end)))
|
||||
}
|
||||
} else {
|
||||
if selection.is_empty() {
|
||||
new_anchors.push((true, snapshot.anchor_after(start)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,6 +230,8 @@ mod test {
|
||||
cx.assert_matches_neovim("(ˇ0b10f)", ["ctrl-a"], "(0b1ˇ1f)")
|
||||
.await;
|
||||
cx.assert_matches_neovim("ˇ-1", ["ctrl-a"], "ˇ0").await;
|
||||
cx.assert_matches_neovim("banˇana", ["ctrl-a"], "banˇana")
|
||||
.await;
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
@@ -13,3 +13,6 @@
|
||||
{"Put":{"state":"ˇ-1"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"ˇ0","mode":"Normal"}}
|
||||
{"Put":{"state":"banˇana"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"banˇana","mode":"Normal"}}
|
||||
|
||||
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.107.0"
|
||||
version = "0.107.1"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
preview
|
||||
@@ -87,7 +87,19 @@ export default function chat_panel(): any {
|
||||
...text(layer, "sans", "base", { weight: "bold" }),
|
||||
},
|
||||
timestamp: text(layer, "sans", "base", "disabled"),
|
||||
margin: { bottom: SPACING }
|
||||
margin: { top: SPACING }
|
||||
},
|
||||
last_message_bottom_spacing: SPACING,
|
||||
continuation_message: {
|
||||
body: text(layer, "sans", "base"),
|
||||
sender: {
|
||||
margin: {
|
||||
right: 8,
|
||||
},
|
||||
...text(layer, "sans", "base", { weight: "bold" }),
|
||||
},
|
||||
timestamp: text(layer, "sans", "base", "disabled"),
|
||||
|
||||
},
|
||||
pending_message: {
|
||||
body: text(layer, "sans", "base"),
|
||||
|
||||
@@ -21,6 +21,7 @@ export default function contacts_panel(): any {
|
||||
...text(theme.lowest, "sans", "base"),
|
||||
button: icon_button({ variant: "ghost" }),
|
||||
spacing: 4,
|
||||
padding: 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user