Auto-open remote projects on creation (#11826)

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2024-05-14 16:05:26 -06:00 committed by GitHub
parent 67c9fc575f
commit 18b6ded8f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 154 additions and 126 deletions

View file

@ -4,6 +4,7 @@ use dev_server_projects::{DevServer, DevServerId, DevServerProject, DevServerPro
use editor::Editor; use editor::Editor;
use feature_flags::FeatureFlagAppExt; use feature_flags::FeatureFlagAppExt;
use feature_flags::FeatureFlagViewExt; use feature_flags::FeatureFlagViewExt;
use gpui::Subscription;
use gpui::{ use gpui::{
percentage, Action, Animation, AnimationExt, AnyElement, AppContext, ClipboardItem, percentage, Action, Animation, AnimationExt, AnyElement, AppContext, ClipboardItem,
DismissEvent, EventEmitter, FocusHandle, FocusableView, Model, ScrollHandle, Transformation, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model, ScrollHandle, Transformation,
@ -53,10 +54,10 @@ enum EditDevServerState {
RegeneratedToken(RegenerateDevServerTokenResponse), RegeneratedToken(RegenerateDevServerTokenResponse),
} }
#[derive(Clone)]
struct CreateDevServerProject { struct CreateDevServerProject {
dev_server_id: DevServerId, dev_server_id: DevServerId,
creating: bool, creating: bool,
_opening: Option<Subscription>,
} }
enum Mode { enum Mode {
@ -94,7 +95,7 @@ impl DevServerProjects {
pub fn new(cx: &mut ViewContext<Self>) -> Self { pub fn new(cx: &mut ViewContext<Self>) -> Self {
let project_path_input = cx.new_view(|cx| { let project_path_input = cx.new_view(|cx| {
let mut editor = Editor::single_line(cx); let mut editor = Editor::single_line(cx);
editor.set_placeholder_text("Project path", cx); editor.set_placeholder_text("Project path (~/work/zed, /workspace/zed, …)", cx);
editor editor
}); });
let dev_server_name_input = let dev_server_name_input =
@ -165,15 +166,45 @@ impl DevServerProjects {
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let result = create.await; let result = create.await;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
if result.is_ok() { if let Ok(result) = &result {
this.project_path_input.update(cx, |editor, cx| { if let Some(dev_server_project_id) =
editor.set_text("", cx); result.dev_server_project.as_ref().map(|p| p.id)
}); {
this.mode = Mode::Default(None); let subscription =
cx.observe(&this.dev_server_store, move |this, store, cx| {
if let Some(project_id) = store
.read(cx)
.dev_server_project(DevServerProjectId(dev_server_project_id))
.and_then(|p| p.project_id)
{
this.project_path_input.update(cx, |editor, cx| {
editor.set_text("", cx);
});
this.mode = Mode::Default(None);
if let Some(app_state) = AppState::global(cx).upgrade() {
workspace::join_dev_server_project(
project_id, app_state, None, cx,
)
.detach_and_prompt_err(
"Could not join project",
cx,
|_, _| None,
)
}
}
});
this.mode = Mode::Default(Some(CreateDevServerProject {
dev_server_id,
creating: true,
_opening: Some(subscription),
}));
}
} else { } else {
this.mode = Mode::Default(Some(CreateDevServerProject { this.mode = Mode::Default(Some(CreateDevServerProject {
dev_server_id, dev_server_id,
creating: false, creating: false,
_opening: None,
})); }));
} }
}) })
@ -196,6 +227,7 @@ impl DevServerProjects {
self.mode = Mode::Default(Some(CreateDevServerProject { self.mode = Mode::Default(Some(CreateDevServerProject {
dev_server_id, dev_server_id,
creating: true, creating: true,
_opening: None,
})); }));
} }
@ -443,109 +475,74 @@ impl DevServerProjects {
fn render_dev_server( fn render_dev_server(
&mut self, &mut self,
dev_server: &DevServer, dev_server: &DevServer,
mut create_project: Option<CreateDevServerProject>, create_project: Option<bool>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl IntoElement { ) -> impl IntoElement {
let dev_server_id = dev_server.id; let dev_server_id = dev_server.id;
let status = dev_server.status; let status = dev_server.status;
let dev_server_name = dev_server.name.clone(); let dev_server_name = dev_server.name.clone();
if create_project
.as_ref()
.is_some_and(|cp| cp.dev_server_id != dev_server.id)
{
create_project = None;
}
v_flex() v_flex()
.w_full() .w_full()
.child( .child(
h_flex() h_flex().group("dev-server").justify_between().child(
.group("dev-server") h_flex()
.justify_between() .gap_2()
.child( .child(
h_flex() div()
.gap_2() .id(("status", dev_server.id.0))
.child( .relative()
div() .child(Icon::new(IconName::Server).size(IconSize::Small))
.id(("status", dev_server.id.0)) .child(div().absolute().bottom_0().left(rems_from_px(8.0)).child(
.relative() Indicator::dot().color(match status {
.child(Icon::new(IconName::Server).size(IconSize::Small)) DevServerStatus::Online => Color::Created,
.child( DevServerStatus::Offline => Color::Hidden,
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(dev_server_name.clone()) .tooltip(move |cx| {
.child( Tooltip::text(
h_flex() match status {
.visible_on_hover("dev-server") DevServerStatus::Online => "Online",
.gap_1() DevServerStatus::Offline => "Offline",
.child( },
IconButton::new("edit-dev-server", IconName::Pencil) cx,
.on_click(cx.listener(move |this, _, cx| {
this.mode = Mode::EditDevServer(EditDevServer {
dev_server_id,
state: EditDevServerState::Default,
});
let dev_server_name = dev_server_name.clone();
this.rename_dev_server_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::Trash) .child(dev_server_name.clone())
.on_click(cx.listener(move |this, _, cx| { .child(
this.delete_dev_server(dev_server_id, cx) h_flex()
})) .visible_on_hover("dev-server")
.tooltip(|cx| Tooltip::text("Remove dev server", cx)) .gap_1()
}), .child(
), IconButton::new("edit-dev-server", IconName::Pencil)
) .on_click(cx.listener(move |this, _, cx| {
.child( this.mode = Mode::EditDevServer(EditDevServer {
h_flex().gap_1().child( dev_server_id,
IconButton::new( state: EditDevServerState::Default,
("add-remote-project", dev_server_id.0), });
IconName::Plus, let dev_server_name = dev_server_name.clone();
) this.rename_dev_server_input.update(
.tooltip(|cx| Tooltip::text("Add a remote project", cx)) cx,
.on_click(cx.listener( move |input, cx| {
move |this, _, cx| { input.editor().update(cx, move |editor, cx| {
if let Mode::Default(project) = &mut this.mode { editor.set_text(dev_server_name, cx)
*project = Some(CreateDevServerProject { })
dev_server_id, },
creating: false, )
}); }))
} .tooltip(|cx| Tooltip::text("Edit dev server", cx)),
this.project_path_input.read(cx).focus_handle(cx).focus(cx); )
cx.notify(); .child({
}, let dev_server_id = dev_server.id;
)), IconButton::new("remove-dev-server", IconName::Trash)
.on_click(cx.listener(move |this, _, cx| {
this.delete_dev_server(dev_server_id, cx)
}))
.tooltip(|cx| Tooltip::text("Remove dev server", cx))
}),
), ),
), ),
) )
.child( .child(
v_flex() v_flex()
@ -567,8 +564,32 @@ impl DevServerProjects {
.iter() .iter()
.map(|p| self.render_dev_server_project(p, cx)), .map(|p| self.render_dev_server_project(p, cx)),
) )
.when_some(create_project, |el, create_project| { .when(
el.child(self.render_create_new_project(&create_project, cx)) 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))
}), }),
), ),
) )
@ -576,29 +597,24 @@ impl DevServerProjects {
fn render_create_new_project( fn render_create_new_project(
&mut self, &mut self,
create_project: &CreateDevServerProject, creating: bool,
_: &mut ViewContext<Self>, _: &mut ViewContext<Self>,
) -> impl IntoElement { ) -> impl IntoElement {
ListItem::new("create-remote-project") ListItem::new("create-remote-project")
.disabled(true)
.start_slot(Icon::new(IconName::FileTree).color(Color::Muted)) .start_slot(Icon::new(IconName::FileTree).color(Color::Muted))
.child(self.project_path_input.clone()) .child(self.project_path_input.clone())
.child( .child(div().w(IconSize::Medium.rems()).when(creating, |el| {
div() el.child(
.w(IconSize::Medium.rems()) Icon::new(IconName::ArrowCircle)
.when(create_project.creating, |el| { .size(IconSize::Medium)
el.child( .with_animation(
Icon::new(IconName::ArrowCircle) "arrow-circle",
.size(IconSize::Medium) Animation::new(Duration::from_secs(2)).repeat(),
.with_animation( |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
"arrow-circle", ),
Animation::new(Duration::from_secs(2)).repeat(), )
|icon, delta| { }))
icon.transform(Transformation::rotate(percentage(delta)))
},
),
)
}),
)
} }
fn render_dev_server_project( fn render_dev_server_project(
@ -920,7 +936,18 @@ impl DevServerProjects {
let Mode::Default(create_dev_server_project) = &self.mode else { let Mode::Default(create_dev_server_project) = &self.mode else {
unreachable!() unreachable!()
}; };
let create_dev_server_project = create_dev_server_project.clone();
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);
};
v_flex() v_flex()
.id("scroll-container") .id("scroll-container")
@ -960,12 +987,13 @@ impl DevServerProjects {
), ),
)) ))
.children(dev_servers.iter().map(|dev_server| { .children(dev_servers.iter().map(|dev_server| {
self.render_dev_server( let creating = if creating_dev_server == Some(dev_server.id) {
dev_server, is_creating
create_dev_server_project.clone(), } else {
cx, None
) };
.into_any_element() self.render_dev_server(dev_server, creating, cx)
.into_any_element()
})), })),
), ),
) )

View file

@ -157,7 +157,7 @@ impl RenderOnce for ListItem {
this.ml(self.indent_level as f32 * self.indent_step_size) this.ml(self.indent_level as f32 * self.indent_step_size)
.px_2() .px_2()
}) })
.when(!self.inset, |this| { .when(!self.inset && !self.disabled, |this| {
this this
// TODO: Add focus state // TODO: Add focus state
// .when(self.state == InteractionState::Focused, |this| { // .when(self.state == InteractionState::Focused, |this| {