Fix loading state in New Message Fragment

This commit is contained in:
Andrew 2024-06-12 13:59:40 +09:30
parent 60d41ad10d
commit f8e3bc7d9a
3 changed files with 31 additions and 13 deletions

View File

@ -183,6 +183,7 @@ fun EnterAccountId(
.padding(horizontal = LocalDimensions.current.marginLarge) .padding(horizontal = LocalDimensions.current.marginLarge)
.fillMaxWidth() .fillMaxWidth()
.contentDescription(R.string.next), .contentDescription(R.string.next),
enabled = state.isNextButtonEnabled,
onClick = { callbacks.onContinue() } onClick = { callbacks.onContinue() }
) { ) {
LoadingArcOr(state.loading) { LoadingArcOr(state.loading) {

View File

@ -4,27 +4,29 @@ import android.app.Application
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.Job
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeAPI
import org.session.libsignal.utilities.PublicKeyValidation import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
@HiltViewModel @HiltViewModel
class NewMessageViewModel @Inject constructor( class NewMessageViewModel @Inject constructor(
private val application: Application private val application: Application
): AndroidViewModel(application), Callbacks { ): AndroidViewModel(application), Callbacks {
private val _state = MutableStateFlow(State()) private val _state = MutableStateFlow(State())
val state = _state.asStateFlow() val state = _state.asStateFlow()
@ -34,11 +36,13 @@ class NewMessageViewModel @Inject constructor(
private val _qrErrors = Channel<String>() private val _qrErrors = Channel<String>()
val qrErrors: Flow<String> = _qrErrors.receiveAsFlow() val qrErrors: Flow<String> = _qrErrors.receiveAsFlow()
private var job: Job? = null
override fun onChange(value: String) { override fun onChange(value: String) {
_state.update { it.copy( job?.cancel()
newMessageIdOrOns = value, job = null
error = null
) } _state.update { State(newMessageIdOrOns = value) }
} }
override fun onContinue() { override fun onContinue() {
@ -54,6 +58,8 @@ class NewMessageViewModel @Inject constructor(
} }
private fun createPrivateChatIfPossible(onsNameOrPublicKey: String) { private fun createPrivateChatIfPossible(onsNameOrPublicKey: String) {
if (job?.isActive == true) return
if (PublicKeyValidation.isValid(onsNameOrPublicKey, isPrefixRequired = false)) { if (PublicKeyValidation.isValid(onsNameOrPublicKey, isPrefixRequired = false)) {
if (PublicKeyValidation.hasValidPrefix(onsNameOrPublicKey)) { if (PublicKeyValidation.hasValidPrefix(onsNameOrPublicKey)) {
onPublicKey(onsNameOrPublicKey) onPublicKey(onsNameOrPublicKey)
@ -64,11 +70,18 @@ class NewMessageViewModel @Inject constructor(
// This could be an ONS name // This could be an ONS name
_state.update { it.copy(error = null, loading = true) } _state.update { it.copy(error = null, loading = true) }
SnodeAPI.getSessionID(onsNameOrPublicKey).successUi { hexEncodedPublicKey -> job = viewModelScope.launch(Dispatchers.IO) {
_state.update { it.copy(loading = false) } try {
onPublicKey(onsNameOrPublicKey) withTimeout(5.seconds) {
}.failUi { exception -> SnodeAPI.getSessionID(onsNameOrPublicKey).get()
_state.update { it.copy(loading = false, error = GetString(exception) { it.toMessage() }) } }
if (isActive) {
_state.update { it.copy(loading = false) }
onPublicKey(onsNameOrPublicKey)
}
} catch (e: Exception) {
if (isActive) _state.update { it.copy(loading = false, error = GetString(e) { it.toMessage() }) }
}
} }
} }
} }
@ -87,7 +100,9 @@ data class State(
val newMessageIdOrOns: String = "", val newMessageIdOrOns: String = "",
val error: GetString? = null, val error: GetString? = null,
val loading: Boolean = false val loading: Boolean = false
) ) {
val isNextButtonEnabled: Boolean get() = newMessageIdOrOns.isNotBlank()
}
sealed interface Event { sealed interface Event {
data class Success(val key: String): Event data class Success(val key: String): Event

View File

@ -93,12 +93,14 @@ fun OutlineButton(
@Composable @Composable
fun OutlineButton( fun OutlineButton(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onClick: () -> Unit, onClick: () -> Unit,
content: @Composable () -> Unit = {} content: @Composable () -> Unit = {}
) { ) {
OutlinedButton( OutlinedButton(
modifier = modifier.applyButtonSize(), modifier = modifier.applyButtonSize(),
enabled = enabled,
interactionSource = interactionSource, interactionSource = interactionSource,
onClick = onClick, onClick = onClick,
border = BorderStroke(1.dp, LocalButtonColor.current), border = BorderStroke(1.dp, LocalButtonColor.current),