fix(usercenter): 删冗余 Preferences 卡片 + 修登录态进用户中心闪登录页
1. Settings 页删掉 Preferences 卡片(语言/主题切换)——顶栏已有语言下拉 + 主题 切换按钮,重复。连带删只被它用的 Preferences/Row/Seg 函数 + useUI 导入。 2. 修 bug:从官网登录态点"用户中心"进 /user/ 会先闪登录页再跳面板。根因:渲染 守卫在 !ready(会话续期中)时也直接渲染 Login。改为:本地有 refresh token 且 续期未完成时显示加载态(非登录页),续期成功→面板、失败/无 token→登录页。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,6 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Icon } from './icons';
|
||||
import { card, input } from './shared';
|
||||
import { ErrorLine } from './Login';
|
||||
import { useUI } from '../lib/theme';
|
||||
import type { TFn, Lang } from '../lib/i18n';
|
||||
import { getClient } from '../lib/api/client';
|
||||
import { bilingual } from '../lib/api/errors';
|
||||
@@ -15,7 +14,6 @@ export default function Settings({ t, lang, totpEnabled, onTotpChange }: { t: TF
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
|
||||
<div style={{ fontFamily: 'var(--font-display)', fontSize: 25, fontWeight: 700, color: 'var(--fg1)' }}>{t('settingsTitle')}</div>
|
||||
<Preferences t={t} />
|
||||
<TotpSection t={t} lang={lang} enabled={totpEnabled} onChange={onTotpChange} />
|
||||
<Devices t={t} lang={lang} />
|
||||
</div>
|
||||
@@ -24,57 +22,6 @@ export default function Settings({ t, lang, totpEnabled, onTotpChange }: { t: TF
|
||||
|
||||
const sectionTitle: React.CSSProperties = { fontSize: 14.5, fontWeight: 700, color: 'var(--fg1)' };
|
||||
|
||||
function Preferences({ t }: { t: TFn }) {
|
||||
const { lang, setLang, theme, setTheme } = useUI();
|
||||
return (
|
||||
<div style={{ ...card, padding: '18px 20px', display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div style={sectionTitle}>{t('prefTitle')}</div>
|
||||
<Row label={t('prefLang')}>
|
||||
<Seg
|
||||
value={lang}
|
||||
options={[['zh', '中文'], ['en', 'EN']]}
|
||||
onPick={(v) => setLang(v as Lang)}
|
||||
/>
|
||||
</Row>
|
||||
<Row label={t('prefTheme')}>
|
||||
<Seg
|
||||
value={theme}
|
||||
options={[['light', t('themeLight')], ['dark', t('themeDark')]]}
|
||||
icons={{ light: 'sun', dark: 'moon' }}
|
||||
onPick={(v) => setTheme(v as 'light' | 'dark')}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Row({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
||||
<span style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--fg2)' }}>{label}</span>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Seg({ value, options, onPick, icons }: { value: string; options: [string, string][]; onPick: (v: string) => void; icons?: Record<string, string> }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', background: 'var(--bg-subtle)', borderRadius: 999, padding: 3, gap: 2 }}>
|
||||
{options.map(([v, l]) => (
|
||||
<button
|
||||
key={v}
|
||||
onClick={() => onPick(v)}
|
||||
aria-pressed={value === v}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', cursor: 'pointer', borderRadius: 999, padding: '6px 14px', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 700, background: value === v ? 'var(--accent)' : 'transparent', color: value === v ? 'var(--fg-on-accent)' : 'var(--fg3)' }}
|
||||
>
|
||||
{icons && <Icon name={icons[v]} size={14} color={value === v ? 'var(--fg-on-accent)' : 'var(--fg3)'} />}
|
||||
{l}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ───────── TOTP 2FA ───────── */
|
||||
function TotpSection({ t, lang, enabled, onChange }: { t: TFn; lang: Lang; enabled: boolean; onChange: () => void }) {
|
||||
const api = getClient();
|
||||
|
||||
@@ -120,8 +120,16 @@ export default function UserCenter() {
|
||||
setView('overview');
|
||||
}
|
||||
|
||||
// 静态导出无服务端会话:首屏(!ready)与未登录一律直接渲染登录页,避免出现空白
|
||||
// 背景(慢网络下用户会看到"空的")。已登录用户(有 refresh)会话续期完成后再切面板。
|
||||
// 已登录(本地有 refresh token)但会话续期尚未完成:显示加载态而非登录页,
|
||||
// 避免"先闪登录页再跳面板"(从官网带登录态进来时的 bug)。续期成功→面板、失败→登录页。
|
||||
if (!ready && hasRefresh()) {
|
||||
return (
|
||||
<div style={{ minHeight: '70vh', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--fg3)', fontSize: 14 }}>
|
||||
{t('loading')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 无 refresh token(未登录)或续期失败:渲染登录页。
|
||||
if (!ready || !authed) {
|
||||
return <Login onDone={onLoginDone} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user