import { useEffect, useState } from 'react' import { createPortal } from 'react-dom' import { Check, Copy, Loader2, RefreshCw, Tv, X } from 'lucide-react' import toast from 'react-hot-toast' import { authAPI } from '../api/auth' import { useAuthStore } from '../stores/auth' export function TemporaryPasswordDialog({ isOpen, onClose, }: { isOpen: boolean onClose: () => void }) { const user = useAuthStore((s) => s.user) const [code, setCode] = useState('') const [expiresIn, setExpiresIn] = useState(0) const [loading, setLoading] = useState(false) const [copied, setCopied] = useState(false) const fetchCode = async () => { setLoading(true) try { const res = await authAPI.createTemporaryPassword() setCode(res.code) setExpiresIn(res.expires_in) setCopied(false) } catch (err: unknown) { const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? '获取临时密码失败' toast.error(msg) } finally { setLoading(false) } } useEffect(() => { if (isOpen) { fetchCode() } else { setCode('') setExpiresIn(0) } }, [isOpen]) useEffect(() => { if (expiresIn <= 0) return const timer = setInterval(() => { setExpiresIn((prev) => { if (prev <= 1) { clearInterval(timer) return 0 } return prev - 1 }) }, 1000) return () => clearInterval(timer) }, [expiresIn]) const copyCode = async () => { if (!code) return try { await navigator.clipboard.writeText(code) setCopied(true) toast.success('已复制到剪贴板') setTimeout(() => setCopied(false), 2000) } catch { toast.error('复制失败,请手动长按复制') } } if (!isOpen || typeof document === 'undefined') return null return createPortal(
e.stopPropagation()} > {/* Header */}

电视端临时登录码 (OTP)

适用于 Emby / Jellyfin / Infuse 客户端快速免密登录

{/* Content */}
登录账号: {user?.username}
{expiresIn > 0 ? (
有效时间剩余: {Math.floor(expiresIn / 60).toString().padStart(2, '0')}:{(expiresIn % 60).toString().padStart(2, '0')}
) : null}
{loading ? (
) : code && expiresIn > 0 ? ( <>
{code}
) : (
登录码已失效或未生成
)}
使用方法: 在电视端或外部设备的 Emby 登录界面输入用户名 {user?.username} 和上方 6 位临时码。登录后临时码立即作废(一次性阅后即焚),客户端将自动获取长期持久令牌。
{/* Footer */}
, document.body ) }