//! Настройки desktop-обёртки. //! //! Лежат в `tauri-plugin-store` (`settings.json` в каталоге конфигурации //! приложения). Обёртка хранит только то, что относится к платформе: адрес //! инстанса и переключатели поведения. Сессия целиком живёт в cookie webview //! (docs/client-tauri.md §2): токенов и паролей здесь нет. use serde::{Deserialize, Serialize}; use tauri::{AppHandle, Runtime}; use tauri_plugin_store::StoreExt; /// Файл хранилища в каталоге конфигурации приложения. const STORE_FILE: &str = "settings.json"; /// Ключ, под которым лежит объект настроек. const STORE_KEY: &str = "settings"; /// Адрес стенда по умолчанию: тот же клиент, что открывается в браузере. pub const DEFAULT_INSTANCE: &str = "https://gl.mhspx.su"; /// Комбинация push-to-talk по умолчанию. /// /// Голых клавиш (как `V` в браузерном PTT) в глобальном шорткате быть не может: /// системный перехват занял бы клавишу у всех приложений, поэтому по умолчанию /// комбинация с модификаторами, а пустая строка означает «выключено». pub const DEFAULT_PTT_SHORTCUT: &str = "CommandOrControl+Shift+Space"; /// Комбинация «выключить/включить микрофон» по умолчанию. pub const DEFAULT_MUTE_SHORTCUT: &str = "CommandOrControl+Shift+M"; /// Комбинация «не слышу» (deafen) по умолчанию. pub const DEFAULT_DEAFEN_SHORTCUT: &str = "CommandOrControl+Shift+D"; /// Настройки обёртки. Поля в camelCase — их читает и пишет страница настроек. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default, rename_all = "camelCase")] pub struct Settings { /// Адрес инстанса, который загружается в главное окно. pub instance_url: String, /// Закрытие окна сворачивает приложение в трей. pub close_to_tray: bool, /// Запускать приложение при входе в систему (свёрнутым в трей). pub autostart: bool, /// Пользователю уже задавали вопрос про автозапуск при первом запуске. pub autostart_prompted: bool, /// Глобальный push-to-talk (пустая строка — выключен). pub ptt_shortcut: String, /// Глобальное переключение микрофона (пустая строка — выключено). pub mute_shortcut: String, /// Глобальное переключение «не слышу» (пустая строка — выключено). pub deafen_shortcut: String, /// Показывать нативные уведомления об упоминаниях и личных сообщениях. pub notifications: bool, } impl Default for Settings { fn default() -> Self { Self { instance_url: DEFAULT_INSTANCE.to_string(), close_to_tray: true, autostart: false, autostart_prompted: false, ptt_shortcut: DEFAULT_PTT_SHORTCUT.to_string(), mute_shortcut: DEFAULT_MUTE_SHORTCUT.to_string(), deafen_shortcut: DEFAULT_DEAFEN_SHORTCUT.to_string(), notifications: true, } } } /// Читает настройки из хранилища; при отсутствии или порче — значения по умолчанию. pub fn load(app: &AppHandle) -> Settings { let Ok(store) = app.store(STORE_FILE) else { log::warn!("не удалось открыть хранилище настроек, используются значения по умолчанию"); return Settings::default(); }; let Some(value) = store.get(STORE_KEY) else { return Settings::default(); }; match serde_json::from_value::(value) { Ok(settings) => settings, Err(error) => { log::warn!("настройки в хранилище повреждены ({error}), берём значения по умолчанию"); Settings::default() } } } /// Сохраняет настройки в хранилище. pub fn save(app: &AppHandle, settings: &Settings) -> Result<(), String> { let store = app.store(STORE_FILE).map_err(|error| error.to_string())?; let value = serde_json::to_value(settings).map_err(|error| error.to_string())?; store.set(STORE_KEY, value); store.save().map_err(|error| error.to_string()) }