link to channel notes

Conrad Irwin created

Change summary

crates/workspace/src/workspace.rs | 62 ++++++++++++++++++++++----------
crates/zed/src/main.rs            | 31 +++++++++++++++-
crates/zed/src/open_listener.rs   |  9 ++++
3 files changed, 80 insertions(+), 22 deletions(-)

Detailed changes

crates/workspace/src/workspace.rs 🔗

@@ -35,9 +35,9 @@ use gpui::{
         CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel,
         WindowBounds, WindowOptions,
     },
-    AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AnyWindowHandle, AppContext, AsyncAppContext,
-    Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext,
-    ViewHandle, WeakViewHandle, WindowContext, WindowHandle,
+    AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
+    ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
+    WeakViewHandle, WindowContext, WindowHandle,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
 use itertools::Itertools;
@@ -4295,12 +4295,14 @@ pub fn join_channel(
         }
 
         if let Err(err) = result {
-            let prompt = active_window.unwrap().prompt(
-                PromptLevel::Critical,
-                &format!("Failed to join channel: {}", err),
-                &["Ok"],
-                &mut cx,
-            );
+            let prompt = active_window.unwrap().update(&mut cx, |_, cx| {
+                cx.prompt(
+                    PromptLevel::Critical,
+                    &format!("Failed to join channel: {}", err),
+                    &["Ok"],
+                )
+            });
+
             if let Some(mut prompt) = prompt {
                 prompt.next().await;
             } else {
@@ -4313,17 +4315,39 @@ pub fn join_channel(
     })
 }
 
-pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
+pub async fn get_any_active_workspace(
+    app_state: Arc<AppState>,
+    mut cx: AsyncAppContext,
+) -> Result<ViewHandle<Workspace>> {
+    // find an existing workspace to focus and show call controls
+    let active_window = activate_any_workspace_window(&mut cx);
+    if active_window.is_none() {
+        cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), None, cx))
+            .await;
+    }
+
+    let Some(active_window) = activate_any_workspace_window(&mut cx) else {
+        return Err(anyhow!("could not open zed"))?;
+    };
+
+    Ok(active_window)
+}
+
+pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<ViewHandle<Workspace>> {
     for window in cx.windows() {
-        let found = window.update(cx, |cx| {
-            let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
-            if is_workspace {
-                cx.activate_window();
-            }
-            is_workspace
-        });
-        if found == Some(true) {
-            return Some(window);
+        if let Some(workspace) = window
+            .update(cx, |cx| {
+                cx.root_view()
+                    .clone()
+                    .downcast::<Workspace>()
+                    .map(|workspace| {
+                        cx.activate_window();
+                        workspace
+                    })
+            })
+            .flatten()
+        {
+            return Some(workspace);
         }
     }
     None

crates/zed/src/main.rs 🔗

@@ -7,6 +7,7 @@ use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
 use client::{
     self, Client, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
 };
+use collab_ui::channel_view::ChannelView;
 use db::kvp::KEY_VALUE_STORE;
 use editor::Editor;
 use futures::StreamExt;
@@ -240,6 +241,20 @@ fn main() {
                 })
                 .detach_and_log_err(cx)
             }
+            Ok(Some(OpenRequest::OpenChannelNotes { channel_id })) => {
+                triggered_authentication = true;
+                let app_state = app_state.clone();
+                let client = client.clone();
+                cx.spawn(|mut cx| async move {
+                    // ignore errors here, we'll show a generic "not signed in"
+                    let _ = authenticate(client, &cx).await;
+                    let workspace =
+                        workspace::get_any_active_workspace(app_state, cx.clone()).await?;
+                    cx.update(|cx| ChannelView::open(channel_id, workspace, cx))
+                        .await
+                })
+                .detach_and_log_err(cx)
+            }
             Ok(None) | Err(_) => cx
                 .spawn({
                     let app_state = app_state.clone();
@@ -254,8 +269,10 @@ fn main() {
                 while let Some(request) = open_rx.next().await {
                     match request {
                         OpenRequest::Paths { paths } => {
-                            cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
-                                .detach();
+                            cx.update(|cx| {
+                                workspace::open_paths(&paths, &app_state.clone(), None, cx)
+                            })
+                            .detach();
                         }
                         OpenRequest::CliConnection { connection } => {
                             cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
@@ -266,6 +283,16 @@ fn main() {
                                 workspace::join_channel(channel_id, app_state.clone(), None, cx)
                             })
                             .detach(),
+                        OpenRequest::OpenChannelNotes { channel_id } => {
+                            let app_state = app_state.clone();
+                            if let Ok(workspace) =
+                                workspace::get_any_active_workspace(app_state, cx.clone()).await
+                            {
+                                cx.update(|cx| {
+                                    ChannelView::open(channel_id, workspace, cx).detach();
+                                })
+                            }
+                        }
                     }
                 }
             }

crates/zed/src/open_listener.rs 🔗

@@ -32,6 +32,9 @@ pub enum OpenRequest {
     JoinChannel {
         channel_id: u64,
     },
+    OpenChannelNotes {
+        channel_id: u64,
+    },
 }
 
 pub struct OpenListener {
@@ -85,7 +88,11 @@ impl OpenListener {
             if let Some(slug) = parts.next() {
                 if let Some(id_str) = slug.split("-").last() {
                     if let Ok(channel_id) = id_str.parse::<u64>() {
-                        return Some(OpenRequest::JoinChannel { channel_id });
+                        if Some("notes") == parts.next() {
+                            return Some(OpenRequest::OpenChannelNotes { channel_id });
+                        } else {
+                            return Some(OpenRequest::JoinChannel { channel_id });
+                        }
                     }
                 }
             }