Fix multiple loading activities launched

This commit is contained in:
Andrew 2024-03-12 20:12:18 +10:30
parent 0e7f95dede
commit 0dde7297b8
2 changed files with 37 additions and 23 deletions

View File

@ -66,6 +66,7 @@ import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
@ -78,6 +79,7 @@ import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.components.SessionTabRow
import java.util.concurrent.Executors
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
private const val TAG = "LinkDeviceActivity"
@ -108,6 +110,7 @@ class LinkDeviceActivity : BaseActionBarActivity() {
lifecycleScope.launch {
viewModel.eventFlow.collect {
startLoadingActivity(it.mnemonic)
finish()
}
}
@ -115,7 +118,7 @@ class LinkDeviceActivity : BaseActionBarActivity() {
setContent {
val state by viewModel.stateFlow.collectAsState()
AppTheme {
LoadAccountScreen(state, viewModel::onChange, viewModel::tryPhrase)
LoadAccountScreen(state, viewModel::onChange, viewModel::onContinue)
}
}
}.let(::setContentView)

View File

@ -1,28 +1,26 @@
package org.thoughtcrime.securesms.onboarding
import android.app.Application
import androidx.compose.runtime.snapshots.SnapshotApplyResult
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.session.libsignal.crypto.MnemonicCodec
import org.session.libsignal.utilities.Hex
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
class LinkDeviceEvent(val mnemonic: ByteArray)
@ -30,38 +28,51 @@ class LinkDeviceEvent(val mnemonic: ByteArray)
class LinkDeviceViewModel @Inject constructor(
application: Application
): AndroidViewModel(application) {
private val QR_ERROR_TIME = 3.seconds
private val state = MutableStateFlow(LinkDeviceState())
val stateFlow = state.asStateFlow()
private val qrErrors = Channel<Throwable>()
private val event = Channel<LinkDeviceEvent>()
val eventFlow = event.receiveAsFlow()
val eventFlow = event.receiveAsFlow().take(1)
private val qrErrors = Channel<Throwable>()
private val qrErrorsFlow = qrErrors.receiveAsFlow().debounce(QR_ERROR_TIME).takeWhile { event.isEmpty }
private val phrases = Channel<String>()
private val codec by lazy { MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) } }
init {
fun onContinue() {
viewModelScope.launch {
phrases.receiveAsFlow().map {
runCatching {
MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) }
.decode(it)
.let(Hex::fromStringCondensed)
}
}.takeWhile {
it.getOrNull()?.let(::LinkDeviceEvent)?.let { event.send(it) }
it.exceptionOrNull()?.let { qrErrors.send(it) }
it.isFailure
}.collect()
runDecodeCatching(state.value.recoveryPhrase)
.onSuccess(::onSuccess)
.onFailure(::onFailure)
}
}
fun tryPhrase(string: String = state.value.recoveryPhrase) {
fun scan(string: String) {
viewModelScope.launch {
phrases.send(string)
runDecodeCatching(string)
.onSuccess(::onSuccess)
.onFailure(::onScanFailure)
}
}
fun onChange(recoveryPhrase: String) {
state.value = LinkDeviceState(recoveryPhrase)
}
private fun onSuccess(seed: ByteArray) {
viewModelScope.launch { event.send(LinkDeviceEvent(seed)) }
}
private fun onFailure(error: Throwable) {
state.update { it.copy(error = error.message) }
}
private fun onScanFailure(error: Throwable) {
state.update { it.copy(error = error.message) }
}
private fun runDecodeCatching(mnemonic: String) = runCatching {
decode(mnemonic)
}
private fun decode(mnemonic: String) = codec.decode(mnemonic).let(Hex::fromStringCondensed)!!
}