fix(desktop): ClaudeOfficialLogin — i18n、错误反馈、冗余清理

Code review follow-ups on ae5437a:
- I1: 使用 useTranslation 替代硬编码中文, 新增 settings.claudeOfficialLogin.* 双语 key
- I2: shellOpen 失败时 console.error + 写入 store.error, 不再静默吞
- M1: disabled={isLoading || false} → disabled={isLoading}
- M2: onClick={() => logout()} → onClick={logout}
- M3: 首次 fetchStatus 失败时显示 error 而非永远 "加载中…"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes) 2026-04-17 20:33:08 +08:00
parent ae5437af43
commit 8ae68c1805
3 changed files with 55 additions and 12 deletions

View File

@ -7,8 +7,10 @@
import { useEffect } from 'react'
import { open as shellOpen } from '@tauri-apps/plugin-shell'
import { useHahaOAuthStore } from '../../stores/hahaOAuthStore'
import { useTranslation } from '../../i18n'
export function ClaudeOfficialLogin() {
const t = useTranslation()
const { status, isLoading, error, fetchStatus, login, logout, stopPolling } =
useHahaOAuthStore()
@ -20,34 +22,52 @@ export function ClaudeOfficialLogin() {
const handleLogin = async () => {
try {
const { authorizeUrl } = await login()
await shellOpen(authorizeUrl)
try {
await shellOpen(authorizeUrl)
} catch (err) {
console.error('[ClaudeOfficialLogin] shellOpen failed:', err)
useHahaOAuthStore.setState({
error: t('settings.claudeOfficialLogin.openBrowserFailed'),
})
}
} catch {
// error 已经在 store 里,不做额外处理
// store.login() errors are already captured into store.error
}
}
if (status === null) {
if (error) {
return (
<div className="text-xs text-[var(--color-error,#dc2626)]">
{t('settings.claudeOfficialLogin.errorPrefix')}{error}
</div>
)
}
return (
<div className="text-xs text-[var(--color-text-tertiary)]"></div>
<div className="text-xs text-[var(--color-text-tertiary)]">
{t('common.loading')}
</div>
)
}
if (status.loggedIn) {
const subTypeLabel = status.subscriptionType
? status.subscriptionType.toUpperCase()
: '未知'
: t('settings.claudeOfficialLogin.subTypeUnknown')
return (
<div className="flex items-center gap-3 text-sm">
<span className="text-[var(--color-success)]">
(Claude {subTypeLabel})
{t('settings.claudeOfficialLogin.loggedInPrefix')} {subTypeLabel})
</span>
<button
type="button"
onClick={() => logout()}
onClick={logout}
disabled={isLoading}
className="px-3 py-1 text-xs rounded-md border border-[var(--color-border-separator)] bg-[var(--color-surface)] hover:bg-[var(--color-surface-hover)] disabled:opacity-50 transition-colors"
>
{isLoading ? '处理中…' : '登出'}
{isLoading
? t('settings.claudeOfficialLogin.logoutProcessing')
: t('settings.claudeOfficialLogin.logoutButton')}
</button>
</div>
)
@ -56,20 +76,21 @@ export function ClaudeOfficialLogin() {
return (
<div className="flex flex-col gap-2">
<div className="text-sm text-[var(--color-text-secondary)]">
使 Claude Claude.ai ,
Claude ,
{t('settings.claudeOfficialLogin.intro')}
</div>
<button
type="button"
onClick={handleLogin}
disabled={isLoading || false}
disabled={isLoading}
className="self-start px-4 py-2 text-sm rounded-md bg-[var(--color-accent,#c96342)] text-white hover:opacity-90 disabled:opacity-50 transition-opacity"
>
{isLoading ? '正在启动…' : '登录 Claude 账号'}
{isLoading
? t('settings.claudeOfficialLogin.loginStarting')
: t('settings.claudeOfficialLogin.loginButton')}
</button>
{error && (
<div className="text-xs text-[var(--color-error,#dc2626)]">
:{error}
{t('settings.claudeOfficialLogin.errorPrefix')}{error}
</div>
)}
</div>

View File

@ -50,6 +50,17 @@ export const en = {
'settings.tab.general': 'General',
'settings.tab.skills': 'Skills',
// Settings > Claude Official Login
'settings.claudeOfficialLogin.intro': 'Using official Claude models requires signing in to your Claude.ai account. Click the button below to open the official Claude login page in your browser; you\'ll be returned here after authorizing.',
'settings.claudeOfficialLogin.loginButton': 'Sign in to Claude',
'settings.claudeOfficialLogin.loginStarting': 'Opening…',
'settings.claudeOfficialLogin.logoutButton': 'Sign out',
'settings.claudeOfficialLogin.logoutProcessing': 'Processing…',
'settings.claudeOfficialLogin.loggedInPrefix': 'Signed in (Claude',
'settings.claudeOfficialLogin.subTypeUnknown': 'Unknown',
'settings.claudeOfficialLogin.errorPrefix': 'Error: ',
'settings.claudeOfficialLogin.openBrowserFailed': 'Failed to open browser; please visit the authorization URL manually.',
// Settings > Providers
'settings.providers.title': 'Providers',
'settings.providers.description': 'Manage API providers for model access.',

View File

@ -52,6 +52,17 @@ export const zh: Record<TranslationKey, string> = {
'settings.tab.general': '通用',
'settings.tab.skills': '技能',
// Settings > Claude Official Login
'settings.claudeOfficialLogin.intro': '使用官方 Claude 模型需要登录你的 Claude.ai 账号。点击下方按钮,浏览器会打开 Claude 官方登录页面,授权后自动回到这里。',
'settings.claudeOfficialLogin.loginButton': '登录 Claude 账号',
'settings.claudeOfficialLogin.loginStarting': '正在启动…',
'settings.claudeOfficialLogin.logoutButton': '登出',
'settings.claudeOfficialLogin.logoutProcessing': '处理中…',
'settings.claudeOfficialLogin.loggedInPrefix': '已登录(Claude',
'settings.claudeOfficialLogin.subTypeUnknown': '未知',
'settings.claudeOfficialLogin.errorPrefix': '错误:',
'settings.claudeOfficialLogin.openBrowserFailed': '无法打开浏览器,请手动访问授权链接。',
// Settings > Providers
'settings.providers.title': '服务商',
'settings.providers.description': '管理 API 服务商以访问模型。',