feat: add notification screen

This commit is contained in:
reya
2024-05-06 15:17:34 +07:00
parent 28337e5915
commit c843626bca
37 changed files with 729 additions and 263 deletions

View File

@@ -2,6 +2,7 @@ use std::path::PathBuf;
use tauri::utils::config::WindowEffectsConfig;
use tauri::window::Effect;
use tauri::TitleBarStyle;
use tauri::Url;
use tauri::WebviewWindowBuilder;
use tauri::{LogicalPosition, LogicalSize, Manager, WebviewUrl};
@@ -54,6 +55,30 @@ pub fn close_column(label: &str, app_handle: tauri::AppHandle) -> Result<bool, (
}
}
#[tauri::command]
pub fn get_path(label: &str, app_handle: tauri::AppHandle) -> Result<String, String> {
match app_handle.get_webview(label) {
Some(webview) => Ok(webview.url().to_string()),
None => Err("Webview not found".into()),
}
}
#[tauri::command]
pub fn navigate(label: &str, url: &str, app_handle: tauri::AppHandle) -> Result<(), String> {
match app_handle.get_webview(label) {
Some(mut webview) => {
if let Ok(new_url) = Url::parse(url) {
println!("navigate to: {}", new_url);
webview.navigate(new_url);
Ok(())
} else {
Err("URL is not valid".into())
}
}
None => Err("Webview not found".into()),
}
}
#[tauri::command]
pub fn reposition_column(
label: &str,
@@ -125,6 +150,11 @@ pub fn open_window(
})
.build()
.unwrap();
// [macOS] Custom traffic light possition
// #[cfg(target_os = "macos")]
// setup_traffic_light_positioner(app_handle.get_window(label).unwrap());
#[cfg(not(target_os = "macos"))]
let _ = WebviewWindowBuilder::new(&app_handle, label, WebviewUrl::App(PathBuf::from(url)))
.title(title)

View File

