#[cfg(target_os = "macos")] #[macro_use] extern crate cocoa; #[cfg(target_os = "macos")] #[macro_use] extern crate objc; use tauri::utils::config::BackgroundThrottlingPolicy; #[cfg(target_os = "macos")] use tauri::TitleBarStyle; #[cfg(desktop)] use std::path::PathBuf; #[cfg(desktop)] use tauri::{AppHandle, Listener, Manager, Url}; #[cfg(desktop)] use tauri_plugin_fs::FsExt; #[cfg(target_os = "macos")] mod macos; mod transfer_file; use tauri::{command, Emitter, WebviewUrl, WebviewWindowBuilder, Window}; use tauri_plugin_oauth::start; use transfer_file::{download_file, upload_file}; #[cfg(desktop)] fn allow_file_in_scopes(app: &AppHandle, files: Vec) { let fs_scope = app.fs_scope(); let asset_protocol_scope = app.asset_protocol_scope(); for file in &files { if let Err(e) = fs_scope.allow_file(file) { eprintln!("Failed to allow file in fs_scope: {e}"); } else { println!("Allowed file in fs_scope: {file:?}"); } if let Err(e) = asset_protocol_scope.allow_file(file) { eprintln!("Failed to allow file in asset_protocol_scope: {e}"); } else { println!("Allowed file in asset_protocol_scope: {file:?}"); } } } #[cfg(desktop)] fn allow_dir_in_scopes(app: &AppHandle, dir: &PathBuf) { let fs_scope = app.fs_scope(); let asset_protocol_scope = app.asset_protocol_scope(); if let Err(e) = fs_scope.allow_directory(dir, true) { eprintln!("Failed to allow directory in fs_scope: {e}"); } else { println!("Allowed directory in fs_scope: {dir:?}"); } if let Err(e) = asset_protocol_scope.allow_directory(dir, true) { eprintln!("Failed to allow directory in asset_protocol_scope: {e}"); } else { println!("Allowed directory in asset_protocol_scope: {dir:?}"); } } #[cfg(desktop)] fn get_files_from_argv(argv: Vec) -> Vec { let mut files = Vec::new(); // NOTICE: `args` may include URL protocol (`your-app-protocol://`) // or arguments (`--`) if your app supports them. // files may also be passed as `file://path/to/file` for (_, maybe_file) in argv.iter().enumerate().skip(1) { // skip flags like -f or --flag if maybe_file.starts_with("-") { continue; } // handle `file://` path urls and skip other urls if let Ok(url) = Url::parse(maybe_file) { if let Ok(path) = url.to_file_path() { files.push(path); } else { files.push(PathBuf::from(maybe_file)) } } else { files.push(PathBuf::from(maybe_file)) } } files } #[cfg(desktop)] fn set_window_open_with_files(app: &AppHandle, files: Vec) { let files = files .into_iter() .map(|f| { let file = f .to_string_lossy() .replace("\\", "\\\\") .replace("\"", "\\\""); format!("\"{file}\"",) }) .collect::>() .join(","); let window = app.get_webview_window("main").unwrap(); let script = format!("window.OPEN_WITH_FILES = [{files}];"); if let Err(e) = window.eval(&script) { eprintln!("Failed to set open files variable: {e}"); } } #[command] async fn start_server(window: Window) -> Result { start(move |url| { // Because of the unprotected localhost port, you must verify the URL here. // Preferebly send back only the token, or nothing at all if you can handle everything else in Rust. let _ = window.emit("redirect_uri", url); }) .map_err(|err| err.to_string()) } #[tauri::command] fn get_environment_variable(name: &str) -> String { std::env::var(String::from(name)).unwrap_or(String::from("")) } #[tauri::command] fn get_executable_dir() -> String { std::env::current_exe() .ok() .and_then(|path| path.parent().map(|p| p.to_path_buf())) .map(|p| p.to_string_lossy().to_string()) .unwrap_or_default() } #[derive(Clone, serde::Serialize)] #[allow(dead_code)] struct Payload { args: Vec, cwd: String, } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let builder = tauri::Builder::default() .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_oauth::init()) .invoke_handler(tauri::generate_handler![ start_server, download_file, upload_file, get_environment_variable, get_executable_dir, #[cfg(target_os = "macos")] macos::safari_auth::auth_with_safari, #[cfg(target_os = "macos")] macos::apple_auth::start_apple_sign_in, #[cfg(target_os = "macos")] macos::traffic_light::set_traffic_lights, ]) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_persisted_scope::init()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_native_bridge::init()) .plugin(tauri_plugin_native_tts::init()); #[cfg(desktop)] let builder = builder.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { let _ = app .get_webview_window("main") .expect("no main window") .set_focus(); let files = get_files_from_argv(argv.clone()); if !files.is_empty() { allow_file_in_scopes(app, files.clone()); } app.emit("single-instance", Payload { args: argv, cwd }) .unwrap(); })); let builder = builder.plugin(tauri_plugin_deep_link::init()); #[cfg(desktop)] let builder = builder.plugin(tauri_plugin_updater::Builder::new().build()); #[cfg(desktop)] let builder = builder.plugin(tauri_plugin_window_state::Builder::default().build()); #[cfg(target_os = "macos")] let builder = builder.plugin(macos::traffic_light::init()); #[cfg(target_os = "macos")] let builder = builder.plugin(macos::safari_auth::init()); #[cfg(target_os = "ios")] let builder = builder.plugin(tauri_plugin_sign_in_with_apple::init()); #[cfg(any(target_os = "ios", target_os = "android"))] let builder = builder.plugin(tauri_plugin_haptics::init()); builder .setup(|#[allow(unused_variables)] app| { #[cfg(desktop)] { let files = get_files_from_argv(std::env::args().collect()); if !files.is_empty() { let app_handle = app.handle().clone(); allow_file_in_scopes(&app_handle, files.clone()); app.listen("window-ready", move |_| { println!("Window is ready, proceeding to handle files."); set_window_open_with_files(&app_handle, files.clone()); }); } } #[cfg(desktop)] { allow_dir_in_scopes(app.handle(), &PathBuf::from(get_executable_dir())); } #[cfg(desktop)] { app.handle().plugin(tauri_plugin_cli::init())?; let app_handle = app.handle().clone(); app.listen("window-ready", move |_| { let webview = app_handle.get_webview_window("main").unwrap(); webview .eval("window.__READEST_CLI_ACCESS = true;") .expect("Failed to set cli access config"); #[cfg(target_os = "linux")] { let is_appimage = std::env::var("APPIMAGE").is_ok() || std::env::current_exe() .map(|path| path.to_string_lossy().contains("/tmp/.mount_")) .unwrap_or(false); let script = format!("window.__READEST_UPDATER_DISABLED = {};", !is_appimage); webview .eval(&script) .expect("Failed to set updater disabled config"); } }); } #[cfg(any(target_os = "windows", target_os = "linux"))] { use tauri_plugin_deep_link::DeepLinkExt; let _ = app.deep_link().register_all(); } if let Err(e) = app.handle().plugin( tauri_plugin_log::Builder::default() .level(log::LevelFilter::Info) .build(), ) { eprintln!("Failed to initialize tauri_plugin_log: {e}"); }; let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) .background_throttling(BackgroundThrottlingPolicy::Disabled) .background_color(tauri::window::Color(50, 49, 48, 255)); #[cfg(desktop)] let win_builder = win_builder.inner_size(800.0, 600.0).resizable(true); #[cfg(target_os = "macos")] let win_builder = win_builder .decorations(true) .title_bar_style(TitleBarStyle::Overlay) .title(""); #[cfg(all(not(target_os = "macos"), desktop))] let win_builder = { let mut builder = win_builder .decorations(false) .visible(false) .shadow(true) .title("Readest"); #[cfg(target_os = "windows")] { builder = builder.transparent(false); } #[cfg(target_os = "linux")] { builder = builder .transparent(true) .background_color(tauri::window::Color(0, 0, 0, 0)); } builder }; win_builder.build().unwrap(); // let win = win_builder.build().unwrap(); // win.open_devtools(); #[cfg(target_os = "macos")] macos::menu::setup_macos_menu(app.handle())?; app.handle().emit("window-ready", ()).unwrap(); Ok(()) }) .build(tauri::generate_context!()) .expect("error while running tauri application") .run( #[allow(unused_variables)] |app_handle, event| { #[cfg(target_os = "macos")] if let tauri::RunEvent::Opened { urls } = event { let files = urls .into_iter() .filter_map(|url| url.to_file_path().ok()) .collect::>(); let app_handler_clone = app_handle.clone(); allow_file_in_scopes(app_handle, files.clone()); app_handle.listen("window-ready", move |_| { println!("Window is ready, proceeding to handle files."); set_window_open_with_files(&app_handler_clone, files.clone()); }); } }, ); }