Files
cc-switch/src/components/settings/AboutSection.tsx
Jason f3e7412a14 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

231 lines
7.2 KiB
TypeScript

import { useCallback, useEffect, useState } from "react";
import { Download, ExternalLink, Info, Loader2, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import { getVersion } from "@tauri-apps/api/app";
import { settingsApi } from "@/lib/api";
import { useUpdate } from "@/contexts/UpdateContext";
import { relaunchApp } from "@/lib/updater";
interface AboutSectionProps {
isPortable: boolean;
}
export function AboutSection({ isPortable }: AboutSectionProps) {
const { t } = useTranslation();
const [version, setVersion] = useState<string | null>(null);
const [isLoadingVersion, setIsLoadingVersion] = useState(true);
const [isDownloading, setIsDownloading] = useState(false);
const {
hasUpdate,
updateInfo,
updateHandle,
checkUpdate,
resetDismiss,
isChecking,
} = useUpdate();
useEffect(() => {
let active = true;
const load = async () => {
try {
const loaded = await getVersion();
if (active) {
setVersion(loaded);
}
} catch (error) {
console.error("[AboutSection] Failed to get version", error);
if (active) {
setVersion(null);
}
} finally {
if (active) {
setIsLoadingVersion(false);
}
}
};
void load();
return () => {
active = false;
};
}, []);
const handleOpenReleaseNotes = useCallback(async () => {
try {
const targetVersion = updateInfo?.availableVersion ?? version ?? "";
const displayVersion = targetVersion.startsWith("v")
? targetVersion
: targetVersion
? `v${targetVersion}`
: "";
if (!displayVersion) {
await settingsApi.openExternal(
"https://github.com/farion1231/cc-switch/releases",
);
return;
}
await settingsApi.openExternal(
`https://github.com/farion1231/cc-switch/releases/tag/${displayVersion}`,
);
} catch (error) {
console.error("[AboutSection] Failed to open release notes", error);
toast.error(
t("settings.openReleaseNotesFailed", {
defaultValue: "打开更新日志失败",
}),
);
}
}, [t, updateInfo?.availableVersion, version]);
const handleCheckUpdate = useCallback(async () => {
if (hasUpdate && updateHandle) {
if (isPortable) {
try {
await settingsApi.checkUpdates();
} catch (error) {
console.error("[AboutSection] Portable update failed", error);
}
return;
}
setIsDownloading(true);
try {
resetDismiss();
await updateHandle.downloadAndInstall();
await relaunchApp();
} catch (error) {
console.error("[AboutSection] Update failed", error);
toast.error(
t("settings.updateFailed", {
defaultValue: "更新安装失败,已尝试打开下载页面。",
}),
);
try {
await settingsApi.checkUpdates();
} catch (fallbackError) {
console.error(
"[AboutSection] Failed to open fallback updater",
fallbackError,
);
}
} finally {
setIsDownloading(false);
}
return;
}
try {
const available = await checkUpdate();
if (!available) {
toast.success(t("settings.upToDate", { defaultValue: "已是最新版本" }));
}
} catch (error) {
console.error("[AboutSection] Check update failed", error);
toast.error(
t("settings.checkUpdateFailed", {
defaultValue: "检查更新失败,请稍后重试。",
}),
);
}
}, [checkUpdate, hasUpdate, isPortable, resetDismiss, t, updateHandle]);
const displayVersion =
version ?? t("common.unknown", { defaultValue: "未知" });
return (
<section className="space-y-4">
<header className="space-y-1">
<h3 className="text-sm font-medium">{t("common.about")}</h3>
<p className="text-xs text-muted-foreground">
{t("settings.aboutHint", {
defaultValue: "查看版本信息与更新状态。",
})}
</p>
</header>
<div className="space-y-4 rounded-lg border border-border p-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-foreground">CC Switch</p>
<p className="text-xs text-muted-foreground">
{t("common.version")}{" "}
{isLoadingVersion ? (
<Loader2 className="inline h-3 w-3 animate-spin" />
) : (
`v${displayVersion}`
)}
</p>
{isPortable ? (
<p className="inline-flex items-center gap-1 text-xs text-muted-foreground">
<Info className="h-3 w-3" />
{t("settings.portableMode", {
defaultValue: "当前为便携版,更新需手动下载。",
})}
</p>
) : null}
</div>
<div className="flex flex-wrap items-center gap-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={handleOpenReleaseNotes}
>
<ExternalLink className="mr-2 h-4 w-4" />
{t("settings.releaseNotes")}
</Button>
<Button
type="button"
size="sm"
onClick={handleCheckUpdate}
disabled={isChecking || isDownloading}
>
{isDownloading ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t("settings.updating", { defaultValue: "安装更新..." })}
</span>
) : hasUpdate ? (
<span className="inline-flex items-center gap-2">
<Download className="h-4 w-4" />
{t("settings.updateTo", {
defaultValue: "更新到 {{version}}",
version: updateInfo?.availableVersion ?? "",
})}
</span>
) : isChecking ? (
<span className="inline-flex items-center gap-2">
<RefreshCw className="h-4 w-4 animate-spin" />
{t("settings.checking", { defaultValue: "检查中..." })}
</span>
) : (
t("settings.checkForUpdates")
)}
</Button>
</div>
</div>
{hasUpdate && updateInfo ? (
<div className="rounded-md bg-muted/40 px-3 py-2 text-xs text-muted-foreground">
<p>
{t("settings.updateAvailable", {
defaultValue: "检测到新版本:{{version}}",
version: updateInfo.availableVersion,
})}
</p>
{updateInfo.notes ? (
<p className="mt-1 line-clamp-3">{updateInfo.notes}</p>
) : null}
</div>
) : null}
</div>
</section>
);
}