diff --git a/src-tauri/bindings/analytics.ts b/src-tauri/bindings/analytics.ts deleted file mode 100644 index fd29157e..00000000 --- a/src-tauri/bindings/analytics.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type AnalyticsAction = "cancel" | "click" | "commit" | "create" | "delete" | "delete_many" | "duplicate" | "error" | "export" | "hide" | "import" | "launch" | "launch_first" | "launch_update" | "send" | "show" | "toggle" | "update" | "upsert"; - -export type AnalyticsResource = "app" | "appearance" | "button" | "checkbox" | "cookie_jar" | "dialog" | "environment" | "folder" | "grpc_connection" | "grpc_event" | "grpc_request" | "http_request" | "http_response" | "key_value" | "link" | "mutation" | "plugin" | "select" | "setting" | "sidebar" | "tab" | "theme" | "websocket_connection" | "websocket_event" | "websocket_request" | "workspace"; diff --git a/src-tauri/src/analytics.rs b/src-tauri/src/analytics.rs deleted file mode 100644 index 15348dee..00000000 --- a/src-tauri/src/analytics.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::fmt::Display; - -use log::{debug, info}; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; -use tauri::{Manager, Runtime, WebviewWindow}; - -use ts_rs::TS; -use yaak_models::queries::{ - generate_id, get_key_value_int, get_key_value_string, get_or_create_settings, - set_key_value_int, set_key_value_string, UpdateSource, -}; - -use crate::is_dev; - -const NAMESPACE: &str = "analytics"; -const NUM_LAUNCHES_KEY: &str = "num_launches"; - -// serializable -#[derive(Serialize, Deserialize, Debug, TS)] -#[serde(rename_all = "snake_case")] -#[ts(export, export_to = "analytics.ts")] -pub enum AnalyticsResource { - App, - Appearance, - Button, - Checkbox, - CookieJar, - Dialog, - Environment, - Folder, - GrpcConnection, - GrpcEvent, - GrpcRequest, - HttpRequest, - HttpResponse, - KeyValue, - Link, - Mutation, - Plugin, - Select, - Setting, - Sidebar, - Tab, - Theme, - WebsocketConnection, - WebsocketEvent, - WebsocketRequest, - Workspace, -} - -impl AnalyticsResource { - pub fn from_str(s: &str) -> serde_json::Result { - serde_json::from_str(format!("\"{s}\"").as_str()) - } -} - -impl Display for AnalyticsResource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(self).unwrap().replace("\"", "")) - } -} - -#[derive(Serialize, Deserialize, Debug, TS)] -#[serde(rename_all = "snake_case")] -#[ts(export, export_to = "analytics.ts")] -pub enum AnalyticsAction { - Cancel, - Click, - Commit, - Create, - Delete, - DeleteMany, - Duplicate, - Error, - Export, - Hide, - Import, - Launch, - LaunchFirst, - LaunchUpdate, - Send, - Show, - Toggle, - Update, - Upsert, -} - -impl AnalyticsAction { - pub fn from_str(s: &str) -> serde_json::Result { - serde_json::from_str(format!("\"{s}\"").as_str()) - } -} - -impl Display for AnalyticsAction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(self).unwrap().replace("\"", "")) - } -} - -#[derive(Default, Debug)] -pub struct LaunchEventInfo { - pub current_version: String, - pub previous_version: String, - pub launched_after_update: bool, - pub num_launches: i32, -} - -pub async fn track_launch_event(w: &WebviewWindow) -> LaunchEventInfo { - let last_tracked_version_key = "last_tracked_version"; - - let mut info = LaunchEventInfo::default(); - - info.num_launches = get_num_launches(w).await + 1; - info.previous_version = get_key_value_string(w, NAMESPACE, last_tracked_version_key, "").await; - info.current_version = w.package_info().version.to_string(); - - if info.previous_version.is_empty() { - track_event(w, AnalyticsResource::App, AnalyticsAction::LaunchFirst, None).await; - } else { - info.launched_after_update = info.current_version != info.previous_version; - if info.launched_after_update { - track_event( - w, - AnalyticsResource::App, - AnalyticsAction::LaunchUpdate, - Some(json!({ NUM_LAUNCHES_KEY: info.num_launches })), - ) - .await; - } - }; - - // Track a launch event in all cases - track_event( - w, - AnalyticsResource::App, - AnalyticsAction::Launch, - Some(json!({ NUM_LAUNCHES_KEY: info.num_launches })), - ) - .await; - - // Update key values - - set_key_value_string( - w, - NAMESPACE, - last_tracked_version_key, - info.current_version.as_str(), - &UpdateSource::Background, - ) - .await; - set_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, &UpdateSource::Background) - .await; - - info -} - -pub async fn track_event( - w: &WebviewWindow, - resource: AnalyticsResource, - action: AnalyticsAction, - attributes: Option, -) { - let id = get_id(w).await; - let event = format!("{}.{}", resource, action); - let attributes_json = attributes.unwrap_or("{}".to_string().into()).to_string(); - let info = w.app_handle().package_info(); - let tz = datetime::sys_timezone().unwrap_or("unknown".to_string()); - let site = match is_dev() { - true => "site_TkHWjoXwZPq3HfhERb", - false => "site_zOK0d7jeBy2TLxFCnZ", - }; - let base_url = match is_dev() { - true => "http://localhost:7194", - false => "https://t.yaak.app", - }; - let params = vec![ - ("u", id), - ("e", event.clone()), - ("a", attributes_json.clone()), - ("id", site.to_string()), - ("v", info.version.clone().to_string()), - ("os", get_os().to_string()), - ("tz", tz), - ("xy", get_window_size(w)), - ]; - let req = - reqwest::Client::builder().build().unwrap().get(format!("{base_url}/t/e")).query(¶ms); - - let settings = get_or_create_settings(w).await; - if !settings.telemetry { - info!("Track event (disabled): {}", event); - return; - } - - // Disable analytics actual sending in dev - if is_dev() { - debug!("Track event: {} {}", event, attributes_json); - return; - } - - if let Err(e) = req.send().await { - info!("Error sending analytics event: {}", e); - } -} - -pub fn get_os() -> &'static str { - if cfg!(target_os = "windows") { - "windows" - } else if cfg!(target_os = "macos") { - "macos" - } else if cfg!(target_os = "linux") { - "linux" - } else { - "unknown" - } -} - -fn get_window_size(w: &WebviewWindow) -> String { - let current_monitor = match w.current_monitor() { - Ok(Some(m)) => m, - _ => return "unknown".to_string(), - }; - - let scale_factor = current_monitor.scale_factor(); - let size = current_monitor.size(); - let width: f64 = size.width as f64 / scale_factor; - let height: f64 = size.height as f64 / scale_factor; - - format!("{}x{}", (width / 100.0).round() * 100.0, (height / 100.0).round() * 100.0) -} - -async fn get_id(w: &WebviewWindow) -> String { - let id = get_key_value_string(w, "analytics", "id", "").await; - if id.is_empty() { - let new_id = generate_id(); - set_key_value_string(w, "analytics", "id", new_id.as_str(), &UpdateSource::Background) - .await; - new_id - } else { - id - } -} - -pub async fn get_num_launches(w: &WebviewWindow) -> i32 { - get_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, 0).await -} diff --git a/src-tauri/src/history.rs b/src-tauri/src/history.rs new file mode 100644 index 00000000..0e430bfa --- /dev/null +++ b/src-tauri/src/history.rs @@ -0,0 +1,64 @@ +use tauri::{Manager, Runtime, WebviewWindow}; + +use yaak_models::queries::{ + get_key_value_int, get_key_value_string, + set_key_value_int, set_key_value_string, UpdateSource, +}; + +const NAMESPACE: &str = "analytics"; +const NUM_LAUNCHES_KEY: &str = "num_launches"; + +#[derive(Default, Debug)] +pub struct LaunchEventInfo { + pub current_version: String, + pub previous_version: String, + pub launched_after_update: bool, + pub num_launches: i32, +} + +pub async fn store_launch_history(w: &WebviewWindow) -> LaunchEventInfo { + let last_tracked_version_key = "last_tracked_version"; + + let mut info = LaunchEventInfo::default(); + + info.num_launches = get_num_launches(w).await + 1; + info.previous_version = get_key_value_string(w, NAMESPACE, last_tracked_version_key, "").await; + info.current_version = w.package_info().version.to_string(); + + if info.previous_version.is_empty() { + } else { + info.launched_after_update = info.current_version != info.previous_version; + }; + + // Update key values + + set_key_value_string( + w, + NAMESPACE, + last_tracked_version_key, + info.current_version.as_str(), + &UpdateSource::Background, + ) + .await; + + set_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, &UpdateSource::Background) + .await; + + info +} + +pub fn get_os() -> &'static str { + if cfg!(target_os = "windows") { + "windows" + } else if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "linux") { + "linux" + } else { + "unknown" + } +} + +pub async fn get_num_launches(w: &WebviewWindow) -> i32 { + get_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, 0).await +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f276e375..dd07f706 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,13 +1,12 @@ extern crate core; #[cfg(target_os = "macos")] extern crate objc; -use crate::analytics::{AnalyticsAction, AnalyticsResource}; use crate::encoding::read_response_body; use crate::grpc::metadata_to_map; use crate::http_request::send_http_request; use crate::notifications::YaakNotifier; use crate::render::{render_grpc_request, render_template}; -use crate::updates::{UpdateMode, YaakUpdater}; +use crate::updates::{UpdateMode, UpdateTrigger, YaakUpdater}; use eventsource_client::{EventParser, SSE}; use log::{debug, error, warn}; use rand::random; @@ -30,8 +29,30 @@ use tokio::sync::Mutex; use tokio::task::block_in_place; use yaak_grpc::manager::{DynamicMessage, GrpcHandle}; use yaak_grpc::{deserialize_message, serialize_message, Code, ServiceDefinition}; -use yaak_models::models::{CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType, GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, KeyValue, ModelType, Plugin, Settings, WebsocketRequest, Workspace, WorkspaceMeta}; -use yaak_models::queries::{batch_upsert, cancel_pending_grpc_connections, cancel_pending_responses, create_default_http_response, delete_all_grpc_connections, delete_all_grpc_connections_for_workspace, delete_all_http_responses_for_request, delete_all_http_responses_for_workspace, delete_all_websocket_connections_for_workspace, delete_cookie_jar, delete_environment, delete_folder, delete_grpc_connection, delete_grpc_request, delete_http_request, delete_http_response, delete_plugin, delete_workspace, duplicate_folder, duplicate_grpc_request, duplicate_http_request, ensure_base_environment, generate_model_id, get_base_environment, get_cookie_jar, get_environment, get_folder, get_grpc_connection, get_grpc_request, get_http_request, get_http_response, get_key_value_raw, get_or_create_settings, get_or_create_workspace_meta, get_plugin, get_workspace, get_workspace_export_resources, list_cookie_jars, list_environments, list_folders, list_grpc_connections_for_workspace, list_grpc_events, list_grpc_requests, list_http_requests, list_http_responses_for_workspace, list_key_values_raw, list_plugins, list_workspaces, set_key_value_raw, update_response_if_id, update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection, upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace, upsert_workspace_meta, BatchUpsertResult, UpdateSource}; +use yaak_models::models::{ + CookieJar, Environment, EnvironmentVariable, Folder, GrpcConnection, GrpcConnectionState, + GrpcEvent, GrpcEventType, GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, KeyValue, + ModelType, Plugin, Settings, WebsocketRequest, Workspace, WorkspaceMeta, +}; +use yaak_models::queries::{ + batch_upsert, cancel_pending_grpc_connections, cancel_pending_responses, + create_default_http_response, delete_all_grpc_connections, + delete_all_grpc_connections_for_workspace, delete_all_http_responses_for_request, + delete_all_http_responses_for_workspace, delete_all_websocket_connections_for_workspace, + delete_cookie_jar, delete_environment, delete_folder, delete_grpc_connection, + delete_grpc_request, delete_http_request, delete_http_response, delete_plugin, + delete_workspace, duplicate_folder, duplicate_grpc_request, duplicate_http_request, + ensure_base_environment, generate_model_id, get_base_environment, get_cookie_jar, + get_environment, get_folder, get_grpc_connection, get_grpc_request, get_http_request, + get_http_response, get_key_value_raw, get_or_create_settings, get_or_create_workspace_meta, + get_plugin, get_workspace, get_workspace_export_resources, list_cookie_jars, list_environments, + list_folders, list_grpc_connections_for_workspace, list_grpc_events, list_grpc_requests, + list_http_requests, list_http_responses_for_workspace, list_key_values_raw, list_plugins, + list_workspaces, set_key_value_raw, update_response_if_id, update_settings, upsert_cookie_jar, + upsert_environment, upsert_folder, upsert_grpc_connection, upsert_grpc_event, + upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace, + upsert_workspace_meta, BatchUpsertResult, UpdateSource, +}; use yaak_plugins::events::{ BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse, GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse, @@ -44,9 +65,9 @@ use yaak_sse::sse::ServerSentEvent; use yaak_templates::format::format_json; use yaak_templates::{Parser, Tokens}; -mod analytics; mod encoding; mod grpc; +mod history; mod http_request; mod notifications; mod plugin_events; @@ -807,7 +828,7 @@ async fn cmd_import_data( .await .unwrap_or_else(|_| panic!("Unable to read file {}", file_path)); let file_contents = file.as_str(); - let (import_result, plugin_name) = + let import_result = plugin_manager.import_data(&window, file_contents).await.map_err(|e| e.to_string())?; let mut id_map: BTreeMap = BTreeMap::new(); @@ -923,14 +944,6 @@ async fn cmd_import_data( .await .map_err(|e| e.to_string())?; - analytics::track_event( - &window, - AnalyticsResource::App, - AnalyticsAction::Import, - Some(json!({ "plugin": plugin_name })), - ) - .await; - Ok(upserted) } @@ -1007,17 +1020,9 @@ async fn cmd_curl_to_request( plugin_manager: State<'_, PluginManager>, workspace_id: &str, ) -> Result { - let (import_result, plugin_name) = + let import_result = { plugin_manager.import_data(&window, command).await.map_err(|e| e.to_string())? }; - analytics::track_event( - &window, - AnalyticsResource::App, - AnalyticsAction::Import, - Some(json!({ "plugin": plugin_name })), - ) - .await; - import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map( |r| { let mut request = r.clone(); @@ -1051,8 +1056,6 @@ async fn cmd_export_data( f.sync_all().expect("Failed to sync"); - analytics::track_event(&window, AnalyticsResource::App, AnalyticsAction::Export, None).await; - Ok(()) } @@ -1131,28 +1134,6 @@ async fn response_err( response } -#[tauri::command] -async fn cmd_track_event( - window: WebviewWindow, - resource: &str, - action: &str, - attributes: Option, -) -> Result<(), String> { - match (AnalyticsResource::from_str(resource), AnalyticsAction::from_str(action)) { - (Ok(resource), Ok(action)) => { - analytics::track_event(&window, resource, action, attributes).await; - } - (r, a) => { - error!( - "Invalid action/resource for track_event: {resource}.{action} = {:?}.{:?}", - r, a - ); - return Err("Invalid analytics event".to_string()); - } - }; - Ok(()) -} - #[tauri::command] async fn cmd_set_update_mode(update_mode: &str, w: WebviewWindow) -> Result { cmd_set_key_value("app", "update_mode", update_mode, w).await.map_err(|e| e.to_string()) @@ -1739,7 +1720,12 @@ async fn cmd_check_for_updates( yaak_updater: State<'_, Mutex>, ) -> Result { let update_mode = get_update_mode(&app_handle).await; - yaak_updater.lock().await.force_check(&app_handle, update_mode).await.map_err(|e| e.to_string()) + yaak_updater + .lock() + .await + .check_now(&app_handle, update_mode, UpdateTrigger::User) + .await + .map_err(|e| e.to_string()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -1918,7 +1904,6 @@ pub fn run() { cmd_set_update_mode, cmd_template_functions, cmd_template_tokens_to_string, - cmd_track_event, cmd_uninstall_plugin, cmd_update_cookie_jar, cmd_update_environment, @@ -1941,7 +1926,7 @@ pub fn run() { RunEvent::Ready => { let w = create_main_window(app_handle, "/"); tauri::async_runtime::spawn(async move { - let info = analytics::track_launch_event(&w).await; + let info = history::store_launch_history(&w).await; debug!("Launched Yaak {:?}", info); }); @@ -1961,7 +1946,7 @@ pub fn run() { tauri::async_runtime::spawn(async move { let val: State<'_, Mutex> = h.state(); let update_mode = get_update_mode(&h).await; - if let Err(e) = val.lock().await.check(&h, update_mode).await { + if let Err(e) = val.lock().await.maybe_check(&h, update_mode).await { warn!("Failed to check for updates {e:?}"); }; }); @@ -2070,7 +2055,7 @@ fn monitor_plugin_events(app_handle: &AppHandle) { // We might have recursive back-and-forth calls between app and plugin, so we don't // want to block here tauri::async_runtime::spawn(async move { - crate::plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await; + plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await; }); } plugin_manager.unsubscribe(rx_id.as_str()).await; diff --git a/src-tauri/src/notifications.rs b/src-tauri/src/notifications.rs index d90fb8e4..f6096943 100644 --- a/src-tauri/src/notifications.rs +++ b/src-tauri/src/notifications.rs @@ -1,6 +1,6 @@ use std::time::SystemTime; -use crate::analytics::{get_num_launches, get_os}; +use crate::history::{get_num_launches, get_os}; use chrono::{DateTime, Duration, Utc}; use log::debug; use reqwest::Method; diff --git a/src-tauri/src/updates.rs b/src-tauri/src/updates.rs index 968b1479..8d108bae 100644 --- a/src-tauri/src/updates.rs +++ b/src-tauri/src/updates.rs @@ -6,6 +6,7 @@ use tauri::{AppHandle, Manager}; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons}; use tauri_plugin_updater::UpdaterExt; use tokio::task::block_in_place; +use yaak_models::queries::get_or_create_settings; use yaak_plugins::manager::PluginManager; use crate::is_dev; @@ -46,6 +47,11 @@ impl UpdateMode { } } +pub enum UpdateTrigger { + Background, + User, +} + impl YaakUpdater { pub fn new() -> Self { Self { @@ -53,11 +59,14 @@ impl YaakUpdater { } } - pub async fn force_check( + pub async fn check_now( &mut self, app_handle: &AppHandle, mode: UpdateMode, + update_trigger: UpdateTrigger, ) -> Result { + let settings = get_or_create_settings(app_handle).await; + let update_key = format!("{:x}", md5::compute(settings.id)); self.last_update_check = SystemTime::now(); info!("Checking for updates mode={}", mode); @@ -79,6 +88,14 @@ impl YaakUpdater { }); }) .header("X-Update-Mode", mode.to_string())? + .header("X-Update-Key", update_key)? + .header( + "X-Update-Trigger", + match update_trigger { + UpdateTrigger::Background => "background", + UpdateTrigger::User => "user", + }, + )? .build()? .check() .await; @@ -129,7 +146,7 @@ impl YaakUpdater { Err(e) => Err(e), } } - pub async fn check( + pub async fn maybe_check( &mut self, app_handle: &AppHandle, mode: UpdateMode, @@ -150,6 +167,6 @@ impl YaakUpdater { return Ok(false); } - self.force_check(app_handle, mode).await + self.check_now(app_handle, mode, UpdateTrigger::Background).await } } diff --git a/src-tauri/yaak-git/bindings/gen_git.ts b/src-tauri/yaak-git/bindings/gen_git.ts index c410c6ad..9308c283 100644 --- a/src-tauri/yaak-git/bindings/gen_git.ts +++ b/src-tauri/yaak-git/bindings/gen_git.ts @@ -1,5 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { SyncModel } from "./gen_models"; +import type { SyncModel } from "./gen_models.js"; export type GitAuthor = { name: string | null, email: string | null, }; diff --git a/src-tauri/yaak-models/bindings/gen_models.ts b/src-tauri/yaak-models/bindings/gen_models.ts index bce3d18c..9883ee2a 100644 --- a/src-tauri/yaak-models/bindings/gen_models.ts +++ b/src-tauri/yaak-models/bindings/gen_models.ts @@ -54,7 +54,7 @@ export type ProxySetting = { "type": "enabled", http: string, https: string, aut export type ProxySettingAuth = { user: string, password: string, }; -export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, telemetry: boolean, theme: string, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, }; +export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, theme: string, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, }; export type SyncHistory = { model: "sync_history", id: string, workspaceId: string, createdAt: string, states: Array, checksum: string, relPath: string, syncDir: string, }; diff --git a/src-tauri/yaak-models/src/models.rs b/src-tauri/yaak-models/src/models.rs index 6320a455..da78f87d 100644 --- a/src-tauri/yaak-models/src/models.rs +++ b/src-tauri/yaak-models/src/models.rs @@ -87,7 +87,6 @@ pub struct Settings { pub interface_scale: f32, pub open_workspace_new_window: Option, pub proxy: Option, - pub telemetry: bool, pub theme: String, pub theme_dark: String, pub theme_light: String, @@ -112,7 +111,6 @@ pub enum SettingsIden { InterfaceScale, OpenWorkspaceNewWindow, Proxy, - Telemetry, Theme, ThemeDark, ThemeLight, @@ -138,7 +136,6 @@ impl<'s> TryFrom<&Row<'s>> for Settings { interface_scale: r.get("interface_scale")?, open_workspace_new_window: r.get("open_workspace_new_window")?, proxy: proxy.map(|p| -> ProxySetting { serde_json::from_str(p.as_str()).unwrap() }), - telemetry: r.get("telemetry")?, theme: r.get("theme")?, theme_dark: r.get("theme_dark")?, theme_light: r.get("theme_light")?, diff --git a/src-tauri/yaak-models/src/queries.rs b/src-tauri/yaak-models/src/queries.rs index 1420f951..5e3cb68e 100644 --- a/src-tauri/yaak-models/src/queries.rs +++ b/src-tauri/yaak-models/src/queries.rs @@ -1478,7 +1478,6 @@ pub async fn update_settings( (SettingsIden::EditorFontSize, settings.editor_font_size.into()), (SettingsIden::EditorKeymap, settings.editor_keymap.to_string().into()), (SettingsIden::EditorSoftWrap, settings.editor_soft_wrap.into()), - (SettingsIden::Telemetry, settings.telemetry.into()), (SettingsIden::OpenWorkspaceNewWindow, settings.open_workspace_new_window.into()), ( SettingsIden::Proxy, diff --git a/src-tauri/yaak-plugins/src/manager.rs b/src-tauri/yaak-plugins/src/manager.rs index 0419c000..e86439d2 100644 --- a/src-tauri/yaak-plugins/src/manager.rs +++ b/src-tauri/yaak-plugins/src/manager.rs @@ -559,7 +559,7 @@ impl PluginManager { Some(JsonPrimitive::Boolean(v)) => v.clone(), _ => false, }; - + // Auth is disabled, so don't do anything if disabled { info!("Not applying disabled auth {:?}", auth_name); @@ -623,7 +623,7 @@ impl PluginManager { &self, window: &WebviewWindow, content: &str, - ) -> Result<(ImportResponse, String)> { + ) -> Result { let reply_events = self .send_and_wait( &WindowContext::from_window(window), @@ -635,19 +635,13 @@ impl PluginManager { // TODO: Don't just return the first valid response let result = reply_events.into_iter().find_map(|e| match e.payload { - InternalEventPayload::ImportResponse(resp) => Some((resp, e.plugin_ref_id)), + InternalEventPayload::ImportResponse(resp) => Some(resp), _ => None, }); match result { None => Err(PluginErr("No importers found for file contents".to_string())), - Some((resp, ref_id)) => { - let plugin = self - .get_plugin_by_ref_id(ref_id.as_str()) - .await - .ok_or(PluginNotFoundErr(ref_id))?; - Ok((resp, plugin.info().await.name)) - } + Some(resp) => Ok(resp), } } diff --git a/src-web/commands/commands.tsx b/src-web/commands/commands.tsx index f10c02f2..d3d6c869 100644 --- a/src-web/commands/commands.tsx +++ b/src-web/commands/commands.tsx @@ -5,7 +5,6 @@ import { InlineCode } from '../components/core/InlineCode'; import { VStack } from '../components/core/Stacks'; import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { showConfirm } from '../lib/confirm'; import { resolvedModelNameWithFolders } from '../lib/resolvedModelName'; import { pluralizeCount } from '../lib/pluralize'; @@ -42,7 +41,6 @@ export const createFolder = createFastMutation< patch.sortPriority = patch.sortPriority || -Date.now(); return invokeCmd('cmd_update_folder', { folder: { workspaceId, ...patch } }); }, - onSettled: () => trackEvent('folder', 'create'), }); export const syncWorkspace = createFastMutation< diff --git a/src-web/commands/deleteWebsocketConnection.ts b/src-web/commands/deleteWebsocketConnection.ts index 59f002f6..0599c6b9 100644 --- a/src-web/commands/deleteWebsocketConnection.ts +++ b/src-web/commands/deleteWebsocketConnection.ts @@ -1,14 +1,10 @@ import type { WebsocketConnection } from '@yaakapp-internal/models'; import { deleteWebsocketConnection as cmdDeleteWebsocketConnection } from '@yaakapp-internal/ws'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; export const deleteWebsocketConnection = createFastMutation({ mutationKey: ['delete_websocket_connection'], mutationFn: async function (connection: WebsocketConnection) { return cmdDeleteWebsocketConnection(connection.id); }, - onSuccess: async () => { - trackEvent('websocket_connection', 'delete'); - }, }); diff --git a/src-web/commands/deleteWebsocketConnections.ts b/src-web/commands/deleteWebsocketConnections.ts index 2560be22..ce9c150e 100644 --- a/src-web/commands/deleteWebsocketConnections.ts +++ b/src-web/commands/deleteWebsocketConnections.ts @@ -1,14 +1,10 @@ import type { WebsocketRequest } from '@yaakapp-internal/models'; import { deleteWebsocketConnections as cmdDeleteWebsocketConnections } from '@yaakapp-internal/ws'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; export const deleteWebsocketConnections = createFastMutation({ mutationKey: ['delete_websocket_connections'], mutationFn: async function (request: WebsocketRequest) { return cmdDeleteWebsocketConnections(request.id); }, - onSuccess: async () => { - trackEvent('websocket_connection', 'delete_many'); - }, }); diff --git a/src-web/commands/deleteWebsocketRequest.tsx b/src-web/commands/deleteWebsocketRequest.tsx index 1b0e911b..8cce263f 100644 --- a/src-web/commands/deleteWebsocketRequest.tsx +++ b/src-web/commands/deleteWebsocketRequest.tsx @@ -2,7 +2,6 @@ import type { WebsocketRequest } from '@yaakapp-internal/models'; import { deleteWebsocketRequest as cmdDeleteWebsocketRequest } from '@yaakapp-internal/ws'; import { InlineCode } from '../components/core/InlineCode'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { showConfirmDelete } from '../lib/confirm'; import { resolvedModelName } from '../lib/resolvedModelName'; @@ -24,7 +23,4 @@ export const deleteWebsocketRequest = createFastMutation({ return cmdDeleteWebsocketRequest(request.id); }, - onSuccess: async () => { - trackEvent('websocket_request', 'delete'); - }, }); diff --git a/src-web/commands/duplicateWebsocketRequest.ts b/src-web/commands/duplicateWebsocketRequest.ts index d6a47e57..7f5ac999 100644 --- a/src-web/commands/duplicateWebsocketRequest.ts +++ b/src-web/commands/duplicateWebsocketRequest.ts @@ -1,7 +1,6 @@ import type { WebsocketRequest } from '@yaakapp-internal/models'; import { duplicateWebsocketRequest as cmdDuplicateWebsocketRequest } from '@yaakapp-internal/ws'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { router } from '../lib/router'; export const duplicateWebsocketRequest = createFastMutation({ @@ -10,7 +9,6 @@ export const duplicateWebsocketRequest = createFastMutation({ return cmdDuplicateWebsocketRequest(request.id); }, onSuccess: async (request) => { - trackEvent('websocket_request', 'duplicate'); await router.navigate({ to: '/workspaces/$workspaceId', params: { workspaceId: request.workspaceId }, diff --git a/src-web/commands/openSettings.ts b/src-web/commands/openSettings.ts index 5a47fb3c..34e882b3 100644 --- a/src-web/commands/openSettings.ts +++ b/src-web/commands/openSettings.ts @@ -1,7 +1,6 @@ import { SettingsTab } from '../components/Settings/SettingsTab'; import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { router } from '../lib/router'; import { invokeCmd } from '../lib/tauri'; @@ -11,7 +10,6 @@ export const openSettings = createFastMutation const workspaceId = getActiveWorkspaceId(); if (workspaceId == null) return; - trackEvent('dialog', 'show', { id: 'settings', tab: `${tab}` }); const location = router.buildLocation({ to: '/workspaces/$workspaceId/settings', params: { workspaceId }, diff --git a/src-web/commands/upsertWebsocketRequest.ts b/src-web/commands/upsertWebsocketRequest.ts index 65858f1c..93716939 100644 --- a/src-web/commands/upsertWebsocketRequest.ts +++ b/src-web/commands/upsertWebsocketRequest.ts @@ -2,7 +2,6 @@ import type { WebsocketRequest } from '@yaakapp-internal/models'; import { upsertWebsocketRequest as cmdUpsertWebsocketRequest } from '@yaakapp-internal/ws'; import { differenceInMilliseconds } from 'date-fns'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { router } from '../lib/router'; export const upsertWebsocketRequest = createFastMutation< @@ -16,12 +15,11 @@ export const upsertWebsocketRequest = createFastMutation< const isNew = differenceInMilliseconds(new Date(), request.createdAt + 'Z') < 100; if (isNew) { - trackEvent('websocket_request', 'create'); await router.navigate({ to: '/workspaces/$workspaceId', params: { workspaceId: request.workspaceId }, search: (prev) => ({ ...prev, request_id: request.id }), }); - } else trackEvent('websocket_request', 'update'); + } }, }); diff --git a/src-web/commands/upsertWorkspace.ts b/src-web/commands/upsertWorkspace.ts index 882475eb..25ae4025 100644 --- a/src-web/commands/upsertWorkspace.ts +++ b/src-web/commands/upsertWorkspace.ts @@ -1,7 +1,5 @@ import type { Workspace } from '@yaakapp-internal/models'; -import { differenceInMilliseconds } from 'date-fns'; import { createFastMutation } from '../hooks/useFastMutation'; -import { trackEvent } from '../lib/analytics'; import { invokeCmd } from '../lib/tauri'; export const upsertWorkspace = createFastMutation< @@ -11,10 +9,4 @@ export const upsertWorkspace = createFastMutation< >({ mutationKey: ['upsert_workspace'], mutationFn: (workspace) => invokeCmd('cmd_update_workspace', { workspace }), - onSuccess: async (workspace) => { - const isNew = differenceInMilliseconds(new Date(), workspace.createdAt + 'Z') < 100; - - if (isNew) trackEvent('workspace', 'create'); - else trackEvent('workspace', 'update'); - }, }); diff --git a/src-web/components/LicenseBadge.tsx b/src-web/components/LicenseBadge.tsx index 45ca9057..13566799 100644 --- a/src-web/components/LicenseBadge.tsx +++ b/src-web/components/LicenseBadge.tsx @@ -60,6 +60,7 @@ export function LicenseBadge() { size="2xs" variant="border" className="!rounded-full mx-1" + color={detail.color} onClick={async () => { if (check.data.type === 'trialing') { await setLicenseDetails((v) => ({ @@ -69,8 +70,6 @@ export function LicenseBadge() { } openSettings.mutate(SettingsTab.License); }} - color={detail.color} - event={{ id: 'license-badge', status: check.data.type }} > {detail.label} diff --git a/src-web/components/MarkdownEditor.tsx b/src-web/components/MarkdownEditor.tsx index 955487fe..0caa682f 100644 --- a/src-web/components/MarkdownEditor.tsx +++ b/src-web/components/MarkdownEditor.tsx @@ -66,18 +66,8 @@ export function MarkdownEditor({ onChange={setViewMode} value={viewMode} options={[ - { - event: { id: 'md_mode', mode: 'preview' }, - icon: 'eye', - label: 'Preview mode', - value: 'preview', - }, - { - event: { id: 'md_mode', mode: 'edit' }, - icon: 'pencil', - label: 'Edit mode', - value: 'edit', - }, + { icon: 'eye', label: 'Preview mode', value: 'preview' }, + { icon: 'pencil', label: 'Edit mode', value: 'edit' }, ]} /> diff --git a/src-web/components/Settings/SettingsAppearance.tsx b/src-web/components/Settings/SettingsAppearance.tsx index 54bc10ff..18ae5515 100644 --- a/src-web/components/Settings/SettingsAppearance.tsx +++ b/src-web/components/Settings/SettingsAppearance.tsx @@ -96,7 +96,6 @@ export function SettingsAppearance() { value={`${settings.interfaceFontSize}`} options={fontSizeOptions} onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })} - event="ui-font-size" /> updateSettings.mutate({ editorKeymap: v })} - event="editor-keymap" /> updateSettings.mutate({ editorSoftWrap })} - event="editor-wrap-lines" /> @@ -134,7 +130,6 @@ export function SettingsAppearance() { size="sm" value={settings.appearance} onChange={(appearance) => updateSettings.mutate({ appearance })} - event="appearance" options={[ { label: 'Automatic', value: 'system' }, { label: 'Light', value: 'light' }, @@ -152,7 +147,6 @@ export function SettingsAppearance() { className="flex-1" value={activeTheme.light.id} options={lightThemes} - event="theme.light" onChange={(themeLight) => updateSettings.mutate({ ...settings, themeLight })} /> )} @@ -166,7 +160,6 @@ export function SettingsAppearance() { size="sm" value={activeTheme.dark.id} options={darkThemes} - event="theme.dark" onChange={(themeDark) => updateSettings.mutate({ ...settings, themeDark })} /> )} diff --git a/src-web/components/Settings/SettingsGeneral.tsx b/src-web/components/Settings/SettingsGeneral.tsx index 296a3124..18a5e17f 100644 --- a/src-web/components/Settings/SettingsGeneral.tsx +++ b/src-web/components/Settings/SettingsGeneral.tsx @@ -38,7 +38,6 @@ export function SettingsGeneral() { size="sm" value={settings.updateChannel} onChange={(updateChannel) => updateSettings.mutate({ updateChannel })} - event="update-channel" options={[ { label: 'Stable (less frequent)', value: 'stable' }, { label: 'Beta (more frequent)', value: 'beta' }, @@ -59,7 +58,6 @@ export function SettingsGeneral() { labelPosition="left" labelClassName="w-[14rem]" size="sm" - event="workspace-switch-behavior" value={ settings.openWorkspaceNewWindow === true ? 'new' @@ -81,9 +79,9 @@ export function SettingsGeneral() { updateSettings.mutate({ telemetry })} /> @@ -115,7 +113,6 @@ export function SettingsGeneral() { upsertWorkspace.mutate({ ...workspace, settingValidateCertificates }) } @@ -124,7 +121,6 @@ export function SettingsGeneral() { upsertWorkspace.mutate({ ...workspace, diff --git a/src-web/components/Settings/SettingsLicense.tsx b/src-web/components/Settings/SettingsLicense.tsx index 9cb58c90..e88760ef 100644 --- a/src-web/components/Settings/SettingsLicense.tsx +++ b/src-web/components/Settings/SettingsLicense.tsx @@ -81,7 +81,6 @@ export function SettingsLicense() { color="secondary" size="sm" onClick={toggleActivateFormVisible} - event="license.another" > Activate Another License @@ -90,7 +89,6 @@ export function SettingsLicense() { size="sm" onClick={() => openUrl('https://yaak.app/dashboard')} rightSlot={} - event="license.support" > Direct Support @@ -101,7 +99,6 @@ export function SettingsLicense() { color="primary" size="sm" onClick={toggleActivateFormVisible} - event="license.activate" > Activate @@ -110,7 +107,6 @@ export function SettingsLicense() { size="sm" onClick={() => openUrl('https://yaak.app/pricing?ref=app.yaak.desktop')} rightSlot={} - event="license.purchase" > Purchase @@ -140,7 +136,6 @@ export function SettingsLicense() { color="primary" size="sm" isLoading={activate.isPending} - event="license.submit" > Submit diff --git a/src-web/components/Settings/SettingsPlugins.tsx b/src-web/components/Settings/SettingsPlugins.tsx index 10bf1236..9869bf1a 100644 --- a/src-web/components/Settings/SettingsPlugins.tsx +++ b/src-web/components/Settings/SettingsPlugins.tsx @@ -66,7 +66,6 @@ export function SettingsPlugins() { type="submit" color="primary" className="ml-auto" - event="plugin.add" > Add Plugin @@ -76,14 +75,12 @@ export function SettingsPlugins() { icon="refresh" title="Reload plugins" spin={refreshPlugins.isPending} - event="plugin.reload" onClick={() => refreshPlugins.mutate()} /> openUrl('https://feedback.yaak.app/help/articles/6911763-quick-start')} /> @@ -107,7 +104,6 @@ function PluginInfo({ plugin }: { plugin: Plugin }) { size="sm" icon="trash" title="Uninstall plugin" - event="plugin.delete" onClick={() => deletePlugin.mutate()} /> diff --git a/src-web/components/Settings/SettingsProxy.tsx b/src-web/components/Settings/SettingsProxy.tsx index 7b5b58f0..8409c03d 100644 --- a/src-web/components/Settings/SettingsProxy.tsx +++ b/src-web/components/Settings/SettingsProxy.tsx @@ -19,7 +19,6 @@ export function SettingsProxy() { hideLabel size="sm" value={settings.proxy?.type ?? 'automatic'} - event="proxy" onChange={(v) => { if (v === 'automatic') { updateSettings.mutate({ proxy: undefined }); diff --git a/src-web/components/WebsocketRequestPane.tsx b/src-web/components/WebsocketRequestPane.tsx index 7b9c5e86..58acdd63 100644 --- a/src-web/components/WebsocketRequestPane.tsx +++ b/src-web/components/WebsocketRequestPane.tsx @@ -18,7 +18,6 @@ import { requestsAtom } from '../hooks/useRequests'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; import { useLatestWebsocketConnection } from '../hooks/useWebsocketConnections'; -import { trackEvent } from '../lib/analytics'; import { deepEqualAtom } from '../lib/atoms'; import { languageFromContentType } from '../lib/contentType'; import { generateId } from '../lib/generateId'; @@ -190,7 +189,6 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque environmentId: getActiveEnvironment()?.id ?? null, cookieJarId: getActiveCookieJar()?.id ?? null, }); - trackEvent('websocket_request', 'send'); }, [activeRequest.id]); const handleSend = useCallback(async () => { @@ -199,13 +197,11 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque connectionId: connection?.id, environmentId: getActiveEnvironment()?.id ?? null, }); - trackEvent('websocket_connection', 'send'); }, [connection]); const handleCancel = useCallback(async () => { if (connection == null) return; await closeWebsocket({ connectionId: connection?.id }); - trackEvent('websocket_connection', 'cancel'); }, [connection]); const handleUrlChange = useCallback( diff --git a/src-web/components/WorkspaceHeader.tsx b/src-web/components/WorkspaceHeader.tsx index b2b85697..39669424 100644 --- a/src-web/components/WorkspaceHeader.tsx +++ b/src-web/components/WorkspaceHeader.tsx @@ -46,7 +46,6 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop icon="search" title="Search or execute a command" size="sm" - event="search" iconColor="secondary" onClick={togglePalette} /> diff --git a/src-web/components/core/Button.tsx b/src-web/components/core/Button.tsx index fb6fd86b..c229e87a 100644 --- a/src-web/components/core/Button.tsx +++ b/src-web/components/core/Button.tsx @@ -4,7 +4,6 @@ import type { HTMLAttributes, ReactNode } from 'react'; import { forwardRef, useImperativeHandle, useRef } from 'react'; import type { HotkeyAction } from '../../hooks/useHotKey'; import { useFormattedHotkey, useHotKey } from '../../hooks/useHotKey'; -import { trackEvent } from '../../lib/analytics'; import { Icon } from './Icon'; import { LoadingIcon } from './LoadingIcon'; @@ -22,7 +21,6 @@ export type ButtonProps = Omit, 'color' | 'onC leftSlot?: ReactNode; rightSlot?: ReactNode; hotkeyAction?: HotkeyAction; - event?: string | { id: string; [attr: string]: number | string }; }; export const Button = forwardRef(function Button( @@ -43,7 +41,6 @@ export const Button = forwardRef(function Button hotkeyAction, title, onClick, - event, ...props }: ButtonProps, ref, @@ -107,12 +104,7 @@ export const Button = forwardRef(function Button type={type} className={classes} disabled={disabled} - onClick={(e) => { - onClick?.(e); - if (event != null) { - trackEvent('button', 'click', typeof event === 'string' ? { id: event } : event); - } - }} + onClick={onClick} onDoubleClick={(e) => { // Kind of a hack? This prevents double-clicks from going through buttons. For example, when // double-clicking the workspace header to toggle window maximization diff --git a/src-web/components/core/Checkbox.tsx b/src-web/components/core/Checkbox.tsx index 48769936..458b4a42 100644 --- a/src-web/components/core/Checkbox.tsx +++ b/src-web/components/core/Checkbox.tsx @@ -1,6 +1,5 @@ import classNames from 'classnames'; import { type ReactNode } from 'react'; -import { trackEvent } from '../../lib/analytics'; import { Icon } from './Icon'; import { HStack } from './Stacks'; @@ -13,7 +12,6 @@ export interface CheckboxProps { inputWrapperClassName?: string; hideLabel?: boolean; fullWidth?: boolean; - event?: string; } export function Checkbox({ @@ -25,7 +23,6 @@ export function Checkbox({ title, hideLabel, fullWidth, - event, }: CheckboxProps) { return ( @@ -42,9 +39,6 @@ export function Checkbox({ disabled={disabled} onChange={() => { onChange(checked === 'indeterminate' ? true : !checked); - if (event != null) { - trackEvent('button', 'click', { id: event, checked: checked ? 'on' : 'off' }); - } }} />
diff --git a/src-web/components/core/Link.tsx b/src-web/components/core/Link.tsx index 6c3c03c2..2e0988ce 100644 --- a/src-web/components/core/Link.tsx +++ b/src-web/components/core/Link.tsx @@ -1,15 +1,13 @@ +import { Link as RouterLink } from '@tanstack/react-router'; import classNames from 'classnames'; import type { HTMLAttributes } from 'react'; -import { Link as RouterLink } from '@tanstack/react-router'; -import { trackEvent } from '../../lib/analytics'; import { Icon } from './Icon'; interface Props extends HTMLAttributes { href: string; - event?: string; } -export function Link({ href, children, className, event, ...other }: Props) { +export function Link({ href, children, className, ...other }: Props) { const isExternal = href.match(/^https?:\/\//); className = classNames(className, 'relative underline hover:text-violet-600'); @@ -23,9 +21,6 @@ export function Link({ href, children, className, event, ...other }: Props) { className={classNames(className, 'pr-4 inline-flex items-center')} onClick={(e) => { e.preventDefault(); - if (event != null) { - trackEvent('link', 'click', { id: event }); - } }} {...other} > diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index c8a2b675..fc960b92 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -261,7 +261,6 @@ export const PairEditor = forwardRef(function Pa variant="border" className="m-2" size="xs" - event="pairs.reveal-more" > Show {pairs.length - MAX_INITIAL_PAIRS} More diff --git a/src-web/components/core/SegmentedControl.tsx b/src-web/components/core/SegmentedControl.tsx index 6716026e..af73b2b9 100644 --- a/src-web/components/core/SegmentedControl.tsx +++ b/src-web/components/core/SegmentedControl.tsx @@ -2,18 +2,17 @@ import classNames from 'classnames'; import { useRef } from 'react'; import { useStateWithDeps } from '../../hooks/useStateWithDeps'; import type { IconProps } from './Icon'; -import type { IconButtonProps } from './IconButton'; import { IconButton } from './IconButton'; import { HStack } from './Stacks'; interface Props { - options: { value: T; label: string; icon: IconProps['icon']; event?: IconButtonProps['event'] }[]; + options: { value: T; label: string; icon: IconProps['icon'] }[]; onChange: (value: T) => void; value: T; name: string; } -export function SegmentedControl({ value, onChange, options, name }: Props) { +export function SegmentedControl({ value, onChange, options }: Props) { const [selectedValue, setSelectedValue] = useStateWithDeps(value, [value]); const containerRef = useRef(null); return ( @@ -45,14 +44,13 @@ export function SegmentedControl({ value, onChange, options, n { onChange: (value: T) => void; size?: ButtonProps['size']; className?: string; - event?: string; disabled?: boolean; } @@ -38,7 +36,6 @@ export function Select({ leftSlot, onChange, className, - event, size = 'md', }: SelectProps) { const osInfo = useOsInfo(); @@ -48,9 +45,6 @@ export function Select({ const handleChange = (value: T) => { onChange?.(value); - if (event != null) { - trackEvent('select', 'click', { id: event, value }); - } }; return ( diff --git a/src-web/components/core/Tabs/Tabs.tsx b/src-web/components/core/Tabs/Tabs.tsx index 1765a9df..2f90c4fe 100644 --- a/src-web/components/core/Tabs/Tabs.tsx +++ b/src-web/components/core/Tabs/Tabs.tsx @@ -1,7 +1,6 @@ import classNames from 'classnames'; import type { ReactNode } from 'react'; import { memo, useEffect, useRef } from 'react'; -import { trackEvent } from '../../../lib/analytics'; import { Icon } from '../Icon'; import type { RadioDropdownProps } from '../RadioDropdown'; import { RadioDropdown } from '../RadioDropdown'; @@ -104,14 +103,7 @@ export function Tabs({ onChange={t.options.onChange} >