chore: setup tauri
This commit is contained in:
1656
src-tauri/Cargo.lock
generated
1656
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,35 @@ edition = "2021"
|
|||||||
tauri-build = { version = "2.0.0-beta", features = [] }
|
tauri-build = { version = "2.0.0-beta", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2.0.0-beta", features = [] }
|
nostr-sdk = { version = "0.33", features = ["sqlite"] }
|
||||||
|
tauri = { version = "2.0.0-beta", features = [
|
||||||
|
"tray-icon",
|
||||||
|
"macos-private-api",
|
||||||
|
"protocol-asset",
|
||||||
|
] }
|
||||||
tauri-plugin-shell = "2.0.0-beta"
|
tauri-plugin-shell = "2.0.0-beta"
|
||||||
|
tauri-plugin-decorum = "0.1.5"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
keyring = { version = "3", features = [
|
||||||
|
"apple-native",
|
||||||
|
"windows-native",
|
||||||
|
"linux-native",
|
||||||
|
] }
|
||||||
|
keyring-search = "1.2.0"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
panic = "abort"
|
||||||
|
incremental = false
|
||||||
|
opt-level = "z"
|
||||||
|
strip = true
|
||||||
|
rpath = false
|
||||||
|
debug = false
|
||||||
|
debug-assertions = false
|
||||||
|
overflow-checks = false
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": ["main"],
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"path:default",
|
"path:default",
|
||||||
"event:default",
|
"event:default",
|
||||||
@@ -12,6 +14,14 @@
|
|||||||
"resources:default",
|
"resources:default",
|
||||||
"menu:default",
|
"menu:default",
|
||||||
"tray:default",
|
"tray:default",
|
||||||
"shell:allow-open"
|
"shell:allow-open",
|
||||||
|
"window:allow-close",
|
||||||
|
"window:allow-center",
|
||||||
|
"window:allow-minimize",
|
||||||
|
"window:allow-maximize",
|
||||||
|
"window:allow-set-size",
|
||||||
|
"window:allow-set-focus",
|
||||||
|
"window:allow-start-dragging",
|
||||||
|
"decorum:allow-show-snap-overlay"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
100
src-tauri/src/commands/account.rs
Normal file
100
src-tauri/src/commands/account.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use keyring::Entry;
|
||||||
|
use keyring_search::{Limit, List, Search};
|
||||||
|
use nostr_sdk::prelude::*;
|
||||||
|
use std::{collections::HashSet, str::FromStr};
|
||||||
|
use tauri::State;
|
||||||
|
|
||||||
|
use crate::Nostr;
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_accounts() -> Vec<String> {
|
||||||
|
let search = Search::new().expect("Unexpected.");
|
||||||
|
let results = search.by_user("nostr_secret");
|
||||||
|
let list = List::list_credentials(&results, Limit::All);
|
||||||
|
let accounts: HashSet<String> =
|
||||||
|
list.split_whitespace().filter(|v| v.starts_with("npub1")).map(String::from).collect();
|
||||||
|
|
||||||
|
accounts.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_profile(id: String, state: State<'_, Nostr>) -> Result<String, ()> {
|
||||||
|
let client = &state.client;
|
||||||
|
let public_key = PublicKey::from_str(&id).unwrap();
|
||||||
|
let filter = Filter::new().author(public_key).kind(Kind::Metadata).limit(1);
|
||||||
|
|
||||||
|
let events = client.get_events_of(vec![filter], None).await.unwrap();
|
||||||
|
|
||||||
|
if let Some(event) = events.first() {
|
||||||
|
Ok(Metadata::from_json(&event.content).unwrap().as_json())
|
||||||
|
} else {
|
||||||
|
Ok(Metadata::new().as_json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn login(id: String, state: State<'_, Nostr>) -> Result<(), String> {
|
||||||
|
let client = &state.client;
|
||||||
|
let keyring = Entry::new(&id, "nostr_secret").expect("Unexpected.");
|
||||||
|
|
||||||
|
let password = match keyring.get_password() {
|
||||||
|
Ok(pw) => pw,
|
||||||
|
Err(_) => return Err("Cancelled".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let keys = Keys::parse(password).expect("Secret Key is modified, please check again.");
|
||||||
|
let signer = NostrSigner::Keys(keys);
|
||||||
|
|
||||||
|
// Set signer
|
||||||
|
client.set_signer(Some(signer)).await;
|
||||||
|
|
||||||
|
let public_key = PublicKey::from_str(&id).unwrap();
|
||||||
|
let incoming = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||||
|
let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1);
|
||||||
|
|
||||||
|
if let Ok(events) = client.get_events_of(vec![inbox], None).await {
|
||||||
|
if let Some(event) = events.into_iter().next() {
|
||||||
|
for tag in &event.tags {
|
||||||
|
if let Some(TagStandard::Relay(url)) = tag.as_standardized() {
|
||||||
|
let relay = url.to_string();
|
||||||
|
let _ = client.add_relay(&relay).await;
|
||||||
|
let _ = client.connect_relay(&relay).await;
|
||||||
|
|
||||||
|
println!("Connecting to {} ...", relay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(report) = client.reconcile(incoming.clone(), NegentropyOptions::default()).await {
|
||||||
|
let receives = report.received.clone();
|
||||||
|
let ids = receives.into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if let Ok(events) = client.database().query(vec![Filter::new().ids(ids)], Order::Desc).await
|
||||||
|
{
|
||||||
|
let pubkeys = events
|
||||||
|
.into_iter()
|
||||||
|
.unique_by(|ev| ev.pubkey)
|
||||||
|
.map(|ev| ev.pubkey)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if client
|
||||||
|
.reconcile(
|
||||||
|
Filter::new().kind(Kind::GiftWrap).pubkeys(pubkeys),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
println!("Sync done.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.subscribe(vec![incoming.limit(0)], None).await.is_ok() {
|
||||||
|
println!("Waiting for new message...")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
1
src-tauri/src/commands/mod.rs
Normal file
1
src-tauri/src/commands/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod account;
|
||||||
@@ -1,16 +1,66 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
use border::WebviewWindowExt as WebviewWindowExtAlt;
|
||||||
#[tauri::command]
|
use commands::account::{get_accounts, get_profile, login};
|
||||||
fn greet(name: &str) -> String {
|
use nostr_sdk::prelude::*;
|
||||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
use serde::Serialize;
|
||||||
|
use std::{fs, sync::Mutex};
|
||||||
|
use tauri::Manager;
|
||||||
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
|
|
||||||
|
mod commands;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Nostr {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
client: Client,
|
||||||
|
contact_list: Mutex<Vec<Contact>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.setup(|app| {
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
let main_window = app.get_webview_window("main").unwrap();
|
||||||
|
|
||||||
|
// Set custom decoration
|
||||||
|
main_window.create_overlay_titlebar().unwrap();
|
||||||
|
|
||||||
|
// Restore native border
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
main_window.add_border(None);
|
||||||
|
|
||||||
|
tauri::async_runtime::block_on(async move {
|
||||||
|
// Create data folder if not exist
|
||||||
|
let dir = app.path().config_dir().expect("Config Directory not found.");
|
||||||
|
let _ = fs::create_dir_all(dir.join("Coop/"));
|
||||||
|
|
||||||
|
// Setup database
|
||||||
|
let database = SQLiteDatabase::open(dir.join("Coop/coop.db")).await;
|
||||||
|
|
||||||
|
// Setup nostr client
|
||||||
|
let client = match database {
|
||||||
|
Ok(db) => ClientBuilder::default().database(db).build(),
|
||||||
|
Err(_) => ClientBuilder::default().build(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add bootstrap relay
|
||||||
|
let _ = client.add_relay("wss://relay.damus.io/").await;
|
||||||
|
let _ = client.add_relay("wss://relay.nostr.net/").await;
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
client.connect().await;
|
||||||
|
|
||||||
|
// Create global state
|
||||||
|
app.handle().manage(Nostr { client, contact_list: Mutex::new(vec![]) })
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.plugin(tauri_plugin_decorum::init())
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.invoke_handler(tauri::generate_handler![greet])
|
.invoke_handler(tauri::generate_handler![login, get_accounts, get_profile])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"productName": "coop",
|
"productName": "Coop",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"identifier": "com.tauri.dev",
|
"identifier": "com.tauri.dev",
|
||||||
"build": {
|
"build": {
|
||||||
@@ -9,15 +9,27 @@
|
|||||||
"frontendDist": "../dist"
|
"frontendDist": "../dist"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"macOSPrivateApi": true,
|
||||||
{
|
"withGlobalTauri": true,
|
||||||
"title": "coop",
|
|
||||||
"width": 800,
|
|
||||||
"height": 600
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null,
|
||||||
|
"assetProtocol": {
|
||||||
|
"enable": true,
|
||||||
|
"scope": [
|
||||||
|
"$APPDATA/*",
|
||||||
|
"$DATA/*",
|
||||||
|
"$LOCALDATA/*",
|
||||||
|
"$DESKTOP/*",
|
||||||
|
"$DOCUMENT/*",
|
||||||
|
"$DOWNLOAD/*",
|
||||||
|
"$HOME/*",
|
||||||
|
"$PICTURE/*",
|
||||||
|
"$PUBLIC/*",
|
||||||
|
"$VIDEO/*",
|
||||||
|
"$APPCONFIG/*",
|
||||||
|
"$RESOURCE/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|||||||
14
src-tauri/tauri.linux.conf.json
Normal file
14
src-tauri/tauri.linux.conf.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "Coop",
|
||||||
|
"label": "main",
|
||||||
|
"width": 860,
|
||||||
|
"height": 650,
|
||||||
|
"minWidth": 860,
|
||||||
|
"minHeight": 650
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src-tauri/tauri.macos.conf.json
Normal file
23
src-tauri/tauri.macos.conf.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "Coop",
|
||||||
|
"label": "main",
|
||||||
|
"width": 860,
|
||||||
|
"height": 650,
|
||||||
|
"minWidth": 860,
|
||||||
|
"minHeight": 650,
|
||||||
|
"titleBarStyle": "Overlay",
|
||||||
|
"hiddenTitle": true,
|
||||||
|
"transparent": true,
|
||||||
|
"windowEffects": {
|
||||||
|
"state": "followsWindowActiveState",
|
||||||
|
"effects": [
|
||||||
|
"underWindowBackground"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src-tauri/tauri.windows.conf.json
Normal file
20
src-tauri/tauri.windows.conf.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "Coop",
|
||||||
|
"label": "main",
|
||||||
|
"width": 860,
|
||||||
|
"height": 650,
|
||||||
|
"minWidth": 860,
|
||||||
|
"minHeight": 650,
|
||||||
|
"transparent": true,
|
||||||
|
"windowEffects": {
|
||||||
|
"effects": [
|
||||||
|
"mica"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user