import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import styles from './Join.module.css'; interface SessionInfo { sessionId: string; walletName: string; threshold: { t: number; n: number }; initiator: string; createdAt: string; currentParticipants: number; totalParticipants?: number; } // 生成默认参与者名称 function generateParticipantName(): string { const timestamp = Date.now().toString(36).slice(-4); return `参与者-${timestamp}`; } interface ValidateResult { success: boolean; error?: string; sessionInfo?: SessionInfo; joinToken?: string; } export default function Join() { const { inviteCode } = useParams<{ inviteCode?: string }>(); const navigate = useNavigate(); const [code, setCode] = useState(inviteCode || ''); const [participantName] = useState(generateParticipantName()); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [sessionInfo, setSessionInfo] = useState(null); const [joinToken, setJoinToken] = useState(null); const [partyId, setPartyId] = useState(null); const [step, setStep] = useState<'input' | 'confirm' | 'joining'>('input'); const [autoJoinAttempted, setAutoJoinAttempted] = useState(false); useEffect(() => { if (inviteCode) { handleValidateCode(inviteCode); } }, [inviteCode]); // 自动加入:验证成功后自动加入会话 useEffect(() => { if ( step === 'confirm' && sessionInfo && joinToken && partyId && participantName && !autoJoinAttempted && !isLoading ) { setAutoJoinAttempted(true); handleJoinSession(); } }, [step, sessionInfo, joinToken, partyId, participantName, autoJoinAttempted, isLoading]); const handleValidateCode = async (codeToValidate: string) => { if (!codeToValidate.trim()) { setError('请输入邀请码'); return; } setIsLoading(true); setError(null); try { // 获取当前 partyId const partyResult = await window.electronAPI.grpc.getPartyId(); if (!partyResult.success || !partyResult.partyId) { setError('请先连接到消息路由器'); setIsLoading(false); return; } setPartyId(partyResult.partyId); // 解析邀请码获取会话信息 const result: ValidateResult = await window.electronAPI.grpc.validateInviteCode(codeToValidate); if (result.success && result.sessionInfo) { setSessionInfo(result.sessionInfo); if (result.joinToken) { setJoinToken(result.joinToken); } setStep('confirm'); } else { setError(result.error || '无效的邀请码'); } } catch (err) { setError('验证邀请码失败,请检查网络连接'); } finally { setIsLoading(false); } }; const handleJoinSession = async () => { if (!sessionInfo) { setError('会话信息不完整'); return; } if (!partyId) { setError('未获取到 Party ID,请重试'); return; } if (!joinToken) { setError('未获取到加入令牌,请重新验证邀请码'); return; } setStep('joining'); setIsLoading(true); setError(null); try { const result = await window.electronAPI.grpc.joinSession({ sessionId: sessionInfo.sessionId, partyId: partyId, joinToken: joinToken, walletName: sessionInfo.walletName, }); if (result.success) { navigate(`/session/${sessionInfo.sessionId}`); } else { setError(result.error || '加入会话失败'); setStep('confirm'); } } catch (err) { setError('加入会话失败,请重试'); setStep('confirm'); } finally { setIsLoading(false); } }; const handlePaste = async () => { try { const text = await navigator.clipboard.readText(); setCode(text.trim()); } catch (err) { console.error('Failed to read clipboard:', err); } }; return (

加入共管钱包

输入邀请码或扫描二维码加入

{step === 'input' && (
setCode(e.target.value)} placeholder="粘贴邀请码或邀请链接" className={styles.input} disabled={isLoading} />
{error &&
{error}
}
)} {step === 'confirm' && sessionInfo && (

会话信息

钱包名称 {sessionInfo.walletName}
阈值设置 {sessionInfo.threshold.t}-of-{sessionInfo.threshold.n}
发起者 {sessionInfo.initiator}
当前参与者 {sessionInfo.currentParticipants} / {sessionInfo.threshold.n}
{error ? ( <>
{error}
) : (

正在自动加入会话...

)}
)} {step === 'joining' && (

正在加入会话...

)}
); }