Allow sharing/unsharing of projects

Antonio Scandurra created

Change summary

crates/project/src/project.rs            | 18 ++++++++-
crates/workspace/src/workspace.rs        | 44 ++++++++++++++++++++++++++
crates/zed/assets/icons/broadcast-24.svg |  6 +++
3 files changed, 65 insertions(+), 3 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -357,6 +357,7 @@ impl Project {
             for task in tasks {
                 task.await?;
             }
+            this.update(&mut cx, |_, cx| cx.notify());
             Ok(())
         })
     }
@@ -371,7 +372,7 @@ impl Project {
                     ..
                 } = &mut this.client_state
                 {
-                    *is_shared = true;
+                    *is_shared = false;
                     remote_id_rx
                         .borrow()
                         .ok_or_else(|| anyhow!("no project id"))
@@ -381,7 +382,10 @@ impl Project {
             })?;
 
             rpc.send(proto::UnshareProject { project_id }).await?;
-
+            this.update(&mut cx, |this, cx| {
+                this.collaborators.clear();
+                cx.notify()
+            });
             Ok(())
         })
     }
@@ -396,6 +400,13 @@ impl Project {
         }
     }
 
+    pub fn is_local(&self) -> bool {
+        match &self.client_state {
+            ProjectClientState::Local { .. } => true,
+            ProjectClientState::Remote { .. } => false,
+        }
+    }
+
     pub fn open_buffer(
         &self,
         path: ProjectPath,
@@ -408,7 +419,7 @@ impl Project {
         }
     }
 
-    fn is_shared(&self) -> bool {
+    pub fn is_shared(&self) -> bool {
         match &self.client_state {
             ProjectClientState::Local { is_shared, .. } => *is_shared,
             ProjectClientState::Remote { .. } => false,
@@ -512,6 +523,7 @@ impl Project {
         } = &mut self.client_state
         {
             *sharing_has_stopped = true;
+            self.collaborators.clear();
             cx.notify();
             Ok(())
         } else {

crates/workspace/src/workspace.rs 🔗

@@ -40,6 +40,7 @@ use theme::{Theme, ThemeRegistry};
 action!(Open, Arc<AppState>);
 action!(OpenNew, Arc<AppState>);
 action!(OpenPaths, OpenParams);
+action!(ToggleShare);
 action!(JoinProject, JoinProjectParams);
 action!(Save);
 action!(DebugElements);
@@ -56,6 +57,7 @@ pub fn init(cx: &mut MutableAppContext) {
         join_project(action.0.project_id, &action.0.app_state, cx).detach();
     });
 
+    cx.add_action(Workspace::toggle_share);
     cx.add_action(Workspace::save_active_item);
     cx.add_action(Workspace::debug_elements);
     cx.add_action(Workspace::toggle_sidebar_item);
@@ -992,6 +994,18 @@ impl Workspace {
         &self.active_pane
     }
 
+    fn toggle_share(&mut self, _: &ToggleShare, cx: &mut ViewContext<Self>) {
+        self.project.update(cx, |project, cx| {
+            if project.is_local() {
+                if project.is_shared() {
+                    project.unshare(cx).detach();
+                } else {
+                    project.share(cx).detach();
+                }
+            }
+        });
+    }
+
     fn render_connection_status(&self) -> Option<ElementBox> {
         let theme = &self.settings.borrow().theme;
         match &*self.client.status().borrow() {
@@ -1043,6 +1057,7 @@ impl Workspace {
                     .with_child(
                         Align::new(
                             Flex::row()
+                                .with_children(self.render_share_icon(cx))
                                 .with_children(self.render_collaborators(theme, cx))
                                 .with_child(self.render_avatar(
                                     self.user_store.read(cx).current_user().as_ref(),
@@ -1133,6 +1148,35 @@ impl Workspace {
             .boxed()
         }
     }
+
+    fn render_share_icon(&self, cx: &mut RenderContext<Self>) -> Option<ElementBox> {
+        if self.project().read(cx).is_local() && self.client.user_id().is_some() {
+            enum Share {}
+
+            let color = if self.project().read(cx).is_shared() {
+                Color::green()
+            } else {
+                Color::red()
+            };
+            Some(
+                MouseEventHandler::new::<Share, _, _, _>(0, cx, |_, _| {
+                    Align::new(
+                        ConstrainedBox::new(
+                            Svg::new("icons/broadcast-24.svg").with_color(color).boxed(),
+                        )
+                        .with_width(24.)
+                        .boxed(),
+                    )
+                    .boxed()
+                })
+                .with_cursor_style(CursorStyle::PointingHand)
+                .on_click(|cx| cx.dispatch_action(ToggleShare))
+                .boxed(),
+            )
+        } else {
+            None
+        }
+    }
 }
 
 impl Entity for Workspace {

crates/zed/assets/icons/broadcast-24.svg 🔗

@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.87348 15.1266C4.04217 12.2953 4.04217 7.70484 6.87348 4.87354M17.1265 4.87354C19.9578 7.70484 19.9578 12.2953 17.1265 15.1266" stroke="#636B78" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M8.9948 13.0052C7.33507 11.3454 7.33507 8.65448 8.9948 6.99475M15.0052 6.99475C16.6649 8.65448 16.6649 11.3454 15.0052 13.0052" stroke="#636B78" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12.5 10C12.5 10.2761 12.2761 10.5 12 10.5C11.7239 10.5 11.5 10.2761 11.5 10C11.5 9.72386 11.7239 9.5 12 9.5C12.2761 9.5 12.5 9.72386 12.5 10Z" stroke="#636B78" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12 13.75V19.25" stroke="#636B78" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>