mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-30 16:23:35 +08:00
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:
parent
ae5437af43
commit
8ae68c1805
@ -7,8 +7,10 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { open as shellOpen } from '@tauri-apps/plugin-shell'
|
import { open as shellOpen } from '@tauri-apps/plugin-shell'
|
||||||
import { useHahaOAuthStore } from '../../stores/hahaOAuthStore'
|
import { useHahaOAuthStore } from '../../stores/hahaOAuthStore'
|
||||||
|
import { useTranslation } from '../../i18n'
|
||||||
|
|
||||||
export function ClaudeOfficialLogin() {
|
export function ClaudeOfficialLogin() {
|
||||||
|
const t = useTranslation()
|
||||||
const { status, isLoading, error, fetchStatus, login, logout, stopPolling } =
|
const { status, isLoading, error, fetchStatus, login, logout, stopPolling } =
|
||||||
useHahaOAuthStore()
|
useHahaOAuthStore()
|
||||||
|
|
||||||
@ -20,34 +22,52 @@ export function ClaudeOfficialLogin() {
|
|||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
try {
|
try {
|
||||||
const { authorizeUrl } = await login()
|
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 {
|
} catch {
|
||||||
// error 已经在 store 里,不做额外处理
|
// store.login() errors are already captured into store.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === null) {
|
if (status === null) {
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="text-xs text-[var(--color-error,#dc2626)]">
|
||||||
|
{t('settings.claudeOfficialLogin.errorPrefix')}{error}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
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) {
|
if (status.loggedIn) {
|
||||||
const subTypeLabel = status.subscriptionType
|
const subTypeLabel = status.subscriptionType
|
||||||
? status.subscriptionType.toUpperCase()
|
? status.subscriptionType.toUpperCase()
|
||||||
: '未知'
|
: t('settings.claudeOfficialLogin.subTypeUnknown')
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<span className="text-[var(--color-success)]">
|
<span className="text-[var(--color-success)]">
|
||||||
✓ 已登录(Claude {subTypeLabel})
|
✓ {t('settings.claudeOfficialLogin.loggedInPrefix')} {subTypeLabel})
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => logout()}
|
onClick={logout}
|
||||||
disabled={isLoading}
|
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"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -56,20 +76,21 @@ export function ClaudeOfficialLogin() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="text-sm text-[var(--color-text-secondary)]">
|
<div className="text-sm text-[var(--color-text-secondary)]">
|
||||||
使用官方 Claude 模型需要登录你的 Claude.ai 账号。点击下方按钮,浏览器会
|
{t('settings.claudeOfficialLogin.intro')}
|
||||||
打开 Claude 官方登录页面,授权后自动回到这里。
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleLogin}
|
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"
|
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>
|
</button>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="text-xs text-[var(--color-error,#dc2626)]">
|
<div className="text-xs text-[var(--color-error,#dc2626)]">
|
||||||
错误:{error}
|
{t('settings.claudeOfficialLogin.errorPrefix')}{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -50,6 +50,17 @@ export const en = {
|
|||||||
'settings.tab.general': 'General',
|
'settings.tab.general': 'General',
|
||||||
'settings.tab.skills': 'Skills',
|
'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
|
||||||
'settings.providers.title': 'Providers',
|
'settings.providers.title': 'Providers',
|
||||||
'settings.providers.description': 'Manage API providers for model access.',
|
'settings.providers.description': 'Manage API providers for model access.',
|
||||||
|
|||||||
@ -52,6 +52,17 @@ export const zh: Record<TranslationKey, string> = {
|
|||||||
'settings.tab.general': '通用',
|
'settings.tab.general': '通用',
|
||||||
'settings.tab.skills': '技能',
|
'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
|
||||||
'settings.providers.title': '服务商',
|
'settings.providers.title': '服务商',
|
||||||
'settings.providers.description': '管理 API 服务商以访问模型。',
|
'settings.providers.description': '管理 API 服务商以访问模型。',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user