From 9abf5a1bf272d697aac6a90fd3a2c4145ac35329 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Fri, 8 Dec 2023 18:49:49 -0500 Subject: [PATCH] Add code for submission and error states --- crates/feedback2/src/feedback_modal.rs | 97 ++++++++++++++++++-------- 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/crates/feedback2/src/feedback_modal.rs b/crates/feedback2/src/feedback_modal.rs index 1e219db7be0ef58a791a3e29b51be39776412636..a4ada18f783228c7fbb5a069f2f5858ab817943c 100644 --- a/crates/feedback2/src/feedback_modal.rs +++ b/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 = 10..=5000; @@ -41,8 +51,9 @@ pub struct FeedbackModal { system_specs: SystemSpecs, feedback_editor: View, email_address_editor: View, + awaiting_submission: bool, + user_submitted: bool, character_count: usize, - pending_submission: bool, } impl FocusableView for FeedbackModal { @@ -54,6 +65,11 @@ impl EventEmitter for FeedbackModal {} impl ModalView for FeedbackModal { fn dismiss(&mut self, cx: &mut ViewContext) -> Task { + 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.pending_submission = pending_submission; + fn set_awaiting_submission(&mut self, awaiting_submission: bool, cx: &mut ViewContext) { + self.awaiting_submission = awaiting_submission; + cx.notify(); + } + + fn set_user_submitted(&mut self, user_submitted: bool, cx: &mut ViewContext) { + self.user_submitted = user_submitted; cx.notify(); } @@ -222,6 +255,14 @@ impl FeedbackModal { zed_client: Arc, 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.