mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 16:56:16 +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:
@@ -62,6 +62,7 @@ import PromptPanel from "@/components/prompts/PromptPanel";
|
||||
import { SkillsPage } from "@/components/skills/SkillsPage";
|
||||
import UnifiedSkillsPanel from "@/components/skills/UnifiedSkillsPanel";
|
||||
import { DeepLinkImportDialog } from "@/components/DeepLinkImportDialog";
|
||||
import { FirstRunNoticeDialog } from "@/components/FirstRunNoticeDialog";
|
||||
import { AgentsPanel } from "@/components/agents/AgentsPanel";
|
||||
import { UniversalProviderPanel } from "@/components/universal";
|
||||
import { McpIcon } from "@/components/BrandIcons";
|
||||
@@ -1354,6 +1355,7 @@ function App() {
|
||||
/>
|
||||
|
||||
<DeepLinkImportDialog />
|
||||
<FirstRunNoticeDialog />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Sparkles } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useSettingsQuery } from "@/lib/query";
|
||||
import { settingsApi } from "@/lib/api";
|
||||
|
||||
/** 首次运行欢迎提示:仅当后端启动阶段保留 firstRunNoticeConfirmed 为空时弹出。 */
|
||||
export function FirstRunNoticeDialog() {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: settings } = useSettingsQuery();
|
||||
|
||||
// 后端启动时已经决定好要不要弹:条件不满足的话字段会立即被写成 true,
|
||||
// 所以前端这里只需要判空即可——完全对齐 streamCheckConfirmed 等既有 flag 的模式。
|
||||
const isOpen = settings != null && settings.firstRunNoticeConfirmed !== true;
|
||||
|
||||
const handleAcknowledge = async () => {
|
||||
if (!settings) return;
|
||||
try {
|
||||
const { webdavSync: _, ...rest } = settings;
|
||||
await settingsApi.save({ ...rest, firstRunNoticeConfirmed: true });
|
||||
await queryClient.invalidateQueries({ queryKey: ["settings"] });
|
||||
} catch (error) {
|
||||
console.error("Failed to save firstRunNoticeConfirmed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) void handleAcknowledge();
|
||||
}}
|
||||
>
|
||||
<DialogContent className="max-w-md" zIndex="top">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Sparkles className="h-5 w-5 text-blue-500" />
|
||||
{t("firstRunNotice.title")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 px-6 py-5">
|
||||
<DialogDescription className="whitespace-pre-line leading-relaxed">
|
||||
{t("firstRunNotice.bodyDefault")}
|
||||
</DialogDescription>
|
||||
<DialogDescription className="whitespace-pre-line leading-relaxed">
|
||||
{t("firstRunNotice.bodyOfficial")}
|
||||
</DialogDescription>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button onClick={handleAcknowledge}>
|
||||
{t("firstRunNotice.confirm")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -42,6 +42,12 @@
|
||||
"enabled": "Enabled",
|
||||
"notSet": "Not Set"
|
||||
},
|
||||
"firstRunNotice": {
|
||||
"title": "Welcome to CC Switch",
|
||||
"bodyDefault": "CC Switch lets you switch between multiple Claude Code / Codex / Gemini CLI providers with a single click. If you already had these tools configured, CC Switch has saved your existing setup as a provider named “default” so none of your previous configuration is lost.",
|
||||
"bodyOfficial": "An “Official” preset is also included in the list — click it any time you want to switch back to the official default. CC Switch automatically backs up your current config to “default” before switching, so you can move back and forth freely. That's how CC Switch works 😊",
|
||||
"confirm": "Got it"
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "Enter API Key",
|
||||
"show": "Show API Key",
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
"enabled": "有効",
|
||||
"notSet": "未設定"
|
||||
},
|
||||
"firstRunNotice": {
|
||||
"title": "CC Switch へようこそ",
|
||||
"bodyDefault": "CC Switch は Claude Code / Codex / Gemini CLI の複数プロバイダーをワンクリックで切り替えられるツールです。すでにこれらのツールを設定済みの場合、CC Switch は現在の設定を「default」という名前のプロバイダーとして自動的に保存するので、既存の設定が失われることはありません。",
|
||||
"bodyOfficial": "リストには「Official(公式)」プリセットも用意されており、公式バージョンに戻したくなったらクリックするだけで切り替えられます。切り替える前に現在の設定は自動で default にバックアップされるので、自由に行き来できます。これが CC Switch の仕組みです 😊",
|
||||
"confirm": "了解しました"
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "API Key を入力",
|
||||
"show": "API Key を表示",
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
"enabled": "已开启",
|
||||
"notSet": "未设置"
|
||||
},
|
||||
"firstRunNotice": {
|
||||
"title": "欢迎使用 CC Switch",
|
||||
"bodyDefault": "CC Switch 可以帮你在 Claude Code / Codex / Gemini CLI 的多个供应商之间一键切换。如果你之前已经配置过这些工具,CC Switch 会自动把现有设置保存为名为 “default” 的供应商,保证你的配置不会丢失。",
|
||||
"bodyOfficial": "列表里还预置了 “官方(Official)” 供应商,随时点一下即可切回官方默认配置。切换前 CC Switch 会自动把当前配置备份回 default,可以放心来回切。CC Switch 就是这样工作的 😊",
|
||||
"confirm": "我知道了"
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "请输入API Key",
|
||||
"show": "显示API Key",
|
||||
|
||||
@@ -264,6 +264,8 @@ export interface Settings {
|
||||
enableFailoverToggle?: boolean;
|
||||
// User has confirmed the failover toggle first-run notice
|
||||
failoverConfirmed?: boolean;
|
||||
// User has confirmed the first-run welcome notice
|
||||
firstRunNoticeConfirmed?: boolean;
|
||||
// User has confirmed the auto-sync traffic warning
|
||||
autoSyncConfirmed?: boolean;
|
||||
// 首选语言(可选,默认中文)
|
||||
|
||||
Reference in New Issue
Block a user