From 8a9a983cbd0da518bf585de53af5fc1f7647f728 Mon Sep 17 00:00:00 2001 From: hailin Date: Tue, 27 Jan 2026 09:08:13 -0800 Subject: [PATCH] =?UTF-8?q?fix(android):=20=E4=BD=BF=E7=94=A8=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=A0=87=E5=BF=97=E4=BF=AE=E5=A4=8D=E5=8F=82=E4=B8=8E?= =?UTF-8?q?=E6=96=B9=E6=98=BE=E7=A4=BA4/3=E7=9A=84=E7=AB=9E=E6=80=81?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题根因: - 之前使用异步的 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 --- .../presentation/viewmodel/MainViewModel.kt | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt index b4612ef5..15643434 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt @@ -45,6 +45,11 @@ class MainViewModel @Inject constructor( private val _hasEnteredSession = MutableStateFlow(false) val hasEnteredSession: StateFlow = _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() }