mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-23 09:08:32 +02:00
Remove analytics and add more update headers
This commit is contained in:
@@ -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";
|
|
||||||
@@ -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<AnalyticsResource> {
|
|
||||||
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<AnalyticsAction> {
|
|
||||||
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<R: Runtime>(w: &WebviewWindow<R>) -> 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<R: Runtime>(
|
|
||||||
w: &WebviewWindow<R>,
|
|
||||||
resource: AnalyticsResource,
|
|
||||||
action: AnalyticsAction,
|
|
||||||
attributes: Option<Value>,
|
|
||||||
) {
|
|
||||||
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<R: Runtime>(w: &WebviewWindow<R>) -> 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<R: Runtime>(w: &WebviewWindow<R>) -> 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<R: Runtime>(w: &WebviewWindow<R>) -> i32 {
|
|
||||||
get_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, 0).await
|
|
||||||
}
|
|
||||||
64
src-tauri/src/history.rs
Normal file
64
src-tauri/src/history.rs
Normal file
@@ -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<R: Runtime>(w: &WebviewWindow<R>) -> 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<R: Runtime>(w: &WebviewWindow<R>) -> i32 {
|
||||||
|
get_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, 0).await
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
extern crate core;
|
extern crate core;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
use crate::analytics::{AnalyticsAction, AnalyticsResource};
|
|
||||||
use crate::encoding::read_response_body;
|
use crate::encoding::read_response_body;
|
||||||
use crate::grpc::metadata_to_map;
|
use crate::grpc::metadata_to_map;
|
||||||
use crate::http_request::send_http_request;
|
use crate::http_request::send_http_request;
|
||||||
use crate::notifications::YaakNotifier;
|
use crate::notifications::YaakNotifier;
|
||||||
use crate::render::{render_grpc_request, render_template};
|
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 eventsource_client::{EventParser, SSE};
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
@@ -30,8 +29,30 @@ use tokio::sync::Mutex;
|
|||||||
use tokio::task::block_in_place;
|
use tokio::task::block_in_place;
|
||||||
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
|
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
|
||||||
use yaak_grpc::{deserialize_message, serialize_message, Code, ServiceDefinition};
|
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::models::{
|
||||||
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};
|
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::{
|
use yaak_plugins::events::{
|
||||||
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
|
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
|
||||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||||
@@ -44,9 +65,9 @@ use yaak_sse::sse::ServerSentEvent;
|
|||||||
use yaak_templates::format::format_json;
|
use yaak_templates::format::format_json;
|
||||||
use yaak_templates::{Parser, Tokens};
|
use yaak_templates::{Parser, Tokens};
|
||||||
|
|
||||||
mod analytics;
|
|
||||||
mod encoding;
|
mod encoding;
|
||||||
mod grpc;
|
mod grpc;
|
||||||
|
mod history;
|
||||||
mod http_request;
|
mod http_request;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
mod plugin_events;
|
mod plugin_events;
|
||||||
@@ -807,7 +828,7 @@ async fn cmd_import_data<R: Runtime>(
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
.unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
||||||
let file_contents = file.as_str();
|
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())?;
|
plugin_manager.import_data(&window, file_contents).await.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
||||||
@@ -923,14 +944,6 @@ async fn cmd_import_data<R: Runtime>(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
analytics::track_event(
|
|
||||||
&window,
|
|
||||||
AnalyticsResource::App,
|
|
||||||
AnalyticsAction::Import,
|
|
||||||
Some(json!({ "plugin": plugin_name })),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(upserted)
|
Ok(upserted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,17 +1020,9 @@ async fn cmd_curl_to_request<R: Runtime>(
|
|||||||
plugin_manager: State<'_, PluginManager>,
|
plugin_manager: State<'_, PluginManager>,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> Result<HttpRequest, String> {
|
) -> Result<HttpRequest, String> {
|
||||||
let (import_result, plugin_name) =
|
let import_result =
|
||||||
{ plugin_manager.import_data(&window, command).await.map_err(|e| e.to_string())? };
|
{ 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(
|
import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map(
|
||||||
|r| {
|
|r| {
|
||||||
let mut request = r.clone();
|
let mut request = r.clone();
|
||||||
@@ -1051,8 +1056,6 @@ async fn cmd_export_data(
|
|||||||
|
|
||||||
f.sync_all().expect("Failed to sync");
|
f.sync_all().expect("Failed to sync");
|
||||||
|
|
||||||
analytics::track_event(&window, AnalyticsResource::App, AnalyticsAction::Export, None).await;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,28 +1134,6 @@ async fn response_err<R: Runtime>(
|
|||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn cmd_track_event(
|
|
||||||
window: WebviewWindow,
|
|
||||||
resource: &str,
|
|
||||||
action: &str,
|
|
||||||
attributes: Option<Value>,
|
|
||||||
) -> 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]
|
#[tauri::command]
|
||||||
async fn cmd_set_update_mode(update_mode: &str, w: WebviewWindow) -> Result<KeyValue, String> {
|
async fn cmd_set_update_mode(update_mode: &str, w: WebviewWindow) -> Result<KeyValue, String> {
|
||||||
cmd_set_key_value("app", "update_mode", update_mode, w).await.map_err(|e| e.to_string())
|
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<YaakUpdater>>,
|
yaak_updater: State<'_, Mutex<YaakUpdater>>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let update_mode = get_update_mode(&app_handle).await;
|
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)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
@@ -1918,7 +1904,6 @@ pub fn run() {
|
|||||||
cmd_set_update_mode,
|
cmd_set_update_mode,
|
||||||
cmd_template_functions,
|
cmd_template_functions,
|
||||||
cmd_template_tokens_to_string,
|
cmd_template_tokens_to_string,
|
||||||
cmd_track_event,
|
|
||||||
cmd_uninstall_plugin,
|
cmd_uninstall_plugin,
|
||||||
cmd_update_cookie_jar,
|
cmd_update_cookie_jar,
|
||||||
cmd_update_environment,
|
cmd_update_environment,
|
||||||
@@ -1941,7 +1926,7 @@ pub fn run() {
|
|||||||
RunEvent::Ready => {
|
RunEvent::Ready => {
|
||||||
let w = create_main_window(app_handle, "/");
|
let w = create_main_window(app_handle, "/");
|
||||||
tauri::async_runtime::spawn(async move {
|
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);
|
debug!("Launched Yaak {:?}", info);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1961,7 +1946,7 @@ pub fn run() {
|
|||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
||||||
let update_mode = get_update_mode(&h).await;
|
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:?}");
|
warn!("Failed to check for updates {e:?}");
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -2070,7 +2055,7 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
|||||||
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
||||||
// want to block here
|
// want to block here
|
||||||
tauri::async_runtime::spawn(async move {
|
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;
|
plugin_manager.unsubscribe(rx_id.as_str()).await;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::SystemTime;
|
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 chrono::{DateTime, Duration, Utc};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use tauri::{AppHandle, Manager};
|
|||||||
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
|
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
|
||||||
use tauri_plugin_updater::UpdaterExt;
|
use tauri_plugin_updater::UpdaterExt;
|
||||||
use tokio::task::block_in_place;
|
use tokio::task::block_in_place;
|
||||||
|
use yaak_models::queries::get_or_create_settings;
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
|
|
||||||
use crate::is_dev;
|
use crate::is_dev;
|
||||||
@@ -46,6 +47,11 @@ impl UpdateMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum UpdateTrigger {
|
||||||
|
Background,
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
impl YaakUpdater {
|
impl YaakUpdater {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -53,11 +59,14 @@ impl YaakUpdater {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn force_check(
|
pub async fn check_now(
|
||||||
&mut self,
|
&mut self,
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
mode: UpdateMode,
|
mode: UpdateMode,
|
||||||
|
update_trigger: UpdateTrigger,
|
||||||
) -> Result<bool, tauri_plugin_updater::Error> {
|
) -> Result<bool, tauri_plugin_updater::Error> {
|
||||||
|
let settings = get_or_create_settings(app_handle).await;
|
||||||
|
let update_key = format!("{:x}", md5::compute(settings.id));
|
||||||
self.last_update_check = SystemTime::now();
|
self.last_update_check = SystemTime::now();
|
||||||
|
|
||||||
info!("Checking for updates mode={}", mode);
|
info!("Checking for updates mode={}", mode);
|
||||||
@@ -79,6 +88,14 @@ impl YaakUpdater {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.header("X-Update-Mode", mode.to_string())?
|
.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()?
|
.build()?
|
||||||
.check()
|
.check()
|
||||||
.await;
|
.await;
|
||||||
@@ -129,7 +146,7 @@ impl YaakUpdater {
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn check(
|
pub async fn maybe_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
mode: UpdateMode,
|
mode: UpdateMode,
|
||||||
@@ -150,6 +167,6 @@ impl YaakUpdater {
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.force_check(app_handle, mode).await
|
self.check_now(app_handle, mode, UpdateTrigger::Background).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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, };
|
export type GitAuthor = { name: string | null, email: string | null, };
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export type ProxySetting = { "type": "enabled", http: string, https: string, aut
|
|||||||
|
|
||||||
export type ProxySettingAuth = { user: string, password: string, };
|
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<SyncState>, checksum: string, relPath: string, syncDir: string, };
|
export type SyncHistory = { model: "sync_history", id: string, workspaceId: string, createdAt: string, states: Array<SyncState>, checksum: string, relPath: string, syncDir: string, };
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ pub struct Settings {
|
|||||||
pub interface_scale: f32,
|
pub interface_scale: f32,
|
||||||
pub open_workspace_new_window: Option<bool>,
|
pub open_workspace_new_window: Option<bool>,
|
||||||
pub proxy: Option<ProxySetting>,
|
pub proxy: Option<ProxySetting>,
|
||||||
pub telemetry: bool,
|
|
||||||
pub theme: String,
|
pub theme: String,
|
||||||
pub theme_dark: String,
|
pub theme_dark: String,
|
||||||
pub theme_light: String,
|
pub theme_light: String,
|
||||||
@@ -112,7 +111,6 @@ pub enum SettingsIden {
|
|||||||
InterfaceScale,
|
InterfaceScale,
|
||||||
OpenWorkspaceNewWindow,
|
OpenWorkspaceNewWindow,
|
||||||
Proxy,
|
Proxy,
|
||||||
Telemetry,
|
|
||||||
Theme,
|
Theme,
|
||||||
ThemeDark,
|
ThemeDark,
|
||||||
ThemeLight,
|
ThemeLight,
|
||||||
@@ -138,7 +136,6 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
|||||||
interface_scale: r.get("interface_scale")?,
|
interface_scale: r.get("interface_scale")?,
|
||||||
open_workspace_new_window: r.get("open_workspace_new_window")?,
|
open_workspace_new_window: r.get("open_workspace_new_window")?,
|
||||||
proxy: proxy.map(|p| -> ProxySetting { serde_json::from_str(p.as_str()).unwrap() }),
|
proxy: proxy.map(|p| -> ProxySetting { serde_json::from_str(p.as_str()).unwrap() }),
|
||||||
telemetry: r.get("telemetry")?,
|
|
||||||
theme: r.get("theme")?,
|
theme: r.get("theme")?,
|
||||||
theme_dark: r.get("theme_dark")?,
|
theme_dark: r.get("theme_dark")?,
|
||||||
theme_light: r.get("theme_light")?,
|
theme_light: r.get("theme_light")?,
|
||||||
|
|||||||
@@ -1478,7 +1478,6 @@ pub async fn update_settings<R: Runtime>(
|
|||||||
(SettingsIden::EditorFontSize, settings.editor_font_size.into()),
|
(SettingsIden::EditorFontSize, settings.editor_font_size.into()),
|
||||||
(SettingsIden::EditorKeymap, settings.editor_keymap.to_string().into()),
|
(SettingsIden::EditorKeymap, settings.editor_keymap.to_string().into()),
|
||||||
(SettingsIden::EditorSoftWrap, settings.editor_soft_wrap.into()),
|
(SettingsIden::EditorSoftWrap, settings.editor_soft_wrap.into()),
|
||||||
(SettingsIden::Telemetry, settings.telemetry.into()),
|
|
||||||
(SettingsIden::OpenWorkspaceNewWindow, settings.open_workspace_new_window.into()),
|
(SettingsIden::OpenWorkspaceNewWindow, settings.open_workspace_new_window.into()),
|
||||||
(
|
(
|
||||||
SettingsIden::Proxy,
|
SettingsIden::Proxy,
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ impl PluginManager {
|
|||||||
Some(JsonPrimitive::Boolean(v)) => v.clone(),
|
Some(JsonPrimitive::Boolean(v)) => v.clone(),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Auth is disabled, so don't do anything
|
// Auth is disabled, so don't do anything
|
||||||
if disabled {
|
if disabled {
|
||||||
info!("Not applying disabled auth {:?}", auth_name);
|
info!("Not applying disabled auth {:?}", auth_name);
|
||||||
@@ -623,7 +623,7 @@ impl PluginManager {
|
|||||||
&self,
|
&self,
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> Result<(ImportResponse, String)> {
|
) -> Result<ImportResponse> {
|
||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(
|
.send_and_wait(
|
||||||
&WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
@@ -635,19 +635,13 @@ impl PluginManager {
|
|||||||
|
|
||||||
// TODO: Don't just return the first valid response
|
// TODO: Don't just return the first valid response
|
||||||
let result = reply_events.into_iter().find_map(|e| match e.payload {
|
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,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
None => Err(PluginErr("No importers found for file contents".to_string())),
|
None => Err(PluginErr("No importers found for file contents".to_string())),
|
||||||
Some((resp, ref_id)) => {
|
Some(resp) => Ok(resp),
|
||||||
let plugin = self
|
|
||||||
.get_plugin_by_ref_id(ref_id.as_str())
|
|
||||||
.await
|
|
||||||
.ok_or(PluginNotFoundErr(ref_id))?;
|
|
||||||
Ok((resp, plugin.info().await.name))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { InlineCode } from '../components/core/InlineCode';
|
|||||||
import { VStack } from '../components/core/Stacks';
|
import { VStack } from '../components/core/Stacks';
|
||||||
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirm } from '../lib/confirm';
|
import { showConfirm } from '../lib/confirm';
|
||||||
import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
|
import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
|
||||||
import { pluralizeCount } from '../lib/pluralize';
|
import { pluralizeCount } from '../lib/pluralize';
|
||||||
@@ -42,7 +41,6 @@ export const createFolder = createFastMutation<
|
|||||||
patch.sortPriority = patch.sortPriority || -Date.now();
|
patch.sortPriority = patch.sortPriority || -Date.now();
|
||||||
return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } });
|
return invokeCmd<Folder>('cmd_update_folder', { folder: { workspaceId, ...patch } });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('folder', 'create'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const syncWorkspace = createFastMutation<
|
export const syncWorkspace = createFastMutation<
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import type { WebsocketConnection } from '@yaakapp-internal/models';
|
import type { WebsocketConnection } from '@yaakapp-internal/models';
|
||||||
import { deleteWebsocketConnection as cmdDeleteWebsocketConnection } from '@yaakapp-internal/ws';
|
import { deleteWebsocketConnection as cmdDeleteWebsocketConnection } from '@yaakapp-internal/ws';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
|
|
||||||
export const deleteWebsocketConnection = createFastMutation({
|
export const deleteWebsocketConnection = createFastMutation({
|
||||||
mutationKey: ['delete_websocket_connection'],
|
mutationKey: ['delete_websocket_connection'],
|
||||||
mutationFn: async function (connection: WebsocketConnection) {
|
mutationFn: async function (connection: WebsocketConnection) {
|
||||||
return cmdDeleteWebsocketConnection(connection.id);
|
return cmdDeleteWebsocketConnection(connection.id);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
|
||||||
trackEvent('websocket_connection', 'delete');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import type { WebsocketRequest } from '@yaakapp-internal/models';
|
import type { WebsocketRequest } from '@yaakapp-internal/models';
|
||||||
import { deleteWebsocketConnections as cmdDeleteWebsocketConnections } from '@yaakapp-internal/ws';
|
import { deleteWebsocketConnections as cmdDeleteWebsocketConnections } from '@yaakapp-internal/ws';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
|
|
||||||
export const deleteWebsocketConnections = createFastMutation({
|
export const deleteWebsocketConnections = createFastMutation({
|
||||||
mutationKey: ['delete_websocket_connections'],
|
mutationKey: ['delete_websocket_connections'],
|
||||||
mutationFn: async function (request: WebsocketRequest) {
|
mutationFn: async function (request: WebsocketRequest) {
|
||||||
return cmdDeleteWebsocketConnections(request.id);
|
return cmdDeleteWebsocketConnections(request.id);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
|
||||||
trackEvent('websocket_connection', 'delete_many');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { WebsocketRequest } from '@yaakapp-internal/models';
|
|||||||
import { deleteWebsocketRequest as cmdDeleteWebsocketRequest } from '@yaakapp-internal/ws';
|
import { deleteWebsocketRequest as cmdDeleteWebsocketRequest } from '@yaakapp-internal/ws';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||||
|
|
||||||
@@ -24,7 +23,4 @@ export const deleteWebsocketRequest = createFastMutation({
|
|||||||
|
|
||||||
return cmdDeleteWebsocketRequest(request.id);
|
return cmdDeleteWebsocketRequest(request.id);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
|
||||||
trackEvent('websocket_request', 'delete');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { WebsocketRequest } from '@yaakapp-internal/models';
|
import type { WebsocketRequest } from '@yaakapp-internal/models';
|
||||||
import { duplicateWebsocketRequest as cmdDuplicateWebsocketRequest } from '@yaakapp-internal/ws';
|
import { duplicateWebsocketRequest as cmdDuplicateWebsocketRequest } from '@yaakapp-internal/ws';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
|
|
||||||
export const duplicateWebsocketRequest = createFastMutation({
|
export const duplicateWebsocketRequest = createFastMutation({
|
||||||
@@ -10,7 +9,6 @@ export const duplicateWebsocketRequest = createFastMutation({
|
|||||||
return cmdDuplicateWebsocketRequest(request.id);
|
return cmdDuplicateWebsocketRequest(request.id);
|
||||||
},
|
},
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
trackEvent('websocket_request', 'duplicate');
|
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
to: '/workspaces/$workspaceId',
|
to: '/workspaces/$workspaceId',
|
||||||
params: { workspaceId: request.workspaceId },
|
params: { workspaceId: request.workspaceId },
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { SettingsTab } from '../components/Settings/SettingsTab';
|
import { SettingsTab } from '../components/Settings/SettingsTab';
|
||||||
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ export const openSettings = createFastMutation<void, string, SettingsTab | null>
|
|||||||
const workspaceId = getActiveWorkspaceId();
|
const workspaceId = getActiveWorkspaceId();
|
||||||
if (workspaceId == null) return;
|
if (workspaceId == null) return;
|
||||||
|
|
||||||
trackEvent('dialog', 'show', { id: 'settings', tab: `${tab}` });
|
|
||||||
const location = router.buildLocation({
|
const location = router.buildLocation({
|
||||||
to: '/workspaces/$workspaceId/settings',
|
to: '/workspaces/$workspaceId/settings',
|
||||||
params: { workspaceId },
|
params: { workspaceId },
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { WebsocketRequest } from '@yaakapp-internal/models';
|
|||||||
import { upsertWebsocketRequest as cmdUpsertWebsocketRequest } from '@yaakapp-internal/ws';
|
import { upsertWebsocketRequest as cmdUpsertWebsocketRequest } from '@yaakapp-internal/ws';
|
||||||
import { differenceInMilliseconds } from 'date-fns';
|
import { differenceInMilliseconds } from 'date-fns';
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
|
|
||||||
export const upsertWebsocketRequest = createFastMutation<
|
export const upsertWebsocketRequest = createFastMutation<
|
||||||
@@ -16,12 +15,11 @@ export const upsertWebsocketRequest = createFastMutation<
|
|||||||
const isNew = differenceInMilliseconds(new Date(), request.createdAt + 'Z') < 100;
|
const isNew = differenceInMilliseconds(new Date(), request.createdAt + 'Z') < 100;
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
trackEvent('websocket_request', 'create');
|
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
to: '/workspaces/$workspaceId',
|
to: '/workspaces/$workspaceId',
|
||||||
params: { workspaceId: request.workspaceId },
|
params: { workspaceId: request.workspaceId },
|
||||||
search: (prev) => ({ ...prev, request_id: request.id }),
|
search: (prev) => ({ ...prev, request_id: request.id }),
|
||||||
});
|
});
|
||||||
} else trackEvent('websocket_request', 'update');
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import type { Workspace } from '@yaakapp-internal/models';
|
import type { Workspace } from '@yaakapp-internal/models';
|
||||||
import { differenceInMilliseconds } from 'date-fns';
|
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
export const upsertWorkspace = createFastMutation<
|
export const upsertWorkspace = createFastMutation<
|
||||||
@@ -11,10 +9,4 @@ export const upsertWorkspace = createFastMutation<
|
|||||||
>({
|
>({
|
||||||
mutationKey: ['upsert_workspace'],
|
mutationKey: ['upsert_workspace'],
|
||||||
mutationFn: (workspace) => invokeCmd<Workspace>('cmd_update_workspace', { workspace }),
|
mutationFn: (workspace) => invokeCmd<Workspace>('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');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export function LicenseBadge() {
|
|||||||
size="2xs"
|
size="2xs"
|
||||||
variant="border"
|
variant="border"
|
||||||
className="!rounded-full mx-1"
|
className="!rounded-full mx-1"
|
||||||
|
color={detail.color}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (check.data.type === 'trialing') {
|
if (check.data.type === 'trialing') {
|
||||||
await setLicenseDetails((v) => ({
|
await setLicenseDetails((v) => ({
|
||||||
@@ -69,8 +70,6 @@ export function LicenseBadge() {
|
|||||||
}
|
}
|
||||||
openSettings.mutate(SettingsTab.License);
|
openSettings.mutate(SettingsTab.License);
|
||||||
}}
|
}}
|
||||||
color={detail.color}
|
|
||||||
event={{ id: 'license-badge', status: check.data.type }}
|
|
||||||
>
|
>
|
||||||
{detail.label}
|
{detail.label}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -66,18 +66,8 @@ export function MarkdownEditor({
|
|||||||
onChange={setViewMode}
|
onChange={setViewMode}
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
options={[
|
options={[
|
||||||
{
|
{ icon: 'eye', label: 'Preview mode', value: 'preview' },
|
||||||
event: { id: 'md_mode', mode: 'preview' },
|
{ icon: 'pencil', label: 'Edit mode', value: 'edit' },
|
||||||
icon: 'eye',
|
|
||||||
label: 'Preview mode',
|
|
||||||
value: 'preview',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: { id: 'md_mode', mode: 'edit' },
|
|
||||||
icon: 'pencil',
|
|
||||||
label: 'Edit mode',
|
|
||||||
value: 'edit',
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ export function SettingsAppearance() {
|
|||||||
value={`${settings.interfaceFontSize}`}
|
value={`${settings.interfaceFontSize}`}
|
||||||
options={fontSizeOptions}
|
options={fontSizeOptions}
|
||||||
onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })}
|
onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })}
|
||||||
event="ui-font-size"
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -106,7 +105,6 @@ export function SettingsAppearance() {
|
|||||||
value={`${settings.editorFontSize}`}
|
value={`${settings.editorFontSize}`}
|
||||||
options={fontSizeOptions}
|
options={fontSizeOptions}
|
||||||
onChange={(v) => updateSettings.mutate({ editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
|
onChange={(v) => updateSettings.mutate({ editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
|
||||||
event="editor-font-size"
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -116,13 +114,11 @@ export function SettingsAppearance() {
|
|||||||
value={`${settings.editorKeymap}`}
|
value={`${settings.editorKeymap}`}
|
||||||
options={keymaps}
|
options={keymaps}
|
||||||
onChange={(v) => updateSettings.mutate({ editorKeymap: v })}
|
onChange={(v) => updateSettings.mutate({ editorKeymap: v })}
|
||||||
event="editor-keymap"
|
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={settings.editorSoftWrap}
|
checked={settings.editorSoftWrap}
|
||||||
title="Wrap Editor Lines"
|
title="Wrap Editor Lines"
|
||||||
onChange={(editorSoftWrap) => updateSettings.mutate({ editorSoftWrap })}
|
onChange={(editorSoftWrap) => updateSettings.mutate({ editorSoftWrap })}
|
||||||
event="editor-wrap-lines"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
@@ -134,7 +130,6 @@ export function SettingsAppearance() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
value={settings.appearance}
|
value={settings.appearance}
|
||||||
onChange={(appearance) => updateSettings.mutate({ appearance })}
|
onChange={(appearance) => updateSettings.mutate({ appearance })}
|
||||||
event="appearance"
|
|
||||||
options={[
|
options={[
|
||||||
{ label: 'Automatic', value: 'system' },
|
{ label: 'Automatic', value: 'system' },
|
||||||
{ label: 'Light', value: 'light' },
|
{ label: 'Light', value: 'light' },
|
||||||
@@ -152,7 +147,6 @@ export function SettingsAppearance() {
|
|||||||
className="flex-1"
|
className="flex-1"
|
||||||
value={activeTheme.light.id}
|
value={activeTheme.light.id}
|
||||||
options={lightThemes}
|
options={lightThemes}
|
||||||
event="theme.light"
|
|
||||||
onChange={(themeLight) => updateSettings.mutate({ ...settings, themeLight })}
|
onChange={(themeLight) => updateSettings.mutate({ ...settings, themeLight })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -166,7 +160,6 @@ export function SettingsAppearance() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
value={activeTheme.dark.id}
|
value={activeTheme.dark.id}
|
||||||
options={darkThemes}
|
options={darkThemes}
|
||||||
event="theme.dark"
|
|
||||||
onChange={(themeDark) => updateSettings.mutate({ ...settings, themeDark })}
|
onChange={(themeDark) => updateSettings.mutate({ ...settings, themeDark })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export function SettingsGeneral() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
value={settings.updateChannel}
|
value={settings.updateChannel}
|
||||||
onChange={(updateChannel) => updateSettings.mutate({ updateChannel })}
|
onChange={(updateChannel) => updateSettings.mutate({ updateChannel })}
|
||||||
event="update-channel"
|
|
||||||
options={[
|
options={[
|
||||||
{ label: 'Stable (less frequent)', value: 'stable' },
|
{ label: 'Stable (less frequent)', value: 'stable' },
|
||||||
{ label: 'Beta (more frequent)', value: 'beta' },
|
{ label: 'Beta (more frequent)', value: 'beta' },
|
||||||
@@ -59,7 +58,6 @@ export function SettingsGeneral() {
|
|||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
labelClassName="w-[14rem]"
|
labelClassName="w-[14rem]"
|
||||||
size="sm"
|
size="sm"
|
||||||
event="workspace-switch-behavior"
|
|
||||||
value={
|
value={
|
||||||
settings.openWorkspaceNewWindow === true
|
settings.openWorkspaceNewWindow === true
|
||||||
? 'new'
|
? 'new'
|
||||||
@@ -81,9 +79,9 @@ export function SettingsGeneral() {
|
|||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
checked={settings.telemetry}
|
checked={false}
|
||||||
title="Send Usage Statistics"
|
title="Send Usage Statistics (all tracking was removed in 2025.1.2)"
|
||||||
event="usage-statistics"
|
disabled
|
||||||
onChange={(telemetry) => updateSettings.mutate({ telemetry })}
|
onChange={(telemetry) => updateSettings.mutate({ telemetry })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -115,7 +113,6 @@ export function SettingsGeneral() {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={workspace.settingValidateCertificates}
|
checked={workspace.settingValidateCertificates}
|
||||||
title="Validate TLS Certificates"
|
title="Validate TLS Certificates"
|
||||||
event="validate-certs"
|
|
||||||
onChange={(settingValidateCertificates) =>
|
onChange={(settingValidateCertificates) =>
|
||||||
upsertWorkspace.mutate({ ...workspace, settingValidateCertificates })
|
upsertWorkspace.mutate({ ...workspace, settingValidateCertificates })
|
||||||
}
|
}
|
||||||
@@ -124,7 +121,6 @@ export function SettingsGeneral() {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={workspace.settingFollowRedirects}
|
checked={workspace.settingFollowRedirects}
|
||||||
title="Follow Redirects"
|
title="Follow Redirects"
|
||||||
event="follow-redirects"
|
|
||||||
onChange={(settingFollowRedirects) =>
|
onChange={(settingFollowRedirects) =>
|
||||||
upsertWorkspace.mutate({
|
upsertWorkspace.mutate({
|
||||||
...workspace,
|
...workspace,
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ export function SettingsLicense() {
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={toggleActivateFormVisible}
|
onClick={toggleActivateFormVisible}
|
||||||
event="license.another"
|
|
||||||
>
|
>
|
||||||
Activate Another License
|
Activate Another License
|
||||||
</Button>
|
</Button>
|
||||||
@@ -90,7 +89,6 @@ export function SettingsLicense() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => openUrl('https://yaak.app/dashboard')}
|
onClick={() => openUrl('https://yaak.app/dashboard')}
|
||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
event="license.support"
|
|
||||||
>
|
>
|
||||||
Direct Support
|
Direct Support
|
||||||
</Button>
|
</Button>
|
||||||
@@ -101,7 +99,6 @@ export function SettingsLicense() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={toggleActivateFormVisible}
|
onClick={toggleActivateFormVisible}
|
||||||
event="license.activate"
|
|
||||||
>
|
>
|
||||||
Activate
|
Activate
|
||||||
</Button>
|
</Button>
|
||||||
@@ -110,7 +107,6 @@ export function SettingsLicense() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => openUrl('https://yaak.app/pricing?ref=app.yaak.desktop')}
|
onClick={() => openUrl('https://yaak.app/pricing?ref=app.yaak.desktop')}
|
||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
event="license.purchase"
|
|
||||||
>
|
>
|
||||||
Purchase
|
Purchase
|
||||||
</Button>
|
</Button>
|
||||||
@@ -140,7 +136,6 @@ export function SettingsLicense() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
isLoading={activate.isPending}
|
isLoading={activate.isPending}
|
||||||
event="license.submit"
|
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ export function SettingsPlugins() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
color="primary"
|
color="primary"
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
event="plugin.add"
|
|
||||||
>
|
>
|
||||||
Add Plugin
|
Add Plugin
|
||||||
</Button>
|
</Button>
|
||||||
@@ -76,14 +75,12 @@ export function SettingsPlugins() {
|
|||||||
icon="refresh"
|
icon="refresh"
|
||||||
title="Reload plugins"
|
title="Reload plugins"
|
||||||
spin={refreshPlugins.isPending}
|
spin={refreshPlugins.isPending}
|
||||||
event="plugin.reload"
|
|
||||||
onClick={() => refreshPlugins.mutate()}
|
onClick={() => refreshPlugins.mutate()}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="sm"
|
size="sm"
|
||||||
icon="help"
|
icon="help"
|
||||||
title="View documentation"
|
title="View documentation"
|
||||||
event="plugin.docs"
|
|
||||||
onClick={() => openUrl('https://feedback.yaak.app/help/articles/6911763-quick-start')}
|
onClick={() => openUrl('https://feedback.yaak.app/help/articles/6911763-quick-start')}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -107,7 +104,6 @@ function PluginInfo({ plugin }: { plugin: Plugin }) {
|
|||||||
size="sm"
|
size="sm"
|
||||||
icon="trash"
|
icon="trash"
|
||||||
title="Uninstall plugin"
|
title="Uninstall plugin"
|
||||||
event="plugin.delete"
|
|
||||||
onClick={() => deletePlugin.mutate()}
|
onClick={() => deletePlugin.mutate()}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export function SettingsProxy() {
|
|||||||
hideLabel
|
hideLabel
|
||||||
size="sm"
|
size="sm"
|
||||||
value={settings.proxy?.type ?? 'automatic'}
|
value={settings.proxy?.type ?? 'automatic'}
|
||||||
event="proxy"
|
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
if (v === 'automatic') {
|
if (v === 'automatic') {
|
||||||
updateSettings.mutate({ proxy: undefined });
|
updateSettings.mutate({ proxy: undefined });
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { requestsAtom } from '../hooks/useRequests';
|
|||||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||||
import { useLatestWebsocketConnection } from '../hooks/useWebsocketConnections';
|
import { useLatestWebsocketConnection } from '../hooks/useWebsocketConnections';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { deepEqualAtom } from '../lib/atoms';
|
import { deepEqualAtom } from '../lib/atoms';
|
||||||
import { languageFromContentType } from '../lib/contentType';
|
import { languageFromContentType } from '../lib/contentType';
|
||||||
import { generateId } from '../lib/generateId';
|
import { generateId } from '../lib/generateId';
|
||||||
@@ -190,7 +189,6 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
|
|||||||
environmentId: getActiveEnvironment()?.id ?? null,
|
environmentId: getActiveEnvironment()?.id ?? null,
|
||||||
cookieJarId: getActiveCookieJar()?.id ?? null,
|
cookieJarId: getActiveCookieJar()?.id ?? null,
|
||||||
});
|
});
|
||||||
trackEvent('websocket_request', 'send');
|
|
||||||
}, [activeRequest.id]);
|
}, [activeRequest.id]);
|
||||||
|
|
||||||
const handleSend = useCallback(async () => {
|
const handleSend = useCallback(async () => {
|
||||||
@@ -199,13 +197,11 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
|
|||||||
connectionId: connection?.id,
|
connectionId: connection?.id,
|
||||||
environmentId: getActiveEnvironment()?.id ?? null,
|
environmentId: getActiveEnvironment()?.id ?? null,
|
||||||
});
|
});
|
||||||
trackEvent('websocket_connection', 'send');
|
|
||||||
}, [connection]);
|
}, [connection]);
|
||||||
|
|
||||||
const handleCancel = useCallback(async () => {
|
const handleCancel = useCallback(async () => {
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
await closeWebsocket({ connectionId: connection?.id });
|
await closeWebsocket({ connectionId: connection?.id });
|
||||||
trackEvent('websocket_connection', 'cancel');
|
|
||||||
}, [connection]);
|
}, [connection]);
|
||||||
|
|
||||||
const handleUrlChange = useCallback(
|
const handleUrlChange = useCallback(
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
icon="search"
|
icon="search"
|
||||||
title="Search or execute a command"
|
title="Search or execute a command"
|
||||||
size="sm"
|
size="sm"
|
||||||
event="search"
|
|
||||||
iconColor="secondary"
|
iconColor="secondary"
|
||||||
onClick={togglePalette}
|
onClick={togglePalette}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { HTMLAttributes, ReactNode } from 'react';
|
|||||||
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import type { HotkeyAction } from '../../hooks/useHotKey';
|
import type { HotkeyAction } from '../../hooks/useHotKey';
|
||||||
import { useFormattedHotkey, useHotKey } from '../../hooks/useHotKey';
|
import { useFormattedHotkey, useHotKey } from '../../hooks/useHotKey';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { LoadingIcon } from './LoadingIcon';
|
import { LoadingIcon } from './LoadingIcon';
|
||||||
|
|
||||||
@@ -22,7 +21,6 @@ export type ButtonProps = Omit<HTMLAttributes<HTMLButtonElement>, 'color' | 'onC
|
|||||||
leftSlot?: ReactNode;
|
leftSlot?: ReactNode;
|
||||||
rightSlot?: ReactNode;
|
rightSlot?: ReactNode;
|
||||||
hotkeyAction?: HotkeyAction;
|
hotkeyAction?: HotkeyAction;
|
||||||
event?: string | { id: string; [attr: string]: number | string };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
||||||
@@ -43,7 +41,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
|||||||
hotkeyAction,
|
hotkeyAction,
|
||||||
title,
|
title,
|
||||||
onClick,
|
onClick,
|
||||||
event,
|
|
||||||
...props
|
...props
|
||||||
}: ButtonProps,
|
}: ButtonProps,
|
||||||
ref,
|
ref,
|
||||||
@@ -107,12 +104,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
|||||||
type={type}
|
type={type}
|
||||||
className={classes}
|
className={classes}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={(e) => {
|
onClick={onClick}
|
||||||
onClick?.(e);
|
|
||||||
if (event != null) {
|
|
||||||
trackEvent('button', 'click', typeof event === 'string' ? { id: event } : event);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDoubleClick={(e) => {
|
onDoubleClick={(e) => {
|
||||||
// Kind of a hack? This prevents double-clicks from going through buttons. For example, when
|
// Kind of a hack? This prevents double-clicks from going through buttons. For example, when
|
||||||
// double-clicking the workspace header to toggle window maximization
|
// double-clicking the workspace header to toggle window maximization
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { HStack } from './Stacks';
|
import { HStack } from './Stacks';
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ export interface CheckboxProps {
|
|||||||
inputWrapperClassName?: string;
|
inputWrapperClassName?: string;
|
||||||
hideLabel?: boolean;
|
hideLabel?: boolean;
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
event?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Checkbox({
|
export function Checkbox({
|
||||||
@@ -25,7 +23,6 @@ export function Checkbox({
|
|||||||
title,
|
title,
|
||||||
hideLabel,
|
hideLabel,
|
||||||
fullWidth,
|
fullWidth,
|
||||||
event,
|
|
||||||
}: CheckboxProps) {
|
}: CheckboxProps) {
|
||||||
return (
|
return (
|
||||||
<HStack as="label" space={2} className={classNames(className, 'text-text mr-auto')}>
|
<HStack as="label" space={2} className={classNames(className, 'text-text mr-auto')}>
|
||||||
@@ -42,9 +39,6 @@ export function Checkbox({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
onChange(checked === 'indeterminate' ? true : !checked);
|
onChange(checked === 'indeterminate' ? true : !checked);
|
||||||
if (event != null) {
|
|
||||||
trackEvent('button', 'click', { id: event, checked: checked ? 'on' : 'off' });
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
|
import { Link as RouterLink } from '@tanstack/react-router';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
import { Link as RouterLink } from '@tanstack/react-router';
|
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
interface Props extends HTMLAttributes<HTMLAnchorElement> {
|
interface Props extends HTMLAttributes<HTMLAnchorElement> {
|
||||||
href: string;
|
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?:\/\//);
|
const isExternal = href.match(/^https?:\/\//);
|
||||||
|
|
||||||
className = classNames(className, 'relative underline hover:text-violet-600');
|
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')}
|
className={classNames(className, 'pr-4 inline-flex items-center')}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (event != null) {
|
|
||||||
trackEvent('link', 'click', { id: event });
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
{...other}
|
{...other}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -261,7 +261,6 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
variant="border"
|
variant="border"
|
||||||
className="m-2"
|
className="m-2"
|
||||||
size="xs"
|
size="xs"
|
||||||
event="pairs.reveal-more"
|
|
||||||
>
|
>
|
||||||
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -2,18 +2,17 @@ import classNames from 'classnames';
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||||
import type { IconProps } from './Icon';
|
import type { IconProps } from './Icon';
|
||||||
import type { IconButtonProps } from './IconButton';
|
|
||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
import { HStack } from './Stacks';
|
import { HStack } from './Stacks';
|
||||||
|
|
||||||
interface Props<T extends string> {
|
interface Props<T extends string> {
|
||||||
options: { value: T; label: string; icon: IconProps['icon']; event?: IconButtonProps['event'] }[];
|
options: { value: T; label: string; icon: IconProps['icon'] }[];
|
||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
value: T;
|
value: T;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SegmentedControl<T extends string>({ value, onChange, options, name }: Props<T>) {
|
export function SegmentedControl<T extends string>({ value, onChange, options }: Props<T>) {
|
||||||
const [selectedValue, setSelectedValue] = useStateWithDeps<T>(value, [value]);
|
const [selectedValue, setSelectedValue] = useStateWithDeps<T>(value, [value]);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
return (
|
return (
|
||||||
@@ -45,14 +44,13 @@ export function SegmentedControl<T extends string>({ value, onChange, options, n
|
|||||||
<IconButton
|
<IconButton
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color={isActive ? "secondary" : undefined}
|
color={isActive ? 'secondary' : undefined}
|
||||||
role="radio"
|
role="radio"
|
||||||
event={{ id: name, value: String(o.value) }}
|
|
||||||
tabIndex={isSelected ? 0 : -1}
|
tabIndex={isSelected ? 0 : -1}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
isActive && '!text-text',
|
isActive && '!text-text',
|
||||||
'!px-1.5 !w-auto',
|
'!px-1.5 !w-auto',
|
||||||
'focus:ring-border-focus',
|
'focus:ring-border-focus',
|
||||||
)}
|
)}
|
||||||
key={i}
|
key={i}
|
||||||
title={o.label}
|
title={o.label}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import classNames from 'classnames';
|
|||||||
import type { CSSProperties, ReactNode } from 'react';
|
import type { CSSProperties, ReactNode } from 'react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useOsInfo } from '../../hooks/useOsInfo';
|
import { useOsInfo } from '../../hooks/useOsInfo';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import type { ButtonProps } from './Button';
|
import type { ButtonProps } from './Button';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { Label } from './Label';
|
import { Label } from './Label';
|
||||||
@@ -22,7 +21,6 @@ export interface SelectProps<T extends string> {
|
|||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
size?: ButtonProps['size'];
|
size?: ButtonProps['size'];
|
||||||
className?: string;
|
className?: string;
|
||||||
event?: string;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +36,6 @@ export function Select<T extends string>({
|
|||||||
leftSlot,
|
leftSlot,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
event,
|
|
||||||
size = 'md',
|
size = 'md',
|
||||||
}: SelectProps<T>) {
|
}: SelectProps<T>) {
|
||||||
const osInfo = useOsInfo();
|
const osInfo = useOsInfo();
|
||||||
@@ -48,9 +45,6 @@ export function Select<T extends string>({
|
|||||||
|
|
||||||
const handleChange = (value: T) => {
|
const handleChange = (value: T) => {
|
||||||
onChange?.(value);
|
onChange?.(value);
|
||||||
if (event != null) {
|
|
||||||
trackEvent('select', 'click', { id: event, value });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { memo, useEffect, useRef } from 'react';
|
import { memo, useEffect, useRef } from 'react';
|
||||||
import { trackEvent } from '../../../lib/analytics';
|
|
||||||
import { Icon } from '../Icon';
|
import { Icon } from '../Icon';
|
||||||
import type { RadioDropdownProps } from '../RadioDropdown';
|
import type { RadioDropdownProps } from '../RadioDropdown';
|
||||||
import { RadioDropdown } from '../RadioDropdown';
|
import { RadioDropdown } from '../RadioDropdown';
|
||||||
@@ -104,14 +103,7 @@ export function Tabs({
|
|||||||
onChange={t.options.onChange}
|
onChange={t.options.onChange}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={
|
onClick={isActive ? undefined : () => onChangeValue(t.value)}
|
||||||
isActive
|
|
||||||
? undefined
|
|
||||||
: () => {
|
|
||||||
trackEvent('tab', 'click', { label, tab: t.value });
|
|
||||||
onChangeValue(t.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
className={btnClassName}
|
className={btnClassName}
|
||||||
>
|
>
|
||||||
{option && 'shortLabel' in option && option.shortLabel
|
{option && 'shortLabel' in option && option.shortLabel
|
||||||
@@ -133,14 +125,7 @@ export function Tabs({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={t.value}
|
key={t.value}
|
||||||
onClick={
|
onClick={isActive ? undefined : () => onChangeValue(t.value)}
|
||||||
isActive
|
|
||||||
? undefined
|
|
||||||
: () => {
|
|
||||||
trackEvent('tab', 'click', { label, tab: t.value });
|
|
||||||
onChangeValue(t.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
className={btnClassName}
|
className={btnClassName}
|
||||||
>
|
>
|
||||||
{t.label}
|
{t.label}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useMemo } from 'react';
|
|||||||
import { useFloatingSidebarHidden } from '../../hooks/useFloatingSidebarHidden';
|
import { useFloatingSidebarHidden } from '../../hooks/useFloatingSidebarHidden';
|
||||||
import { useShouldFloatSidebar } from '../../hooks/useShouldFloatSidebar';
|
import { useShouldFloatSidebar } from '../../hooks/useShouldFloatSidebar';
|
||||||
import { useSidebarHidden } from '../../hooks/useSidebarHidden';
|
import { useSidebarHidden } from '../../hooks/useSidebarHidden';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { IconButton } from '../core/IconButton';
|
import { IconButton } from '../core/IconButton';
|
||||||
import { HStack } from '../core/Stacks';
|
import { HStack } from '../core/Stacks';
|
||||||
import { CreateDropdown } from '../CreateDropdown';
|
import { CreateDropdown } from '../CreateDropdown';
|
||||||
@@ -22,8 +21,6 @@ export function SidebarActions() {
|
|||||||
<HStack className="h-full">
|
<HStack className="h-full">
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
trackEvent('sidebar', 'toggle');
|
|
||||||
|
|
||||||
// NOTE: We're not using the (h) => !h pattern here because the data
|
// NOTE: We're not using the (h) => !h pattern here because the data
|
||||||
// might be different if another window changed it (out of sync)
|
// might be different if another window changed it (out of sync)
|
||||||
await setHidden(!hidden);
|
await setHidden(!hidden);
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import { event } from '@tauri-apps/api';
|
import { event } from '@tauri-apps/api';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
|
|
||||||
export function useCancelHttpResponse(id: string | null) {
|
export function useCancelHttpResponse(id: string | null) {
|
||||||
return useFastMutation<void>({
|
return useFastMutation<void>({
|
||||||
mutationKey: ['cancel_http_response', id],
|
mutationKey: ['cancel_http_response', id],
|
||||||
mutationFn: () => event.emit(`cancel_http_response_${id}`),
|
mutationFn: () => event.emit(`cancel_http_response_${id}`),
|
||||||
onSettled: () => trackEvent('http_response', 'cancel'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { CookieJar } from '@yaakapp-internal/models';
|
import type { CookieJar } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showPrompt } from '../lib/prompt';
|
import { showPrompt } from '../lib/prompt';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
import { getActiveWorkspaceId } from './useActiveWorkspace';
|
||||||
@@ -25,6 +24,5 @@ export function useCreateCookieJar() {
|
|||||||
|
|
||||||
return invokeCmd('cmd_create_cookie_jar', { workspaceId, name });
|
return invokeCmd('cmd_create_cookie_jar', { workspaceId, name });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('cookie_jar', 'create'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { Environment } from '@yaakapp-internal/models';
|
import type { Environment } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showPrompt } from '../lib/prompt';
|
import { showPrompt } from '../lib/prompt';
|
||||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
@@ -33,7 +32,6 @@ export function useCreateEnvironment() {
|
|||||||
environmentId: baseEnvironment.id,
|
environmentId: baseEnvironment.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('environment', 'create'),
|
|
||||||
onSuccess: async (environment) => {
|
onSuccess: async (environment) => {
|
||||||
if (environment == null) return;
|
if (environment == null) return;
|
||||||
setWorkspaceSearchParams({ environment_id: environment.id });
|
setWorkspaceSearchParams({ environment_id: environment.id });
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { jotaiStore } from '../lib/jotai';
|
import { jotaiStore } from '../lib/jotai';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { getActiveRequest } from './useActiveRequest';
|
import { getActiveRequest } from './useActiveRequest';
|
||||||
@@ -36,7 +35,6 @@ export function useCreateGrpcRequest() {
|
|||||||
...patch,
|
...patch,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('grpc_request', 'create'),
|
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
to: '/workspaces/$workspaceId',
|
to: '/workspaces/$workspaceId',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { getActiveRequest } from './useActiveRequest';
|
import { getActiveRequest } from './useActiveRequest';
|
||||||
@@ -30,7 +29,6 @@ export function useCreateHttpRequest() {
|
|||||||
request: { workspaceId, ...patch },
|
request: { workspaceId, ...patch },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_request', 'create'),
|
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
to: '/workspaces/$workspaceId',
|
to: '/workspaces/$workspaceId',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { Workspace } from '@yaakapp-internal/models';
|
import type { Workspace } from '@yaakapp-internal/models';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
@@ -24,7 +23,6 @@ export function useDeleteActiveWorkspace() {
|
|||||||
if (!confirmed) return null;
|
if (!confirmed) return null;
|
||||||
return invokeCmd('cmd_delete_workspace', { workspaceId: workspace?.id });
|
return invokeCmd('cmd_delete_workspace', { workspaceId: workspace?.id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('workspace', 'delete'),
|
|
||||||
onSuccess: async (workspace) => {
|
onSuccess: async (workspace) => {
|
||||||
if (workspace === null) return;
|
if (workspace === null) return;
|
||||||
await router.navigate({ to: '/workspaces' });
|
await router.navigate({ to: '/workspaces' });
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
@@ -24,6 +23,5 @@ export function useDeleteAnyGrpcRequest() {
|
|||||||
}
|
}
|
||||||
return invokeCmd('cmd_delete_grpc_request', { requestId: request.id });
|
return invokeCmd('cmd_delete_grpc_request', { requestId: request.id });
|
||||||
},
|
},
|
||||||
onSuccess: () => trackEvent('grpc_request', 'delete'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
@@ -24,6 +23,5 @@ export function useDeleteAnyHttpRequest() {
|
|||||||
}
|
}
|
||||||
return invokeCmd<HttpRequest>('cmd_delete_http_request', { requestId: request.id });
|
return invokeCmd<HttpRequest>('cmd_delete_http_request', { requestId: request.id });
|
||||||
},
|
},
|
||||||
onSuccess: () => trackEvent('http_request', 'delete'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { CookieJar } from '@yaakapp-internal/models';
|
import type { CookieJar } from '@yaakapp-internal/models';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { cookieJarsAtom } from './useCookieJars';
|
import { cookieJarsAtom } from './useCookieJars';
|
||||||
@@ -26,7 +25,6 @@ export function useDeleteCookieJar(cookieJar: CookieJar | null) {
|
|||||||
if (!confirmed) return null;
|
if (!confirmed) return null;
|
||||||
return invokeCmd('cmd_delete_cookie_jar', { cookieJarId: cookieJar?.id });
|
return invokeCmd('cmd_delete_cookie_jar', { cookieJarId: cookieJar?.id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('cookie_jar', 'delete'),
|
|
||||||
onSuccess: (cookieJar) => {
|
onSuccess: (cookieJar) => {
|
||||||
if (cookieJar == null) return;
|
if (cookieJar == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Environment } from '@yaakapp-internal/models';
|
import type { Environment } from '@yaakapp-internal/models';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { environmentsAtom } from './useEnvironments';
|
import { environmentsAtom } from './useEnvironments';
|
||||||
@@ -26,7 +25,6 @@ export function useDeleteEnvironment(environment: Environment | null) {
|
|||||||
if (!confirmed) return null;
|
if (!confirmed) return null;
|
||||||
return invokeCmd('cmd_delete_environment', { environmentId: environment?.id });
|
return invokeCmd('cmd_delete_environment', { environmentId: environment?.id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('environment', 'delete'),
|
|
||||||
onSuccess: (environment) => {
|
onSuccess: (environment) => {
|
||||||
if (environment == null) return;
|
if (environment == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Folder } from '@yaakapp-internal/models';
|
import type { Folder } from '@yaakapp-internal/models';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { showConfirmDelete } from '../lib/confirm';
|
import { showConfirmDelete } from '../lib/confirm';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
@@ -27,7 +26,6 @@ export function useDeleteFolder(id: string | null) {
|
|||||||
if (!confirmed) return null;
|
if (!confirmed) return null;
|
||||||
return invokeCmd('cmd_delete_folder', { folderId: id });
|
return invokeCmd('cmd_delete_folder', { folderId: id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('folder', 'delete'),
|
|
||||||
onSuccess: (folder) => {
|
onSuccess: (folder) => {
|
||||||
if (folder == null) return;
|
if (folder == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import type { GrpcConnection } from '@yaakapp-internal/models';
|
import type { GrpcConnection } from '@yaakapp-internal/models';
|
||||||
import {useSetAtom} from "jotai";
|
import {useSetAtom} from "jotai";
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import {grpcConnectionsAtom} from "./useGrpcConnections";
|
import {grpcConnectionsAtom} from "./useGrpcConnections";
|
||||||
import {removeModelById} from "./useSyncModelStores";
|
import {removeModelById} from "./useSyncModelStores";
|
||||||
@@ -13,7 +12,6 @@ export function useDeleteGrpcConnection(id: string | null) {
|
|||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
return await invokeCmd('cmd_delete_grpc_connection', { id: id });
|
return await invokeCmd('cmd_delete_grpc_connection', { id: id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('grpc_connection', 'delete'),
|
|
||||||
onSuccess: (connection) => {
|
onSuccess: (connection) => {
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { grpcConnectionsAtom } from './useGrpcConnections';
|
import { grpcConnectionsAtom } from './useGrpcConnections';
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ export function useDeleteGrpcConnections(requestId?: string) {
|
|||||||
if (requestId === undefined) return;
|
if (requestId === undefined) return;
|
||||||
await invokeCmd('cmd_delete_all_grpc_connections', { requestId });
|
await invokeCmd('cmd_delete_all_grpc_connections', { requestId });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('grpc_connection', 'delete_many'),
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setGrpcConnections((all) => all.filter((r) => r.requestId !== requestId));
|
setGrpcConnections((all) => all.filter((r) => r.requestId !== requestId));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||||
import {useSetAtom} from "jotai";
|
import {useSetAtom} from "jotai";
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import {httpResponsesAtom} from "./useHttpResponses";
|
import {httpResponsesAtom} from "./useHttpResponses";
|
||||||
import {removeModelById} from "./useSyncModelStores";
|
import {removeModelById} from "./useSyncModelStores";
|
||||||
@@ -13,7 +12,6 @@ export function useDeleteHttpResponse(id: string | null) {
|
|||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
return await invokeCmd('cmd_delete_http_response', { id: id });
|
return await invokeCmd('cmd_delete_http_response', { id: id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_response', 'delete'),
|
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
setHttpResponses(removeModelById(response));
|
setHttpResponses(removeModelById(response));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { httpResponsesAtom } from './useHttpResponses';
|
import { httpResponsesAtom } from './useHttpResponses';
|
||||||
|
|
||||||
@@ -15,6 +14,5 @@ export function useDeleteHttpResponses(requestId?: string) {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setHttpResponses((all) => all.filter((r) => r.requestId !== requestId));
|
setHttpResponses((all) => all.filter((r) => r.requestId !== requestId));
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_response', 'delete_many'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
export function useDuplicateFolder(id: string) {
|
export function useDuplicateFolder(id: string) {
|
||||||
return useFastMutation<void, string>({
|
return useFastMutation<void, string>({
|
||||||
mutationKey: ['duplicate_folder', id],
|
mutationKey: ['duplicate_folder', id],
|
||||||
mutationFn: () => invokeCmd('cmd_duplicate_folder', { id }),
|
mutationFn: () => invokeCmd('cmd_duplicate_folder', { id }),
|
||||||
onSettled: () => trackEvent('folder', 'duplicate'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { GrpcRequest } from '@yaakapp-internal/models';
|
import type { GrpcRequest } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
@@ -18,7 +17,6 @@ export function useDuplicateGrpcRequest({
|
|||||||
if (id === null) throw new Error("Can't duplicate a null grpc request");
|
if (id === null) throw new Error("Can't duplicate a null grpc request");
|
||||||
return invokeCmd('cmd_duplicate_grpc_request', { id });
|
return invokeCmd('cmd_duplicate_grpc_request', { id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('grpc_request', 'duplicate'),
|
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
@@ -17,7 +16,6 @@ export function useDuplicateHttpRequest({
|
|||||||
if (id === null) throw new Error("Can't duplicate a null request");
|
if (id === null) throw new Error("Can't duplicate a null request");
|
||||||
return invokeCmd('cmd_duplicate_http_request', { id });
|
return invokeCmd('cmd_duplicate_http_request', { id });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_request', 'duplicate'),
|
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
if (navigateAfter) {
|
if (navigateAfter) {
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { MutationKey } from '@tanstack/react-query';
|
import type { MutationKey } from '@tanstack/react-query';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { showToast } from '../lib/toast';
|
import { showToast } from '../lib/toast';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
|
|
||||||
interface MutationOptions<TData, TError, TVariables> {
|
interface MutationOptions<TData, TError, TVariables> {
|
||||||
mutationKey: MutationKey;
|
mutationKey: MutationKey;
|
||||||
@@ -36,7 +35,6 @@ export function createFastMutation<TData = unknown, TError = unknown, TVariables
|
|||||||
const stringKey = mutationKey.join('.');
|
const stringKey = mutationKey.join('.');
|
||||||
const e = err as TError;
|
const e = err as TError;
|
||||||
console.log('mutation error', stringKey, e);
|
console.log('mutation error', stringKey, e);
|
||||||
trackEvent('mutation', 'error', { key: stringKey });
|
|
||||||
if (!disableToastError) {
|
if (!disableToastError) {
|
||||||
showToast({
|
showToast({
|
||||||
id: stringKey,
|
id: stringKey,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
import { emit } from '@tauri-apps/api/event';
|
import { emit } from '@tauri-apps/api/event';
|
||||||
import type { GrpcConnection, GrpcRequest } from '@yaakapp-internal/models';
|
import type { GrpcConnection, GrpcRequest } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { minPromiseMillis } from '../lib/minPromiseMillis';
|
import { minPromiseMillis } from '../lib/minPromiseMillis';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { useActiveEnvironment } from './useActiveEnvironment';
|
import { useActiveEnvironment } from './useActiveEnvironment';
|
||||||
@@ -24,26 +23,22 @@ export function useGrpc(
|
|||||||
mutationKey: ['grpc_go', conn?.id],
|
mutationKey: ['grpc_go', conn?.id],
|
||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
invokeCmd<void>('cmd_grpc_go', { requestId, environmentId: environment?.id, protoFiles }),
|
invokeCmd<void>('cmd_grpc_go', { requestId, environmentId: environment?.id, protoFiles }),
|
||||||
onSettled: () => trackEvent('grpc_request', 'send'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const send = useMutation({
|
const send = useMutation({
|
||||||
mutationKey: ['grpc_send', conn?.id],
|
mutationKey: ['grpc_send', conn?.id],
|
||||||
mutationFn: ({ message }: { message: string }) =>
|
mutationFn: ({ message }: { message: string }) =>
|
||||||
emit(`grpc_client_msg_${conn?.id ?? 'none'}`, { Message: message }),
|
emit(`grpc_client_msg_${conn?.id ?? 'none'}`, { Message: message }),
|
||||||
onSettled: () => trackEvent('grpc_connection', 'send'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cancel = useMutation({
|
const cancel = useMutation({
|
||||||
mutationKey: ['grpc_cancel', conn?.id ?? 'n/a'],
|
mutationKey: ['grpc_cancel', conn?.id ?? 'n/a'],
|
||||||
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Cancel'),
|
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Cancel'),
|
||||||
onSettled: () => trackEvent('grpc_connection', 'cancel'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const commit = useMutation({
|
const commit = useMutation({
|
||||||
mutationKey: ['grpc_commit', conn?.id ?? 'n/a'],
|
mutationKey: ['grpc_commit', conn?.id ?? 'n/a'],
|
||||||
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Commit'),
|
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? 'none'}`, 'Commit'),
|
||||||
onSettled: () => trackEvent('grpc_connection', 'commit'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const debouncedUrl = useDebouncedValue<string>(req?.url ?? '', 1000);
|
const debouncedUrl = useDebouncedValue<string>(req?.url ?? '', 1000);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
export function useInstallPlugin() {
|
export function useInstallPlugin() {
|
||||||
@@ -8,6 +7,5 @@ export function useInstallPlugin() {
|
|||||||
mutationFn: async (directory: string) => {
|
mutationFn: async (directory: string) => {
|
||||||
await invokeCmd('cmd_install_plugin', { directory });
|
await invokeCmd('cmd_install_plugin', { directory });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('plugin', 'create'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { getActiveCookieJar } from './useActiveCookieJar';
|
import { getActiveCookieJar } from './useActiveCookieJar';
|
||||||
import { getActiveEnvironment } from './useActiveEnvironment';
|
import { getActiveEnvironment } from './useActiveEnvironment';
|
||||||
@@ -21,6 +20,5 @@ export function useSendAnyHttpRequest() {
|
|||||||
cookieJarId: getActiveCookieJar()?.id,
|
cookieJarId: getActiveCookieJar()?.id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_request', 'send'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useFastMutation } from './useFastMutation';
|
import { useFastMutation } from './useFastMutation';
|
||||||
import type { Plugin } from '@yaakapp-internal/models';
|
import type { Plugin } from '@yaakapp-internal/models';
|
||||||
import { trackEvent } from '../lib/analytics';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
export function useUninstallPlugin(pluginId: string) {
|
export function useUninstallPlugin(pluginId: string) {
|
||||||
@@ -9,6 +8,5 @@ export function useUninstallPlugin(pluginId: string) {
|
|||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
return invokeCmd('cmd_uninstall_plugin', { pluginId });
|
return invokeCmd('cmd_uninstall_plugin', { pluginId });
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('plugin', 'delete'),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import type { AnalyticsAction, AnalyticsResource } from '../../src-tauri/bindings/analytics';
|
|
||||||
import { invokeCmd } from './tauri';
|
|
||||||
|
|
||||||
export function trackEvent(
|
|
||||||
resource: AnalyticsResource,
|
|
||||||
action: AnalyticsAction,
|
|
||||||
attributes: Record<string, string | number> = {},
|
|
||||||
) {
|
|
||||||
invokeCmd('cmd_track_event', {
|
|
||||||
resource: resource,
|
|
||||||
action,
|
|
||||||
attributes,
|
|
||||||
}).catch(console.error);
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { atom } from 'jotai/index';
|
import { atom } from 'jotai/index';
|
||||||
import type { DialogInstance } from '../components/Dialogs';
|
import type { DialogInstance } from '../components/Dialogs';
|
||||||
import { trackEvent } from './analytics';
|
|
||||||
import { jotaiStore } from './jotai';
|
import { jotaiStore } from './jotai';
|
||||||
|
|
||||||
export const dialogsAtom = atom<DialogInstance[]>([]);
|
export const dialogsAtom = atom<DialogInstance[]>([]);
|
||||||
|
|
||||||
export function showDialog({ id, ...props }: DialogInstance) {
|
export function showDialog({ id, ...props }: DialogInstance) {
|
||||||
trackEvent('dialog', 'show', { id });
|
|
||||||
jotaiStore.set(dialogsAtom, (a) => [...a.filter((d) => d.id !== id), { id, ...props }]);
|
jotaiStore.set(dialogsAtom, (a) => [...a.filter((d) => d.id !== id), { id, ...props }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ type TauriCmd =
|
|||||||
| 'cmd_set_update_mode'
|
| 'cmd_set_update_mode'
|
||||||
| 'cmd_template_functions'
|
| 'cmd_template_functions'
|
||||||
| 'cmd_template_tokens_to_string'
|
| 'cmd_template_tokens_to_string'
|
||||||
| 'cmd_track_event'
|
|
||||||
| 'cmd_uninstall_plugin'
|
| 'cmd_uninstall_plugin'
|
||||||
| 'cmd_update_cookie_jar'
|
| 'cmd_update_cookie_jar'
|
||||||
| 'cmd_update_environment'
|
| 'cmd_update_environment'
|
||||||
|
|||||||
Reference in New Issue
Block a user