Display a temporary window while remote project is loading

Antonio Scandurra created

Change summary

assets/themes/cave-dark.json         |  6 ++
assets/themes/cave-light.json        |  6 ++
assets/themes/dark.json              |  6 ++
assets/themes/light.json             |  6 ++
assets/themes/solarized-dark.json    |  6 ++
assets/themes/solarized-light.json   |  6 ++
assets/themes/sulphurpool-dark.json  |  6 ++
assets/themes/sulphurpool-light.json |  6 ++
crates/gpui/src/app.rs               | 14 ++++++
crates/theme/src/theme.rs            |  1 
crates/workspace/src/workspace.rs    | 61 ++++++++++++++++++++++++++---
styles/src/styleTree/workspace.ts    |  4 +
12 files changed, 121 insertions(+), 7 deletions(-)

Detailed changes

assets/themes/cave-dark.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#26232a",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#e2dfe7",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/cave-light.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#e2dfe7",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#26232a",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/dark.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#1c1c1c",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#f1f1f1",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/light.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#f8f8f8",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#2b2b2b",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/solarized-dark.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#073642",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#eee8d5",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/solarized-light.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#eee8d5",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#073642",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/sulphurpool-dark.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#293256",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#dfe2f1",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

assets/themes/sulphurpool-light.json 🔗

@@ -90,6 +90,12 @@
   },
   "workspace": {
     "background": "#dfe2f1",
+    "joining_project_message": {
+      "padding": 12,
+      "family": "Zed Sans",
+      "color": "#293256",
+      "size": 18
+    },
     "leader_border_opacity": 0.7,
     "leader_border_width": 2,
     "tab": {

crates/gpui/src/app.rs 🔗

@@ -1604,6 +1604,20 @@ impl MutableAppContext {
         })
     }
 
+    pub fn replace_root_view<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
+    where
+        T: View,
+        F: FnOnce(&mut ViewContext<T>) -> T,
+    {
+        self.update(|this| {
+            let root_view = this.add_view(window_id, build_root_view);
+            let window = this.cx.windows.get_mut(&window_id).unwrap();
+            window.root_view = root_view.clone().into();
+            window.focused_view_id = Some(root_view.id());
+            root_view
+        })
+    }
+
     pub fn remove_window(&mut self, window_id: usize) {
         self.cx.windows.remove(&window_id);
         self.presenters_and_platform_windows.remove(&window_id);

crates/theme/src/theme.rs 🔗

@@ -48,6 +48,7 @@ pub struct Workspace {
     pub modal: ContainerStyle,
     pub notification: ContainerStyle,
     pub notifications: Notifications,
+    pub joining_project_message: ContainedText,
 }
 
 #[derive(Clone, Deserialize, Default)]

crates/workspace/src/workspace.rs 🔗

@@ -2272,6 +2272,34 @@ pub fn join_project(
     app_state: &Arc<AppState>,
     cx: &mut MutableAppContext,
 ) -> Task<Result<ViewHandle<Workspace>>> {
+    struct JoiningNotice {
+        message: &'static str,
+    }
+
+    impl Entity for JoiningNotice {
+        type Event = ();
+    }
+
+    impl View for JoiningNotice {
+        fn ui_name() -> &'static str {
+            "JoiningProjectWindow"
+        }
+
+        fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+            let theme = &cx.global::<Settings>().theme.workspace;
+            Text::new(
+                self.message.to_string(),
+                theme.joining_project_message.text.clone(),
+            )
+            .contained()
+            .with_style(theme.joining_project_message.container)
+            .aligned()
+            .contained()
+            .with_background_color(theme.background)
+            .boxed()
+        }
+    }
+
     for window_id in cx.window_ids().collect::<Vec<_>>() {
         if let Some(workspace) = cx.root_view::<Workspace>(window_id) {
             if workspace.read(cx).project().read(cx).remote_id() == Some(project_id) {
@@ -2282,6 +2310,10 @@ pub fn join_project(
 
     let app_state = app_state.clone();
     cx.spawn(|mut cx| async move {
+        let (window, joining_notice) =
+            cx.update(|cx| cx.add_window((app_state.build_window_options)(), |_| JoiningNotice {
+                message: "Loading remote project...",
+            }));
         let project = Project::remote(
             project_id,
             app_state.client.clone(),
@@ -2290,13 +2322,28 @@ pub fn join_project(
             app_state.fs.clone(),
             &mut cx,
         )
-        .await?;
-        Ok(cx.update(|cx| {
-            cx.add_window((app_state.build_window_options)(), |cx| {
-                (app_state.build_workspace)(project, &app_state, cx)
-            })
-            .1
-        }))
+        .await;
+
+        cx.update(|cx| match project {
+            Ok(project) => Ok(cx.replace_root_view(window, |cx| {
+                let mut workspace = (app_state.build_workspace)(project, &app_state, cx);
+                workspace.toggle_sidebar_item(
+                    &ToggleSidebarItem {
+                        side: Side::Left,
+                        item_index: 0,
+                    },
+                    cx,
+                );
+                workspace
+            })),
+            Err(error) => {
+                joining_notice.update(cx, |joining_notice, cx| {
+                    joining_notice.message = "An error occurred trying to join the project. Please, close this window and retry.";
+                    cx.notify();
+                });
+                Err(error)
+            },
+        })
     })
 }
 

styles/src/styleTree/workspace.ts 🔗

@@ -41,6 +41,10 @@ export default function workspace(theme: Theme) {
 
   return {
     background: backgroundColor(theme, 300),
+    joiningProjectMessage: {
+      padding: 12,
+      ...text(theme, "sans", "primary", { size: "lg" })
+    },
     leaderBorderOpacity: 0.7,
     leaderBorderWidth: 2.0,
     tab,