friends list CRD

This commit is contained in:
2026-08-01 02:28:58 +08:00
parent 5539fcc729
commit 7db736e418
7 changed files with 895 additions and 9 deletions
Generated
+752 -4
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -28,6 +28,7 @@ ed25519-dalek = "3.0.0"
getrandom = "0.4.3"
device_query = "4.0.1"
tokio = "1.53.1"
sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate"] }
tauri-specta = { version = "2.0.0-rc.21", features = ["derive", "typescript"] }
specta = "2.0.0-rc.21"
specta-typescript = { version = "0.0.9" }
@@ -0,0 +1,4 @@
CREATE TABLE friends (
id TEXT PRIMARY KEY NOT NULL,
display_name TEXT NOT NULL
);
+48
View File
@@ -0,0 +1,48 @@
use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions};
use sqlx::{Pool, Sqlite};
use std::error::Error;
use tauri::{AppHandle, Manager};
const DATABASE_FILE: &str = "wyd.sqlite";
pub struct AppDatabase {
pool: Pool<Sqlite>,
}
impl AppDatabase {
pub fn new(pool: Pool<Sqlite>) -> Self {
Self { pool }
}
pub fn pool(&self) -> &Pool<Sqlite> {
&self.pool
}
}
pub async fn init(app: &AppHandle) -> Result<(), Box<dyn Error>> {
let app_data_dir = app.path().app_data_dir()?;
std::fs::create_dir_all(&app_data_dir)?;
let db_path = app_data_dir.join(DATABASE_FILE);
let options = SqliteConnectOptions::new()
.filename(db_path)
.create_if_missing(true)
.journal_mode(SqliteJournalMode::Wal)
.foreign_keys(true);
let pool = SqlitePoolOptions::new()
.max_connections(5)
.connect_with(options)
.await?;
sqlx::migrate!("./migrations").run(&pool).await?;
app.manage(AppDatabase::new(pool));
Ok(())
}
#[inline]
pub fn command_error(error: impl std::fmt::Display) -> String {
error.to_string()
}
+63
View File
@@ -0,0 +1,63 @@
use crate::db::{self, AppDatabase};
use serde::{Deserialize, Serialize};
use specta::Type;
use tauri::State;
#[derive(Debug, Clone, Serialize, Deserialize, Type, sqlx::FromRow)]
#[serde(rename_all = "camelCase")]
pub struct Friend {
pub id: String,
pub display_name: String,
}
#[tauri::command]
#[specta::specta]
pub async fn create_friend(
database: State<'_, AppDatabase>,
friend: Friend,
) -> Result<Friend, String> {
sqlx::query("INSERT INTO friends (id, display_name) VALUES (?1, ?2)")
.bind(&friend.id)
.bind(&friend.display_name)
.execute(database.pool())
.await
.map_err(db::command_error)?;
Ok(friend)
}
#[tauri::command]
#[specta::specta]
pub async fn list_friends(database: State<'_, AppDatabase>) -> Result<Vec<Friend>, String> {
sqlx::query_as::<_, Friend>(
"SELECT id, display_name FROM friends ORDER BY display_name COLLATE NOCASE, id",
)
.fetch_all(database.pool())
.await
.map_err(db::command_error)
}
#[tauri::command]
#[specta::specta]
pub async fn get_friend(
database: State<'_, AppDatabase>,
id: String,
) -> Result<Option<Friend>, String> {
sqlx::query_as::<_, Friend>("SELECT id, display_name FROM friends WHERE id = ?1")
.bind(id)
.fetch_optional(database.pool())
.await
.map_err(db::command_error)
}
#[tauri::command]
#[specta::specta]
pub async fn delete_friend(database: State<'_, AppDatabase>, id: String) -> Result<bool, String> {
let result = sqlx::query("DELETE FROM friends WHERE id = ?1")
.bind(id)
.execute(database.pool())
.await
.map_err(db::command_error)?;
Ok(result.rows_affected() > 0)
}
+14 -3
View File
@@ -1,17 +1,28 @@
mod cursor;
mod db;
mod friends;
mod ufa;
mod windowing;
fn launch_app(app: &tauri::App) {
async fn launch_app(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
let handle = app.handle();
crate::db::init(handle).await?;
crate::ufa::init();
crate::cursor::init(handle);
crate::windowing::init(handle);
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let specta_builder = tauri_specta::Builder::<tauri::Wry>::new()
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
.commands(tauri_specta::collect_commands![])
.commands(tauri_specta::collect_commands![
crate::friends::create_friend,
crate::friends::list_friends,
crate::friends::get_friend,
crate::friends::delete_friend
])
.events(tauri_specta::collect_events![]);
#[cfg(debug_assertions)]
@@ -26,7 +37,7 @@ pub fn run() {
.plugin(tauri_plugin_opener::init())
.invoke_handler(specta_builder.invoke_handler())
.setup(|app| {
launch_app(app);
tauri::async_runtime::block_on(launch_app(app))?;
Ok(())
})
.build(tauri::generate_context!())
+13 -2
View File
@@ -5,7 +5,18 @@
export const commands = {
async createFriend(friend: Friend) : Promise<Friend> {
return await TAURI_INVOKE("create_friend", { friend });
},
async listFriends() : Promise<Friend[]> {
return await TAURI_INVOKE("list_friends");
},
async getFriend(id: string) : Promise<Friend | null> {
return await TAURI_INVOKE("get_friend", { id });
},
async deleteFriend(id: string) : Promise<boolean> {
return await TAURI_INVOKE("delete_friend", { id });
}
}
/** user-defined events **/
@@ -18,7 +29,7 @@ export const commands = {
/** user-defined types **/
export type Friend = { id: string; displayName: string }
/** tauri-specta globals **/