Files
cc-switch/src/components/settings/SettingsDialog.tsx

296 lines
9.2 KiB
TypeScript
Raw Normal View History

import { useCallback, useEffect, useMemo, useState } from "react";
import { Loader2, Save } from "lucide-react";
import { toast } from "sonner";
feat: complete stage 4 cleanup and code formatting This commit completes stage 4 of the refactoring plan, focusing on cleanup and optimization of the modernized codebase. ## Key Changes ### Code Cleanup - Remove legacy `src/lib/styles.ts` (no longer needed) - Remove old modal components (`ImportProgressModal.tsx`, `ProviderList.tsx`) - Streamline `src/lib/tauri-api.ts` from 712 lines to 17 lines (-97.6%) - Remove global `window.api` pollution - Keep only event listeners (`tauriEvents.onProviderSwitched`) - All API calls now use modular `@/lib/api/*` layer ### Type System - Clean up `src/vite-env.d.ts` (remove 156 lines of outdated types) - Remove obsolete global type declarations - All TypeScript checks pass with zero errors ### Code Formatting - Format all source files with Prettier (82 files) - Fix formatting issues in 15 files: - App.tsx and core components - MCP management components - Settings module components - Provider management components - UI components ### Documentation Updates - Update `REFACTORING_CHECKLIST.md` with stage 4 progress - Mark completed tasks in `REFACTORING_MASTER_PLAN.md` ## Impact **Code Reduction:** - Total: -1,753 lines, +384 lines (net -1,369 lines) - tauri-api.ts: 712 → 17 lines (-97.6%) - Removed styles.ts: -82 lines - Removed vite-env.d.ts declarations: -156 lines **Quality Improvements:** - ✅ Zero TypeScript errors - ✅ Zero TODO/FIXME comments - ✅ 100% Prettier compliant - ✅ Zero `window.api` references - ✅ Fully modular API layer ## Testing - [x] TypeScript compilation passes - [x] Code formatting validated - [x] No linting errors Stage 4 completion: 100% Ready for stage 5 (testing and bug fixes)
2025-10-16 12:13:51 +08:00
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { settingsApi } from "@/lib/api";
import { LanguageSettings } from "@/components/settings/LanguageSettings";
import { ThemeSettings } from "@/components/settings/ThemeSettings";
import { WindowSettings } from "@/components/settings/WindowSettings";
import { DirectorySettings } from "@/components/settings/DirectorySettings";
import { ImportExportSection } from "@/components/settings/ImportExportSection";
import { AboutSection } from "@/components/settings/AboutSection";
import { useSettings } from "@/hooks/useSettings";
import { useImportExport } from "@/hooks/useImportExport";
import { useTranslation } from "react-i18next";
interface SettingsDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onImportSuccess?: () => void | Promise<void>;
}
export function SettingsDialog({
open,
onOpenChange,
onImportSuccess,
}: SettingsDialogProps) {
const { t } = useTranslation();
const {
settings,
isLoading,
isSaving,
isPortable,
appConfigDir,
resolvedDirs,
updateSettings,
updateDirectory,
updateAppConfigDir,
browseDirectory,
browseAppConfigDir,
resetDirectory,
resetAppConfigDir,
saveSettings,
resetSettings,
requiresRestart,
acknowledgeRestart,
} = useSettings();
const {
selectedFile,
status: importStatus,
errorMessage,
backupId,
isImporting,
selectImportFile,
importConfig,
exportConfig,
clearSelection,
resetStatus,
} = useImportExport({ onImportSuccess });
const [activeTab, setActiveTab] = useState<string>("general");
const [showRestartPrompt, setShowRestartPrompt] = useState(false);
useEffect(() => {
if (open) {
setActiveTab("general");
resetStatus();
}
}, [open, resetStatus]);
useEffect(() => {
if (requiresRestart) {
setShowRestartPrompt(true);
}
}, [requiresRestart]);
const closeDialog = useCallback(() => {
// 取消/直接关闭:恢复到初始设置(包括语言回滚)
resetSettings();
acknowledgeRestart();
clearSelection();
resetStatus();
onOpenChange(false);
feat: complete stage 4 cleanup and code formatting This commit completes stage 4 of the refactoring plan, focusing on cleanup and optimization of the modernized codebase. ## Key Changes ### Code Cleanup - Remove legacy `src/lib/styles.ts` (no longer needed) - Remove old modal components (`ImportProgressModal.tsx`, `ProviderList.tsx`) - Streamline `src/lib/tauri-api.ts` from 712 lines to 17 lines (-97.6%) - Remove global `window.api` pollution - Keep only event listeners (`tauriEvents.onProviderSwitched`) - All API calls now use modular `@/lib/api/*` layer ### Type System - Clean up `src/vite-env.d.ts` (remove 156 lines of outdated types) - Remove obsolete global type declarations - All TypeScript checks pass with zero errors ### Code Formatting - Format all source files with Prettier (82 files) - Fix formatting issues in 15 files: - App.tsx and core components - MCP management components - Settings module components - Provider management components - UI components ### Documentation Updates - Update `REFACTORING_CHECKLIST.md` with stage 4 progress - Mark completed tasks in `REFACTORING_MASTER_PLAN.md` ## Impact **Code Reduction:** - Total: -1,753 lines, +384 lines (net -1,369 lines) - tauri-api.ts: 712 → 17 lines (-97.6%) - Removed styles.ts: -82 lines - Removed vite-env.d.ts declarations: -156 lines **Quality Improvements:** - ✅ Zero TypeScript errors - ✅ Zero TODO/FIXME comments - ✅ 100% Prettier compliant - ✅ Zero `window.api` references - ✅ Fully modular API layer ## Testing - [x] TypeScript compilation passes - [x] Code formatting validated - [x] No linting errors Stage 4 completion: 100% Ready for stage 5 (testing and bug fixes)
2025-10-16 12:13:51 +08:00
}, [
acknowledgeRestart,
clearSelection,
onOpenChange,
resetSettings,
resetStatus,
]);
const closeAfterSave = useCallback(() => {
// 保存成功后关闭:不再重置语言,避免需要“保存两次”才生效
acknowledgeRestart();
clearSelection();
resetStatus();
onOpenChange(false);
}, [acknowledgeRestart, clearSelection, onOpenChange, resetStatus]);
const handleDialogChange = useCallback(
(nextOpen: boolean) => {
if (!nextOpen) {
closeDialog();
} else {
onOpenChange(true);
}
},
[closeDialog, onOpenChange],
);
const handleCancel = useCallback(() => {
closeDialog();
}, [closeDialog]);
const handleSave = useCallback(async () => {
try {
const result = await saveSettings();
if (!result) return;
if (result.requiresRestart) {
setShowRestartPrompt(true);
return;
}
closeAfterSave();
} catch (error) {
console.error("[SettingsDialog] Failed to save settings", error);
}
}, [closeDialog, saveSettings]);
const handleRestartLater = useCallback(() => {
setShowRestartPrompt(false);
closeAfterSave();
}, [closeAfterSave]);
const handleRestartNow = useCallback(async () => {
setShowRestartPrompt(false);
if (import.meta.env.DEV) {
toast.success(
t("settings.devModeRestartHint", {
defaultValue: "开发模式下不支持自动重启,请手动重新启动应用。",
}),
);
closeAfterSave();
return;
}
try {
await settingsApi.restart();
} catch (error) {
console.error("[SettingsDialog] Failed to restart app", error);
toast.error(
t("settings.restartFailed", {
defaultValue: "应用重启失败,请手动关闭后重新打开。",
}),
);
} finally {
closeAfterSave();
}
}, [closeAfterSave, t]);
const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]);
return (
<Dialog open={open} onOpenChange={handleDialogChange}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>{t("settings.title")}</DialogTitle>
</DialogHeader>
{isBusy ? (
<div className="flex min-h-[320px] items-center justify-center px-6">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
) : (
<div className="flex-1 overflow-y-scroll px-6 py-4">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="flex flex-col h-full"
>
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="general">
{t("settings.tabGeneral", { defaultValue: "通用" })}
</TabsTrigger>
<TabsTrigger value="advanced">
{t("settings.tabAdvanced", { defaultValue: "高级" })}
</TabsTrigger>
feat: complete stage 4 cleanup and code formatting This commit completes stage 4 of the refactoring plan, focusing on cleanup and optimization of the modernized codebase. ## Key Changes ### Code Cleanup - Remove legacy `src/lib/styles.ts` (no longer needed) - Remove old modal components (`ImportProgressModal.tsx`, `ProviderList.tsx`) - Streamline `src/lib/tauri-api.ts` from 712 lines to 17 lines (-97.6%) - Remove global `window.api` pollution - Keep only event listeners (`tauriEvents.onProviderSwitched`) - All API calls now use modular `@/lib/api/*` layer ### Type System - Clean up `src/vite-env.d.ts` (remove 156 lines of outdated types) - Remove obsolete global type declarations - All TypeScript checks pass with zero errors ### Code Formatting - Format all source files with Prettier (82 files) - Fix formatting issues in 15 files: - App.tsx and core components - MCP management components - Settings module components - Provider management components - UI components ### Documentation Updates - Update `REFACTORING_CHECKLIST.md` with stage 4 progress - Mark completed tasks in `REFACTORING_MASTER_PLAN.md` ## Impact **Code Reduction:** - Total: -1,753 lines, +384 lines (net -1,369 lines) - tauri-api.ts: 712 → 17 lines (-97.6%) - Removed styles.ts: -82 lines - Removed vite-env.d.ts declarations: -156 lines **Quality Improvements:** - ✅ Zero TypeScript errors - ✅ Zero TODO/FIXME comments - ✅ 100% Prettier compliant - ✅ Zero `window.api` references - ✅ Fully modular API layer ## Testing - [x] TypeScript compilation passes - [x] Code formatting validated - [x] No linting errors Stage 4 completion: 100% Ready for stage 5 (testing and bug fixes)
2025-10-16 12:13:51 +08:00
<TabsTrigger value="about">{t("common.about")}</TabsTrigger>
</TabsList>
<TabsContent value="general" className="space-y-6 mt-6 min-h-[400px]">
{settings ? (
<>
<LanguageSettings
value={settings.language}
onChange={(lang) => updateSettings({ language: lang })}
/>
<ThemeSettings />
<WindowSettings
settings={settings}
onChange={updateSettings}
/>
</>
) : null}
</TabsContent>
<TabsContent value="advanced" className="space-y-6 mt-6 min-h-[400px]">
{settings ? (
<>
<DirectorySettings
appConfigDir={appConfigDir}
resolvedDirs={resolvedDirs}
onAppConfigChange={updateAppConfigDir}
onBrowseAppConfig={browseAppConfigDir}
onResetAppConfig={resetAppConfigDir}
claudeDir={settings.claudeConfigDir}
codexDir={settings.codexConfigDir}
onDirectoryChange={updateDirectory}
onBrowseDirectory={browseDirectory}
onResetDirectory={resetDirectory}
/>
<ImportExportSection
status={importStatus}
selectedFile={selectedFile}
errorMessage={errorMessage}
backupId={backupId}
isImporting={isImporting}
onSelectFile={selectImportFile}
onImport={importConfig}
onExport={exportConfig}
onClear={clearSelection}
/>
</>
) : null}
</TabsContent>
<TabsContent value="about" className="mt-6 min-h-[400px]">
<AboutSection isPortable={isPortable} />
</TabsContent>
</Tabs>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={handleCancel}>
{t("common.cancel")}
</Button>
<Button onClick={handleSave} disabled={isSaving || isBusy}>
{isSaving ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t("settings.saving", { defaultValue: "正在保存..." })}
</span>
) : (
<>
<Save className="mr-2 h-4 w-4" />
{t("common.save")}
</>
)}
</Button>
</DialogFooter>
</DialogContent>
<Dialog open={showRestartPrompt} onOpenChange={(open) => !open && handleRestartLater()}>
<DialogContent zIndex="alert" className="max-w-md">
<DialogHeader>
<DialogTitle>{t("settings.restartRequired")}</DialogTitle>
</DialogHeader>
<div className="px-6">
<p className="text-sm text-muted-foreground">
{t("settings.restartRequiredMessage", {
defaultValue: "配置目录已变更,需要重启应用生效。",
})}
</p>
</div>
<DialogFooter>
<Button variant="outline" onClick={handleRestartLater}>
{t("settings.restartLater")}
</Button>
<Button onClick={handleRestartNow}>
{t("settings.restartNow")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Dialog>
);
}