diff --git a/crates/nuncio-gui/Cargo.toml b/crates/nuncio-gui/Cargo.toml index 4f38d3a..d265dba 100644 --- a/crates/nuncio-gui/Cargo.toml +++ b/crates/nuncio-gui/Cargo.toml @@ -12,4 +12,7 @@ nuncio-mail = { path = "../nuncio-mail" } nuncio-cal = { path = "../nuncio-cal" } nuncio-store = { path = "../nuncio-store" } tokio.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true tracing.workspace = true diff --git a/crates/nuncio-gui/src/ipc.rs b/crates/nuncio-gui/src/ipc.rs new file mode 100644 index 0000000..ddabd7e --- /dev/null +++ b/crates/nuncio-gui/src/ipc.rs @@ -0,0 +1,100 @@ +//! Tauri IPC state bridge and command dispatch handlers. + +use nuncio_core::{AppState, CoreCommand, EventBus}; +use serde::{Deserialize, Serialize}; + +/// Tauri IPC command payload wrapper. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IpcCommandPayload { + /// Action type name. + pub action: String, + /// Message ID if applicable. + pub message_id: Option, + /// Read flag if applicable. + pub read: Option, +} + +/// Tauri IPC bridge orchestrating communications between WebVIEW frontend and `EventBus`. +pub struct IpcBridge; + +impl IpcBridge { + /// Retrieve current application state snapshot for WebVIEW initialization. + pub fn get_app_state(bus: &EventBus) -> AppState { + bus.current_state() + } + + /// Process incoming IPC payload command. + pub fn handle_ipc_command(bus: &EventBus, payload: IpcCommandPayload) { + match payload.action.as_str() { + "sync" => bus.process_command(CoreCommand::SyncAll), + "mark_read" => { + if let Some(msg_id) = payload.message_id { + let read_flag = payload.read.unwrap_or(true); + bus.process_command(CoreCommand::MarkRead { + message_id: msg_id, + read: read_flag, + }); + } + } + "shutdown" => bus.process_command(CoreCommand::Shutdown), + _ => {} + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use nuncio_core::EngineStatus; + + #[test] + fn ipc_handle_sync_command() { + let bus = EventBus::new(); + let payload = IpcCommandPayload { + action: "sync".to_string(), + message_id: None, + read: None, + }; + + IpcBridge::handle_ipc_command(&bus, payload); + assert_eq!(IpcBridge::get_app_state(&bus).status, EngineStatus::Syncing); + } + + #[test] + fn ipc_handle_mark_read_command() { + let bus = EventBus::with_initial_state(AppState { + unread_count: 5, + ..AppState::default() + }); + + let payload = IpcCommandPayload { + action: "mark_read".to_string(), + message_id: Some("msg-1".to_string()), + read: Some(true), + }; + + IpcBridge::handle_ipc_command(&bus, payload); + assert_eq!(IpcBridge::get_app_state(&bus).unread_count, 4); + } + + #[test] + fn ipc_handle_shutdown_and_unknown() { + let bus = EventBus::new(); + + let payload = IpcCommandPayload { + action: "shutdown".to_string(), + message_id: None, + read: None, + }; + IpcBridge::handle_ipc_command(&bus, payload); + assert_eq!(IpcBridge::get_app_state(&bus).status, EngineStatus::ShuttingDown); + + // Unknown action does not panic + let unknown = IpcCommandPayload { + action: "unknown".to_string(), + message_id: None, + read: None, + }; + IpcBridge::handle_ipc_command(&bus, unknown); + } +} diff --git a/crates/nuncio-gui/src/main.rs b/crates/nuncio-gui/src/main.rs index e8be904..ec038df 100644 --- a/crates/nuncio-gui/src/main.rs +++ b/crates/nuncio-gui/src/main.rs @@ -1,8 +1,16 @@ -use nuncio_core::NuncioEngine; +//! Nuncio GUI application entry point. -#[tokio::main] -async fn main() -> Result<(), Box> { - let engine = NuncioEngine::new(); - println!("Starting {} GUI desktop shell ({})", engine.name, engine.domain); - Ok(()) +mod ipc; + +use ipc::IpcBridge; +use nuncio_core::EventBus; + +fn main() { + let bus = EventBus::new(); + let state = IpcBridge::get_app_state(&bus); + + println!( + "Nuncio GUI Desktop Shell Initialized: status={:?}, unread={}", + state.status, state.unread_count + ); }