restructure

This commit is contained in:
PandaDEV 2024-08-24 16:03:18 +10:00
parent 96f9f475df
commit 266b6ff3e1
No known key found for this signature in database
GPG key ID: 13EFF9BAF70EE75C
12 changed files with 158 additions and 103 deletions

1
src-tauri/Cargo.lock generated
View file

@ -3625,6 +3625,7 @@ dependencies = [
"tauri-plugin-prevent-default", "tauri-plugin-prevent-default",
"tauri-plugin-sql", "tauri-plugin-sql",
"tauri-plugin-updater", "tauri-plugin-updater",
"time",
"tokio", "tokio",
"url", "url",
] ]

View file

@ -10,7 +10,7 @@ rust-version = "1.70"
tauri-build = { version = "2.0.0-rc.6", features = [] } tauri-build = { version = "2.0.0-rc.6", features = [] }
[dependencies] [dependencies]
tauri = { version = "2.0.0-rc.6", features = ["tray-icon", "image-png"] } tauri = { version = "2.0.0-rc.6", features = [ "macos-private-api", "tray-icon", "image-png"] }
tauri-plugin-sql = {version = "2.0.0-rc.0", features = ["sqlite"] } tauri-plugin-sql = {version = "2.0.0-rc.0", features = ["sqlite"] }
tauri-plugin-autostart = "2.0.0-rc.0" tauri-plugin-autostart = "2.0.0-rc.0"
tauri-plugin-os = "2.0.0-rc.0" tauri-plugin-os = "2.0.0-rc.0"
@ -31,6 +31,7 @@ url = "2.5.2"
regex = "1" regex = "1"
sha2 = "0.10.6" sha2 = "0.10.6"
lazy_static = "1.4.0" lazy_static = "1.4.0"
time = "0.3"
[features] [features]
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]

View file

@ -1,6 +1,6 @@
use base64::Engine; use base64::Engine;
use base64::engine::general_purpose::STANDARD; use base64::engine::general_purpose::STANDARD;
use tauri::{AppHandle, Manager, Runtime, Emitter, Listener}; use tauri::{AppHandle, Manager, Emitter, Listener};
use tauri_plugin_clipboard::Clipboard; use tauri_plugin_clipboard::Clipboard;
use tokio::runtime::Runtime as TokioRuntime; use tokio::runtime::Runtime as TokioRuntime;
use regex::Regex; use regex::Regex;
@ -16,14 +16,20 @@ use sha2::{Sha256, Digest};
use rdev::{simulate, Key, EventType}; use rdev::{simulate, Key, EventType};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use image::ImageFormat; use image::ImageFormat;
use tauri::plugin::TauriPlugin;
lazy_static! { pub fn init() -> TauriPlugin<tauri::Wry> {
static ref APP_DATA_DIR: Mutex<Option<std::path::PathBuf>> = Mutex::new(None); tauri::plugin::Builder::new("clipboard")
} .invoke_handler(tauri::generate_handler![
read_image,
pub fn set_app_data_dir(path: std::path::PathBuf) { simulate_paste,
let mut dir = APP_DATA_DIR.lock().unwrap(); get_image_path
*dir = Some(path); ])
.setup(|app_handle, _api| {
setup(app_handle.clone());
Ok(())
})
.build()
} }
#[tauri::command] #[tauri::command]
@ -58,7 +64,15 @@ pub fn get_image_path(app_handle: tauri::AppHandle, filename: String) -> String
image_path.to_str().unwrap_or("").to_string() image_path.to_str().unwrap_or("").to_string()
} }
pub fn setup<R: Runtime>(app: &AppHandle<R>) { #[tauri::command]
pub fn start_monitor(app_handle: tauri::AppHandle) -> Result<(), String> {
let clipboard = app_handle.state::<Clipboard>();
clipboard.start_monitor(app_handle.clone()).map_err(|e| e.to_string())?;
app_handle.emit("plugin:clipboard://clipboard-monitor/status", true).map_err(|e| e.to_string())?;
Ok(())
}
fn setup<R: tauri::Runtime>(app: AppHandle<R>) {
let app = app.clone(); let app = app.clone();
let runtime = TokioRuntime::new().expect("Failed to create Tokio runtime"); let runtime = TokioRuntime::new().expect("Failed to create Tokio runtime");
@ -110,14 +124,14 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
}); });
} }
async fn get_pool<R: Runtime>(app_handle: &AppHandle<R>) -> Result<SqlitePool, Box<dyn std::error::Error + Send + Sync>> { async fn get_pool<R: tauri::Runtime>(app_handle: &tauri::AppHandle<R>) -> Result<SqlitePool, Box<dyn std::error::Error + Send + Sync>> {
let app_data_dir = app_handle.path().app_data_dir().expect("Failed to get app data directory"); let app_data_dir = app_handle.path().app_data_dir().expect("Failed to get app data directory");
let db_path = app_data_dir.join("data.db"); let db_path = app_data_dir.join("data.db");
let database_url = format!("sqlite:{}", db_path.to_str().unwrap()); let database_url = format!("sqlite:{}", db_path.to_str().unwrap());
SqlitePool::connect(&database_url).await.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>) SqlitePool::connect(&database_url).await.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
} }
async fn insert_content_if_not_exists<R: Runtime>(app_handle: AppHandle<R>, pool: SqlitePool, content_type: &str, content: String) { async fn insert_content_if_not_exists<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>, pool: SqlitePool, content_type: &str, content: String) {
let last_content: Option<String> = sqlx::query_scalar( let last_content: Option<String> = sqlx::query_scalar(
"SELECT content FROM history WHERE content_type = ? ORDER BY timestamp DESC LIMIT 1", "SELECT content FROM history WHERE content_type = ? ORDER BY timestamp DESC LIMIT 1",
) )
@ -177,7 +191,7 @@ async fn insert_content_if_not_exists<R: Runtime>(app_handle: AppHandle<R>, pool
} }
} }
async fn save_image<R: Runtime>(app_handle: &AppHandle<R>, base64_image: &str) -> Result<String, Box<dyn std::error::Error>> { async fn save_image<R: tauri::Runtime>(app_handle: &tauri::AppHandle<R>, base64_image: &str) -> Result<String, Box<dyn std::error::Error>> {
let image_data = STANDARD.decode(base64_image)?; let image_data = STANDARD.decode(base64_image)?;
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(&image_data); hasher.update(&image_data);
@ -212,10 +226,11 @@ async fn fetch_favicon_as_base64(url: url::Url) -> Result<Option<String>, Box<dy
} }
} }
#[tauri::command] lazy_static! {
pub fn start_monitor(app_handle: AppHandle) -> Result<(), String> { static ref APP_DATA_DIR: Mutex<Option<std::path::PathBuf>> = Mutex::new(None);
let clipboard = app_handle.state::<Clipboard>(); }
clipboard.start_monitor(app_handle.clone()).map_err(|e| e.to_string())?;
app_handle.emit("plugin:clipboard://clipboard-monitor/status", true).map_err(|e| e.to_string())?; pub fn set_app_data_dir(path: std::path::PathBuf) {
Ok(()) let mut dir = APP_DATA_DIR.lock().unwrap();
*dir = Some(path);
} }

