fix: 登录状态检查改为手动触发,避免自动请求不断弹出浏览器窗口
- useLoginStatus 默认 auto=false,不再挂载时自动请求 - Dashboard 登录状态卡片改为 "Click to check" 手动触发 - Login 页面初始显示 "Click Refresh to check" 提示 - Header 添加 Token 未配置警告横幅,引导用户去 Settings
This commit is contained in:
@@ -1,12 +1,29 @@
|
||||
import { useHealth } from '@/hooks/useHealth';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export function Header() {
|
||||
const { health } = useHealth(15_000);
|
||||
const { token } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<header className="h-14 bg-dark-card border-b border-dark-border flex items-center justify-between px-6 shrink-0">
|
||||
<div />
|
||||
<div>
|
||||
{!token && (
|
||||
<button
|
||||
onClick={() => navigate('/settings')}
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-dark-warning/15 border border-dark-warning/30 text-dark-warning text-xs font-medium hover:bg-dark-warning/25 transition-colors"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<line x1="12" y1="9" x2="12" y2="13" /><line x1="12" y1="17" x2="12.01" y2="17" />
|
||||
</svg>
|
||||
Token 未配置 — 点击前往 Settings
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
{health && (
|
||||
<Badge variant={health.healthy ? 'success' : 'danger'}>
|
||||
|
||||
@@ -2,12 +2,14 @@ import { useState, useEffect, useCallback } from 'react';
|
||||
import { getLoginStatus } from '@/api/endpoints';
|
||||
import type { LoginStatus } from '@/api/types';
|
||||
|
||||
export function useLoginStatus(intervalMs = 0) {
|
||||
export function useLoginStatus(options: { auto?: boolean; intervalMs?: number } = {}) {
|
||||
const { auto = false, intervalMs = 0 } = options;
|
||||
const [status, setStatus] = useState<LoginStatus | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loading, setLoading] = useState(auto);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getLoginStatus();
|
||||
if (res.success && res.data) {
|
||||
@@ -24,12 +26,13 @@ export function useLoginStatus(intervalMs = 0) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!auto) return;
|
||||
void refresh();
|
||||
if (intervalMs > 0) {
|
||||
const id = setInterval(() => void refresh(), intervalMs);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
}, [refresh, intervalMs]);
|
||||
}, [refresh, auto, intervalMs]);
|
||||
|
||||
return { status, error, loading, refresh };
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export function DashboardPage() {
|
||||
const { health, loading: healthLoading, refresh: refreshHealth } = useHealth(10_000);
|
||||
const { status: loginStatus, loading: loginLoading } = useLoginStatus();
|
||||
const { status: loginStatus, loading: loginLoading, refresh: refreshLogin } = useLoginStatus();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -65,7 +65,12 @@ export function DashboardPage() {
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Badge variant="warning">Unknown</Badge>
|
||||
<button
|
||||
onClick={() => void refreshLogin()}
|
||||
className="text-xs text-dark-accent hover:underline"
|
||||
>
|
||||
Click to check
|
||||
</button>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Spinner } from '@/components/ui/Spinner';
|
||||
import { useLoginStatus } from '@/hooks/useLoginStatus';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useToast } from '@/context/ToastContext';
|
||||
import { getLoginQRCode, deleteCookies, getLoginStatus } from '@/api/endpoints';
|
||||
|
||||
export function LoginPage() {
|
||||
const { status, loading: statusLoading, refresh: refreshStatus } = useLoginStatus();
|
||||
const { token } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [qrData, setQrData] = useState<string | null>(null);
|
||||
const [qrLoading, setQrLoading] = useState(false);
|
||||
@@ -107,6 +111,19 @@ export function LoginPage() {
|
||||
<div className="max-w-2xl space-y-6">
|
||||
<h1 className="text-2xl font-bold">Xiaohongshu Login</h1>
|
||||
|
||||
{!token && (
|
||||
<Card className="border-dark-warning/30 bg-dark-warning/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-dark-warning">
|
||||
Bearer Token 未配置,API 请求将返回 401。请先在 Settings 中配置 Token。
|
||||
</p>
|
||||
<Button size="sm" variant="secondary" onClick={() => navigate('/settings')}>
|
||||
前往 Settings
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Current Status */}
|
||||
<Card>
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -124,7 +141,7 @@ export function LoginPage() {
|
||||
{status.username && <span className="text-sm">{status.username}</span>}
|
||||
</div>
|
||||
) : (
|
||||
<Badge variant="danger">Unable to check</Badge>
|
||||
<span className="text-sm text-dark-muted">Click Refresh to check login status</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
|
||||
Reference in New Issue
Block a user