import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import styles from './Join.module.css'; interface Share { id: string; walletName: string; publicKey: string; sessionId: string; threshold: { t: number; n: number }; } interface SignPartyInfo { party_id: string; party_index: number; } interface SessionInfo { sessionId: string; keygenSessionId: string; walletName: string; messageHash: string; threshold: { t: number; n: number }; status: string; currentParticipants: number; parties?: SignPartyInfo[]; } interface ValidateResult { success: boolean; error?: string; sessionInfo?: SessionInfo; joinToken?: string; } export default function CoSignJoin() { const { inviteCode } = useParams<{ inviteCode?: string }>(); const navigate = useNavigate(); const [code, setCode] = useState(inviteCode || ''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [sessionInfo, setSessionInfo] = useState(null); const [joinToken, setJoinToken] = useState(null); const [step, setStep] = useState<'input' | 'select_share' | 'joining'>('input'); // Share 选择相关 const [shares, setShares] = useState([]); const [selectedShareId, setSelectedShareId] = useState(''); const [sharePassword, setSharePassword] = useState(''); const [autoJoinAttempted, setAutoJoinAttempted] = useState(false); // 加载本地 shares useEffect(() => { const loadShares = async () => { try { const result = await window.electronAPI.storage.listShares(); // 兼容不同返回格式 const shareList = Array.isArray(result) ? result : ((result as any)?.data || []); console.log('[CoSignJoin] Loaded shares:', shareList.map((s: Share) => ({ id: s.id, sessionId: s.sessionId, walletName: s.walletName, }))); setShares(shareList); } catch (err) { console.error('Failed to load shares:', err); } }; loadShares(); }, []); // 如果 URL 中有邀请码,自动验证 useEffect(() => { if (inviteCode) { handleValidateCode(inviteCode); } }, [inviteCode]); // 自动选择匹配的 share useEffect(() => { if (sessionInfo && shares.length > 0 && !selectedShareId) { // 尝试找到匹配的 share(基于 keygen session ID) const matchingShare = shares.find(s => s.sessionId === sessionInfo.keygenSessionId); console.log('[CoSignJoin] Auto-select share check:', { keygenSessionId: sessionInfo.keygenSessionId, sharesSessionIds: shares.map(s => s.sessionId), matchingShare: matchingShare ? { id: matchingShare.id, sessionId: matchingShare.sessionId } : null, }); if (matchingShare) { console.log('[CoSignJoin] Auto-selecting matching share:', matchingShare.id); setSelectedShareId(matchingShare.id); } else if (shares.length === 1) { // 如果只有一个 share,自动选择 console.log('[CoSignJoin] Auto-selecting only share:', shares[0].id); setSelectedShareId(shares[0].id); } else { console.log('[CoSignJoin] No matching share found, user must select manually'); } } }, [sessionInfo, shares, selectedShareId]); // 自动加入 useEffect(() => { console.log('[CoSignJoin] Auto-join check:', { step, hasSessionInfo: !!sessionInfo, hasJoinToken: !!joinToken, selectedShareId, autoJoinAttempted, isLoading, sharesCount: shares.length, }); if ( step === 'select_share' && sessionInfo && joinToken && selectedShareId && !autoJoinAttempted && !isLoading ) { // 找到匹配的 share 且未尝试过自动加入,则自动加入 const matchingShare = shares.find(s => s.sessionId === sessionInfo.keygenSessionId); console.log('[CoSignJoin] Auto-join conditions met, checking share match:', { keygenSessionId: sessionInfo.keygenSessionId, matchingShareId: matchingShare?.id, selectedShareId, isMatch: matchingShare && matchingShare.id === selectedShareId, }); if (matchingShare && matchingShare.id === selectedShareId) { console.log('[CoSignJoin] Auto-joining session...'); setAutoJoinAttempted(true); handleJoinSession(); } else { console.log('[CoSignJoin] Share mismatch, not auto-joining'); } } }, [step, sessionInfo, joinToken, selectedShareId, autoJoinAttempted, isLoading, shares]); const handleValidateCode = async (codeToValidate: string) => { console.log('[CoSignJoin] handleValidateCode called:', codeToValidate); if (!codeToValidate.trim()) { setError('请输入邀请码'); return; } setIsLoading(true); setError(null); try { const result: ValidateResult = await window.electronAPI.cosign.validateInviteCode(codeToValidate); console.log('[CoSignJoin] validateInviteCode result:', { success: result.success, sessionInfo: result.sessionInfo, hasJoinToken: !!result.joinToken, joinTokenPreview: result.joinToken?.substring(0, 20) + '...', error: result.error, }); if (result.success && result.sessionInfo) { setSessionInfo(result.sessionInfo); if (result.joinToken) { setJoinToken(result.joinToken); } else { console.warn('[CoSignJoin] WARNING: No joinToken in response!'); } setStep('select_share'); } else { setError(result.error || '无效的邀请码'); } } catch (err) { console.error('[CoSignJoin] validateInviteCode error:', err); setError('验证邀请码失败,请检查网络连接'); } finally { setIsLoading(false); } }; const handleJoinSession = async () => { console.log('[CoSignJoin] handleJoinSession called:', { hasSessionInfo: !!sessionInfo, hasJoinToken: !!joinToken, selectedShareId, }); if (!sessionInfo) { setError('会话信息不完整'); return; } if (!joinToken) { setError('未获取到加入令牌,请重新验证邀请码'); return; } if (!selectedShareId) { setError('请选择一个钱包'); return; } setStep('joining'); setIsLoading(true); setError(null); console.log('[CoSignJoin] Calling cosign.joinSession with:', { sessionId: sessionInfo.sessionId, shareId: selectedShareId, walletName: sessionInfo.walletName, messageHash: sessionInfo.messageHash, threshold: sessionInfo.threshold, parties: sessionInfo.parties, }); try { const result = await window.electronAPI.cosign.joinSession({ sessionId: sessionInfo.sessionId, shareId: selectedShareId, sharePassword: sharePassword, joinToken: joinToken, walletName: sessionInfo.walletName, messageHash: sessionInfo.messageHash, threshold: sessionInfo.threshold, parties: sessionInfo.parties, }); console.log('[CoSignJoin] joinSession result:', result); if (result.success) { navigate(`/cosign/session/${sessionInfo.sessionId}`); } else { setError(result.error || '加入会话失败'); setStep('select_share'); } } catch (err) { setError('加入会话失败,请重试'); setStep('select_share'); } 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); } }; const selectedShare = shares.find(s => s.id === selectedShareId); return (

加入多方签名

输入邀请码加入签名会话

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

签名会话信息

钱包名称 {sessionInfo.walletName}
签名阈值 {sessionInfo.threshold.t}-of-{sessionInfo.threshold.n}
消息哈希 {sessionInfo.messageHash.substring(0, 16)}...
当前参与者 {sessionInfo.currentParticipants} / {sessionInfo.threshold.t}
{/* 选择本地 share */}
{shares.length === 0 ? (

暂无可用钱包,请先创建或加入共管钱包

) : ( )} {selectedShare && (

公钥: {selectedShare.publicKey.substring(0, 16)}...

)}
{/* 钱包密码 */}
setSharePassword(e.target.value)} placeholder="如果设置了密码,请输入" className={styles.input} disabled={isLoading} />
{error &&
{error}
}
)} {step === 'joining' && (

正在加入签名会话...

)}
); }