feat(gotify): Phase 3 C2 — UI 接线 + Connection status + 真实数据

- src/config.rs: AppConfig::placeholder() 硬编码 NAS 地址 + 假 token
  (§20 token 永不日志;Phase 5 接持久化前占位)
- src/models.rs: gotify_messages_to_items 把 GotifyMessage 列表转
  Slint MessageItem[];try_fetch_real_messages 一次完成 health →
  applications → messages(50);失败 None 让调用方 fallback mock
- src/gotify/mod.rs: re-export ServerConfig
- src/lib.rs: run() 启动时调 try_fetch_real_messages,成功用真实数
  据 + Connected + 服务端 URL,失败 fallback mock + Disconnected;
  on_test_connection_requested 重新跑同一路径
- Cargo.toml: 加 chrono 0.4 (RFC3339 解析)
- ui/app.slint: is-connected (绿/灰 dot) + status-text (顶部副标)
- ui/settings-page.slint: Connection section 加 Test Connection 按钮
  (in-progress 时禁用显示 "Testing...")

避坑:
- Gotify RFC3339 date 解析失败 silent fallback 到 unix=0(显示
  "just now" 而不是崩)
- Gotify priority 0..10 → Slint Priority 4 桶分桶:<=0 Low / <=3
  Normal / <=7 High / else Max
- ServerConfig 在 crate::gotify::model 子模块,mod.rs 必须 re-export

验证:cargo build / build --release 都 exit 0,0 warning
- cargo test --test gotify_api: 6 passed
- debug   33.36 MB, subsystem = WINDOWS_CUI
- release  9.43 MB, subsystem = WINDOWS_GUI(chrono +1.3 MB)
This commit is contained in:
2026-09-19 05:18:26 +08:00
parent c6ad3883f8
commit 0cf7a4eaaa
8 changed files with 260 additions and 41 deletions
+29
View File
@@ -0,0 +1,29 @@
//! Application configuration placeholder.
//!
//! Phase 3 ships only a hardcoded `placeholder()` so the UI wiring can
//! demonstrate the real-data path. Phase 5 replaces this with persistent
//! JSON storage at `%APPDATA%/WindowsGotify/config.json` (AGENTS.md §5/§19/§20).
//!
//! AGENTS.md §20 — the placeholder token below is intentionally not a
//! real credential. Real auth fails, the UI falls back to mock data, and
//! nothing about the token ever reaches a log line.
use crate::gotify::ServerConfig;
pub struct AppConfig {
pub server: Option<ServerConfig>,
}
impl AppConfig {
/// Hardcoded single-server placeholder. Returns `None` here means
/// "no server configured" — but for now Phase 3 always returns `Some`
/// so the UI wiring is exercised on every start.
pub fn placeholder() -> Self {
Self {
server: Some(ServerConfig::new(
"http://192.168.50.103:50334",
"placeholder-token-do-not-log",
)),
}
}
}