feat(service-party-app): 验证成功后自动加入会话

- 移除手动输入名称和点击"确认加入"按钮的步骤
- 验证邀请码成功后自动触发 joinSession
- 生成默认参与者名称(参与者-xxxx 格式)
- 保留错误处理和重试功能
- 减少用户操作步骤,提高 co_managed_keygen 可靠性

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-29 10:48:41 -08:00
parent bd6537a2cb
commit e72f96da10
1 changed files with 62 additions and 39 deletions

View File

@ -12,6 +12,12 @@ interface SessionInfo {
totalParticipants?: number;
}
// 生成默认参与者名称
function generateParticipantName(): string {
const timestamp = Date.now().toString(36).slice(-4);
return `参与者-${timestamp}`;
}
interface ValidateResult {
success: boolean;
error?: string;
@ -24,13 +30,14 @@ export default function Join() {
const navigate = useNavigate();
const [code, setCode] = useState(inviteCode || '');
const [participantName, setParticipantName] = useState('');
const [participantName] = useState(generateParticipantName());
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [sessionInfo, setSessionInfo] = useState<SessionInfo | null>(null);
const [joinToken, setJoinToken] = useState<string | null>(null);
const [partyId, setPartyId] = useState<string | null>(null);
const [step, setStep] = useState<'input' | 'confirm' | 'joining'>('input');
const [autoJoinAttempted, setAutoJoinAttempted] = useState(false);
useEffect(() => {
if (inviteCode) {
@ -38,6 +45,22 @@ export default function Join() {
}
}, [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('请输入邀请码');
@ -76,8 +99,8 @@ export default function Join() {
};
const handleJoinSession = async () => {
if (!sessionInfo || !participantName.trim()) {
setError('请输入参与者名称');
if (!sessionInfo) {
setError('会话信息不完整');
return;
}
@ -204,42 +227,42 @@ export default function Join() {
</div>
</div>
<div className={styles.inputGroup}>
<label className={styles.label}></label>
<input
type="text"
value={participantName}
onChange={(e) => setParticipantName(e.target.value)}
placeholder="输入您的名称(其他参与者可见)"
className={styles.input}
disabled={isLoading}
/>
</div>
{error && <div className={styles.error}>{error}</div>}
<div className={styles.actions}>
<button
className={styles.secondaryButton}
onClick={() => {
setStep('input');
setSessionInfo(null);
setJoinToken(null);
setPartyId(null);
setError(null);
}}
disabled={isLoading}
>
</button>
<button
className={styles.primaryButton}
onClick={handleJoinSession}
disabled={isLoading || !participantName.trim()}
>
{isLoading ? '加入中...' : '确认加入'}
</button>
</div>
{error ? (
<>
<div className={styles.error}>{error}</div>
<div className={styles.actions}>
<button
className={styles.secondaryButton}
onClick={() => {
setStep('input');
setSessionInfo(null);
setJoinToken(null);
setPartyId(null);
setError(null);
setAutoJoinAttempted(false);
}}
disabled={isLoading}
>
</button>
<button
className={styles.primaryButton}
onClick={() => {
setAutoJoinAttempted(false);
setError(null);
}}
disabled={isLoading}
>
</button>
</div>
</>
) : (
<div className={styles.joining}>
<div className={styles.spinner}></div>
<p>...</p>
</div>
)}
</div>
)}