//! Команды обёртки: типизированный мост «веб-клиент → Rust» (docs/client-tauri.md §2). //! //! Команд ровно столько, сколько нужно странице: прочитать и сохранить //! настройки, сообщить о готовности, показать уведомление, обновить счётчик //! непрочитанных, открыть настройки/клиент и проверить обновления. Никакой //! бизнес-логики здесь нет — только платформенные действия. use tauri::{AppHandle, Manager, Runtime}; use crate::bridge::{self, WebMessage}; use crate::settings::Settings; use crate::state::AppState; use crate::{autostart, instance, notifications, shortcuts, updates, window}; /// Текущие настройки обёртки. #[tauri::command] pub fn desktop_settings(app: AppHandle) -> Settings { app.state::().settings() } /// Версия приложения — показывается на странице настроек. #[tauri::command] pub fn desktop_version(app: AppHandle) -> String { app.package_info().version.to_string() } /// Сохраняет настройки и применяет их: автозапуск, шорткаты, адрес инстанса. #[tauri::command] pub fn desktop_save_settings( app: AppHandle, settings: Settings, ) -> Result { // Адрес проверяем до применения, чтобы не оставить обёртку без клиента. let url = instance::normalize(&settings.instance_url)?; let mut next = settings; next.instance_url = instance::display(&url); let previous = app.state::().settings(); // Шорткаты применяем первыми: если комбинация занята, настройки не сохраняем. if next.ptt_shortcut != previous.ptt_shortcut || next.mute_shortcut != previous.mute_shortcut || next.deafen_shortcut != previous.deafen_shortcut { if let Err(error) = shortcuts::apply(&app, &next) { let _ = shortcuts::apply(&app, &previous); return Err(error); } } if let Err(error) = autostart::apply(&app, next.autostart) { log::warn!("{error}"); } app.state::().set_settings(next.clone()); crate::settings::save(&app, &next)?; crate::tray::refresh(&app); announce_settings(&app, &next); if next.instance_url != previous.instance_url { window::open_instance_page(&app)?; } Ok(next) } /// Веб-клиент загрузился: отдаём накопленные deep link. #[tauri::command] pub fn desktop_ready(app: AppHandle) { let pending = app.state::().mark_web_ready(); for url in pending { bridge::dispatch(&app, &WebMessage::DeepLink { url }); } } /// Показывает системное уведомление. #[tauri::command] pub fn desktop_notify( app: AppHandle, payload: notifications::NotifyPayload, ) -> Result<(), String> { notifications::show(&app, &payload) } /// Обновляет счётчик непрочитанных в доке и трее. #[tauri::command] pub fn desktop_set_badge(app: AppHandle, count: u32) { notifications::set_badge(&app, count); } /// Веб-клиент сообщает, есть ли несохранённые изменения: от этого зависит /// перехват закрытия окна (docs/client-tauri.md §5). #[tauri::command] pub fn desktop_set_dirty(app: AppHandle, dirty: bool) { app.state::().set_dirty(dirty); } /// Закрывает приложение по решению пользователя в диалоге клиента /// («Сохранить» после успешного сохранения или «Выйти без сохранения»). #[tauri::command] pub fn desktop_close_window(app: AppHandle) { log::info!("закрываем приложение по решению пользователя"); app.exit(0); } /// Открывает локальную страницу настроек обёртки. #[tauri::command] pub fn desktop_open_settings(app: AppHandle) -> Result<(), String> { window::open_settings_page(&app) } /// Возвращает окно к клиенту инстанса. #[tauri::command] pub fn desktop_open_instance(app: AppHandle) -> Result<(), String> { window::open_instance_page(&app) } /// Перезагружает клиент инстанса. #[tauri::command] pub fn desktop_reload(app: AppHandle) -> Result<(), String> { window::reload_instance(&app) } /// Проверяет обновления (кнопка на странице настроек). #[tauri::command] pub async fn desktop_check_updates(app: AppHandle) { updates::check(app, true, false).await; } /// Открывает ссылку в системном браузере. #[tauri::command] pub fn desktop_open_external(app: AppHandle, url: String) -> Result<(), String> { let parsed = instance::normalize(&url)?; tauri_plugin_opener::OpenerExt::opener(&app) .open_url(parsed.as_str(), None::<&str>) .map_err(|error| format!("не удалось открыть ссылку: {error}")) } /// Сообщает веб-клиенту, что настройки обёртки изменились. pub fn announce_settings(app: &AppHandle, settings: &Settings) { bridge::send( app, &WebMessage::Settings { settings: settings.clone(), }, ); }