feat(desktop): обёртка Tauri 2 вокруг веб-клиента
Первый инкремент Фазы 6 (docs/client-tauri.md): окно с тем же веб-клиентом (по умолчанию https://gl.mhspx.su, адрес настраивается), трей с меню и индикатором непрочитанных, нативные уведомления, глобальный push-to-talk и переключение микрофона/deafen, автозапуск, single-instance, сохранение геометрии окна, deep links glchat://…, автообновление по подписанному манифесту. Связь страницы с обёрткой — через платформенный адаптер (web/src/lib/platform.ts) и минимальный типизированный набор команд; в веб-клиенте появились системные уведомления об упоминаниях и личных сообщениях и переключатель в настройках. В web/src/stores/gateway.ts вместе с вызовом уведомления лежат правки по дефектам realtime (READY и сессии) из параллельной волны: файл коммитится целиком по договорённости с координатором.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
//! Команды обёртки: типизированный мост «веб-клиент → 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);
|
||||
}
|
||||
|
||||
/// Открывает локальную страницу настроек обёртки.
|
||||
#[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).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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user