Checkpoint

Antonio Scandurra created

Change summary

crates/client2/src/client2.rs         | 86 +++++++---------------------
crates/client2/src/telemetry.rs       |  2 
crates/client2/src/user.rs            | 70 +++++++++++------------
crates/gpui2/src/window.rs            |  1 
crates/settings2/src/settings_file.rs |  2 
crates/storybook2/src/storybook2.rs   | 36 +++--------
crates/ui2/src/settings.rs            | 79 --------------------------
crates/ui2/src/theme.rs               | 38 +++++++-----
crates/zed2/src/main.rs               | 37 +++++------
9 files changed, 109 insertions(+), 242 deletions(-)

Detailed changes

crates/client2/src/client2.rs 🔗

@@ -14,8 +14,8 @@ use futures::{
     future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt,
 };
 use gpui2::{
-    serde_json, AnyHandle, AnyWeakHandle, AnyWindowHandle, AppContext, AsyncAppContext, Handle,
-    SemanticVersion, Task, ViewContext, WeakHandle,
+    serde_json, AnyHandle, AnyWeakHandle, AppContext, AsyncAppContext, Handle, SemanticVersion,
+    Task, WeakHandle,
 };
 use lazy_static::lazy_static;
 use parking_lot::RwLock;
@@ -235,7 +235,7 @@ struct ClientState {
             dyn Send
                 + Sync
                 + Fn(
-                    Subscriber,
+                    AnyHandle,
                     Box<dyn AnyTypedEnvelope>,
                     &Arc<Client>,
                     AsyncAppContext,
@@ -245,18 +245,10 @@ struct ClientState {
 }
 
 enum WeakSubscriber {
-    Entity {
-        handle: AnyWeakHandle,
-        window_handle: Option<AnyWindowHandle>,
-    },
+    Entity { handle: AnyWeakHandle },
     Pending(Vec<Box<dyn AnyTypedEnvelope>>),
 }
 
-struct Subscriber {
-    handle: AnyHandle,
-    window_handle: Option<AnyWindowHandle>,
-}
-
 #[derive(Clone, Debug)]
 pub struct Credentials {
     pub user_id: u64,
@@ -310,7 +302,7 @@ impl Drop for Subscription {
     }
 }
 
-pub struct PendingEntitySubscription<T> {
+pub struct PendingEntitySubscription<T: 'static> {
     client: Arc<Client>,
     remote_id: u64,
     _entity_type: PhantomData<T>,
@@ -335,7 +327,6 @@ where
             id,
             WeakSubscriber::Entity {
                 handle: model.downgrade().into(),
-                window_handle: None,
             },
         );
         drop(state);
@@ -349,7 +340,10 @@ where
     }
 }
 
