feat(ui): Phase 2 C2 — Mock 消息模型 + Message list + Empty state

- ui/message-card.slint: export enum Priority + struct MessageItem +
  MessageCard 组件(§7 优先级圆点 + 标题 + 应用·预览 + 相对时间 +
  URL 指示)
- ui/message-list.slint: VerticalLayout + for 渲染 MessageCard
  (避开 std-widgets 的 include_paths 配置需求)
- ui/empty-state.slint: §9 ✦ glyph(Path 自绘)+ "You're all caught up"
  + Test Notification 按钮
- ui/app.slint: messages.length 切换 list / empty-state;点击回
  调到 MainWindow 顶层 callback
- src/models.rs: mock_messages() 生成 12 条覆盖 4 个 priority 的
  假消息;format_relative() 输出 "2 min ago" / Yesterday 等
- src/lib.rs: ui.set_messages(mock_messages_model()) 装载;wire
  on_test_notification_clicked / on_message_card_clicked

避坑:
- Slint 1.18 不允许覆盖 Rectangle 内建 border-color,命名用 card-border
- ModelRc::from 需要 Rc<VecModel<T>> 不是裸 VecModel

验证:cargo build / build --release 都 exit 0,0 warning
- debug   26.55 MB, subsystem = WINDOWS_CUI
- release  7.94 MB, subsystem = WINDOWS_GUI
This commit is contained in:
2026-09-19 04:39:24 +08:00
parent 49cbd9e371
commit 11a122216b
6 changed files with 436 additions and 21 deletions
+19 -5
View File
@@ -1,17 +1,18 @@
//! Windows Gotify Client (Rust) — core library.
//!
//! Phase 2 C1 only:
//! Phase 2 surface:
//! - [`error`]: AppError / AppResult
//! - [`logging`]: tracing-subscriber init
//! - [`models`]: MessageItem / Priority (re-exported from slint-generated
//! types) + the mock data feed used until Phase 3 lands.
//! - [`run`]: builds the MainWindow and runs its event loop
//!
//! Larger subsystems (gotify / storage / windows integration / mock data)
//! land in later commits per AGENTS.md §57.
//! Larger subsystems (gotify / storage / windows integration) land in later
//! phases per AGENTS.md §57.
pub mod error;
pub mod logging;
use slint::ComponentHandle;
pub mod models;
slint::include_modules!();
@@ -31,6 +32,19 @@ pub fn run() -> error::AppResult<()> {
// of the Settings page and to `state.json` so the choice persists.
ui.set_theme(AppearanceMode::Dark);
// Phase 2 C2: hook up the two top-level callbacks. C6/Phase 6 swap these
// for real Windows toast firing.
ui.on_test_notification_clicked(|| {
tracing::info!("empty-state: Test Notification clicked (stub)");
});
ui.on_message_card_clicked(|idx| {
tracing::debug!(idx, "message card clicked");
});
// Phase 2 C2: feed mock data into the message list. Phase 3 swaps this
// for an HTTP catch-up + WebSocket live stream.
ui.set_messages(models::mock_messages_model());
ui.run()
.map_err(|e| error::AppError::Ui(e.to_string()))
}