cffcec72b8
Первый инкремент Фазы 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 и сессии) из параллельной волны: файл коммитится целиком по договорённости с координатором.
60 lines
2.7 KiB
Rust
60 lines
2.7 KiB
Rust
//! Адрес инстанса и границы навигации.
|
|
//!
|
|
//! Desktop-обёртка загружает тот же клиент, что и браузер, но только с одного
|
|
//! адреса: переходы на сторонние сайты уходят в системный браузер
|
|
//! (docs/client-tauri.md §6).
|
|
|
|
use url::Url;
|
|
|
|
/// Приводит введённый пользователем адрес к абсолютному URL.
|
|
///
|
|
/// Допускаются `http` и `https`; схема по умолчанию — `https`, чтобы в поле
|
|
/// настроек можно было написать просто `gl.mhspx.su`.
|
|
pub fn normalize(input: &str) -> Result<Url, String> {
|
|
let trimmed = input.trim();
|
|
if trimmed.is_empty() {
|
|
return Err("адрес инстанса не заполнен".to_string());
|
|
}
|
|
let candidate = if trimmed.contains("://") {
|
|
trimmed.to_string()
|
|
} else {
|
|
format!("https://{trimmed}")
|
|
};
|
|
let url = Url::parse(&candidate).map_err(|error| format!("некорректный адрес: {error}"))?;
|
|
match url.scheme() {
|
|
"http" | "https" => {}
|
|
other => return Err(format!("поддерживаются только http и https, а не «{other}»")),
|
|
}
|
|
if url.host_str().unwrap_or_default().is_empty() {
|
|
return Err("в адресе нет имени хоста".to_string());
|
|
}
|
|
Ok(url)
|
|
}
|
|
|
|
/// Строка адреса для загрузки в окно: без завершающего слэша.
|
|
pub fn display(url: &Url) -> String {
|
|
url.as_str().trim_end_matches('/').to_string()
|
|
}
|
|
|
|
/// Хост адреса — граница, внутри которой разрешена навигация webview.
|
|
pub fn host(url: &Url) -> String {
|
|
url.host_str().unwrap_or_default().to_ascii_lowercase()
|
|
}
|
|
|
|
/// Разрешена ли навигация внутри окна.
|
|
///
|
|
/// Свой инстанс и локальные страницы обёртки (`tauri://localhost`) — да,
|
|
/// всё остальное открывается в системном браузере.
|
|
pub fn navigation_allowed(target: &Url, instance_host: &str) -> bool {
|
|
match target.scheme() {
|
|
// Локальные ресурсы обёртки: страница настроек.
|
|
"tauri" | "asset" => true,
|
|
"http" | "https" => {
|
|
let host = target.host_str().unwrap_or_default().to_ascii_lowercase();
|
|
// На Windows/Linux локальная страница отдаётся как http://tauri.localhost.
|
|
host == "tauri.localhost" || (!instance_host.is_empty() && host == instance_host)
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|