mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
feat(welcome): show first-run welcome dialog on fresh install
Introduce a one-time welcome dialog that explains CC Switch's workflow to new users: how their existing config is preserved as a "default" provider and how the bundled "Official" preset enables one-click revert. Upgrade users are excluded by checking is_providers_empty() at startup and never see the dialog. Persistence follows the existing *_confirmed convention in AppSettings (proxy/usage/stream_check/failover), stored in settings.json. The field is only written when the user explicitly clicks the confirm button, keeping its semantics strictly about user acknowledgement. Also adds two reusable DAO helpers: - Database::is_providers_empty for fresh-install detection, using EXISTS(SELECT 1) for a short-circuit query. - Database::get_bool_flag accepting "true" | "1", with init_default_official_providers migrated to use it. Dialog copy in zh/en/ja uses conditional phrasing so it stays accurate whether or not existing live config was found.
This commit is contained in:
@@ -502,6 +502,20 @@ impl Database {
|
||||
}))
|
||||
}
|
||||
|
||||
/// 判断 providers 表是否为空(全 app_type 一起算)。
|
||||
///
|
||||
/// 用于区分"全新安装"和"升级用户":在启动流程 import/seed 之前调用。
|
||||
/// 使用 `EXISTS` 短路查询,比 `COUNT(*)` 在将来表变大时更高效。
|
||||
pub fn is_providers_empty(&self) -> Result<bool, AppError> {
|
||||
let conn = lock_conn!(self.conn);
|
||||
let exists: bool = conn
|
||||
.query_row("SELECT EXISTS(SELECT 1 FROM providers)", [], |row| {
|
||||
row.get(0)
|
||||
})
|
||||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||||
Ok(!exists)
|
||||
}
|
||||
|
||||
/// 仅获取指定 app 下所有 provider 的 id 集合。
|
||||
///
|
||||
/// 比 `get_all_providers` 轻量得多:只读 id 列、无 endpoint 子查询。
|
||||
@@ -568,11 +582,11 @@ impl Database {
|
||||
pub fn init_default_official_providers(&self) -> Result<usize, AppError> {
|
||||
use crate::database::dao::providers_seed::OFFICIAL_SEEDS;
|
||||
|
||||
// flag 检查:已执行过则跳过
|
||||
if let Ok(Some(flag)) = self.get_setting("official_providers_seeded") {
|
||||
if flag == "true" || flag == "1" {
|
||||
return Ok(0);
|
||||
}
|
||||
if self
|
||||
.get_bool_flag("official_providers_seeded")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut inserted = 0_usize;
|
||||
|
||||
@@ -33,6 +33,18 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
/// 以布尔语义读取 flag:`"true"` 或 `"1"` → true,其它全部 false。
|
||||
///
|
||||
/// 用于一次性启动 flag(`official_providers_seeded` / `first_run_notice_shown` 等)。
|
||||
/// 与 `is_legacy_common_config_migrated` 等只认 `"true"` 的历史辅助函数**不同**——
|
||||
/// 这里同时接受 `"1"` 是为了兼容 `init_default_official_providers` 既有写法。
|
||||
pub fn get_bool_flag(&self, key: &str) -> Result<bool, AppError> {
|
||||
Ok(matches!(
|
||||
self.get_setting(key)?.as_deref(),
|
||||
Some("true") | Some("1")
|
||||
))
|
||||
}
|
||||
|
||||
/// 设置值
|
||||
pub fn set_setting(&self, key: &str, value: &str) -> Result<(), AppError> {
|
||||
let conn = lock_conn!(self.conn);
|
||||
|
||||
@@ -472,6 +472,15 @@ pub fn run() {
|
||||
// 先 import 后 seed 是有意为之:先把用户手动配置的 settings.json / auth.json / .env
|
||||
// 落成 "default" provider 设为 current,再追加官方预设(is_current=false)。
|
||||
// 这样用户切到官方预设时,回填机制会保护原 live 配置不丢失。
|
||||
//
|
||||
// 捕获首次运行快照:所有全新装用户都会看到欢迎弹窗介绍 CC Switch 的工作方式。
|
||||
// 读失败时默认不弹,宁可漏弹也不要因为故障打扰用户。
|
||||
let first_run_already_confirmed = crate::settings::get_settings()
|
||||
.first_run_notice_confirmed
|
||||
.unwrap_or(false);
|
||||
let fresh_install_at_startup =
|
||||
app_state.db.is_providers_empty().unwrap_or(false);
|
||||
|
||||
for app_type in
|
||||
crate::app_config::AppType::all().filter(|t| !t.is_additive_mode())
|
||||
{
|
||||
@@ -502,6 +511,12 @@ pub fn run() {
|
||||
Err(e) => log::warn!("✗ Failed to seed official providers: {e}"),
|
||||
}
|
||||
|
||||
// 老用户 / 已确认的路径由 `fresh_install_at_startup` 自行拦截,这里不做写入。
|
||||
// 字段只由前端在用户点击"我知道了"时 save_settings 回写,语义是"用户显式确认过"。
|
||||
if !first_run_already_confirmed && fresh_install_at_startup {
|
||||
log::info!("✓ First-run welcome notice pending");
|
||||
}
|
||||
|
||||
// 1.6. 自动同步 OpenCode / OpenClaw 的 live providers 到数据库
|
||||
//
|
||||
// additive 模式(OpenCode / OpenClaw)的 import 函数本身按 id 幂等,
|
||||
|
||||
@@ -205,6 +205,9 @@ pub struct AppSettings {
|
||||
/// User has confirmed the failover toggle first-run notice
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub failover_confirmed: Option<bool>,
|
||||
/// User has confirmed the first-run welcome notice
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub first_run_notice_confirmed: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub language: Option<String>,
|
||||
|
||||
@@ -297,6 +300,7 @@ impl Default for AppSettings {
|
||||
stream_check_confirmed: None,
|
||||
enable_failover_toggle: false,
|
||||
failover_confirmed: None,
|
||||
first_run_notice_confirmed: None,
|
||||
language: None,
|
||||
visible_apps: None,
|
||||
claude_config_dir: None,
|
||||
|
||||
Reference in New Issue
Block a user