feat(desktop): Client meldet sich per desktop=1, Beta-Hinweis nennt den Stand

- with_desktop_marker haengt desktop=1 an beide Navigationen (save_server_url,
  Startnavigation im setup); der Store-Wert server_url bleibt ohne Parameter
- update_labels liefert Menue-/Benachrichtigungstext: bei Versionswechsel wie
  bisher, bei gleicher Version (Beta-Kanal) nennt der Text den Commit-Stempel
- mod tests deckt beide Helfer ab (fmt/check/clippy/test --lib gruen)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 12:35:20 +02:00
parent 98fad866bf
commit 5bdabf558b
+105 -13
View File
@@ -30,6 +30,47 @@ fn api_url(server: &str, path: &str) -> String {
format!("{}/api-proxy{}", server.trim_end_matches('/'), path) format!("{}/api-proxy{}", server.trim_end_matches('/'), path)
} }
/// Haengt das Query-Paar `desktop=1` an einen Klon der Adresse an und gibt
/// den Klon zurueck -- das Original bleibt unveraendert. Der Parameter geht
/// NUR in die Navigation, niemals in den gespeicherten Server-Wert
/// (`server_url` im Store bleibt die reine Adresse); daraus liest die
/// Next.js-Middleware (apps/web/src/middleware.ts) das Cookie
/// `tessera_desktop`, an dem der Web-Client den Desktop-Client erkennt. Die
/// Tray-URL "Update herunterladen" bekommt den Parameter bewusst NICHT --
/// sie oeffnet im System-Browser, dort muessen die Download-Links sichtbar
/// bleiben.
fn with_desktop_marker(url: &tauri::Url) -> tauri::Url {
let mut marked = url.clone();
marked.query_pairs_mut().append_pair("desktop", "1");
marked
}
/// Liefert (Menuetext, Benachrichtigungstext) fuer den Update-Hinweis. Der
/// Beta-Kanal (main) vergibt jedem Commit dieselbe X.Y.Z-Version (D-07,
/// desktop-collect.sh) -- hat sich nur der Commit geaendert, waere der reine
/// Versionstext ("Version 1.1.0 herunterladen") verwirrend, weil der Client
/// bereits genau diese Version hat. Der zweite Zweig nennt darum den
/// Commit-Stempel statt der unveraenderten Version.
fn update_labels(version_changed: bool, version: &str, commit: &str) -> (String, String) {
if version_changed {
(
format!("Version {} herunterladen", version),
format!(
"Neue Version {} verfügbar – Download über das Symbol im Infobereich.",
version
),
)
} else {
(
"Neuen Beta-Stand herunterladen".to_string(),
format!(
"Neuer Beta-Stand {} verfügbar – Download über das Symbol im Infobereich.",
commit
),
)
}
}
#[tauri::command] #[tauri::command]
async fn check_server(url: String) -> Result<String, String> { async fn check_server(url: String) -> Result<String, String> {
let parsed = tauri::Url::parse(&url).map_err(|_| "Diese Adresse ist ungültig.".to_string())?; let parsed = tauri::Url::parse(&url).map_err(|_| "Diese Adresse ist ungültig.".to_string())?;
@@ -73,7 +114,7 @@ fn save_server_url(app: AppHandle, url: String) -> Result<(), String> {
store.save().map_err(|e| e.to_string())?; store.save().map_err(|e| e.to_string())?;
if let Some(window) = app.get_webview_window("main") { if let Some(window) = app.get_webview_window("main") {
let _ = window.navigate(parsed); let _ = window.navigate(with_desktop_marker(&parsed));
} }
Ok(()) Ok(())
@@ -84,7 +125,10 @@ pub fn run() {
.plugin(tauri_plugin_store::Builder::new().build()) .plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_window_state::Builder::default().build()) .plugin(tauri_plugin_window_state::Builder::default().build())
.plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, None)) .plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
None,
))
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![check_server, save_server_url]) .invoke_handler(tauri::generate_handler![check_server, save_server_url])
.setup(|app| { .setup(|app| {
@@ -99,7 +143,7 @@ pub fn run() {
url_for_check = Some(url_str.to_string()); url_for_check = Some(url_str.to_string());
if let Some(window) = app.get_webview_window("main") { if let Some(window) = app.get_webview_window("main") {
if let Ok(parsed) = url_str.parse::<tauri::Url>() { if let Ok(parsed) = url_str.parse::<tauri::Url>() {
let _ = window.navigate(parsed); let _ = window.navigate(with_desktop_marker(&parsed));
} }
} }
} }
@@ -151,8 +195,10 @@ pub fn run() {
} }
"update" => { "update" => {
if let Some(server) = &server_for_menu { if let Some(server) = &server_for_menu {
let target = let target = format!(
format!("{}/settings/general/desktop", server.trim_end_matches('/')); "{}/settings/general/desktop",
server.trim_end_matches('/')
);
let _ = app.opener().open_url(target, None::<&str>); let _ = app.opener().open_url(target, None::<&str>);
} }
} }
@@ -208,21 +254,21 @@ pub fn run() {
// (D-07, desktop-collect.sh) -- ohne den Commit-Vergleich saehe // (D-07, desktop-collect.sh) -- ohne den Commit-Vergleich saehe
// ein Beta-Client zwischen zwei Freigabe-Tags nie einen neueren // ein Beta-Client zwischen zwei Freigabe-Tags nie einen neueren
// Bau (WR-02, Code-Review Phase 18). Fuer den Live-Kanal bleibt // Bau (WR-02, Code-Review Phase 18). Fuer den Live-Kanal bleibt
// es beim reinen Versionsvergleich. // es beim reinen Versionsvergleich. Die Texte fuer Menue und
let is_newer = info.version != app_version // Benachrichtigung liefert `update_labels`.
let version_changed = info.version != app_version;
let is_newer = version_changed
|| (info.channel == "beta" && info.commit != app_commit); || (info.channel == "beta" && info.commit != app_commit);
if is_newer { if is_newer {
let (menu_text, body) =
update_labels(version_changed, &info.version, &info.commit);
let _ = app_handle let _ = app_handle
.notification() .notification()
.builder() .builder()
.title("Tessera-Update") .title("Tessera-Update")
.body(format!( .body(body)
"Neue Version {} verfügbar – Download über das Symbol im Infobereich.",
info.version
))
.show(); .show();
let _ = update_item let _ = update_item.set_text(menu_text);
.set_text(format!("Version {} herunterladen", info.version));
let _ = update_item.set_enabled(true); let _ = update_item.set_enabled(true);
} }
} }
@@ -255,3 +301,49 @@ pub fn run() {
} }
}); });
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_desktop_marker_ohne_pfad_haengt_query_an() {
let url = tauri::Url::parse("https://tessera.example.com").unwrap();
let marked = with_desktop_marker(&url);
assert_eq!(marked.as_str(), "https://tessera.example.com/?desktop=1");
}
#[test]
fn with_desktop_marker_mit_vorhandenem_query_haengt_an() {
let url = tauri::Url::parse("https://host/app?x=1").unwrap();
let marked = with_desktop_marker(&url);
assert_eq!(marked.as_str(), "https://host/app?x=1&desktop=1");
}
#[test]
fn with_desktop_marker_laesst_original_unveraendert() {
let url = tauri::Url::parse("https://host/app").unwrap();
let _ = with_desktop_marker(&url);
assert_eq!(url.query(), None);
}
#[test]
fn update_labels_bei_versionswechsel() {
let (menu_text, body) = update_labels(true, "1.2.0", "abc1234");
assert_eq!(menu_text, "Version 1.2.0 herunterladen");
assert_eq!(
body,
"Neue Version 1.2.0 verfügbar – Download über das Symbol im Infobereich."
);
}
#[test]
fn update_labels_bei_gleicher_version_beta_commit() {
let (menu_text, body) = update_labels(false, "1.1.0", "abc1234");
assert_eq!(menu_text, "Neuen Beta-Stand herunterladen");
assert_eq!(
body,
"Neuer Beta-Stand abc1234 verfügbar – Download über das Symbol im Infobereich."
);
}
}