Skip to content

Commit

Permalink
feat: integrate event tracking for hotkey actions, history management…
Browse files Browse the repository at this point in the history
…, and settings updates
  • Loading branch information
0PandaDEV committed Dec 23, 2024
1 parent 3824f24 commit f44be51
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 36 deletions.
40 changes: 38 additions & 2 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ tauri-build = { version = "2.0.3", features = [] }
tauri = { version = "2.1.1", features = [
"macos-private-api",
"tray-icon",
"image-png",
"image-png"
] }
tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] }
tauri-plugin-autostart = "2.2.0"
Expand All @@ -24,10 +24,11 @@ tauri-plugin-fs = "2.2.0"
tauri-plugin-clipboard = "2.1.11"
tauri-plugin-prevent-default = "1.0.1"
tauri-plugin-global-shortcut = "2.2.0"
tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" }
sqlx = { version = "0.8.2", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] }
serde = { version = "1.0.216", features = ["derive"] }
tokio = { version = "1.42.0", features = ["full"] }
serde_json = "1.0.133"
serde_json = "1.0.134"
rdev = "0.5.3"
rand = "0.8.5"
base64 = "0.22.1"
Expand Down
51 changes: 34 additions & 17 deletions src-tauri/src/api/clipboard.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use tauri_plugin_aptabase::EventTracker;
use base64::{engine::general_purpose::STANDARD, Engine};
// use hyperpolyglot;
use lazy_static::lazy_static;
Expand All @@ -7,7 +8,7 @@ use sqlx::SqlitePool;
use std::fs;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{thread, time::Duration};
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime};
use tauri::{AppHandle, Emitter, Listener, Manager};
use tauri_plugin_clipboard::Clipboard;
use tokio::runtime::Runtime as TokioRuntime;
use url::Url;
Expand All @@ -23,8 +24,8 @@ lazy_static! {
}

#[tauri::command]
pub async fn write_and_paste<R: Runtime>(
app_handle: tauri::AppHandle<R>,
pub async fn write_and_paste(
app_handle: AppHandle,
content: String,
content_type: String,
) -> Result<(), String> {
Expand Down Expand Up @@ -80,46 +81,52 @@ pub async fn write_and_paste<R: Runtime>(
IS_PROGRAMMATIC_PASTE.store(false, Ordering::SeqCst);
});

let _ = app_handle.track_event("clipboard_paste", Some(serde_json::json!({
"content_type": content_type
})));

Ok(())
}

