diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt index 379aa7351e..0f89b097c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt @@ -44,7 +44,7 @@ private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan) @Composable internal fun NewMessage( state: State, - errors: Flow = emptyFlow(), + qrErrors: Flow = emptyFlow(), callbacks: Callbacks = object: Callbacks {}, onClose: () -> Unit = {}, onBack: () -> Unit = {}, @@ -58,7 +58,7 @@ internal fun NewMessage( HorizontalPager(pagerState) { when (TITLES[it]) { R.string.enter_account_id -> EnterAccountId(state, callbacks, onHelp) - R.string.qrScan -> MaybeScanQrCode(errors, onScan = callbacks::onScanQrCode) + R.string.qrScan -> MaybeScanQrCode(qrErrors, onScan = callbacks::onScanQrCode) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt index d484474146..732aca2c6d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt @@ -7,6 +7,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -34,7 +35,7 @@ internal class NewMessageViewModel @Inject constructor( private val _success = MutableSharedFlow() val success get() = _success.asSharedFlow() - private val _qrErrors = MutableSharedFlow() + private val _qrErrors = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) val qrErrors = _qrErrors.asSharedFlow() private var loadOnsJob: Job? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt index 086b79bdea..4a3b207c86 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt @@ -4,10 +4,13 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -36,7 +39,7 @@ internal class LinkDeviceViewModel @Inject constructor( private val _events = MutableSharedFlow() val events = _events.asSharedFlow() - private val _qrErrors = MutableSharedFlow() + private val _qrErrors = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) val qrErrors = _qrErrors.asSharedFlow() .mapNotNull { application.getString(R.string.qrNotRecoveryPassword) } @@ -57,7 +60,7 @@ internal class LinkDeviceViewModel @Inject constructor( try { decode(string).let(::onSuccess) } catch (e: Exception) { - onFailure(e) + onQrCodeScanFailure(e) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt index 6d34fabe6c..5e14e46dd1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -38,7 +39,7 @@ private val TITLES = listOf(R.string.view, R.string.scan) class QRCodeActivity : PassphraseRequiredActionBarActivity() { - private val errors = MutableSharedFlow() + private val errors = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt index 08ab554f82..2d95a13d5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt @@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -51,15 +52,13 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.base +import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.xl import java.util.concurrent.Executors @@ -161,12 +160,25 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() + LaunchedEffect(Unit) { - errors.filter { scaffoldState.snackbarHostState.currentSnackbarData == null } - .buffer(0, BufferOverflow.DROP_OLDEST) - .collect { error -> - scaffoldState.snackbarHostState.showSnackbar(message = error) - } + errors.collect { error -> + scaffoldState.snackbarHostState + .takeIf { it.currentSnackbarData == null } + ?.run { + scope.launch { + // showSnackbar() suspends until the Snackbar is dismissed. + // Launch in new scope so we drop new QR scan events, to prevent spamming + // snackbars to the user, or worse, queuing a chain of snackbars one after + // another to show and hide for the next minute or 2. + // Don't use debounce() because many QR scans can come through each second, + // and each scan could restart the timer which could mean no scan gets + // through until the user stops scanning; quite perplexing. + showSnackbar(message = error) + } + } + } } Scaffold(