@@ -29,20 +29,18 @@ pub struct Nostr {
fn main() {
tauri::Builder::default()
.setup(|app| {
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Regular);
#[cfg(target_os = "macos")]
setup_traffic_light_positioner(app.get_window("main").unwrap());
let _tray = tray::create_tray(app.handle()).unwrap();
// Setup app tray
let handle = app.handle().clone();
let home_dir = handle.path().home_dir().unwrap();
let _ = tray::create_tray(app.handle()).unwrap();
// create data folder if not exist
// Create data folder if not exist
let home_dir = app.path().home_dir().unwrap();
fs::create_dir_all(home_dir.join("Lume/")).unwrap();
tauri::async_runtime::spawn(async move {
tauri::async_runtime::block_on(async move {
// Create nostr database connection
let sqlite = SQLiteDatabase::open(home_dir.join("Lume/lume.db")).await;
@@ -59,13 +57,9 @@ fn main() {
.await
.expect("Cannot connect to relay.nostr.net, please try again later.");
client
.add_relay("wss://relay.nostr.band")
.add_relay("wss://bostr.nokotaro.work/")
.await
.expect("Cannot connect to relay.nostr.band, please try again later.");
client
.add_relay("wss://welcome.nostr.wine")
.await
.expect("Cannot connect to welcome.nostr.wine, please try again later.");
.expect("Cannot connect to bostr.nokotaro.work, please try again later.");
// Connect
client.connect().await;
@@ -111,7 +105,8 @@ fn main() {
nostr::keys::user_to_bech32,
nostr::keys::to_npub,
nostr::keys::verify_nip05,
nostr::metadata::connect_user_relays,
nostr::metadata::run_notification,
nostr::metadata::get_activities,
nostr::metadata::get_current_user_profile,
nostr::metadata::get_profile,
nostr::metadata::get_contact_list,
@@ -141,7 +136,9 @@ fn main() {
commands::window::close_column,
commands::window::reposition_column,
commands::window::resize_column,
commands::window::open_window
commands::window::open_window,
commands::window::get_path,
commands::window::navigate
])
.run(tauri::generate_context!())
.expect("error while running tauri application")

View File

@@ -54,7 +54,7 @@ pub async fn get_events_from(
};
let filter = Filter::new()
.kinds(vec![Kind::TextNote, Kind::Repost])
.authors(vec![author])
.author(author)
.limit(limit)
.until(until);

View File

@@ -172,6 +172,41 @@ pub async fn load_selected_account(npub: &str, state: State<'_, Nostr>) -> Resul
client.set_signer(Some(signer)).await;
}
// Verify signer
let signer = client.signer().await.unwrap();
let public_key = signer.public_key().await.unwrap();
// Connect to user's relay
let filter = Filter::new()
.author(public_key)
.kind(Kind::RelayList)
.limit(1);
match client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.await
{
Ok(events) => {
if let Some(event) = events.first() {
let relay_list = nip65::extract_relay_list(&event);
for item in relay_list.into_iter() {
println!("connecting to relay: {}", item.0.to_string());
// Add relay to pool
let _ = client
.add_relay(item.0.to_string())
.await
.unwrap_or_default();
// Connect relay
let _ = client
.connect_relay(item.0.to_string())
.await
.unwrap_or_default();
}
}
}
Err(_) => todo!(),
};
Ok(true)
}
Err(err) => Err(err.to_string()),

View File

@@ -2,7 +2,7 @@ use crate::Nostr;
use keyring::Entry;
use nostr_sdk::prelude::*;
use std::{str::FromStr, time::Duration};
use tauri::State;
use tauri::{Manager, State};
use url::Url;
#[derive(serde::Serialize)]
@@ -12,37 +12,80 @@ pub struct CacheContact {
}
#[tauri::command]
pub async fn connect_user_relays(state: State<'_, Nostr>) -> Result<(), ()> {
let client = &state.client;
let signer = client.signer().await.unwrap();
let public_key = signer.public_key().await.unwrap();
pub fn run_notification(accounts: Vec<String>, app: tauri::AppHandle) -> Result<(), ()> {
tauri::async_runtime::spawn(async move {
let window = app.get_window("main").unwrap();
let state = window.state::<Nostr>();
let client = &state.client;
let pubkeys: Vec<PublicKey> = accounts
.into_iter()
.map(|f| PublicKey::from_bech32(f).unwrap())
.collect();
let subscription = Filter::new()
.pubkeys(pubkeys)
.kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::ZapReceipt,
Kind::EncryptedDirectMessage,
])
.since(Timestamp::now());
let activity_id = SubscriptionId::new("activity");
// Get user's relay list
let filter = Filter::new()
.author(public_key)
.kind(Kind::RelayList)
.limit(1);
let query = client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.await;
// Create a subscription for activity
client
.subscribe_with_id(activity_id.clone(), vec![subscription], None)
.await;
// Connect user's relay list
if let Ok(events) = query {
if let Some(event) = events.first() {
let list = nip65::extract_relay_list(&event);
for item in list.into_iter() {
println!("connecting to relay: {}", item.0.to_string());
client
.connect_relay(item.0.to_string())
.await
.unwrap_or_default();
}
}
}
// Handle notifications
let _ = client
.handle_notifications(|notification| async {
if let RelayPoolNotification::Event {
subscription_id,
event,
..
} = notification
{
if subscription_id == activity_id {
let _ = app.emit_to("main", "activity", event.as_json());
}
}
Ok(false)
})
.await;
});
Ok(())
}
#[tauri::command]
pub async fn get_activities(
account: &str,
kind: &str,
state: State<'_, Nostr>,
) -> Result<Vec<Event>, String> {
let client = &state.client;
if let Ok(pubkey) = PublicKey::from_str(account) {
if let Ok(kind) = Kind::from_str(kind) {
let filter = Filter::new()
.pubkey(pubkey)
.kind(kind)
.limit(100)
.until(Timestamp::now());
match client.get_events_of(vec![filter], None).await {
Ok(events) => Ok(events),
Err(err) => Err(err.to_string()),
}
} else {
Err("Kind is not valid, please check again.".into())
}
} else {
Err("Public Key is not valid, please check again.".into())
}
}
#[tauri::command]
pub async fn get_current_user_profile(state: State<'_, Nostr>) -> Result<Metadata, String> {
let client = &state.client;