feat: add notification screen
This commit is contained in:
@@ -1,70 +1,75 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "desktop-capability",
|
||||
"description": "Capability for the desktop",
|
||||
"platforms": ["linux", "macOS", "windows"],
|
||||
"windows": [
|
||||
"main",
|
||||
"splash",
|
||||
"settings",
|
||||
"search",
|
||||
"nwc",
|
||||
"zap-*",
|
||||
"event-*",
|
||||
"user-*",
|
||||
"editor-*",
|
||||
"column-*"
|
||||
],
|
||||
"permissions": [
|
||||
"path:default",
|
||||
"event:default",
|
||||
"window:default",
|
||||
"app:default",
|
||||
"resources:default",
|
||||
"menu:default",
|
||||
"tray:default",
|
||||
"notification:allow-is-permission-granted",
|
||||
"notification:allow-request-permission",
|
||||
"notification:default",
|
||||
"os:allow-locale",
|
||||
"os:allow-platform",
|
||||
"updater:allow-check",
|
||||
"updater:default",
|
||||
"window:allow-start-dragging",
|
||||
"window:allow-create",
|
||||
"window:allow-close",
|
||||
"window:allow-set-focus",
|
||||
"clipboard-manager:allow-write",
|
||||
"clipboard-manager:allow-read",
|
||||
"webview:allow-create-webview-window",
|
||||
"webview:allow-create-webview",
|
||||
"webview:allow-set-webview-size",
|
||||
"webview:allow-set-webview-position",
|
||||
"webview:allow-webview-close",
|
||||
"dialog:allow-open",
|
||||
"fs:allow-read-file",
|
||||
"shell:allow-open",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "http://**/"
|
||||
},
|
||||
{
|
||||
"url": "https://**/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-read-text-file",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$RESOURCE/locales/*"
|
||||
},
|
||||
{
|
||||
"path": "$RESOURCE/resources/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "desktop-capability",
|
||||
"description": "Capability for the desktop",
|
||||
"platforms": [
|
||||
"linux",
|
||||
"macOS",
|
||||
"windows"
|
||||
],
|
||||
"windows": [
|
||||
"main",
|
||||
"splash",
|
||||
"settings",
|
||||
"search",
|
||||
"nwc",
|
||||
"activity",
|
||||
"zap-*",
|
||||
"event-*",
|
||||
"user-*",
|
||||
"editor-*",
|
||||
"column-*"
|
||||
],
|
||||
"permissions": [
|
||||
"path:default",
|
||||
"event:default",
|
||||
"window:default",
|
||||
"app:default",
|
||||
"resources:default",
|
||||
"menu:default",
|
||||
"tray:default",
|
||||
"notification:allow-is-permission-granted",
|
||||
"notification:allow-request-permission",
|
||||
"notification:default",
|
||||
"os:allow-locale",
|
||||
"os:allow-platform",
|
||||
"updater:allow-check",
|
||||
"updater:default",
|
||||
"window:allow-start-dragging",
|
||||
"window:allow-create",
|
||||
"window:allow-close",
|
||||
"window:allow-set-focus",
|
||||
"clipboard-manager:allow-write",
|
||||
"clipboard-manager:allow-read",
|
||||
"webview:allow-create-webview-window",
|
||||
"webview:allow-create-webview",
|
||||
"webview:allow-set-webview-size",
|
||||
"webview:allow-set-webview-position",
|
||||
"webview:allow-webview-close",
|
||||
"dialog:allow-open",
|
||||
"fs:allow-read-file",
|
||||
"shell:allow-open",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "http://**/"
|
||||
},
|
||||
{
|
||||
"url": "https://**/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-read-text-file",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$RESOURCE/locales/*"
|
||||
},
|
||||
{
|
||||
"path": "$RESOURCE/resources/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","splash","settings","search","nwc","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","updater:allow-check","updater:default","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-set-focus","clipboard-manager:allow-write","clipboard-manager:allow-read","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","fs:allow-read-file","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}}
|
||||
{"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","splash","settings","search","nwc","activity","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","updater:allow-check","updater:default","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-set-focus","clipboard-manager:allow-write","clipboard-manager:allow-read","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","fs:allow-read-file","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}}
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user