// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT mod cmd; #[cfg(desktop)] mod menu_plugin; #[cfg(desktop)] mod tray; use serde::Serialize; use tauri::{ ipc::Channel, webview::{PageLoadEvent, WebviewWindowBuilder}, App, AppHandle, Manager, RunEvent, Runtime, WebviewUrl, }; use tauri_plugin_sample::{PingRequest, SampleExt}; pub type SetupHook = Box Result<(), Box> + Send>; pub type OnEvent = Box; #[derive(Clone, Serialize)] struct Reply { data: String, } #[cfg(target_os = "macos")] pub struct AppMenu(pub std::sync::Mutex>>); #[cfg(desktop)] pub struct PopupMenu(tauri::menu::Menu); #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { run_app(tauri::Builder::default(), |_app| {}) } pub fn run_app) + Send + 'static>( builder: tauri::Builder, setup: F, ) { #[allow(unused_mut)] let mut builder = builder .plugin(tauri_plugin_sample::init()) .setup(move |app| { #[cfg(all(desktop, not(test)))] { let handle = app.handle(); tray::create_tray(handle)?; handle.plugin(menu_plugin::init())?; } #[cfg(target_os = "macos")] app.manage(AppMenu::(Default::default())); #[cfg(all(desktop, not(test)))] app.manage(PopupMenu( tauri::menu::MenuBuilder::new(app) .check("check", "Tauri is awesome!") .text("text", "Do something") .copy() .build()?, )); let mut window_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()); #[cfg(all(desktop, not(test)))] { window_builder = window_builder .title("Tauri API Validation") .inner_size(1000., 800.) .min_inner_size(600., 400.) .content_protected(true) .menu(tauri::menu::Menu::default(app.handle())?); } let webview = window_builder.build()?; #[cfg(debug_assertions)] webview.open_devtools(); let value = Some("test".to_string()); let response = app.sample().ping(PingRequest { value: value.clone(), on_event: Channel::new(|event| { println!("got channel event: {:?}", event); Ok(()) }), }); log::info!("got response: {:?}", response); if let Ok(res) = response { assert_eq!(res.value, value); } #[cfg(desktop)] std::thread::spawn(|| { let server = match tiny_http::Server::http("localhost:3003") { Ok(s) => s, Err(e) => { eprintln!("{}", e); std::process::exit(1); } }; loop { if let Ok(mut request) = server.recv() { let mut body = Vec::new(); let _ = request.as_reader().read_to_end(&mut body); let response = tiny_http::Response::new( tiny_http::StatusCode(200), request.headers().to_vec(), std::io::Cursor::new(body), request.body_length(), None, ); let _ = request.respond(response); } } }); setup(app); Ok(()) }) .on_page_load(|webview, payload| { if payload.event() == PageLoadEvent::Finished { let webview_ = webview.clone(); webview.listen("js-event", move |event| { println!("got js-event with message '{:?}'", event.payload()); let reply = Reply { data: "something else".to_string(), }; webview_ .emit("rust-event", Some(reply)) .expect("failed to emit"); }); } }); #[allow(unused_mut)] let mut app = builder .invoke_handler(tauri::generate_handler![ cmd::log_operation, cmd::perform_request, ]) .build(tauri::tauri_build_context!()) .expect("error while building tauri application"); #[cfg(target_os = "macos")] app.set_activation_policy(tauri::ActivationPolicy::Regular); app.run(move |_app_handle, _event| { #[cfg(all(desktop, not(test)))] match &_event { RunEvent::ExitRequested { api, code, .. } => { // Keep the event loop running even if all windows are closed // This allow us to catch tray icon events when there is no window // if we manually requested an exit (code is Some(_)) we will let it go through if code.is_none() { api.prevent_exit(); } } RunEvent::WindowEvent { event: tauri::WindowEvent::CloseRequested { api, .. }, label, .. } => { println!("closing window..."); // run the window destroy manually just for fun :) // usually you'd show a dialog here to ask for confirmation or whatever api.prevent_close(); _app_handle .get_webview_window(label) .unwrap() .destroy() .unwrap(); } _ => (), } }) } #[cfg(test)] mod tests { use tauri::Manager; #[test] fn run_app() { super::run_app(tauri::test::mock_builder(), |app| { let window = app.get_webview_window("main").unwrap(); std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_secs(1)); window.close().unwrap(); }); }) } }