fix(android): enable real-time progress updates for keygen/sign rounds
Connect TssNativeBridge.progress Flow to UI through: - Add progressCallback in TssRepository with startProgressCollection/stopProgressCollection - Subscribe to native bridge progress in keygen and sign methods - Add setProgressCallback in MainViewModel to update appropriate round state - Progress now flows: Go Native → TssNativeBridge → TssRepository → MainViewModel → UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3576af0f25
commit
fd56de5c00
|
|
@ -67,6 +67,13 @@ class TssRepository @Inject constructor(
|
|||
// Called every second with remaining seconds for UI countdown display
|
||||
private var countdownTickCallback: ((Long) -> Unit)? = null
|
||||
|
||||
// Progress callback (set by ViewModel)
|
||||
// Called when TSS protocol progress updates (round/totalRounds)
|
||||
private var progressCallback: ((Int, Int) -> Unit)? = null
|
||||
|
||||
// Job for collecting progress from native bridge
|
||||
private var progressCollectionJob: Job? = null
|
||||
|
||||
// Repository-level CoroutineScope for background tasks
|
||||
// Uses SupervisorJob so individual task failures don't cancel other tasks
|
||||
private val repositoryScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
|
@ -294,6 +301,45 @@ class TssRepository @Inject constructor(
|
|||
countdownTickCallback = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progress callback (called by ViewModel)
|
||||
* This callback is invoked when the TSS protocol progresses through rounds
|
||||
* @param callback receives (currentRound, totalRounds)
|
||||
*/
|
||||
fun setProgressCallback(callback: (Int, Int) -> Unit) {
|
||||
progressCallback = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collecting progress from native bridge
|
||||
* Called when keygen/sign session starts
|
||||
*/
|
||||
private fun startProgressCollection() {
|
||||
// Cancel any existing progress collection
|
||||
progressCollectionJob?.cancel()
|
||||
|
||||
android.util.Log.d("TssRepository", "[PROGRESS] Starting progress collection from native bridge")
|
||||
|
||||
progressCollectionJob = repositoryScope.launch {
|
||||
tssNativeBridge.progress.collect { (round, totalRounds) ->
|
||||
android.util.Log.d("TssRepository", "[PROGRESS] Round $round / $totalRounds")
|
||||
withContext(Dispatchers.Main) {
|
||||
progressCallback?.invoke(round, totalRounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop collecting progress from native bridge
|
||||
* Called when session ends or is cancelled
|
||||
*/
|
||||
private fun stopProgressCollection() {
|
||||
progressCollectionJob?.cancel()
|
||||
progressCollectionJob = null
|
||||
android.util.Log.d("TssRepository", "[PROGRESS] Progress collection stopped")
|
||||
}
|
||||
|
||||
/**
|
||||
* Start polling session status as fallback mechanism
|
||||
* This matches Electron's checkAndTriggerKeygen() polling behavior:
|
||||
|
|
@ -961,6 +1007,9 @@ class TssRepository @Inject constructor(
|
|||
return@coroutineScope Result.failure(startResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
||||
// Start collecting progress from native bridge
|
||||
startProgressCollection()
|
||||
|
||||
_sessionStatus.value = SessionStatus.IN_PROGRESS
|
||||
|
||||
// Mark ready
|
||||
|
|
@ -969,6 +1018,7 @@ class TssRepository @Inject constructor(
|
|||
// Wait for keygen result
|
||||
val keygenResult = tssNativeBridge.waitForKeygenResult(password)
|
||||
if (keygenResult.isFailure) {
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
return@coroutineScope Result.failure(keygenResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
|
@ -994,6 +1044,7 @@ class TssRepository @Inject constructor(
|
|||
// Report completion
|
||||
grpcClient.reportCompletion(sessionId, partyId, publicKeyBytes)
|
||||
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.COMPLETED
|
||||
pendingSessionId = null // Clear pending session ID on completion
|
||||
|
||||
|
|
@ -1003,6 +1054,7 @@ class TssRepository @Inject constructor(
|
|||
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("TssRepository", "Execute keygen as joiner failed", e)
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
pendingSessionId = null // Clear pending session ID on failure
|
||||
Result.failure(e)
|
||||
|
|
@ -1156,12 +1208,16 @@ class TssRepository @Inject constructor(
|
|||
return@coroutineScope Result.failure(startResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
||||
// Start collecting progress from native bridge
|
||||
startProgressCollection()
|
||||
|
||||
// Mark ready
|
||||
grpcClient.markPartyReady(sessionId, partyId)
|
||||
|
||||
// Wait for sign result
|
||||
val signResult = tssNativeBridge.waitForSignResult()
|
||||
if (signResult.isFailure) {
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
return@coroutineScope Result.failure(signResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
|
@ -1172,6 +1228,7 @@ class TssRepository @Inject constructor(
|
|||
val signatureBytes = android.util.Base64.decode(result.signature, android.util.Base64.NO_WRAP)
|
||||
grpcClient.reportCompletion(sessionId, partyId, signature = signatureBytes)
|
||||
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.COMPLETED
|
||||
pendingSessionId = null // Clear pending session ID on completion
|
||||
messageCollectionJob?.cancel()
|
||||
|
|
@ -1182,6 +1239,7 @@ class TssRepository @Inject constructor(
|
|||
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("TssRepository", "Execute sign as joiner failed", e)
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
pendingSessionId = null // Clear pending session ID on failure
|
||||
Result.failure(e)
|
||||
|
|
@ -1672,6 +1730,9 @@ class TssRepository @Inject constructor(
|
|||
return@coroutineScope Result.failure(startResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
||||
// Start collecting progress from native bridge
|
||||
startProgressCollection()
|
||||
|
||||
_sessionStatus.value = SessionStatus.IN_PROGRESS
|
||||
|
||||
// Mark ready
|
||||
|
|
@ -1680,6 +1741,7 @@ class TssRepository @Inject constructor(
|
|||
// Wait for keygen result
|
||||
val keygenResult = tssNativeBridge.waitForKeygenResult(password)
|
||||
if (keygenResult.isFailure) {
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
return@coroutineScope Result.failure(keygenResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
|
@ -1705,6 +1767,7 @@ class TssRepository @Inject constructor(
|
|||
// Report completion
|
||||
grpcClient.reportCompletion(sessionId, partyId, publicKeyBytes)
|
||||
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.COMPLETED
|
||||
pendingSessionId = null // Clear pending session ID on completion
|
||||
sessionEventJob?.cancel()
|
||||
|
|
@ -1713,6 +1776,7 @@ class TssRepository @Inject constructor(
|
|||
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("TssRepository", "Start keygen as initiator failed", e)
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
pendingSessionId = null // Clear pending session ID on failure
|
||||
Result.failure(e)
|
||||
|
|
@ -2055,6 +2119,9 @@ class TssRepository @Inject constructor(
|
|||
return@withContext Result.failure(startResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
||||
// Start collecting progress from native bridge
|
||||
startProgressCollection()
|
||||
|
||||
_sessionStatus.value = SessionStatus.IN_PROGRESS
|
||||
|
||||
// Note: Message routing is already started in createSignSession after auto-join
|
||||
|
|
@ -2068,6 +2135,7 @@ class TssRepository @Inject constructor(
|
|||
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
Result.failure(e)
|
||||
}
|
||||
|
|
@ -2082,6 +2150,7 @@ class TssRepository @Inject constructor(
|
|||
try {
|
||||
val signResult = tssNativeBridge.waitForSignResult()
|
||||
if (signResult.isFailure) {
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.FAILED
|
||||
return@withContext Result.failure(signResult.exceptionOrNull()!!)
|
||||
}
|
||||
|
|
@ -2095,6 +2164,7 @@ class TssRepository @Inject constructor(
|
|||
grpcClient.reportCompletion(session.sessionId, partyId, signature = signatureBytes)
|
||||
}
|
||||
|
||||
stopProgressCollection()
|
||||
_sessionStatus.value = SessionStatus.COMPLETED
|
||||
messageCollectionJob?.cancel()
|
||||
|
||||
|
|
|
|||
|
|
@ -298,6 +298,30 @@ class MainViewModel @Inject constructor(
|
|||
_uiState.update { it.copy(countdownSeconds = remainingSeconds) }
|
||||
}
|
||||
|
||||
// Setup progress callback for real-time round updates from native TSS bridge
|
||||
repository.setProgressCallback { round, totalRounds ->
|
||||
android.util.Log.d("MainViewModel", "Progress update: $round / $totalRounds")
|
||||
// Update the appropriate round state based on which session type is active
|
||||
when {
|
||||
// Initiator keygen (CreateWallet)
|
||||
_currentSessionId.value != null && pendingJoinKeygenInfo == null && pendingJoinSignInfo == null -> {
|
||||
_currentRound.value = round
|
||||
}
|
||||
// Joiner keygen (JoinKeygen)
|
||||
pendingJoinKeygenInfo != null -> {
|
||||
_joinKeygenRound.value = round
|
||||
}
|
||||
// Joiner sign (CoSign/参与签名)
|
||||
pendingJoinSignInfo != null -> {
|
||||
_coSignRound.value = round
|
||||
}
|
||||
// Initiator sign (Transfer)
|
||||
_signSessionId.value != null -> {
|
||||
_signCurrentRound.value = round
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repository.setSessionEventCallback { event ->
|
||||
android.util.Log.d("MainViewModel", "=== MainViewModel received session event ===")
|
||||
android.util.Log.d("MainViewModel", " eventType: ${event.eventType}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue