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

View File

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