View file

@ -1,11 +1,21 @@
use tauri::plugin::TauriPlugin;
use tauri::{AppHandle, Manager, Emitter};
use sqlx::sqlite::SqlitePoolOptions; use sqlx::sqlite::SqlitePoolOptions;
use std::fs; use std::fs;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tauri::Manager;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { pub fn init() -> TauriPlugin<tauri::Wry> {
tauri::plugin::Builder::new("database")
.setup(|app_handle: &AppHandle, _api| {
setup(app_handle)?;
Ok(())
})
.build()
}
fn setup(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
let rt = Runtime::new().expect("Failed to create Tokio runtime"); let rt = Runtime::new().expect("Failed to create Tokio runtime");
let app_data_dir = app.path().app_data_dir().unwrap(); let app_data_dir = app.path().app_data_dir().unwrap();
@ -66,5 +76,7 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
app.manage(pool); app.manage(pool);
app.manage(rt); app.manage(rt);
app.emit("database_initialized", ()).expect("Failed to emit database_initialized event");
Ok(()) Ok(())
} }

View file

@ -1,9 +1,18 @@
use rdev::{listen, EventType, Key}; use tauri::plugin::TauriPlugin;
use tauri::Manager; use tauri::Manager;
use rdev::{listen, EventType, Key};
use crate::utils::commands;
use crate::center_window_on_current_monitor; pub fn init() -> TauriPlugin<tauri::Wry> {
tauri::plugin::Builder::new("hotkeys")
.setup(|app, _| {
setup(app.app_handle().clone());
Ok(())
})
.build()
}
pub fn setup(app_handle: tauri::AppHandle) { fn setup(app_handle: tauri::AppHandle) {
std::thread::spawn(move || { std::thread::spawn(move || {
let mut meta_pressed = false; let mut meta_pressed = false;
listen(move |event| { listen(move |event| {
@ -20,7 +29,7 @@ pub fn setup(app_handle: tauri::AppHandle) {
let window = app_handle.get_webview_window("main").unwrap(); let window = app_handle.get_webview_window("main").unwrap();
window.show().unwrap(); window.show().unwrap();
window.set_focus().unwrap(); window.set_focus().unwrap();
center_window_on_current_monitor(&window); commands::center_window_on_current_monitor(&window);
} }
} }
_ => {} _ => {}

5
src-tauri/src/api/mod.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod updater;
pub mod clipboard;
pub mod database;
pub mod tray;
pub mod hotkeys;

View file

@ -1,15 +1,24 @@
use tauri::{ use tauri::AppHandle;
Manager, use tauri::plugin::TauriPlugin;
menu::{MenuBuilder, MenuItemBuilder}, use tauri::Manager;
tray::{MouseButton, TrayIconBuilder, TrayIconEvent}, use tauri::menu::{MenuBuilder, MenuItemBuilder};
}; use tauri::tray::{MouseButton, TrayIconBuilder, TrayIconEvent};
pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { pub fn init() -> TauriPlugin<tauri::Wry> {
tauri::plugin::Builder::new("tray")
.setup(|app, _api| {
setup(app)?;
Ok(())
})
.build()
}
fn setup(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
let window = app.get_webview_window("main").unwrap(); let window = app.get_webview_window("main").unwrap();
let window_clone_for_tray = window.clone(); let window_clone_for_tray = window.clone();
let window_clone_for_click = window.clone(); let window_clone_for_click = window.clone();
let icon_bytes = include_bytes!("../icons/Square71x71Logo.png"); let icon_bytes = include_bytes!("../../icons/Square71x71Logo.png");
let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap(); let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap();
let _tray = TrayIconBuilder::new() let _tray = TrayIconBuilder::new()

View file

@ -1,8 +1,16 @@
use tauri::{AppHandle, async_runtime}; use tauri::plugin::TauriPlugin;
use tauri::AppHandle;
use tauri_plugin_dialog::{DialogExt, MessageDialogKind}; use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
use tauri_plugin_updater::UpdaterExt; use tauri_plugin_updater::UpdaterExt;
use tokio;
pub fn init() -> TauriPlugin<tauri::Wry> {
tauri::plugin::Builder::new("updater")
.invoke_handler(tauri::generate_handler![check_for_updates])
.build()
}
#[tauri::command]
pub async fn check_for_updates(app: AppHandle) { pub async fn check_for_updates(app: AppHandle) {
println!("Checking for updates..."); println!("Checking for updates...");
@ -28,7 +36,7 @@ pub async fn check_for_updates(app: AppHandle) {
if !response { if !response {
return; return;
} }
async_runtime::spawn(async move { tokio::spawn(async move {
if let Err(e) = update.download_and_install(|_, _| {}, || {}).await { if let Err(e) = update.download_and_install(|_, _| {}, || {}).await {
println!("Error installing new update: {:?}", e); println!("Error installing new update: {:?}", e);
app.dialog().message( app.dialog().message(

View file

@ -3,46 +3,13 @@
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
mod clipboard; mod api;
mod database; mod utils;
mod hotkeys;
mod tray;
mod updater;
use tauri::Manager; use tauri::{Manager, Listener};
use tauri::PhysicalPosition;
use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_prevent_default::Flags; use tauri_plugin_prevent_default::Flags;
pub fn center_window_on_current_monitor(window: &tauri::WebviewWindow) {
if let Some(monitor) = window.available_monitors().unwrap().iter().find(|m| {
let primary_monitor = window
.primary_monitor()
.unwrap()
.expect("Failed to get primary monitor");
let mouse_position = primary_monitor.position();
let monitor_position = m.position();
let monitor_size = m.size();
mouse_position.x >= monitor_position.x
&& mouse_position.x < monitor_position.x + monitor_size.width as i32
&& mouse_position.y >= monitor_position.y
&& mouse_position.y < monitor_position.y + monitor_size.height as i32
}) {
let monitor_size = monitor.size();
let window_size = window.outer_size().unwrap();
let x = (monitor_size.width as i32 - window_size.width as i32) / 2;
let y = (monitor_size.height as i32 - window_size.height as i32) / 2;
window
.set_position(PhysicalPosition::new(
monitor.position().x + x,
monitor.position().y + y,
))
.unwrap();
}
}
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_clipboard::init()) .plugin(tauri_plugin_clipboard::init())
@ -54,6 +21,11 @@ fn main() {
MacosLauncher::LaunchAgent, MacosLauncher::LaunchAgent,
Some(vec![]), Some(vec![]),
)) ))
.plugin(api::updater::init())
.plugin(api::database::init())
.plugin(api::tray::init())
.plugin(api::hotkeys::init())
.plugin(api::clipboard::init())
.plugin( .plugin(
tauri_plugin_prevent_default::Builder::new() tauri_plugin_prevent_default::Builder::new()
.with_flags(Flags::all().difference(Flags::CONTEXT_MENU)) .with_flags(Flags::all().difference(Flags::CONTEXT_MENU))
@ -62,49 +34,41 @@ fn main() {
.setup(|app| { .setup(|app| {
let app_handle = app.handle().clone(); let app_handle = app.handle().clone();
hotkeys::setup(app_handle.clone());
tray::setup(app)?;
database::setup(app)?;
clipboard::setup(app.handle());
let _ = clipboard::start_monitor(app_handle.clone());
if let Some(window) = app.get_webview_window("main") {
center_window_on_current_monitor(&window);
window.hide().unwrap();
}
// #[cfg(dev)]
// {
// let window = app.get_webview_window("main").unwrap();
// window.open_devtools();
// window.close_devtools();
// }
let app_data_dir = app let app_data_dir = app
.path() .path()
.app_data_dir() .app_data_dir()
.expect("Failed to get app data directory"); .expect("Failed to get app data directory");
clipboard::set_app_data_dir(app_data_dir); api::clipboard::set_app_data_dir(app_data_dir);
if let Some(window) = app.get_webview_window("main") {
utils::commands::center_window_on_current_monitor(&window);
window.hide().unwrap();
}
let update_handle = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
updater::check_for_updates(app_handle).await; api::updater::check_for_updates(update_handle).await;
});
let monitor_handle = app_handle.clone();
app_handle.listen("database_initialized", move |_| {
let _ = api::clipboard::start_monitor(monitor_handle.clone());
}); });
Ok(()) Ok(())
}) })
.on_window_event(|app, event| match event { .on_window_event(|app, event| {
#[cfg(not(dev))] #[cfg(not(dev))]
tauri::WindowEvent::Focused(false) => { if let WindowEvent::Focused(false) = event {
if let Some(window) = app.get_webview_window("main") { if let Some(window) = app.get_webview_window("main") {
window.hide().unwrap(); let _ = window.hide();
} }
} }
_ => {}
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
clipboard::simulate_paste, api::clipboard::simulate_paste,
clipboard::get_image_path, api::clipboard::get_image_path,
clipboard::read_image api::clipboard::read_image
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View file

@ -0,0 +1,30 @@
use tauri::PhysicalPosition;
pub fn center_window_on_current_monitor(window: &tauri::WebviewWindow) {
if let Some(monitor) = window.available_monitors().unwrap().iter().find(|m| {
let primary_monitor = window
.primary_monitor()
.unwrap()
.expect("Failed to get primary monitor");
let mouse_position = primary_monitor.position();
let monitor_position = m.position();
let monitor_size = m.size();
mouse_position.x >= monitor_position.x
&& mouse_position.x < monitor_position.x + monitor_size.width as i32
&& mouse_position.y >= monitor_position.y
&& mouse_position.y < monitor_position.y + monitor_size.height as i32
}) {
let monitor_size = monitor.size();
let window_size = window.outer_size().unwrap();
let x = (monitor_size.width as i32 - window_size.width as i32) / 2;
let y = (monitor_size.height as i32 - window_size.height as i32) / 2;
window
.set_position(PhysicalPosition::new(
monitor.position().x + x,
monitor.position().y + y,
))
.unwrap();
}
}

View file

@ -0,0 +1 @@
pub mod commands;

View file

@ -31,7 +31,8 @@
"security": { "security": {
"csp": null "csp": null
}, },
"withGlobalTauri": true "withGlobalTauri": true,
"macOSPrivateApi": true
}, },
"bundle": { "bundle": {
"createUpdaterArtifacts": true, "createUpdaterArtifacts": true,
@ -49,8 +50,7 @@
"updater": { "updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExNDIzNjA1QjE0NjU1OTkKUldTWlZVYXhCVFpDRWNvNmt0UE5lQmZkblEyZGZiZ2tHelJvT2YvNVpLU1RIM1RKZFQrb2tzWWwK", "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExNDIzNjA1QjE0NjU1OTkKUldTWlZVYXhCVFpDRWNvNmt0UE5lQmZkblEyZGZiZ2tHelJvT2YvNVpLU1RIM1RKZFQrb2tzWWwK",
"endpoints": [ "endpoints": [
"https://qopy-updater-server.pandadev.workers.dev/", "https://qopy.pandadev.net/"
"https://qopy.pandadev.net/updater"
] ]
} }
}, },