Make joining on boot work

This commit is contained in:
Conrad Irwin 2023-10-06 16:11:45 -06:00
parent 4128e2ffcb
commit 63a230f92e
5 changed files with 118 additions and 65 deletions

View file

@ -22,7 +22,7 @@ use drag_and_drop::DragAndDrop;
use futures::{
channel::{mpsc, oneshot},
future::try_join_all,
select_biased, FutureExt, StreamExt,
FutureExt, StreamExt,
};
use gpui::{
actions,
@ -36,9 +36,9 @@ use gpui::{
CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel,
WindowBounds, WindowOptions,
},
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
WeakViewHandle, WindowContext, WindowHandle,
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AnyWindowHandle, AppContext, AsyncAppContext,
Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext,
ViewHandle, WeakViewHandle, WindowContext, WindowHandle,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
use itertools::Itertools;
@ -4156,7 +4156,7 @@ pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
async fn join_channel_internal(
channel_id: u64,
app_state: Arc<AppState>,
app_state: &Arc<AppState>,
requesting_window: Option<WindowHandle<Workspace>>,
active_call: &ModelHandle<ActiveCall>,
cx: &mut AsyncAppContext,
@ -4196,33 +4196,24 @@ async fn join_channel_internal(
let client = cx.read(|cx| active_call.read(cx).client());
let mut timer = cx.background().timer(Duration::from_secs(5)).fuse();
let mut client_status = client.status();
// this loop will terminate within client::CONNECTION_TIMEOUT seconds.
'outer: loop {
select_biased! {
_ = timer => {
return Err(anyhow!("connecting timed out"))
},
status = client_status.recv().fuse() => {
let Some(status) = status else {
return Err(anyhow!("unexpected error reading connection status"))
};
let Some(status) = client_status.recv().await else {
return Err(anyhow!("error connecting"));
};
match status {
Status::Connecting | Status::Authenticating | Status::Reconnecting | Status::Reauthenticating => continue,
Status::Connected { .. } => break 'outer,
Status::SignedOut => {
if client.has_keychain_credentials(&cx) {
client.authenticate_and_connect(true, &cx).await?;
timer = cx.background().timer(Duration::from_secs(5)).fuse();
} else {
return Err(anyhow!("not signed in"))
}
},
Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
| Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => return Err(anyhow!("zed is offline"))
}
match status {
Status::Connecting
| Status::Authenticating
| Status::Reconnecting
| Status::Reauthenticating => continue,
Status::Connected { .. } => break 'outer,
Status::SignedOut => return Err(anyhow!("not signed in")),
Status::UpgradeRequired => return Err(anyhow!("zed is out of date")),
Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
return Err(anyhow!("zed is offline"))
}
}
}
@ -4233,6 +4224,8 @@ async fn join_channel_internal(
})
.await?;
room.update(cx, |room, cx| room.next_room_update()).await;
let task = room.update(cx, |room, cx| {
if let Some((project, host)) = room.most_active_project() {
return Some(join_remote_project(project, host, app_state.clone(), cx));
@ -4255,10 +4248,10 @@ pub fn join_channel(
cx: &mut AppContext,
) -> Task<Result<()>> {
let active_call = ActiveCall::global(cx);
cx.spawn(|mut cx| {
cx.spawn(|mut cx| async move {
let result = join_channel_internal(
channel_id,
app_state,
&app_state,
requesting_window,
&active_call,
&mut cx,
@ -4266,7 +4259,7 @@ pub fn join_channel(
.await;
// join channel succeeded, and opened a window
if Some(true) = result {
if matches!(result, Ok(true)) {
return anyhow::Ok(());
}
@ -4275,28 +4268,53 @@ pub fn join_channel(
}
// find an existing workspace to focus and show call controls
for window in cx.windows() {
let found = window.update(&mut cx, |cx| {
let is_workspace = cx.root_view().clone().downcast::<Workspace>().is_some();
if is_workspace {
cx.activate_window();
}
is_workspace
});
let mut active_window = activate_any_workspace_window(&mut cx);
if active_window.is_none() {
// no open workspaces, make one to show the error in (blergh)
cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
.await;
}
if found.unwrap_or(false) {
return anyhow::Ok(());
active_window = activate_any_workspace_window(&mut cx);
if active_window.is_none() {
return result.map(|_| ()); // unreachable!() assuming new_local always opens a window
}
if let Err(err) = result {
let prompt = active_window.unwrap().prompt(
PromptLevel::Critical,
&format!("Failed to join channel: {}", err),
&["Ok"],
&mut cx,
);
if let Some(mut prompt) = prompt {
prompt.next().await;
} else {
return Err(err);
}
}
// no open workspaces
cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx))
.await;
return connected.map(|_| ());
// return ok, we showed the error to the user.
return anyhow::Ok(());
})
}
pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option<AnyWindowHandle> {
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);
}
}
None
}
#[allow(clippy::type_complexity)]
pub fn open_paths(
abs_paths: &[PathBuf],