-impl<T> Drop for PendingEntitySubscription<T> {
+impl<T> Drop for PendingEntitySubscription<T>
+where
+    T: 'static,
+{
     fn drop(&mut self) {
         if !self.consumed {
             let mut state = self.client.state.write();
@@ -494,7 +488,7 @@ impl Client {
             Status::ConnectionLost => {
                 let this = self.clone();
                 let reconnect_interval = state.reconnect_interval;
-                state._reconnect_task = Some(cx.spawn(|cx| async move {
+                state._reconnect_task = Some(cx.spawn(move |cx| async move {
                     #[cfg(any(test, feature = "test-support"))]
                     let mut rng = StdRng::seed_from_u64(0);
                     #[cfg(not(any(test, feature = "test-support")))]
@@ -521,40 +515,22 @@ impl Client {
                 }));
             }
             Status::SignedOut | Status::UpgradeRequired => {
-                cx.update(|cx| self.telemetry.set_authenticated_user_info(None, false, cx));
+                cx.update(|cx| self.telemetry.set_authenticated_user_info(None, false, cx))
+                    .log_err();
                 state._reconnect_task.take();
             }
             _ => {}
         }
     }
 
-    pub fn add_view_for_remote_entity<T>(
+    pub fn subscribe_to_entity<T>(
         self: &Arc<Self>,
         remote_id: u64,
-        cx: &mut ViewContext<T>,
-    ) -> Subscription
+    ) -> Result<PendingEntitySubscription<T>>
     where
         T: 'static + Send + Sync,
     {
         let id = (TypeId::of::<T>(), remote_id);
-        self.state.write().entities_by_type_and_remote_id.insert(
-            id,
-            WeakSubscriber::Entity {
-                handle: cx.handle().into(),
-                window_handle: Some(cx.window_handle()),
-            },
-        );
-        Subscription::Entity {
-            client: Arc::downgrade(self),
-            id,
-        }
-    }
-
-    pub fn subscribe_to_entity<T>(
-        self: &Arc<Self>,
-        remote_id: u64,
-    ) -> Result<PendingEntitySubscription<T>> {
-        let id = (TypeId::of::<T>(), remote_id);
 
         let mut state = self.state.write();
         if state.entities_by_type_and_remote_id.contains_key(&id) {
@@ -594,7 +570,7 @@ impl Client {
         let prev_handler = state.message_handlers.insert(
             message_type_id,
             Arc::new(move |subscriber, envelope, client, cx| {
-                let subscriber = subscriber.handle.downcast::<E>().unwrap();
+                let subscriber = subscriber.downcast::<E>().unwrap();
                 let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
                 handler(subscriber, *envelope, client.clone(), cx).boxed()
             }),
@@ -643,22 +619,15 @@ impl Client {
         F: 'static + Future<Output = Result<()>> + Send,
     {
         self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
-            handler(
-                subscriber.handle.downcast::<E>().unwrap(),
-                message,
-                client,
-                cx,
-            )
+            handler(subscriber.downcast::<E>().unwrap(), message, client, cx)
         })
     }
 
     fn add_entity_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
     where
         M: EntityMessage,
-        H: 'static
-            + Send
-            + Sync
-            + Fn(Subscriber, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        E: 'static + Send + Sync,
+        H: 'static + Send + Sync + Fn(AnyHandle, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
         F: 'static + Future<Output = Result<()>> + Send,
     {
         let model_type_id = TypeId::of::<E>();
@@ -1079,7 +1048,7 @@ impl Client {
                 write!(&mut url, "&impersonate={}", impersonate_login).unwrap();
             }
 
-            cx.run_on_main(|cx| cx.open_url(&url))?.await;
+            cx.run_on_main(move |cx| cx.open_url(&url))?.await;
 
             // Receive the HTTP request from the user's browser. Retrieve the user id and encrypted
             // access token from the query params.
@@ -1269,10 +1238,7 @@ impl Client {
             .get(&payload_type_id)
             .and_then(|handle| handle.upgrade())
         {
-            subscriber = Some(Subscriber {
-                handle,
-                window_handle: None,
-            });
+            subscriber = Some(handle);
         } else if let Some((extract_entity_id, entity_type_id)) =
             state.entity_id_extractors.get(&payload_type_id).zip(
                 state
@@ -1292,14 +1258,8 @@ impl Client {
                     return;
                 }
                 Some(weak_subscriber @ _) => match weak_subscriber {
-                    WeakSubscriber::Entity {
-                        handle,
-                        window_handle,
-                    } => {
-                        subscriber = handle.upgrade().map(|handle| Subscriber {
-                            handle,
-                            window_handle: window_handle.clone(),
-                        });
+                    WeakSubscriber::Entity { handle } => {
+                        subscriber = handle.upgrade();
                     }
 
                     WeakSubscriber::Pending(_) => {}
@@ -1331,7 +1291,7 @@ impl Client {
                 sender_id,
                 type_name
             );
-            cx.spawn_on_main(|_| async move {
+            cx.spawn_on_main(move |_| async move {
                     match future.await {
                         Ok(()) => {
                             log::debug!(

crates/client2/src/telemetry.rs 🔗

@@ -159,7 +159,7 @@ impl Telemetry {
         }
 
         let this = self.clone();
-        cx.spawn(|mut cx| async move {
+        cx.spawn(|cx| async move {
             let mut system = System::new_all();
             system.refresh_all();
 

crates/client2/src/user.rs 🔗

@@ -1,7 +1,7 @@
 use super::{proto, Client, Status, TypedEnvelope};
 use anyhow::{anyhow, Context, Result};
 use collections::{hash_map::Entry, HashMap, HashSet};
-use feature_flags::FeatureFlagAppExt;
+use feature_flags2::FeatureFlagAppExt;
 use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
 use gpui2::{AsyncAppContext, EventEmitter, Handle, ImageData, ModelContext, Task};
 use postage::{sink::Sink, watch};
@@ -78,7 +78,7 @@ pub struct UserStore {
     client: Weak<Client>,
     http: Arc<dyn HttpClient>,
     _maintain_contacts: Task<()>,
-    _maintain_current_user: Task<()>,
+    _maintain_current_user: Task<Result<()>>,
 }
 
 #[derive(Clone)]
@@ -167,28 +167,20 @@ impl UserStore {
                                     client.request(proto::GetPrivateUserInfo {}).log_err();
                                 let (user, info) = futures::join!(fetch_user, fetch_metrics_id);
 
-                                if let Some(info) = info {
-                                    cx.update(|cx| {
+                                cx.update(|cx| {
+                                    if let Some(info) = info {
                                         cx.update_flags(info.staff, info.flags);
                                         client.telemetry.set_authenticated_user_info(
                                             Some(info.metrics_id.clone()),
                                             info.staff,
                                             cx,
                                         )
-                                    });
-                                } else {
-                                    cx.read(|cx| {
-                                        client
-                                            .telemetry
-                                            .set_authenticated_user_info(None, false, cx)
-                                    });
-                                }
+                                    }
+                                })?;
 
                                 current_user_tx.send(user).await.ok();
 
-                                this.update(&mut cx, |_, cx| {
-                                    cx.notify();
-                                });
+                                this.update(&mut cx, |_, cx| cx.notify())?;
                             }
                         }
                         Status::SignedOut => {
@@ -196,21 +188,20 @@ impl UserStore {
                             this.update(&mut cx, |this, cx| {
                                 cx.notify();
                                 this.clear_contacts()
-                            })
+                            })?
                             .await;
                         }
                         Status::ConnectionLost => {
-                            if let Some(this) = this.upgrade() {
-                                this.update(&mut cx, |this, cx| {
-                                    cx.notify();
-                                    this.clear_contacts()
-                                })
-                                .await;
-                            }
+                            this.update(&mut cx, |this, cx| {
+                                cx.notify();
+                                this.clear_contacts()
+                            })?
+                            .await;
                         }
                         _ => {}
                     }
                 }
+                Ok(())
             }),
             pending_contact_requests: Default::default(),
         }
@@ -233,7 +224,7 @@ impl UserStore {
                 count: message.payload.count,
             });
             cx.notify();
-        });
+        })?;
         Ok(())
     }
 
@@ -243,7 +234,7 @@ impl UserStore {
         _: Arc<Client>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
-        this.update(&mut cx, |_, cx| cx.emit(Event::ShowContacts));
+        this.update(&mut cx, |_, cx| cx.emit(Event::ShowContacts))?;
         Ok(())
     }
 
@@ -261,7 +252,7 @@ impl UserStore {
             this.update_contacts_tx
                 .unbounded_send(UpdateContacts::Update(message.payload))
                 .unwrap();
-        });
+        })?;
         Ok(())
     }
 
@@ -297,6 +288,9 @@ impl UserStore {
                     // Users are fetched in parallel above and cached in call to get_users
                     // No need to paralellize here
                     let mut updated_contacts = Vec::new();
+                    let this = this
+                        .upgrade()
+                        .ok_or_else(|| anyhow!("can't upgrade user store handle"))?;
                     for contact in message.contacts {
                         let should_notify = contact.should_notify;
                         updated_contacts.push((
@@ -309,7 +303,9 @@ impl UserStore {
                     for request in message.incoming_requests {
                         incoming_requests.push({
                             let user = this
-                                .update(&mut cx, |this, cx| this.get_user(request.requester_id, cx))
+                                .update(&mut cx, |this, cx| {
+                                    this.get_user(request.requester_id, cx)
+                                })?
                                 .await?;
                             (user, request.should_notify)
                         });
@@ -318,7 +314,7 @@ impl UserStore {
                     let mut outgoing_requests = Vec::new();
                     for requested_user_id in message.outgoing_requests {
                         outgoing_requests.push(
-                            this.update(&mut cx, |this, cx| this.get_user(requested_user_id, cx))
+                            this.update(&mut cx, |this, cx| this.get_user(requested_user_id, cx))?
                                 .await?,
                         );
                     }
@@ -398,7 +394,7 @@ impl UserStore {
                         }
 
                         cx.notify();
-                    });
+                    })?;
 
                     Ok(())
                 })
@@ -494,7 +490,7 @@ impl UserStore {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<()>> {
         let client = self.client.upgrade();
-        cx.spawn_weak(|_, _| async move {
+        cx.spawn(move |_, _| async move {
             client
                 .ok_or_else(|| anyhow!("can't upgrade client reference"))?
                 .request(proto::RespondToContactRequest {
@@ -516,7 +512,7 @@ impl UserStore {
         *self.pending_contact_requests.entry(user_id).or_insert(0) += 1;
         cx.notify();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let response = client
                 .ok_or_else(|| anyhow!("can't upgrade client reference"))?
                 .request(request)
@@ -531,7 +527,7 @@ impl UserStore {
                     }
                 }
                 cx.notify();
-            });
+            })?;
             response?;
             Ok(())
         })
@@ -574,11 +570,11 @@ impl UserStore {
                         },
                         cx,
                     )
-                })
+                })?
                 .await?;
             }
 
-            this.read_with(&cx, |this, _| {
+            this.update(&mut cx, |this, _| {
                 user_ids
                     .iter()
                     .map(|user_id| {
@@ -588,7 +584,7 @@ impl UserStore {
                             .ok_or_else(|| anyhow!("user {} not found", user_id))
                     })
                     .collect()
-            })
+            })?
         })
     }
 
@@ -614,7 +610,7 @@ impl UserStore {
         }
 
         let load_users = self.get_users(vec![user_id], cx);
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             load_users.await?;
             this.update(&mut cx, |this, _| {
                 this.users
@@ -700,7 +696,7 @@ impl Contact {
         let user = user_store
             .update(cx, |user_store, cx| {
                 user_store.get_user(contact.user_id, cx)
-            })
+            })?
             .await?;
         Ok(Self {
             user,

crates/gpui2/src/window.rs 🔗

@@ -430,7 +430,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     where
         G: 'static + Send + Sync,
     {
-        let global_type = TypeId::of::<G>();
         let mut global = self.app.lease_global::<G>();
         let result = f(global.as_mut(), self);
         self.app.set_global(global);

crates/settings2/src/settings_file.rs 🔗

@@ -2,7 +2,7 @@ use crate::{settings_store::SettingsStore, Setting};
 use anyhow::Result;
 use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
-use gpui2::{AppContext, Context, Executor};
+use gpui2::{AppContext, Executor};
 use std::{
     io::ErrorKind,
     path::{Path, PathBuf},

crates/storybook2/src/storybook2.rs 🔗

@@ -10,14 +10,13 @@ use std::sync::Arc;
 
 use clap::Parser;
 use gpui2::{
-    div, px, size, view, AnyView, BorrowAppContext, Bounds, Context, Element, ViewContext,
-    WindowBounds, WindowOptions,
+    div, px, size, view, AnyView, Bounds, Context, Element, ViewContext, WindowBounds,
+    WindowOptions,
 };
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 use story_selector::ComponentStory;
-use ui::prelude::*;
-use ui::{themed, with_settings, FakeSettings};
+use ui::{prelude::*, themed};
 
 use crate::assets::Assets;
 use crate::story_selector::StorySelector;
@@ -67,13 +66,7 @@ fn main() {
             },
             move |cx| {
                 view(
-                    cx.entity(|cx| {
-                        cx.with_global(FakeSettings::default(), |cx| {
-                            cx.with_global(theme.clone(), |cx| {
-                                StoryWrapper::new(selector.story(cx), theme)
-                            })
-                        })
-                    }),
+                    cx.entity(|cx| StoryWrapper::new(selector.story(cx), theme)),
                     StoryWrapper::render,
                 )
             },
@@ -87,27 +80,20 @@ fn main() {
 pub struct StoryWrapper {
     story: AnyView,
     theme: Theme,
-    settings: FakeSettings,
 }
 
 impl StoryWrapper {
     pub(crate) fn new(story: AnyView, theme: Theme) -> Self {
-        Self {
-            story,
-            theme,
-            settings: FakeSettings::default(),
-        }
+        Self { story, theme }
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
-        with_settings(self.settings.clone(), cx, |cx| {
-            themed(self.theme.clone(), cx, |cx| {
-                div()
-                    .flex()
-                    .flex_col()
-                    .size_full()
-                    .child(self.story.clone())
-            })
+        themed(self.theme.clone(), cx, |cx| {
+            div()
+                .flex()
+                .flex_col()
+                .size_full()
+                .child(self.story.clone())
         })
     }
 }

crates/ui2/src/settings.rs 🔗

@@ -1,8 +1,6 @@
 use std::ops::Deref;
 
-use gpui2::{
-    rems, AbsoluteLength, AnyElement, BorrowAppContext, Bounds, LayoutId, Pixels, WindowContext,
-};
+use gpui2::{rems, AbsoluteLength, WindowContext};
 
 use crate::prelude::*;
 
@@ -72,78 +70,3 @@ impl Default for FakeSettings {
 }
 
 impl FakeSettings {}
-
-pub fn with_settings<E, F>(
-    settings: FakeSettings,
-    cx: &mut ViewContext<E::ViewState>,
-    build_child: F,
-) -> WithSettings<E>
-where
-    E: Element,
-    F: FnOnce(&mut ViewContext<E::ViewState>) -> E,
-{
-    let child = cx.with_global(settings.clone(), |cx| build_child(cx));
-    WithSettings { settings, child }
-}
-
-pub struct WithSettings<E> {
-    pub(crate) settings: FakeSettings,
-    pub(crate) child: E,
-}
-
-impl<E> IntoAnyElement<E::ViewState> for WithSettings<E>
-where
-    E: Element,
-{
-    fn into_any(self) -> AnyElement<E::ViewState> {
-        AnyElement::new(self)
-    }
-}
-
-impl<E: Element> Element for WithSettings<E> {
-    type ViewState = E::ViewState;
-    type ElementState = E::ElementState;
-
-    fn id(&self) -> Option<gpui2::ElementId> {
-        None
-    }
-
-    fn initialize(
-        &mut self,
-        view_state: &mut Self::ViewState,
-        element_state: Option<Self::ElementState>,
-        cx: &mut ViewContext<Self::ViewState>,
-    ) -> Self::ElementState {
-        cx.with_global(self.settings.clone(), |cx| {
-            self.child.initialize(view_state, element_state, cx)
-        })
-    }
-
-    fn layout(
-        &mut self,
-        view_state: &mut E::ViewState,
-        element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<E::ViewState>,
-    ) -> LayoutId
-    where
-        Self: Sized,
-    {
-        cx.with_global(self.settings.clone(), |cx| {
-            self.child.layout(view_state, element_state, cx)
-        })
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        view_state: &mut Self::ViewState,
-        frame_state: &mut Self::ElementState,
-        cx: &mut ViewContext<Self::ViewState>,
-    ) where
-        Self: Sized,
-    {
-        cx.with_global(self.settings.clone(), |cx| {
-            self.child.paint(bounds, view_state, frame_state, cx);
-        });
-    }
-}

crates/ui2/src/theme.rs 🔗

@@ -1,12 +1,11 @@
-use std::collections::HashMap;
-use std::fmt;
-use std::sync::Arc;
-
 use gpui2::{
-    AnyElement, BorrowAppContext, Bounds, Element, Hsla, IntoAnyElement, LayoutId, Pixels, Result,
-    ViewContext, WindowContext,
+    AnyElement, Bounds, Element, Hsla, IntoAnyElement, LayoutId, Pixels, Result, ViewContext,
+    WindowContext,
 };
 use serde::{de::Visitor, Deserialize, Deserializer};
+use std::collections::HashMap;
+use std::fmt;
+use std::sync::Arc;
 
 #[derive(Deserialize, Clone, Default, Debug)]
 pub struct Theme {
@@ -138,7 +137,9 @@ where
     E: Element,
     F: FnOnce(&mut ViewContext<E::ViewState>) -> E,
 {
-    let child = cx.with_global(theme.clone(), |cx| build_child(cx));
+    cx.default_global::<ThemeStack>().0.push(theme.clone());
+    let child = build_child(cx);
+    cx.default_global::<ThemeStack>().0.pop();
     Themed { theme, child }
 }
 
@@ -156,6 +157,9 @@ where
     }
 }
 
+#[derive(Default)]
+struct ThemeStack(Vec<Theme>);
+
 impl<E: Element> Element for Themed<E> {
     type ViewState = E::ViewState;
     type ElementState = E::ElementState;
@@ -170,9 +174,10 @@ impl<E: Element> Element for Themed<E> {
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
     ) -> Self::ElementState {
-        cx.with_global(self.theme.clone(), |cx| {
-            self.child.initialize(view_state, element_state, cx)
-        })
+        cx.default_global::<ThemeStack>().0.push(self.theme.clone());
+        let element_state = self.child.initialize(view_state, element_state, cx);
+        cx.default_global::<ThemeStack>().0.pop();
+        element_state
     }
 
     fn layout(
@@ -184,9 +189,10 @@ impl<E: Element> Element for Themed<E> {
     where
         Self: Sized,
     {
-        cx.with_global(self.theme.clone(), |cx| {
-            self.child.layout(view_state, element_state, cx)
-        })
+        cx.default_global::<ThemeStack>().0.push(self.theme.clone());
+        let layout_id = self.child.layout(view_state, element_state, cx);
+        cx.default_global::<ThemeStack>().0.pop();
+        layout_id
     }
 
     fn paint(
@@ -198,9 +204,9 @@ impl<E: Element> Element for Themed<E> {
     ) where
         Self: Sized,
     {
-        cx.with_global(self.theme.clone(), |cx| {
-            self.child.paint(bounds, view_state, frame_state, cx);
-        });
+        cx.default_global::<ThemeStack>().0.push(self.theme.clone());
+        self.child.paint(bounds, view_state, frame_state, cx);
+        cx.default_global::<ThemeStack>().0.pop();
     }
 }
 

crates/zed2/src/main.rs 🔗

@@ -38,6 +38,7 @@ use util::{
     http::HttpClient,
     paths, ResultExt,
 };
+use uuid::Uuid;
 use zed2::{ensure_only_instance, AppState, Assets, IsOnlyInstance};
 // use zed2::{
 //     assets::Assets,
@@ -59,7 +60,7 @@ fn main() {
     log::info!("========== starting zed ==========");
     let app = App::production(Arc::new(Assets));
 
-    // let installation_id = app.background().block(installation_id()).ok();
+    // let installation_id = app.executor().block(installation_id()).ok();
     // let session_id = Uuid::new_v4().to_string();
     // init_panic_hook(&app, installation_id.clone(), session_id.clone());
 
@@ -259,15 +260,10 @@ fn main() {
                         }
                         OpenRequest::CliConnection { connection } => {
                             let app_state = app_state.clone();
-                            if cx
-                                .spawn(move |cx| {
-                                    handle_cli_connection(connection, app_state.clone(), cx)
-                                })
-                                .map(Task::detach)
-                                .is_err()
-                            {
-                                break;
-                            }
+                            cx.spawn(move |cx| {
+                                handle_cli_connection(connection, app_state.clone(), cx)
+                            })
+                            .detach();
                         }
                         OpenRequest::JoinChannel { channel_id: _ } => {
                             // cx
@@ -404,9 +400,7 @@ static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
 
 fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: String) {
     let is_pty = stdout_is_a_pty();
-    let app_version = app.app_version().ok();
-    let os_name = app.os_name();
-    let os_version = app.os_version().ok();
+    let app_metadata = app.metadata();
 
     panic::set_hook(Box::new(move |info| {
         let prior_panic_count = PANIC_COUNT.fetch_add(1, Ordering::SeqCst);
@@ -442,8 +436,8 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
             std::process::exit(-1);
         }
 
-        let app_version = client::ZED_APP_VERSION
-            .or(app_version)
+        let app_version = client2::ZED_APP_VERSION
+            .or(app_metadata.app_version)
             .map_or("dev".to_string(), |v| v.to_string());
 
         let backtrace = Backtrace::new();
@@ -470,8 +464,11 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
             }),
             app_version: app_version.clone(),
             release_channel: RELEASE_CHANNEL.display_name().into(),
-            os_name: os_name.into(),
-            os_version: os_version.as_ref().map(SemanticVersion::to_string),
+            os_name: app_metadata.os_name.into(),
+            os_version: app_metadata
+                .os_version
+                .as_ref()
+                .map(SemanticVersion::to_string),
             architecture: env::consts::ARCH.into(),
             panicked_on: SystemTime::now()
                 .duration_since(UNIX_EPOCH)
@@ -507,11 +504,11 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
 }
 
 fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
-    let telemetry_settings = *settings2::get::<client::TelemetrySettings>(cx);
+    let telemetry_settings = *settings2::get::<client2::TelemetrySettings>(cx);
 
     cx.executor()
         .spawn(async move {
-            let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
+            let panic_report_url = format!("{}/api/panic", &*client2::ZED_SERVER_URL);
             let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
             while let Some(child) = children.next().await {
                 let child = child?;
@@ -554,7 +551,7 @@ fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
                     if let Some(panic) = panic {
                         let body = serde_json::to_string(&PanicRequest {
                             panic,
-                            token: client::ZED_SECRET_CLIENT_TOKEN.into(),
+                            token: client2::ZED_SECRET_CLIENT_TOKEN.into(),
                         })
                         .unwrap();