Fix scrolling to messages on clicking of notifications

Max Brunsfeld created

Change summary

crates/channel/src/channel_chat.rs             | 15 +++++--
crates/collab_ui/src/chat_panel.rs             | 38 ++++++++++++-------
crates/collab_ui/src/notification_panel.rs     | 19 +++++++--
crates/notifications/src/notification_store.rs | 12 ++++++
4 files changed, 60 insertions(+), 24 deletions(-)

Detailed changes

crates/channel/src/channel_chat.rs 🔗

@@ -257,11 +257,16 @@ impl ChannelChat {
                         let mut cursor = chat.messages.cursor::<(ChannelMessageId, Count)>();
                         let message_id = ChannelMessageId::Saved(message_id);
                         cursor.seek(&message_id, Bias::Left, &());
-                        return ControlFlow::Break(if cursor.start().0 == message_id {
-                            Some(cursor.start().1 .0)
-                        } else {
-                            None
-                        });
+                        return ControlFlow::Break(
+                            if cursor
+                                .item()
+                                .map_or(false, |message| message.id == message_id)
+                            {
+                                Some(cursor.start().1 .0)
+                            } else {
+                                None
+                            },
+                        );
                     }
                 }
                 ControlFlow::Continue(chat.load_more_messages(cx))

crates/collab_ui/src/chat_panel.rs 🔗

@@ -257,6 +257,7 @@ impl ChatPanel {
 
     fn set_active_chat(&mut self, chat: ModelHandle<ChannelChat>, cx: &mut ViewContext<Self>) {
         if self.active_chat.as_ref().map(|e| &e.0) != Some(&chat) {
+            self.markdown_data.clear();
             let id = {
                 let chat = chat.read(cx);
                 let channel = chat.channel().clone();
@@ -635,31 +636,38 @@ impl ChatPanel {
         scroll_to_message_id: Option<u64>,
         cx: &mut ViewContext<ChatPanel>,
     ) -> Task<Result<()>> {
-        if let Some((chat, _)) = &self.active_chat {
-            if chat.read(cx).channel().id == selected_channel_id {
-                return Task::ready(Ok(()));
-            }
-        }
+        let open_chat = self
+            .active_chat
+            .as_ref()
+            .and_then(|(chat, _)| {
+                (chat.read(cx).channel().id == selected_channel_id)
+                    .then(|| Task::ready(anyhow::Ok(chat.clone())))
+            })
+            .unwrap_or_else(|| {
+                self.channel_store.update(cx, |store, cx| {
+                    store.open_channel_chat(selected_channel_id, cx)
+                })
+            });
 
-        let open_chat = self.channel_store.update(cx, |store, cx| {
-            store.open_channel_chat(selected_channel_id, cx)
-        });
         cx.spawn(|this, mut cx| async move {
             let chat = open_chat.await?;
             this.update(&mut cx, |this, cx| {
-                this.markdown_data = Default::default();
                 this.set_active_chat(chat.clone(), cx);
             })?;
 
             if let Some(message_id) = scroll_to_message_id {
                 if let Some(item_ix) =
-                    ChannelChat::load_history_since_message(chat, message_id, cx.clone()).await
+                    ChannelChat::load_history_since_message(chat.clone(), message_id, cx.clone())
+                        .await
                 {
-                    this.update(&mut cx, |this, _| {
-                        this.message_list.scroll_to(ListOffset {
-                            item_ix,
-                            offset_in_item: 0.,
-                        });
+                    this.update(&mut cx, |this, cx| {
+                        if this.active_chat.as_ref().map_or(false, |(c, _)| *c == chat) {
+                            this.message_list.scroll_to(ListOffset {
+                                item_ix,
+                                offset_in_item: 0.,
+                            });
+                            cx.notify();
+                        }
                     })?;
                 }
             }

crates/collab_ui/src/notification_panel.rs 🔗

@@ -119,6 +119,7 @@ impl NotificationPanel {
 
             let mut old_dock_position = this.position(cx);
             this.subscriptions.extend([
+                cx.observe(&this.notification_store, |_, _, cx| cx.notify()),
                 cx.subscribe(&this.notification_store, Self::on_notification_event),
                 cx.observe_global::<SettingsStore, _>(move |this: &mut Self, cx| {
                     let new_dock_position = this.position(cx);
@@ -469,12 +470,12 @@ impl NotificationPanel {
             return;
         };
 
-        let id = entry.id;
+        let notification_id = entry.id;
         self.current_notification_toast = Some((
-            id,
+            notification_id,
             cx.spawn(|this, mut cx| async move {
                 cx.background().timer(TOAST_DURATION).await;
-                this.update(&mut cx, |this, cx| this.remove_toast(id, cx))
+                this.update(&mut cx, |this, cx| this.remove_toast(notification_id, cx))
                     .ok();
             }),
         ));
@@ -484,6 +485,7 @@ impl NotificationPanel {
                 workspace.show_notification(0, cx, |cx| {
                     let workspace = cx.weak_handle();
                     cx.add_view(|_| NotificationToast {
+                        notification_id,
                         actor,
                         text,
                         workspace,
@@ -645,6 +647,7 @@ fn render_icon_button<V: View>(style: &IconButton, svg_path: &'static str) -> im
 }
 
 pub struct NotificationToast {
+    notification_id: u64,
     actor: Option<Arc<User>>,
     text: String,
     workspace: WeakViewHandle<Workspace>,
@@ -657,10 +660,18 @@ pub enum ToastEvent {
 impl NotificationToast {
     fn focus_notification_panel(&self, cx: &mut AppContext) {
         let workspace = self.workspace.clone();
+        let notification_id = self.notification_id;
         cx.defer(move |cx| {
             workspace
                 .update(cx, |workspace, cx| {
-                    workspace.focus_panel::<NotificationPanel>(cx);
+                    if let Some(panel) = workspace.focus_panel::<NotificationPanel>(cx) {
+                        panel.update(cx, |panel, cx| {
+                            let store = panel.notification_store.read(cx);
+                            if let Some(entry) = store.notification_for_id(notification_id) {
+                                panel.did_click_notification(&entry.clone().notification, cx);
+                            }
+                        });
+                    }
                 })
                 .ok();
         })

crates/notifications/src/notification_store.rs 🔗

@@ -131,6 +131,17 @@ impl NotificationStore {
         cursor.item()
     }
 
+    pub fn notification_for_id(&self, id: u64) -> Option<&NotificationEntry> {
+        let mut cursor = self.notifications.cursor::<NotificationId>();
+        cursor.seek(&NotificationId(id), Bias::Left, &());
+        if let Some(item) = cursor.item() {
+            if item.id == id {
+                return Some(item);
+            }
+        }
+        None
+    }
+
     pub fn load_more_notifications(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         let request = self
             .client
@@ -145,6 +156,7 @@ impl NotificationStore {
     fn handle_connect(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         self.notifications = Default::default();
         self.channel_messages = Default::default();
+        cx.notify();
         self.load_more_notifications(cx)
     }