diff --git a/crates/recent_projects/src/dev_servers.rs b/crates/recent_projects/src/dev_servers.rs index 7761461ab51ab..0a05b6a1e9e0a 100644 --- a/crates/recent_projects/src/dev_servers.rs +++ b/crates/recent_projects/src/dev_servers.rs @@ -5,8 +5,7 @@ use std::time::Duration; use anyhow::anyhow; use anyhow::Context; use anyhow::Result; -use client::Client; -use dev_server_projects::{DevServer, DevServerId, DevServerProject, DevServerProjectId}; +use dev_server_projects::{DevServer, DevServerId, DevServerProjectId}; use editor::Editor; use gpui::pulsating_between; use gpui::AsyncWindowContext; @@ -16,17 +15,12 @@ use gpui::Subscription; use gpui::Task; use gpui::WeakView; use gpui::{ - percentage, Action, Animation, AnimationExt, AnyElement, AppContext, DismissEvent, - EventEmitter, FocusHandle, FocusableView, Model, ScrollHandle, Transformation, View, - ViewContext, + Action, Animation, AnimationExt, AnyElement, AppContext, DismissEvent, EventEmitter, + FocusHandle, FocusableView, Model, ScrollHandle, View, ViewContext, }; use project::terminals::wrap_for_ssh; use project::terminals::SshCommand; -use rpc::proto::RegenerateDevServerTokenResponse; -use rpc::{ - proto::{CreateDevServerResponse, DevServerStatus}, - ErrorCode, ErrorExt, -}; +use rpc::{proto::DevServerStatus, ErrorCode, ErrorExt}; use settings::update_settings_file; use settings::Settings; use task::HideStrategy; @@ -35,14 +29,11 @@ use task::SpawnInTerminal; use terminal_view::terminal_panel::TerminalPanel; use ui::ElevationIndex; use ui::Section; -use ui::{ - prelude::*, IconButtonShape, Indicator, List, ListItem, Modal, ModalFooter, ModalHeader, - Tooltip, -}; +use ui::{prelude::*, IconButtonShape, List, ListItem, Modal, ModalFooter, ModalHeader, Tooltip}; use ui_input::{FieldLabelLayout, TextField}; use util::ResultExt; use workspace::OpenOptions; -use workspace::{notifications::DetachAndPromptErr, AppState, ModalView, Workspace, WORKSPACE_DB}; +use workspace::{notifications::DetachAndPromptErr, AppState, ModalView, Workspace}; use crate::open_dev_server_project; use crate::ssh_connections::connect_over_ssh; @@ -69,15 +60,11 @@ pub struct DevServerProjects { #[derive(Default)] struct CreateDevServer { creating: Option>>, - dev_server_id: Option, - access_token: Option, ssh_prompt: Option>, - kind: NewServerKind, } struct CreateDevServerProject { dev_server_id: DevServerId, - creating: bool, _opening: Option, } @@ -86,14 +73,6 @@ enum Mode { CreateDevServer(CreateDevServer), } -#[derive(Default, PartialEq, Eq, Clone, Copy)] -enum NewServerKind { - DirectSSH, - #[default] - LegacySSH, - Manual, -} - impl DevServerProjects { pub fn register(workspace: &mut Workspace, _: &mut ViewContext) { workspace.register_action(|workspace, _: &OpenRemote, cx| { @@ -223,14 +202,12 @@ impl DevServerProjects { this.mode = Mode::Default(Some(CreateDevServerProject { dev_server_id, - creating: true, _opening: Some(subscription), })); } } else { this.mode = Mode::Default(Some(CreateDevServerProject { dev_server_id, - creating: false, _opening: None, })); } @@ -253,7 +230,7 @@ impl DevServerProjects { self.mode = Mode::Default(Some(CreateDevServerProject { dev_server_id, - creating: true, + _opening: None, })); } @@ -309,10 +286,7 @@ impl DevServerProjects { .log_err(), None => this .update(&mut cx, |this, cx| { - this.mode = Mode::CreateDevServer(CreateDevServer { - kind: NewServerKind::DirectSSH, - ..Default::default() - }); + this.mode = Mode::CreateDevServer(CreateDevServer::default()); cx.notify() }) .log_err(), @@ -320,10 +294,8 @@ impl DevServerProjects { None }); self.mode = Mode::CreateDevServer(CreateDevServer { - kind: NewServerKind::DirectSSH, ssh_prompt: Some(ssh_prompt.clone()), creating: Some(creating), - ..Default::default() }); } @@ -460,228 +432,6 @@ impl DevServerProjects { }) } - fn create_or_update_dev_server( - &mut self, - kind: NewServerKind, - existing_id: Option, - access_token: Option, - cx: &mut ViewContext, - ) { - let name = get_text(&self.dev_server_name_input, cx); - if name.is_empty() { - return; - } - - let manual_setup = match kind { - NewServerKind::DirectSSH => unreachable!(), - NewServerKind::LegacySSH => false, - NewServerKind::Manual => true, - }; - - let ssh_connection_string = if manual_setup { - None - } else if name.contains(' ') { - Some(name.clone()) - } else { - Some(format!("ssh {}", name)) - }; - - let dev_server = self.dev_server_store.update(cx, { - let access_token = access_token.clone(); - |store, cx| { - let ssh_connection_string = ssh_connection_string.clone(); - if let Some(dev_server_id) = existing_id { - let rename = store.rename_dev_server( - dev_server_id, - name.clone(), - ssh_connection_string, - cx, - ); - let token = if let Some(access_token) = access_token { - Task::ready(Ok(RegenerateDevServerTokenResponse { - dev_server_id: dev_server_id.0, - access_token, - })) - } else { - store.regenerate_dev_server_token(dev_server_id, cx) - }; - cx.spawn(|_, _| async move { - rename.await?; - let response = token.await?; - Ok(CreateDevServerResponse { - dev_server_id: dev_server_id.0, - name, - access_token: response.access_token, - }) - }) - } else { - store.create_dev_server(name, ssh_connection_string.clone(), cx) - } - } - }); - - let workspace = self.workspace.clone(); - let store = dev_server_projects::Store::global(cx); - - let task = cx - .spawn({ - |this, mut cx| async move { - let result = dev_server.await; - - match result { - Ok(dev_server) => { - if let Some(ssh_connection_string) = ssh_connection_string { - this.update(&mut cx, |this, cx| { - if let Mode::CreateDevServer(CreateDevServer { - access_token, - dev_server_id, - .. - }) = &mut this.mode - { - access_token.replace(dev_server.access_token.clone()); - dev_server_id - .replace(DevServerId(dev_server.dev_server_id)); - } - cx.notify(); - })?; - - spawn_ssh_task( - workspace - .upgrade() - .ok_or_else(|| anyhow!("workspace dropped"))?, - store, - DevServerId(dev_server.dev_server_id), - ssh_connection_string, - dev_server.access_token.clone(), - &mut cx, - ) - .await - .log_err(); - } - - this.update(&mut cx, |this, cx| { - this.focus_handle.focus(cx); - this.mode = Mode::CreateDevServer(CreateDevServer { - dev_server_id: Some(DevServerId(dev_server.dev_server_id)), - access_token: Some(dev_server.access_token), - kind, - ..Default::default() - }); - cx.notify(); - })?; - Ok(()) - } - Err(e) => { - this.update(&mut cx, |this, cx| { - this.mode = Mode::CreateDevServer(CreateDevServer { - dev_server_id: existing_id, - access_token: None, - kind, - ..Default::default() - }); - cx.notify() - }) - .log_err(); - - Err(e) - } - } - } - }) - .prompt_err("Failed to create server", cx, |_, _| None); - - self.mode = Mode::CreateDevServer(CreateDevServer { - creating: Some(task), - dev_server_id: existing_id, - access_token, - kind, - ..Default::default() - }); - cx.notify() - } - - fn delete_dev_server(&mut self, id: DevServerId, cx: &mut ViewContext) { - let store = self.dev_server_store.read(cx); - let prompt = if store.projects_for_server(id).is_empty() - && store - .dev_server(id) - .is_some_and(|server| server.status == DevServerStatus::Offline) - { - None - } else { - Some(cx.prompt( - gpui::PromptLevel::Warning, - "Are you sure?", - Some("This will delete the dev server and all of its remote projects."), - &["Delete", "Cancel"], - )) - }; - - cx.spawn(|this, mut cx| async move { - if let Some(prompt) = prompt { - if prompt.await? != 0 { - return Ok(()); - } - } - - let project_ids: Vec = this.update(&mut cx, |this, cx| { - this.dev_server_store.update(cx, |store, _| { - store - .projects_for_server(id) - .into_iter() - .map(|project| project.id) - .collect() - }) - })?; - - this.update(&mut cx, |this, cx| { - this.dev_server_store - .update(cx, |store, cx| store.delete_dev_server(id, cx)) - })? - .await?; - - for id in project_ids { - WORKSPACE_DB - .delete_workspace_by_dev_server_project_id(id) - .await - .log_err(); - } - Ok(()) - }) - .detach_and_prompt_err("Failed to delete dev server", cx, |_, _| None); - } - - fn delete_dev_server_project(&mut self, id: DevServerProjectId, cx: &mut ViewContext) { - let answer = cx.prompt( - gpui::PromptLevel::Warning, - "Delete this project?", - Some("This will delete the remote project. You can always re-add it later."), - &["Delete", "Cancel"], - ); - - cx.spawn(|this, mut cx| async move { - let answer = answer.await?; - - if answer != 0 { - return Ok(()); - } - - this.update(&mut cx, |this, cx| { - this.dev_server_store - .update(cx, |store, cx| store.delete_dev_server_project(id, cx)) - })? - .await?; - - WORKSPACE_DB - .delete_workspace_by_dev_server_project_id(id) - .await - .log_err(); - - Ok(()) - }) - .detach_and_prompt_err("Failed to delete dev server project", cx, |_, _| None); - } - fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { match &self.mode { Mode::Default(None) => {} @@ -695,18 +445,8 @@ impl DevServerProjects { }); return; } - if state.kind == NewServerKind::DirectSSH { - self.create_ssh_server(cx); - return; - } - if state.creating.is_none() || state.dev_server_id.is_some() { - self.create_or_update_dev_server( - state.kind, - state.dev_server_id, - state.access_token.clone(), - cx, - ); - } + + self.create_ssh_server(cx); } } } @@ -716,7 +456,6 @@ impl DevServerProjects { Mode::Default(None) => cx.emit(DismissEvent), Mode::CreateDevServer(state) if state.ssh_prompt.is_some() => { self.mode = Mode::CreateDevServer(CreateDevServer { - kind: NewServerKind::DirectSSH, ..Default::default() }); cx.notify(); @@ -729,161 +468,6 @@ impl DevServerProjects { } } - fn render_dev_server( - &mut self, - dev_server: &DevServer, - create_project: Option, - cx: &mut ViewContext, - ) -> impl IntoElement { - let dev_server_id = dev_server.id; - let status = dev_server.status; - let dev_server_name = dev_server.name.clone(); - let kind = if dev_server.ssh_connection_string.is_some() { - NewServerKind::LegacySSH - } else { - NewServerKind::Manual - }; - - v_flex() - .w_full() - .child( - h_flex().group("dev-server").justify_between().child( - h_flex() - .gap_2() - .child( - div() - .id(("status", dev_server.id.0)) - .relative() - .child(Icon::new(IconName::Server).size(IconSize::Small)) - .child(div().absolute().bottom_0().left(rems_from_px(8.0)).child( - Indicator::dot().color(match status { - DevServerStatus::Online => Color::Created, - DevServerStatus::Offline => Color::Hidden, - }), - )) - .tooltip(move |cx| { - Tooltip::text( - match status { - DevServerStatus::Online => "Online", - DevServerStatus::Offline => "Offline", - }, - cx, - ) - }), - ) - .child( - div() - .max_w(rems(26.)) - .overflow_hidden() - .whitespace_nowrap() - .child(Label::new(dev_server_name.clone())), - ) - .child( - h_flex() - .visible_on_hover("dev-server") - .gap_1() - .child(if dev_server.ssh_connection_string.is_some() { - let dev_server = dev_server.clone(); - IconButton::new("reconnect-dev-server", IconName::ArrowCircle) - .on_click(cx.listener(move |this, _, cx| { - let Some(workspace) = this.workspace.upgrade() else { - return; - }; - - reconnect_to_dev_server( - workspace, - dev_server.clone(), - cx, - ) - .detach_and_prompt_err( - "Failed to reconnect", - cx, - |_, _| None, - ); - })) - .tooltip(|cx| Tooltip::text("Reconnect", cx)) - } else { - IconButton::new("edit-dev-server", IconName::Pencil) - .on_click(cx.listener(move |this, _, cx| { - this.mode = Mode::CreateDevServer(CreateDevServer { - dev_server_id: Some(dev_server_id), - kind, - ..Default::default() - }); - let dev_server_name = dev_server_name.clone(); - this.dev_server_name_input.update( - cx, - move |input, cx| { - input.editor().update(cx, move |editor, cx| { - editor.set_text(dev_server_name, cx) - }) - }, - ) - })) - .tooltip(|cx| Tooltip::text("Edit dev server", cx)) - }) - .child({ - let dev_server_id = dev_server.id; - IconButton::new("remove-dev-server", IconName::TrashAlt) - .on_click(cx.listener(move |this, _, cx| { - this.delete_dev_server(dev_server_id, cx) - })) - .tooltip(|cx| Tooltip::text("Remove dev server", cx)) - }), - ), - ), - ) - .child( - v_flex() - .w_full() - .bg(cx.theme().colors().background) - .border_1() - .border_color(cx.theme().colors().border_variant) - .rounded_md() - .my_1() - .py_0p5() - .px_3() - .child( - List::new() - .empty_message("No projects.") - .children( - self.dev_server_store - .read(cx) - .projects_for_server(dev_server.id) - .iter() - .map(|p| self.render_dev_server_project(p, cx)), - ) - .when( - create_project.is_none() - && dev_server.status == DevServerStatus::Online, - |el| { - el.child( - ListItem::new("new-remote_project") - .start_slot(Icon::new(IconName::Plus)) - .child(Label::new("Open folder…")) - .on_click(cx.listener(move |this, _, cx| { - this.mode = - Mode::Default(Some(CreateDevServerProject { - dev_server_id, - creating: false, - _opening: None, - })); - this.project_path_input - .read(cx) - .focus_handle(cx) - .focus(cx); - cx.notify(); - })), - ) - }, - ) - .when_some(create_project, |el, creating| { - el.child(self.render_create_new_project(creating, cx)) - }), - ), - ) - } - fn render_ssh_connection( &mut self, ix: usize, @@ -1094,77 +678,13 @@ impl DevServerProjects { }); } - fn render_create_new_project( - &mut self, - creating: bool, - _: &mut ViewContext, - ) -> impl IntoElement { - ListItem::new("create-remote-project") - .disabled(true) - .start_slot(Icon::new(IconName::FileTree).color(Color::Muted)) - .child(self.project_path_input.clone()) - .child(div().w(IconSize::Medium.rems()).when(creating, |el| { - el.child( - Icon::new(IconName::ArrowCircle) - .size(IconSize::Medium) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), - ) - })) - } - - fn render_dev_server_project( - &mut self, - project: &DevServerProject, - cx: &mut ViewContext, - ) -> impl IntoElement { - let dev_server_project_id = project.id; - let project_id = project.project_id; - let is_online = project_id.is_some(); - - ListItem::new(("remote-project", dev_server_project_id.0)) - .start_slot(Icon::new(IconName::FileTree).when(!is_online, |icon| icon.color(Color::Muted))) - .child( - Label::new(project.paths.join(", ")) - ) - .on_click(cx.listener(move |_, _, cx| { - if let Some(project_id) = project_id { - if let Some(app_state) = AppState::global(cx).upgrade() { - workspace::join_dev_server_project(dev_server_project_id, project_id, app_state, None, cx) - .detach_and_prompt_err("Could not join project", cx, |_, _| None) - } - } else { - cx.spawn(|_, mut cx| async move { - cx.prompt(gpui::PromptLevel::Critical, "This project is offline", Some("The `zed` instance running on this dev server is not connected. You will have to restart it."), &["Ok"]).await.log_err(); - }).detach(); - } - })) - .end_hover_slot::(Some(IconButton::new("remove-remote-project", IconName::TrashAlt) - .on_click(cx.listener(move |this, _, cx| { - this.delete_dev_server_project(dev_server_project_id, cx) - })) - .tooltip(|cx| Tooltip::text("Delete remote project", cx)).into_any_element())) - } - fn render_create_dev_server( &self, state: &CreateDevServer, cx: &mut ViewContext, ) -> impl IntoElement { let creating = state.creating.is_some(); - let dev_server_id = state.dev_server_id; - let access_token = state.access_token.clone(); let ssh_prompt = state.ssh_prompt.clone(); - let use_direct_ssh = SshSettings::get_global(cx).use_direct_ssh() - || Client::global(cx).status().borrow().is_signed_out(); - - let mut kind = state.kind; - if use_direct_ssh && kind == NewServerKind::LegacySSH { - kind = NewServerKind::DirectSSH; - } self.dev_server_name_input.update(cx, |input, cx| { input.editor().update(cx, |editor, cx| { @@ -1216,20 +736,10 @@ impl DevServerProjects { Button::new("create-dev-server", "Connect Server") .style(ButtonStyle::Filled) .layer(ElevationIndex::ModalSurface) - .disabled(creating && dev_server_id.is_none()) + .disabled(creating) .on_click(cx.listener({ - let access_token = access_token.clone(); move |this, _, cx| { - if kind == NewServerKind::DirectSSH { - this.create_ssh_server(cx); - return; - } - this.create_or_update_dev_server( - kind, - dev_server_id, - access_token.clone(), - cx, - ); + this.create_ssh_server(cx); } })), ), @@ -1277,22 +787,6 @@ impl DevServerProjects { .ssh_connections() .collect::>(); - let Mode::Default(create_dev_server_project) = &self.mode else { - unreachable!() - }; - - let mut is_creating = None; - let mut creating_dev_server = None; - if let Some(CreateDevServerProject { - creating, - dev_server_id, - .. - }) = create_dev_server_project - { - is_creating = Some(*creating); - creating_dev_server = Some(*dev_server_id); - }; - let footer = format!("Connections: {}", ssh_connections.len() + dev_servers.len()); Modal::new("remote-projects", Some(self.scroll_handle.clone())) .header( @@ -1309,11 +803,6 @@ impl DevServerProjects { .icon_color(Color::Muted) .on_click(cx.listener(|this, _, cx| { this.mode = Mode::CreateDevServer(CreateDevServer { - kind: if SshSettings::get_global(cx).use_direct_ssh() { - NewServerKind::DirectSSH - } else { - NewServerKind::LegacySSH - }, ..Default::default() }); this.dev_server_name_input.update(cx, |text_field, cx| { @@ -1341,17 +830,7 @@ impl DevServerProjects { self.render_ssh_connection(ix, connection, cx) .into_any_element() }, - )) - .children(dev_servers.iter().map(|dev_server| { - let creating = if creating_dev_server == Some(dev_server.id) - { - is_creating - } else { - None - }; - self.render_dev_server(dev_server, creating, cx) - .into_any_element() - })), + )), ), ), ), diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index 554146eab2d18..5862d48e8182b 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -28,10 +28,6 @@ pub struct SshSettings { } impl SshSettings { - pub fn use_direct_ssh(&self) -> bool { - self.ssh_connections.is_some() - } - pub fn ssh_connections(&self) -> impl Iterator { self.ssh_connections.clone().into_iter().flatten() }