Add code for submission and error states

Joseph T. Lyons created

Change summary

crates/feedback2/src/feedback_modal.rs | 97 +++++++++++++++++++--------
1 file changed, 68 insertions(+), 29 deletions(-)

Detailed changes

crates/feedback2/src/feedback_modal.rs 🔗

@@ -1,6 +1,6 @@
 use std::{ops::RangeInclusive, sync::Arc};
 
-use anyhow::bail;
+use anyhow::{anyhow, bail};
 use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
 use db::kvp::KEY_VALUE_STORE;
 use editor::{Editor, EditorEvent};
@@ -20,6 +20,16 @@ use workspace::{ModalView, Workspace};
 
 use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedCommunityRepo};
 
+// For UI testing purposes
+const SEND_SUCCESS_IN_DEV_MODE: bool = true;
+
+// Temporary, until tests are in place
+#[cfg(debug_assertions)]
+const DEV_MODE: bool = true;
+
+#[cfg(not(debug_assertions))]
+const DEV_MODE: bool = false;
+
 const DATABASE_KEY_NAME: &str = "email_address";
 const EMAIL_REGEX: &str = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b";
 const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
@@ -41,8 +51,9 @@ pub struct FeedbackModal {
     system_specs: SystemSpecs,
     feedback_editor: View<Editor>,
     email_address_editor: View<Editor>,
+    awaiting_submission: bool,
+    user_submitted: bool,
     character_count: usize,
-    pending_submission: bool,
 }
 
 impl FocusableView for FeedbackModal {
@@ -54,6 +65,11 @@ impl EventEmitter<DismissEvent> for FeedbackModal {}
 
 impl ModalView for FeedbackModal {
     fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> {
+        if self.user_submitted {
+            self.set_user_submitted(false, cx);
+            return cx.spawn(|_, _| async { true });
+        }
+
         let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some();
 
         if !has_feedback {
@@ -150,7 +166,8 @@ impl FeedbackModal {
             system_specs: system_specs.clone(),
             feedback_editor,
             email_address_editor,
-            pending_submission: false,
+            awaiting_submission: false,
+            user_submitted: false,
             character_count: 0,
         }
     }
@@ -182,37 +199,53 @@ impl FeedbackModal {
                     }
                 };
 
-                this.update(&mut cx, |feedback_editor, cx| {
-                    feedback_editor.set_pending_submission(true, cx);
+                this.update(&mut cx, |this, cx| {
+                    this.set_awaiting_submission(true, cx);
                 })
                 .log_err();
 
-                if let Err(error) =
-                    FeedbackModal::submit_feedback(&feedback_text, email, client, specs).await
-                {
-                    log::error!("{}", error);
-                    this.update(&mut cx, |feedback_editor, cx| {
-                        let prompt = cx.prompt(
-                            PromptLevel::Critical,
-                            FEEDBACK_SUBMISSION_ERROR_TEXT,
-                            &["OK"],
-                        );
-                        cx.spawn(|_, _cx| async move {
-                            prompt.await.ok();
+                let res =
+                    FeedbackModal::submit_feedback(&feedback_text, email, client, specs).await;
+
+                match res {
+                    Ok(_) => {
+                        this.update(&mut cx, |this, cx| {
+                            this.set_user_submitted(true, cx);
+                            cx.emit(DismissEvent)
+                        })
+                        .ok();
+                    }
+                    Err(error) => {
+                        log::error!("{}", error);
+                        this.update(&mut cx, |this, cx| {
+                            let prompt = cx.prompt(
+                                PromptLevel::Critical,
+                                FEEDBACK_SUBMISSION_ERROR_TEXT,
+                                &["OK"],
+                            );
+                            cx.spawn(|_, _cx| async move {
+                                prompt.await.ok();
+                            })
+                            .detach();
+                            this.set_awaiting_submission(false, cx);
                         })
-                        .detach();
-                        feedback_editor.set_pending_submission(false, cx);
-                    })
-                    .log_err();
+                        .log_err();
+                    }
                 }
             }
         })
         .detach();
+
         Task::ready(Ok(()))
     }
 
-    fn set_pending_submission(&mut self, pending_submission: bool, cx: &mut ViewContext<Self>) {
-        self.pending_submission = pending_submission;
+    fn set_awaiting_submission(&mut self, awaiting_submission: bool, cx: &mut ViewContext<Self>) {
+        self.awaiting_submission = awaiting_submission;
+        cx.notify();
+    }
+
+    fn set_user_submitted(&mut self, user_submitted: bool, cx: &mut ViewContext<Self>) {
+        self.user_submitted = user_submitted;
         cx.notify();
     }
 
@@ -222,6 +255,14 @@ impl FeedbackModal {
         zed_client: Arc<Client>,
         system_specs: SystemSpecs,
     ) -> anyhow::Result<()> {
+        if DEV_MODE {
+            if SEND_SUCCESS_IN_DEV_MODE {
+                return Ok(());
+            } else {
+                return Err(anyhow!("Error submitting feedback"));
+            }
+        }
+
         let feedback_endpoint = format!("{}/api/feedback", *ZED_SERVER_URL);
         let telemetry = zed_client.telemetry();
         let metrics_id = telemetry.metrics_id();
@@ -269,9 +310,9 @@ impl Render for FeedbackModal {
         let valid_character_count = FEEDBACK_CHAR_LIMIT.contains(&self.character_count);
 
         let allow_submission =
-            valid_character_count && valid_email_address && !self.pending_submission;
+            valid_character_count && valid_email_address && !self.awaiting_submission;
 
-        let submit_button_text = if self.pending_submission {
+        let submit_button_text = if self.awaiting_submission {
             "Submitting..."
         } else {
             "Submit"
@@ -376,9 +417,8 @@ impl Render for FeedbackModal {
                                             .style(ButtonStyle::Filled)
                                             // TODO: Ensure that while submitting, "Sending..." is shown and disable the button
                                             // TODO: If submit errors: show popup with error, don't close modal, set text back to "Submit", and re-enable button
-                                            // TODO: If submit is successful, close the modal
                                             .on_click(cx.listener(|this, _, cx| {
-                                                let _ = this.submit(cx);
+                                                this.submit(cx).detach();
                                             }))
                                             .tooltip(move |cx| {
                                                 Tooltip::with_meta(
@@ -396,6 +436,5 @@ impl Render for FeedbackModal {
     }
 }
 
-// TODO: Add compilation flags to enable debug mode, where we can simulate sending feedback that both succeeds and fails, so we can test the UI
 // TODO: Maybe store email address whenever the modal is closed, versus just on submit, so users can remove it if they want without submitting
-// TODO: Fix bug of being asked twice to discard feedback when clicking cancel
+// TODO: Testing of various button states, dismissal prompts, etc.