Replace the website notification card with native desktop notification handling

Hop-State: A_06FPG6WWRGQW1NG5ZQZRSY0
Hop-Proposal: R_06FPG6WDJG2WNB21CHD30R0
Hop-Task: T_06FPG5CWT7W6DEF2HVENX20
Hop-Attempt: AT_06FPG5CWT7BEF2WDEP0BJQG
This commit is contained in:
2026-07-15 16:27:47 -07:00
committed by Hop
parent 1e36c03430
commit 4a408edce8
7 changed files with 609 additions and 18 deletions
+46 -3
View File
@@ -1,11 +1,13 @@
use reqwest::{Client, Method, StatusCode, Url};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use tauri::webview::Cookie;
use tauri::{State, WebviewWindow};
use tauri::{Manager, State, WebviewWindow};
struct AppState {
client: Client,
notification_capability_sequence: AtomicU64,
}
#[derive(Debug, Default, Deserialize, Serialize)]
@@ -251,13 +253,39 @@ async fn has_web_session(webview: WebviewWindow, node_url: String) -> Result<boo
}
#[tauri::command]
async fn open_web_app(webview: WebviewWindow, node_url: String) -> Result<(), String> {
async fn open_web_app(
state: State<'_, AppState>,
webview: WebviewWindow,
node_url: String,
) -> Result<(), String> {
let base = normalize_node(&node_url)?;
let origin = base.origin().ascii_serialization();
let capability_id = state
.notification_capability_sequence
.fetch_add(1, Ordering::Relaxed);
let capability = remote_notification_capability(&origin, capability_id);
webview
.add_capability(capability)
.map_err(|error| format!("Couldnt enable native notifications for this node: {error}"))?;
webview
.navigate(base)
.map_err(|error| format!("Couldnt open the Synapsis web app: {error}"))
}
fn remote_notification_capability(origin: &str, sequence: u64) -> String {
serde_json::json!({
"identifier": format!("synapsis-node-notifications-{sequence}"),
"description": "Allow the active Synapsis node to send native desktop notifications",
"local": false,
"windows": ["main"],
"remote": {
"urls": [format!("{origin}/*")]
},
"permissions": ["notification:default"]
})
.to_string()
}
#[tauri::command]
async fn logout(state: State<'_, AppState>, node_url: String) -> Result<(), String> {
let base = normalize_node(&node_url)?;
@@ -285,7 +313,11 @@ pub fn run() {
.expect("failed to build Synapsis HTTP client");
tauri::Builder::default()
.manage(AppState { client })
.plugin(tauri_plugin_notification::init())
.manage(AppState {
client,
notification_capability_sequence: AtomicU64::new(0),
})
.invoke_handler(tauri::generate_handler![
connect,
login,
@@ -346,4 +378,15 @@ mod tests {
assert_eq!(cookies[0].http_only(), Some(true));
assert_eq!(cookies[0].secure(), Some(true));
}
#[test]
fn scopes_native_notifications_to_the_selected_node_origin() {
let capability = remote_notification_capability("https://example.com", 7);
let value: serde_json::Value = serde_json::from_str(&capability).unwrap();
assert_eq!(value["identifier"], "synapsis-node-notifications-7");
assert_eq!(value["local"], false);
assert_eq!(value["remote"]["urls"][0], "https://example.com/*");
assert_eq!(value["permissions"][0], "notification:default");
}
}