From e0ff7ba18082373ab15bb46e44ce43167e08635c Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sun, 1 Oct 2023 19:53:32 -0700 Subject: [PATCH] Add channel note indicator and clear changed status --- crates/channel/src/channel_store.rs | 20 ++++-- .../src/channel_store/channel_index.rs | 12 +++- .../collab/src/tests/channel_buffer_tests.rs | 63 ++++++++++++++++++- crates/collab_ui/src/collab_panel.rs | 33 +++++++++- crates/theme/src/theme.rs | 1 + styles/src/style_tree/collab_panel.ts | 1 + 6 files changed, 119 insertions(+), 11 deletions(-) diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 19fc4f35ee85dec1ed9a9b2d9ba114a14c2c197e..417f486b9e2407622ac24f367c59bc344f60344e 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -43,7 +43,7 @@ pub type ChannelData = (Channel, ChannelPath); pub struct Channel { pub id: ChannelId, pub name: String, - pub has_changed: bool, + pub has_note_changed: bool, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] @@ -200,19 +200,27 @@ impl ChannelStore { ) -> Task>> { let client = self.client.clone(); let user_store = self.user_store.clone(); - self.open_channel_resource( + let open_channel_buffer = self.open_channel_resource( channel_id, |this| &mut this.opened_buffers, |channel, cx| ChannelBuffer::new(channel, client, user_store, cx), cx, - ) + ); + cx.spawn(|this, mut cx| async move { + let buffer = open_channel_buffer.await?; + this.update(&mut cx, |this, cx| { + this.channel_index.clear_note_changed(channel_id); + cx.notify(); + }); + Ok(buffer) + }) } pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> Option { self.channel_index .by_id() .get(&channel_id) - .map(|channel| channel.has_changed) + .map(|channel| channel.has_note_changed) } pub fn open_channel_chat( @@ -787,7 +795,7 @@ impl ChannelStore { Arc::new(Channel { id: channel.id, name: channel.name, - has_changed: false, + has_note_changed: false, }), ), } @@ -825,7 +833,7 @@ impl ChannelStore { } for id_changed in payload.notes_changed { - index.has_changed(id_changed); + index.note_changed(id_changed); } for edge in payload.insert_edge { diff --git a/crates/channel/src/channel_store/channel_index.rs b/crates/channel/src/channel_store/channel_index.rs index 88c6b856983d014fd66f727cd48b496a90d36c6f..42474f1d85da1cf3d581daaae25e58bd2c580d5a 100644 --- a/crates/channel/src/channel_store/channel_index.rs +++ b/crates/channel/src/channel_store/channel_index.rs @@ -38,6 +38,12 @@ impl ChannelIndex { channels_by_id: &mut self.channels_by_id, } } + + pub fn clear_note_changed(&mut self, channel_id: ChannelId) { + if let Some(channel) = self.channels_by_id.get_mut(&channel_id) { + Arc::make_mut(channel).has_note_changed = false; + } + } } impl Deref for ChannelIndex { @@ -76,9 +82,9 @@ impl<'a> ChannelPathsInsertGuard<'a> { } } - pub fn has_changed(&mut self, channel_id: ChannelId) { + pub fn note_changed(&mut self, channel_id: ChannelId) { if let Some(channel) = self.channels_by_id.get_mut(&channel_id) { - Arc::make_mut(channel).has_changed = true; + Arc::make_mut(channel).has_note_changed = true; } } @@ -91,7 +97,7 @@ impl<'a> ChannelPathsInsertGuard<'a> { Arc::new(Channel { id: channel_proto.id, name: channel_proto.name, - has_changed: false, + has_note_changed: false, }), ); self.insert_root(channel_proto.id); diff --git a/crates/collab/src/tests/channel_buffer_tests.rs b/crates/collab/src/tests/channel_buffer_tests.rs index 0f87aeb48f670a1981e6f5791e9aa0627c89d80c..7ca6f0db3f6b3c48ec1bdd249748225e040f7cec 100644 --- a/crates/collab/src/tests/channel_buffer_tests.rs +++ b/crates/collab/src/tests/channel_buffer_tests.rs @@ -445,7 +445,7 @@ fn channel(id: u64, name: &'static str) -> Channel { Channel { id, name: name.to_string(), - has_changed: false, + has_note_changed: false, } } @@ -786,6 +786,7 @@ async fn test_channel_buffer_changes( .await .unwrap(); + // Client A makes an edit, and client B should see that the note has changed. channel_buffer_a.update(cx_a, |buffer, cx| { buffer.buffer().update(cx, |buffer, cx| { buffer.edit([(0..0, "1")], None, cx); @@ -802,6 +803,66 @@ async fn test_channel_buffer_changes( }); assert!(has_buffer_changed); + + // Opening the buffer should clear the changed flag. + let channel_buffer_b = client_b + .channel_store() + .update(cx_b, |store, cx| store.open_channel_buffer(channel_id, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + + let has_buffer_changed = cx_b.read(|cx| { + client_b + .channel_store() + .read(cx) + .has_channel_buffer_changed(channel_id) + .unwrap() + }); + + assert!(!has_buffer_changed); + + // Editing the channel while the buffer is open shuold not show that the buffer has changed. + channel_buffer_a.update(cx_a, |buffer, cx| { + buffer.buffer().update(cx, |buffer, cx| { + buffer.edit([(0..0, "2")], None, cx); + }) + }); + deterministic.run_until_parked(); + + let has_buffer_changed = cx_b.read(|cx| { + client_b + .channel_store() + .read(cx) + .has_channel_buffer_changed(channel_id) + .unwrap() + }); + + assert!(!has_buffer_changed); + + // Closing the buffer should re-enable change tracking + cx_b.update(|_| { + drop(channel_buffer_b); + }); + + deterministic.run_until_parked(); + + channel_buffer_a.update(cx_a, |buffer, cx| { + buffer.buffer().update(cx, |buffer, cx| { + buffer.edit([(0..0, "3")], None, cx); + }) + }); + deterministic.run_until_parked(); + + let has_buffer_changed = cx_b.read(|cx| { + client_b + .channel_store() + .read(cx) + .has_channel_buffer_changed(channel_id) + .unwrap() + }); + + assert!(has_buffer_changed); } #[track_caller] diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 4f81d07ea72b6f376d22b7b01fc185c35ccc1583..0fd265d0aa0c7e22408950f3114bc724a5e9fd98 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1774,6 +1774,7 @@ impl CollabPanel { const FACEPILE_LIMIT: usize = 3; enum ChannelCall {} + enum ChannelNote {} let mut is_dragged_over = false; if cx @@ -1820,7 +1821,7 @@ impl CollabPanel { channel.name.clone(), theme .channel_name - .in_state(channel.has_changed) + .in_state(channel.has_note_changed) .text .clone(), ) @@ -1863,6 +1864,8 @@ impl CollabPanel { .with_color(theme.channel_hash.color) .constrained() .with_width(theme.channel_hash.width) + .contained() + .with_margin_right(theme.channel_hash.container.margin.left) .into_any() } else { Empty::new().into_any() @@ -1872,6 +1875,34 @@ impl CollabPanel { this.join_channel_call(channel_id, cx); }), ) + .with_child( + MouseEventHandler::new::(ix, cx, move |_, cx| { + let participants = + self.channel_store.read(cx).channel_participants(channel_id); + if participants.is_empty() { + if channel.has_note_changed { + Svg::new("icons/terminal.svg") + .with_color(theme.channel_note_active_color) + .constrained() + .with_width(theme.channel_hash.width) + .into_any() + } else if row_hovered { + Svg::new("icons/terminal.svg") + .with_color(theme.channel_hash.color) + .constrained() + .with_width(theme.channel_hash.width) + .into_any() + } else { + Empty::new().into_any() + } + } else { + Empty::new().into_any() + } + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.open_channel_notes(&OpenChannelNotes { channel_id }, cx); + }), + ) .align_children_center() .styleable_component() .disclosable( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 6c32bfd1293d680514bd387d5aaa708ce4a8ad28..875fdd892e2daa6203f50f4c8ce3bbc7dd57ebc5 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -238,6 +238,7 @@ pub struct CollabPanel { pub log_in_button: Interactive, pub channel_editor: ContainerStyle, pub channel_hash: Icon, + pub channel_note_active_color: Color, pub tabbed_modal: TabbedModal, pub contact_finder: ContactFinder, pub channel_modal: ChannelModal, diff --git a/styles/src/style_tree/collab_panel.ts b/styles/src/style_tree/collab_panel.ts index 33b36e36f4b0e52c6cfb42385398fc5d46bae456..2a7702842a121e7caf8cf3c5d2e7dd3192b7b48d 100644 --- a/styles/src/style_tree/collab_panel.ts +++ b/styles/src/style_tree/collab_panel.ts @@ -194,6 +194,7 @@ export default function contacts_panel(): any { }, user_query_editor: filter_input, channel_hash: icon_style, + channel_note_active_color: foreground(layer, "active"), user_query_editor_height: 33, add_contact_button: header_icon_button, add_channel_button: header_icon_button,