Add in-app micro-feedback prompts for new features

Show a one-time toast asking how a feature is working after its third
successful use, with an optional comment sent anonymously to the Yaak
API (feature key, text, app version, and OS only — nothing identifying,
and nothing is sent unless the user clicks Send).

- New cmd_send_feedback Tauri command posts fire-and-forget via the
  shared API client (localhost in dev)
- Feature keys registry (cookie-editor, response-history, sse-summary,
  git-sync) with per-feature use counting in the key-value store
- "Never ask for feedback" setting to disable prompts entirely
- Toast gains dynamicHeight and hideDismiss props for richer content
- Fix missing vertical padding on xs/2xs multiline inputs
- Fix unused-import and dead-code warnings in yaak-system-appearance
  on non-Linux builds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-07-04 15:06:21 -07:00
parent e52853cc2d
commit b1f1363502
18 changed files with 283 additions and 13 deletions
@@ -0,0 +1,47 @@
use log::debug;
use serde::Serialize;
use tauri::{AppHandle, Runtime, is_dev};
use yaak_api::{ApiClientKind, yaak_api_client};
use yaak_common::platform::get_os_str;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct FeedbackPayload {
feature: String,
text: String,
app_version: String,
os: String,
}
/// Send explicit user feedback for a feature. Fire-and-forget: errors are
/// logged and swallowed so a failed send never surfaces to the user.
pub async fn send_feedback<R: Runtime>(app_handle: &AppHandle<R>, feature: String, text: String) {
let app_version = app_handle.package_info().version.to_string();
let payload = FeedbackPayload {
feature,
text,
app_version: app_version.clone(),
os: get_os_str().to_string(),
};
let client = match yaak_api_client(ApiClientKind::App, &app_version) {
Ok(c) => c,
Err(e) => {
debug!("Failed to build feedback client: {e:?}");
return;
}
};
match client.post(build_url("/app-feedback")).json(&payload).send().await {
Ok(resp) => debug!("Sent feedback with status {}", resp.status()),
Err(e) => debug!("Failed to send feedback: {e:?}"),
}
}
fn build_url(path: &str) -> String {
if is_dev() {
format!("http://localhost:9444/api/v1{path}")
} else {
format!("https://api.yaak.app/api/v1{path}")
}
}
+12
View File
@@ -65,6 +65,7 @@ use yaak_tls::find_client_certificate;
mod commands;
mod encoding;
mod error;
mod feedback;
mod git_ext;
mod git_watcher;
mod grpc;
@@ -292,6 +293,16 @@ async fn cmd_render_template<R: Runtime>(
Ok(result)
}
#[tauri::command]
async fn cmd_send_feedback<R: Runtime>(
app_handle: AppHandle<R>,
feature: String,
text: String,
) -> YaakResult<()> {
feedback::send_feedback(&app_handle, feature, text).await;
Ok(())
}
#[tauri::command]
async fn cmd_dismiss_notification<R: Runtime>(
window: WebviewWindow<R>,
@@ -1819,6 +1830,7 @@ pub fn run() {
cmd_delete_send_history,
cmd_dismiss_notification,
cmd_export_data,
cmd_send_feedback,
cmd_http_request_body,
cmd_http_response_body,
cmd_format_json,