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:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user