pub fn setup<R: Runtime>(app: &AppHandle<R>) {
let app = app.clone();
pub fn setup(app: &AppHandle) {
let app_handle = app.clone();
let runtime = TokioRuntime::new().expect("Failed to create Tokio runtime");

app.clone().listen(
app_handle.clone().listen(
"plugin:clipboard://clipboard-monitor/update",
move |_event| {
let app = app.clone();
let app_handle = app_handle.clone();
runtime.block_on(async move {
if IS_PROGRAMMATIC_PASTE.load(Ordering::SeqCst) {
return;
}

let clipboard = app.state::<Clipboard>();
let clipboard = app_handle.state::<Clipboard>();
let available_types = clipboard.available_types().unwrap();

let (app_name, app_icon) = get_app_info();

match get_pool(&app).await {
match get_pool(&app_handle).await {
Ok(pool) => {
if available_types.image {
println!("Handling image change");
if let Ok(image_data) = clipboard.read_image_base64() {
let file_path = save_image_to_file(&app, &image_data)
let file_path = save_image_to_file(&app_handle, &image_data)
.await
.map_err(|e| e.to_string())
.unwrap_or_else(|e| e);
let _ = db::history::add_history_item(
app_handle.clone(),
pool,
HistoryItem::new(app_name, ContentType::Image, file_path, None, app_icon, None),
HistoryItem::new(app_name, ContentType::Image, file_path, None, app_icon, None)
).await;
}
} else if available_types.files {
println!("Handling files change");
if let Ok(files) = clipboard.read_files() {
for file in files {
let _ = db::history::add_history_item(
app_handle.clone(),
pool.clone(),
HistoryItem::new(
app_name.clone(),
Expand All @@ -135,6 +142,7 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
} else if available_types.text {
println!("Handling text change");
if let Ok(text) = clipboard.read_text() {
let text = text.to_string();
let url_regex = Regex::new(r"^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)$").unwrap();

if url_regex.is_match(&text) {
Expand All @@ -145,6 +153,7 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
};

let _ = db::history::add_history_item(
app_handle.clone(),
pool,
HistoryItem::new(app_name, ContentType::Link, text, favicon, app_icon, None)
).await;
Expand All @@ -167,13 +176,15 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
).await;
} else*/ if crate::utils::commands::detect_color(&text) {
let _ = db::history::add_history_item(
app_handle.clone(),
pool,
HistoryItem::new(app_name, ContentType::Color, text, None, app_icon, None)
).await;
} else {
let _ = db::history::add_history_item(
app_handle.clone(),
pool,
HistoryItem::new(app_name, ContentType::Text, text, None, app_icon, None)
HistoryItem::new(app_name, ContentType::Text, text.clone(), None, app_icon, None)
).await;
}
}
Expand All @@ -187,14 +198,20 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
}
}

let _ = app.emit("clipboard-content-updated", ());
let _ = app_handle.emit("clipboard-content-updated", ());
let _ = app_handle.track_event("clipboard_copied", Some(serde_json::json!({
"content_type": if available_types.image { "image" }
else if available_types.files { "files" }
else if available_types.text { "text" }
else { "unknown" }
})));
});
},
);
}

async fn get_pool<R: Runtime>(
app_handle: &AppHandle<R>,
async fn get_pool(
app_handle: &AppHandle,
) -> Result<tauri::State<'_, SqlitePool>, Box<dyn std::error::Error + Send + Sync>> {
Ok(app_handle.state::<SqlitePool>())
}
Expand All @@ -211,8 +228,8 @@ pub fn start_monitor(app_handle: AppHandle) -> Result<(), String> {
Ok(())
}

async fn save_image_to_file<R: Runtime>(
app_handle: &AppHandle<R>,
async fn save_image_to_file(
app_handle: &AppHandle,
base64_data: &str,
) -> Result<String, Box<dyn std::error::Error>> {
let app_data_dir = app_handle.path().app_data_dir().unwrap();
Expand Down
5 changes: 5 additions & 0 deletions src-tauri/src/api/hotkeys.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use tauri_plugin_aptabase::EventTracker;
use crate::utils::commands::center_window_on_current_monitor;
use global_hotkey::{
hotkey::{Code, HotKey, Modifiers},
Expand Down Expand Up @@ -142,4 +143,8 @@ fn handle_hotkey_event(app_handle: &AppHandle) {

center_window_on_current_monitor(&window);
}

let _ = app_handle.track_event("hotkey_triggered", Some(serde_json::json!({
"action": if window.is_visible().unwrap() { "hide" } else { "show" }
})));
}
23 changes: 16 additions & 7 deletions src-tauri/src/api/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ use tauri::{
tray::TrayIconBuilder,
Emitter, Manager,
};
use tauri_plugin_aptabase::EventTracker;

pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
let window = app.get_webview_window("main").unwrap();
let window_clone_for_tray = window.clone();
let is_visible = window.is_visible().unwrap();
let _ = app.track_event("tray_toggle", Some(serde_json::json!({
"action": if is_visible { "hide" } else { "show" }
})));

let icon_bytes = include_bytes!("../../icons/Square71x71Logo.png");
let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap();
Expand All @@ -24,20 +28,25 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
)
.on_menu_event(move |_app, event| match event.id().as_ref() {
"quit" => {
let _ = _app.track_event("app_quit", None);
std::process::exit(0);
}
"show" => {
let is_visible = window_clone_for_tray.is_visible().unwrap();
let _ = _app.track_event("tray_toggle", Some(serde_json::json!({
"action": if is_visible { "hide" } else { "show" }
})));
let is_visible = window.is_visible().unwrap();
if is_visible {
window_clone_for_tray.hide().unwrap();
window.hide().unwrap();
} else {
window_clone_for_tray.show().unwrap();
window_clone_for_tray.set_focus().unwrap();
window.show().unwrap();
window.set_focus().unwrap();
}
window_clone_for_tray.emit("main_route", ()).unwrap();
window.emit("main_route", ()).unwrap();
}
"keybind" => {
window_clone_for_tray.emit("change_keybind", ()).unwrap();
let _ = _app.track_event("tray_keybind_change", None);
window.emit("change_keybind", ()).unwrap();
}
_ => (),
})
Expand Down
16 changes: 15 additions & 1 deletion src-tauri/src/db/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use sqlx::{Row, SqlitePool};
use std::fs;
use tauri_plugin_aptabase::EventTracker;

pub async fn initialize_history(pool: &SqlitePool) -> Result<(), Box<dyn std::error::Error>> {
let id: String = thread_rng()
Expand Down Expand Up @@ -53,6 +54,7 @@ pub async fn get_history(pool: tauri::State<'_, SqlitePool>) -> Result<Vec<Histo

#[tauri::command]
pub async fn add_history_item(
app_handle: tauri::AppHandle,
pool: tauri::State<'_, SqlitePool>,
item: HistoryItem,
) -> Result<(), String> {
Expand Down Expand Up @@ -95,6 +97,10 @@ pub async fn add_history_item(
}
}

let _ = app_handle.track_event("history_item_added", Some(serde_json::json!({
"content_type": item.content_type.to_string()
})));

Ok(())
}

Expand Down Expand Up @@ -163,6 +169,7 @@ pub async fn load_history_chunk(

#[tauri::command]
pub async fn delete_history_item(
app_handle: tauri::AppHandle,
pool: tauri::State<'_, SqlitePool>,
id: String,
) -> Result<(), String> {
Expand All @@ -172,16 +179,23 @@ pub async fn delete_history_item(
.await
.map_err(|e| e.to_string())?;

let _ = app_handle.track_event("history_item_deleted", None);

Ok(())
}

#[tauri::command]
pub async fn clear_history(pool: tauri::State<'_, SqlitePool>) -> Result<(), String> {
pub async fn clear_history(
app_handle: tauri::AppHandle,
pool: tauri::State<'_, SqlitePool>
) -> Result<(), String> {
sqlx::query("DELETE FROM history")
.execute(&*pool)
.await
.map_err(|e| e.to_string())?;

let _ = app_handle.track_event("history_cleared", None);

Ok(())
}

Expand Down
Loading

0 comments on commit f44be51

Please sign in to comment.