fix(android): 使用同步标志修复参与方显示4/3的竞态条件bug

问题根因:
- 之前使用异步的 sessionStatus 检查来防止 participant_joined 事件
  在 session_started 之后继续添加参与方
- 但 sessionStatus 是通过 StateFlow 异步更新的,检查时状态可能还未更新
- 导致 participant_joined 事件仍能添加额外的参与方,显示4/3而非3/3

解决方案:
- 添加同步标志 sessionStartedForSession: String?
- 在 session_started 处理器的最开始同步设置此标志
- 在 participant_joined 处理器中检查此标志,而非异步状态
- 由于回调是顺序处理的,这种方式100%可靠

修改内容:
1. 添加 sessionStartedForSession 私有成员变量
2. 在 session_started 事件处理开始时立即设置标志
3. 在 participant_joined 事件处理开始时检查标志
4. 在所有 reset 方法中重置标志:
   - resetSessionState()
   - resetJoinKeygenState()
   - resetCoSignState()
   - resetTransferState()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-27 09:08:13 -08:00
parent 1bc42c207a
commit 8a9a983cbd
1 changed files with 25 additions and 5 deletions

View File

@ -45,6 +45,11 @@ class MainViewModel @Inject constructor(
private val _hasEnteredSession = MutableStateFlow(false)
val hasEnteredSession: StateFlow<Boolean> = _hasEnteredSession.asStateFlow()
// Synchronous flag to prevent participant_joined from adding duplicates after session_started
// This is set immediately (synchronously) when session_started is processed, ensuring
// any subsequent participant_joined events in the same callback queue will see the flag
private var sessionStartedForSession: String? = null
init {
// Start initialization on app launch
checkAllServices()
@ -339,6 +344,12 @@ class MainViewModel @Inject constructor(
when (event.eventType) {
"session_started" -> {
// CRITICAL: Set flag immediately (synchronously) to prevent subsequent
// participant_joined events from adding duplicates. This must be the
// first line before any async operations.
sessionStartedForSession = event.sessionId
android.util.Log.d("MainViewModel", "Session started flag set for: ${event.sessionId}")
// Check if this is for keygen initiator (CreateWallet)
val currentSessionId = _currentSessionId.value
if (currentSessionId != null && event.sessionId == currentSessionId) {
@ -396,11 +407,12 @@ class MainViewModel @Inject constructor(
"party_joined", "participant_joined" -> {
android.util.Log.d("MainViewModel", "Processing participant_joined event...")
// Don't add participants if keygen/sign has already started
// This prevents duplicate additions after session_started event
val currentStatus = repository.sessionStatus.value
if (currentStatus == SessionStatus.IN_PROGRESS || currentStatus == SessionStatus.COMPLETED) {
android.util.Log.d("MainViewModel", " Session already in progress/completed, ignoring participant_joined")
// CRITICAL: Check synchronous flag first - if session_started was already
// processed for this session, don't add more participants
// This is 100% reliable because the flag is set synchronously in session_started
// handler before any async operations, and callbacks are processed sequentially
if (sessionStartedForSession == event.sessionId) {
android.util.Log.d("MainViewModel", " Session already started for ${event.sessionId}, ignoring participant_joined")
return@setSessionEventCallback
}
@ -548,6 +560,8 @@ class MainViewModel @Inject constructor(
_publicKey.value = null
_createdInviteCode.value = null
_hasEnteredSession.value = false
// Reset synchronous flag for fresh session
sessionStartedForSession = null
// Reset session status to WAITING for fresh start
repository.resetSessionStatus()
}
@ -740,6 +754,8 @@ class MainViewModel @Inject constructor(
pendingJoinToken = ""
pendingPassword = ""
pendingJoinKeygenInfo = null
// Reset synchronous flag for fresh session
sessionStartedForSession = null
// Reset session status to WAITING for fresh start
repository.resetSessionStatus()
}
@ -925,6 +941,8 @@ class MainViewModel @Inject constructor(
pendingCoSignInviteCode = ""
pendingCoSignJoinToken = ""
pendingJoinSignInfo = null
// Reset synchronous flag for fresh session
sessionStartedForSession = null
// Reset session status to WAITING for fresh start
repository.resetSessionStatus()
}
@ -1671,6 +1689,8 @@ class MainViewModel @Inject constructor(
_signature.value = null
_txHash.value = null
pendingSignInitiatorInfo = null
// Reset synchronous flag for fresh session
sessionStartedForSession = null
// Reset session status to WAITING for fresh start
repository.resetSessionStatus()
}