b53cf9d6e7
Установка обновления всегда спрашивала согласие диалогом, поэтому сквозная проверка «поверх старой версии» не автоматизировалась (в headless-прогоне кликнуть некому), а на управляемых машинах обновление ставит оператор, а не пользователь. С флагом `--update-now` обёртка проверяет обновление сразу при старте (не через 30 секунд) и ставит найденное без диалога, после чего перезапускается; при отсутствии обновления работает как обычно. По умолчанию флаг выключен — трей, страница настроек и фоновая проверка по-прежнему спрашивают согласие. Подпись артефакта проверяется в обоих режимах: флаг убирает вопрос, но не проверку. Ход тихого обновления виден в журнале, а в строку запуска добавлена версия сборки — иначе после перезапуска не понять, какая версия работает. Решение — D-077, приёмка — desktop/README.md («Локальная проверка»).
144 lines
5.8 KiB
Rust
144 lines
5.8 KiB
Rust
//! Команды обёртки: типизированный мост «веб-клиент → 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<R: Runtime>(app: AppHandle<R>) -> Settings {
|
|
app.state::<AppState>().settings()
|
|
}
|
|
|
|
/// Версия приложения — показывается на странице настроек.
|
|
#[tauri::command]
|
|
pub fn desktop_version<R: Runtime>(app: AppHandle<R>) -> String {
|
|
app.package_info().version.to_string()
|
|
}
|
|
|
|
/// Сохраняет настройки и применяет их: автозапуск, шорткаты, адрес инстанса.
|
|
#[tauri::command]
|
|
pub fn desktop_save_settings<R: Runtime>(
|
|
app: AppHandle<R>,
|
|
settings: Settings,
|
|
) -> Result<Settings, String> {
|
|
// Адрес проверяем до применения, чтобы не оставить обёртку без клиента.
|
|
let url = instance::normalize(&settings.instance_url)?;
|
|
let mut next = settings;
|
|
next.instance_url = instance::display(&url);
|
|
|
|
let previous = app.state::<AppState>().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::<AppState>().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<R: Runtime>(app: AppHandle<R>) {
|
|
let pending = app.state::<AppState>().mark_web_ready();
|
|
for url in pending {
|
|
bridge::dispatch(&app, &WebMessage::DeepLink { url });
|
|
}
|
|
}
|
|
|
|
/// Показывает системное уведомление.
|
|
#[tauri::command]
|
|
pub fn desktop_notify<R: Runtime>(
|
|
app: AppHandle<R>,
|
|
payload: notifications::NotifyPayload,
|
|
) -> Result<(), String> {
|
|
notifications::show(&app, &payload)
|
|
}
|
|
|
|
/// Обновляет счётчик непрочитанных в доке и трее.
|
|
#[tauri::command]
|
|
pub fn desktop_set_badge<R: Runtime>(app: AppHandle<R>, count: u32) {
|
|
notifications::set_badge(&app, count);
|
|
}
|
|
|
|
/// Веб-клиент сообщает, есть ли несохранённые изменения: от этого зависит
|
|
/// перехват закрытия окна (docs/client-tauri.md §5).
|
|
#[tauri::command]
|
|
pub fn desktop_set_dirty<R: Runtime>(app: AppHandle<R>, dirty: bool) {
|
|
app.state::<AppState>().set_dirty(dirty);
|
|
}
|
|
|
|
/// Закрывает приложение по решению пользователя в диалоге клиента
|
|
/// («Сохранить» после успешного сохранения или «Выйти без сохранения»).
|
|
#[tauri::command]
|
|
pub fn desktop_close_window<R: Runtime>(app: AppHandle<R>) {
|
|
log::info!("закрываем приложение по решению пользователя");
|
|
app.exit(0);
|
|
}
|
|
|
|
/// Открывает локальную страницу настроек обёртки.
|
|
#[tauri::command]
|
|
pub fn desktop_open_settings<R: Runtime>(app: AppHandle<R>) -> Result<(), String> {
|
|
window::open_settings_page(&app)
|
|
}
|
|
|
|
/// Возвращает окно к клиенту инстанса.
|
|
#[tauri::command]
|
|
pub fn desktop_open_instance<R: Runtime>(app: AppHandle<R>) -> Result<(), String> {
|
|
window::open_instance_page(&app)
|
|
}
|
|
|
|
/// Перезагружает клиент инстанса.
|
|
#[tauri::command]
|
|
pub fn desktop_reload<R: Runtime>(app: AppHandle<R>) -> Result<(), String> {
|
|
window::reload_instance(&app)
|
|
}
|
|
|
|
/// Проверяет обновления (кнопка на странице настроек).
|
|
#[tauri::command]
|
|
pub async fn desktop_check_updates<R: Runtime>(app: AppHandle<R>) {
|
|
updates::check(app, true, false).await;
|
|
}
|
|
|
|
/// Открывает ссылку в системном браузере.
|
|
#[tauri::command]
|
|
pub fn desktop_open_external<R: Runtime>(app: AppHandle<R>, 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<R: Runtime>(app: &AppHandle<R>, settings: &Settings) {
|
|
bridge::send(
|
|
app,
|
|
&WebMessage::Settings {
|
|
settings: settings.clone(),
|
|
},
|
|
);
|
|
}
|