@@ -2,7 +2,7 @@ use gpui::{AnyElement, Render, ViewContext, WeakView};
use ui::{prelude::*, ButtonCommon, Icon, IconButton, Tooltip};
use workspace::{item::ItemHandle, StatusItemView, Workspace};
-use crate::feedback_editor::FeedbackEditor;
+use crate::{feedback_editor::GiveFeedback, feedback_modal::FeedbackModal};
pub struct DeployFeedbackButton {
active: bool,
@@ -22,34 +22,32 @@ impl Render for DeployFeedbackButton {
type Element = AnyElement;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- let active = self.active;
-
+ let is_open = self
+ .workspace
+ .upgrade()
+ .and_then(|workspace| {
+ workspace.update(cx, |workspace, cx| {
+ workspace.active_modal::<FeedbackModal>(cx)
+ })
+ })
+ .is_some();
IconButton::new("give-feedback", Icon::Envelope)
.style(ui::ButtonStyle::Subtle)
+ .selected(is_open)
.tooltip(|cx| Tooltip::text("Give Feedback", cx))
- .on_click(cx.listener(move |this, _, cx| {
- let Some(workspace) = this.workspace.upgrade() else {
- return;
- };
-
- if !active {
- workspace.update(cx, |workspace, cx| FeedbackEditor::deploy(workspace, cx))
- }
- }))
+ .on_click(|_, cx| {
+ cx.dispatch_action(Box::new(GiveFeedback));
+ })
.into_any_element()
}
}
impl StatusItemView for DeployFeedbackButton {
- fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
- if let Some(item) = item {
- if let Some(_) = item.downcast::<FeedbackEditor>() {
- self.active = true;
- cx.notify();
- return;
- }
- }
- self.active = false;
- cx.notify();
+ fn set_active_pane_item(
+ &mut self,
+ _item: Option<&dyn ItemHandle>,
+ _cx: &mut ViewContext<Self>,
+ ) {
+ // no-op
}
}
@@ -1,20 +1,34 @@
+use std::ops::RangeInclusive;
+
+use editor::{Editor, EditorEvent};
use gpui::{
- div, rems, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Render,
- ViewContext,
+ div, red, rems, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Model,
+ Render, View, ViewContext,
};
+use language::Buffer;
+use project::Project;
use ui::{prelude::*, Button, ButtonStyle, Label, Tooltip};
-use workspace::Workspace;
+use util::ResultExt;
+use workspace::{item::Item, Workspace};
+
+use crate::{feedback_editor::GiveFeedback, system_specs::SystemSpecs, OpenZedCommunityRepo};
-use crate::feedback_editor::GiveFeedback;
+const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
+const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
+ "Feedback failed to submit, see error log for details.";
pub struct FeedbackModal {
- // editor: View<Editor>,
- tmp_focus_handle: FocusHandle, // TODO: should be editor.focus_handle(cx)
+ system_specs: SystemSpecs,
+ feedback_editor: View<Editor>,
+ email_address_editor: View<Editor>,
+ project: Model<Project>,
+ pub allow_submission: bool,
+ character_count: usize,
}
impl FocusableView for FeedbackModal {
- fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
- self.tmp_focus_handle.clone()
+ fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+ self.feedback_editor.focus_handle(cx)
}
}
impl EventEmitter<DismissEvent> for FeedbackModal {}
@@ -23,28 +37,78 @@ impl FeedbackModal {
pub fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
let _handle = cx.view().downgrade();
workspace.register_action(move |workspace, _: &GiveFeedback, cx| {
- workspace.toggle_modal(cx, move |cx| FeedbackModal::new(cx));
+ let markdown = workspace
+ .app_state()
+ .languages
+ .language_for_name("Markdown");
+
+ let project = workspace.project().clone();
+
+ cx.spawn(|workspace, mut cx| async move {
+ let markdown = markdown.await.log_err();
+ let buffer = project
+ .update(&mut cx, |project, cx| {
+ project.create_buffer("", markdown, cx)
+ })?
+ .expect("creating buffers on a local workspace always succeeds");
+
+ workspace.update(&mut cx, |workspace, cx| {
+ let system_specs = SystemSpecs::new(cx);
+
+ workspace.toggle_modal(cx, move |cx| {
+ FeedbackModal::new(system_specs, project, buffer, cx)
+ });
+ })?;
+
+ anyhow::Ok(())
+ })
+ .detach_and_log_err(cx);
});
}
- pub fn new(cx: &mut ViewContext<Self>) -> Self {
- // let line_editor = cx.build_view(|cx| Editor::single_line(cx));
- // let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
-
- // let editor = active_editor.read(cx);
- // let cursor = editor.selections.last::<Point>(cx).head();
- // let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row;
- // let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx));
-
- // let current_text = format!(
- // "line {} of {} (column {})",
- // cursor.row + 1,
- // last_line + 1,
- // cursor.column + 1,
- // );
+ pub fn new(
+ system_specs: SystemSpecs,
+ project: Model<Project>,
+ buffer: Model<Buffer>,
+ cx: &mut ViewContext<Self>,
+ ) -> Self {
+ let email_address_editor = cx.build_view(|cx| {
+ let mut editor = Editor::single_line(cx);
+ editor.set_placeholder_text("Email address (optional)", cx);
+ editor
+ });
+ let feedback_editor = cx.build_view(|cx| {
+ let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
+ editor.set_vertical_scroll_margin(5, cx);
+ editor
+ });
+
+ cx.subscribe(
+ &feedback_editor,
+ |this, editor, event: &EditorEvent, cx| match event {
+ EditorEvent::Edited => {
+ this.character_count = editor
+ .read(cx)
+ .buffer()
+ .read(cx)
+ .as_singleton()
+ .expect("Feedback editor is never a multi-buffer")
+ .read(cx)
+ .len();
+ cx.notify();
+ }
+ _ => {}
+ },
+ )
+ .detach();
+
Self {
- // editor: line_editor,
- tmp_focus_handle: cx.focus_handle(),
+ system_specs: system_specs.clone(),
+ feedback_editor,
+ email_address_editor,
+ project,
+ allow_submission: true,
+ character_count: 0,
}
}
@@ -127,7 +191,13 @@ impl Render for FeedbackModal {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let character_count_error = (self.character_count < *FEEDBACK_CHAR_LIMIT.start())
+ || (self.character_count > *FEEDBACK_CHAR_LIMIT.end());
+
let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent));
+ // let open_community_issues =
+ // cx.listener(|_, _, cx| cx.dispatch_action(Box::new(OpenZedCommunityRepo)));
+ // let open_community_discussions = cx.listener(|_, _, cx| cx.emit(DismissEvent));
v_stack()
.elevation_3(cx)
@@ -136,81 +206,72 @@ impl Render for FeedbackModal {
.h(rems(40.))
.p_2()
.gap_2()
- .child(h_stack().child(Label::new("Give Feedback").color(Color::Default)))
+ .child(
+ v_stack().child(
+ div()
+ .size_full()
+ .border()
+ .border_color(red())
+ .child(Label::new("Give Feedback").color(Color::Default))
+ .child(Label::new("This editor supports markdown").color(Color::Muted)),
+ ),
+ )
.child(
div()
.flex_1()
.bg(cx.theme().colors().editor_background)
.border()
.border_color(cx.theme().colors().border)
- .child("editor"),
+ .child(self.feedback_editor.clone()),
+ )
+ .child(
+ div().border().border_color(red()).child(
+ Label::new(format!(
+ "{} / {} Characters",
+ self.character_count,
+ FEEDBACK_CHAR_LIMIT.end()
+ ))
+ .color(Color::Default),
+ ),
+ )
+ .child( div()
+ .bg(cx.theme().colors().editor_background)
+ .border()
+ .border_color(cx.theme().colors().border)
+ .child(self.email_address_editor.clone())
)
.child(
h_stack()
- .justify_end()
+ .justify_between()
.gap_1()
- .child(
- Button::new("cancel_feedback", "Cancel")
- .style(ButtonStyle::Subtle)
- .color(Color::Muted)
- .on_click(dismiss),
+ .child(Button::new("community_repo", "Community Repo")
+ .style(ButtonStyle::Filled)
+ .color(Color::Muted)
+ // .on_click(cx.dispatch_action(Box::new(OpenZedCommunityRepo)))
+ )
+ .child(h_stack().justify_between().gap_1()
+ .child(
+ Button::new("cancel_feedback", "Cancel")
+ .style(ButtonStyle::Subtle)
+ .color(Color::Muted)
+ .on_click(dismiss),
+ )
+ .child(
+ Button::new("send_feedback", "Send Feedback")
+ .color(Color::Accent)
+ .style(ButtonStyle::Filled)
+ .tooltip(|cx| {
+ Tooltip::with_meta(
+ "Submit feedback to the Zed team.",
+ None,
+ "Provide an email address if you want us to be able to reply.",
+ cx,
+ )
+ })
+ .when(character_count_error, |this| this.disabled(true)),
+ ),
)
- .child(
- Button::new("send_feedback", "Send Feedback")
- .color(Color::Accent)
- .style(ButtonStyle::Filled)
- .tooltip(|cx| {
- Tooltip::with_meta(
- "Submit feedback to the Zed team.",
- None,
- "Provide an email address if you want us to be able to reply.",
- cx,
- )
- }),
- ),
- )
- // Header
- // - has some info, maybe some links
- // Body
- // - Markdown Editor
- // - Email address
- // Footer
- // - CTA buttons (Send, Cancel)
-
- // div()
- // .elevation_2(cx)
- // .key_context(
- // "FeedbackModal
- // ",
- // )
- // .on_action(cx.listener(Self::cancel))
- // .on_action(cx.listener(Self::confirm))
- // .w_96()
- // .child(
- // v_stack()
- // .px_1()
- // .pt_0p5()
- // .gap_px()
- // .child(
- // v_stack()
- // .py_0p5()
- // .px_1()
- // .child(div().px_1().py_0p5().child(self.line_editor.clone())),
- // )
- // .child(
- // div()
- // .h_px()
- // .w_full()
- // .bg(cx.theme().colors().element_background),
- // )
- // .child(
- // h_stack()
- // .justify_between()
- // .px_2()
- // .py_1()
- // .child(Label::new(self.current_text.clone()).color(Color::Muted)),
- // ),
- // )
+ )
}
}
@@ -10,9 +10,7 @@ pub use assets::*;
use breadcrumbs::Breadcrumbs;
use collections::VecDeque;
use editor::{Editor, MultiBuffer};
-use feedback::{
- feedback_info_text::FeedbackInfoText, submit_feedback_button::SubmitFeedbackButton,
-};
+use feedback::submit_feedback_button::SubmitFeedbackButton;
use gpui::{
actions, point, px, AppContext, Context, FocusableView, PromptLevel, TitlebarOptions,
ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
@@ -115,10 +113,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
// toolbar.add_item(project_search_bar, cx);
let submit_feedback_button =
cx.build_view(|_| SubmitFeedbackButton::new());
- // todo!(tool bar does not display or fire correctly right now, this is only stubbed in)
toolbar.add_item(submit_feedback_button, cx);
- let feedback_info_text = cx.build_view(|_| FeedbackInfoText::new());
- toolbar.add_item(feedback_info_text, cx);
// let lsp_log_item =
// cx.add_view(|_| language_tools::LspLogToolbarItemView::new());
// toolbar.add_item(lsp_log_item, cx);