feat(app-iced): M114 账户页三轨登录(微软正版 / 第三方皮肤站 / 本地档案)
- 账户页:当前账户卡 + 全部账户列表 + 空态三入口 + 皮肤区;五项动作(添加/切换/删除/重新登录/校验) - 三轨添加流程:设备码(用户码+链接+倒计时+过期重试+取消)、授权码(浏览器+可复制链接+取消)、 本地档案(名字校验)、第三方站(服务地址 + 服务发现校验 + 密码登录,密码掩码且用完即弃) - 导航:账户为一等页,入口是轨底头像位(未登录=描边剪影 / 已登录=首字母块+名字;窄高收成图标条) - 凭据红线:DTO 侧形状脱敏 + 状态层二次脱敏 + 密码掩码;Inject-JWT 测试断言视图模型干净(已做变异取证) - presenter:authlib 登录/服务发现/皮肤抓取(复用下载引擎的 HTTP 客户端,无新增依赖)、 皮肤原图经 DTO 到 UI 裁剪成头像(既有 image 依赖,未改 Cargo.toml) - i18n:新增 account 域 51 条中英文案(离线档案口径全量写入),金表同步
This commit is contained in:
1 parent
e7b04c4afe
commit
1ff00bbe50
43 files changed
+4343
-117
No files matched your search
@@ -0,0 +1,158 @@
|
||||
//! 两条登录流的面板态(微软设备码/授权码 + 第三方站校验/登录)。
|
||||
//!
|
||||
//! 从 `model.rs` 拆出来守 400 行硬线:这些类型是「UI 要画什么」的枚举,
|
||||
//! 判定逻辑(倒计时 → 已过期、服务发现 → 可用)也在这里,页面与表单只消费结果。
|
||||
|
||||
use unlucky_presenter::dto::{DeviceLoginPhase, DeviceLoginView};
|
||||
|
||||
/// 微软登录面板要画什么(每态都不同,**不允许只有一个转圈**)。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DevicePanel {
|
||||
/// 没有登录在跑
|
||||
Hidden,
|
||||
/// 正在申请设备码
|
||||
Requesting {
|
||||
/// 状态文案(presenter 给的,已脱敏)
|
||||
message: String,
|
||||
},
|
||||
/// 设备码等待用户操作
|
||||
Code {
|
||||
/// 用户码
|
||||
code: String,
|
||||
/// 验证链接
|
||||
link: String,
|
||||
/// 剩余秒数
|
||||
remaining: Option<u64>,
|
||||
/// 是否已过期
|
||||
expired: bool,
|
||||
/// 状态文案
|
||||
message: String,
|
||||
},
|
||||
/// 授权码流:等浏览器回调
|
||||
Browser {
|
||||
/// 授权链接(可复制)
|
||||
link: String,
|
||||
/// 剩余秒数
|
||||
remaining: Option<u64>,
|
||||
/// 是否已过期
|
||||
expired: bool,
|
||||
/// 状态文案
|
||||
message: String,
|
||||
},
|
||||
/// 失败(原文已脱敏)
|
||||
Failed {
|
||||
/// 失败原因
|
||||
message: String,
|
||||
},
|
||||
/// 成功
|
||||
Succeeded {
|
||||
/// 账户名
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// 第三方轨面板的可见状态。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AuthlibPanel {
|
||||
/// 没在跑
|
||||
Idle,
|
||||
/// 正在校验/登录
|
||||
Running {
|
||||
/// 状态文案
|
||||
message: String,
|
||||
},
|
||||
/// 地址校验通过(服务发现拿到服务器名)
|
||||
Ready {
|
||||
/// 服务器名
|
||||
server_name: String,
|
||||
/// 状态文案
|
||||
message: String,
|
||||
},
|
||||
/// 失败(原文已脱敏)
|
||||
Failed {
|
||||
/// 失败原因
|
||||
message: String,
|
||||
},
|
||||
/// 成功
|
||||
Succeeded {
|
||||
/// 账户名
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// 微软登录状态 → 面板态(倒计时与「已过期」都在这里定性,UI 只画)。
|
||||
pub(super) fn device_panel(view: &DeviceLoginView, remaining: Option<u64>) -> DevicePanel {
|
||||
let expired = remaining == Some(0);
|
||||
match view.phase {
|
||||
DeviceLoginPhase::Idle => DevicePanel::Hidden,
|
||||
DeviceLoginPhase::Requesting => DevicePanel::Requesting {
|
||||
message: view.message.clone(),
|
||||
},
|
||||
DeviceLoginPhase::Waiting => {
|
||||
if view.user_code.trim().is_empty() {
|
||||
DevicePanel::Browser {
|
||||
link: view.verification_uri.clone(),
|
||||
remaining,
|
||||
expired,
|
||||
message: view.message.clone(),
|
||||
}
|
||||
} else {
|
||||
DevicePanel::Code {
|
||||
code: view.user_code.clone(),
|
||||
link: view.qr_link.clone(),
|
||||
remaining,
|
||||
expired,
|
||||
message: view.message.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
DeviceLoginPhase::Failed => DevicePanel::Failed {
|
||||
message: view.message.clone(),
|
||||
},
|
||||
DeviceLoginPhase::Succeeded => DevicePanel::Succeeded {
|
||||
name: view.account_name.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 第三方轨状态 → 面板态(顺带取出「校验通过的服务器名」)。
|
||||
pub(super) fn authlib_panel(
|
||||
view: &unlucky_presenter::dto::AuthlibLoginView,
|
||||
) -> (AuthlibPanel, String) {
|
||||
match view.phase {
|
||||
DeviceLoginPhase::Idle => (AuthlibPanel::Idle, String::new()),
|
||||
DeviceLoginPhase::Requesting => (
|
||||
AuthlibPanel::Running {
|
||||
message: view.message.clone(),
|
||||
},
|
||||
String::new(),
|
||||
),
|
||||
DeviceLoginPhase::Waiting => (
|
||||
AuthlibPanel::Ready {
|
||||
server_name: view.server_name.clone(),
|
||||
message: view.message.clone(),
|
||||
},
|
||||
view.server_name.clone(),
|
||||
),
|
||||
DeviceLoginPhase::Succeeded => (
|
||||
AuthlibPanel::Succeeded {
|
||||
name: view.account_name.clone(),
|
||||
},
|
||||
view.server_name.clone(),
|
||||
),
|
||||
DeviceLoginPhase::Failed => (
|
||||
AuthlibPanel::Failed {
|
||||
message: view.message.clone(),
|
||||
},
|
||||
String::new(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthlibPanel {
|
||||
/// 是否正在跑(此时表单字段禁用,避免改了一半的地址被提交)。
|
||||
#[must_use]
|
||||
pub fn busy(&self) -> bool {
|
||||
matches!(self, Self::Running { .. })
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user