From f7b20e2373c28c23fa993d3f5f9ea9e5066a0e3a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 12 Jun 2023 18:28:57 +0200 Subject: [PATCH] Let HomeserverResolver emit the List and not the Async. --- .../login/impl/resolver/HomeserverResolver.kt | 38 +++++++------------ .../SearchAccountProviderPresenter.kt | 25 +++++++++--- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt index a11b680f63..7c692f5208 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt @@ -17,7 +17,6 @@ package io.element.android.features.login.impl.resolver import io.element.android.features.login.impl.resolver.network.WellknownRequest -import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.data.tryOrNull @@ -26,7 +25,6 @@ import io.element.android.libraries.core.uri.isValidUrl import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.currentCoroutineContext -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext @@ -42,24 +40,20 @@ class HomeserverResolver @Inject constructor( private val wellknownRequest: WellknownRequest, ) { - suspend fun resolve(userInput: String): Flow>> = flow { + suspend fun resolve(userInput: String): Flow> = flow { val flowContext = currentCoroutineContext() - emit(Async.Uninitialized) - // Debounce - delay(300) val trimmedUserInput = userInput.trim() if (trimmedUserInput.length < 4) return@flow - emit(Async.Loading()) val candidateBase = trimmedUserInput.ensureProtocol().removeSuffix("/") val list = getUrlCandidates(candidateBase) val currentList = Collections.synchronizedList(mutableListOf()) // Run all the requests in parallel withContext(dispatchers.io) { - list.map { + list.map { url -> async { val wellKnown = tryOrNull { withTimeout(5000) { - wellknownRequest.execute(it) + wellknownRequest.execute(url) } } val isValid = wellKnown?.isValid().orFalse() @@ -68,35 +62,29 @@ class HomeserverResolver @Inject constructor( // Emit the list as soon as possible currentList.add( HomeserverData( - homeserverUrl = it, + homeserverUrl = url, isWellknownValid = true, supportSlidingSync = supportSlidingSync ) ) withContext(flowContext) { - emit(Async.Success(currentList)) + emit(currentList.toList()) } } } }.awaitAll() } // If list is empty, and the user has entered an URL, do not block the user. - if (currentList.isEmpty()) { - if (trimmedUserInput.isValidUrl()) { - emit( - Async.Success( - listOf( - HomeserverData( - homeserverUrl = trimmedUserInput, - isWellknownValid = false, - supportSlidingSync = false, - ) - ) + if (currentList.isEmpty() && trimmedUserInput.isValidUrl()) { + emit( + listOf( + HomeserverData( + homeserverUrl = trimmedUserInput, + isWellknownValid = false, + supportSlidingSync = false, ) ) - } else { - emit(Async.Uninitialized) - } + ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt index f95c027620..1d8271e394 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt @@ -18,6 +18,7 @@ package io.element.android.features.login.impl.screens.searchaccountprovider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -28,6 +29,9 @@ import io.element.android.features.login.impl.resolver.HomeserverData import io.element.android.features.login.impl.resolver.HomeserverResolver import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import javax.inject.Inject class SearchAccountProviderPresenter @Inject constructor( @@ -42,14 +46,12 @@ class SearchAccountProviderPresenter @Inject constructor( } val changeServerState = changeServerPresenter.present() - var data: Async> by remember { + val data: MutableState>> = remember { mutableStateOf(Async.Uninitialized) } LaunchedEffect(userInput) { - homeserverResolver.resolve(userInput).collect { - data = it - } + onUserInput(userInput, data) } fun handleEvents(event: SearchAccountProviderEvents) { @@ -62,9 +64,22 @@ class SearchAccountProviderPresenter @Inject constructor( return SearchAccountProviderState( userInput = userInput, - userInputResult = data, + userInputResult = data.value, changeServerState = changeServerState, eventSink = ::handleEvents ) } + + private fun CoroutineScope.onUserInput(userInput: String, data: MutableState>>) = launch { + data.value = Async.Uninitialized + // Debounce + delay(300) + data.value = Async.Loading() + homeserverResolver.resolve(userInput).collect { + data.value = Async.Success(it) + } + if (data.value !is Async.Success) { + data.value = Async.Uninitialized + } + } }