Avoid using runBlocking in Node resolve function.

This commit is contained in:
Benoit Marty 2025-08-14 16:41:29 +02:00
parent 7559385439
commit 16acfa28d7
10 changed files with 159 additions and 26 deletions

View file

@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@ -23,11 +24,14 @@ import io.element.android.features.invitepeople.api.InvitePeopleEvents
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
import io.element.android.features.invitepeople.api.InvitePeopleState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.map
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomMembershipState
@ -46,16 +50,18 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class DefaultInvitePeoplePresenter @AssistedInject constructor(
@Assisted private val room: JoinedRoom,
@Assisted private val joinedRoom: JoinedRoom?,
@Assisted private val roomId: RoomId,
private val userRepository: UserRepository,
private val coroutineDispatchers: CoroutineDispatchers,
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
private val appErrorStateService: AppErrorStateService,
private val matrixClient: MatrixClient,
) : InvitePeoplePresenter {
@AssistedFactory
@ContributesBinding(SessionScope::class)
interface Factory : InvitePeoplePresenter.Factory {
override fun create(room: JoinedRoom): DefaultInvitePeoplePresenter
override fun create(joinedRoom: JoinedRoom?, roomId: RoomId): DefaultInvitePeoplePresenter
}
@Composable
@ -66,9 +72,21 @@ class DefaultInvitePeoplePresenter @AssistedInject constructor(
var searchQuery by rememberSaveable { mutableStateOf("") }
var searchActive by rememberSaveable { mutableStateOf(false) }
val showSearchLoader = rememberSaveable { mutableStateOf(false) }
val room by produceState(if (joinedRoom != null) AsyncData.Success(joinedRoom) else AsyncData.Loading()) {
if (joinedRoom == null) {
val result = matrixClient.getJoinedRoom(roomId)
value = if (result == null) {
AsyncData.Failure(Exception("Room not found"))
} else {
AsyncData.Success(result)
}
}
}
LaunchedEffect(Unit) {
fetchMembers(roomMembers)
LaunchedEffect(room.isSuccess()) {
room.dataOrNull()?.let {
fetchMembers(it, roomMembers)
}
}
LaunchedEffect(searchQuery, roomMembers) {
performSearch(
@ -96,7 +114,9 @@ class DefaultInvitePeoplePresenter @AssistedInject constructor(
searchResults.toggleUser(event.user)
}
is InvitePeopleEvents.SendInvites -> {
sessionCoroutineScope.sendInvites(selectedUsers.value)
room.dataOrNull()?.let {
sessionCoroutineScope.sendInvites(it, selectedUsers.value)
}
}
is InvitePeopleEvents.CloseSearch -> {
searchActive = false
@ -106,6 +126,7 @@ class DefaultInvitePeoplePresenter @AssistedInject constructor(
}
return DefaultInvitePeopleState(
room = room.map { },
canInvite = selectedUsers.value.isNotEmpty(),
selectedUsers = selectedUsers.value,
searchQuery = searchQuery,
@ -116,7 +137,10 @@ class DefaultInvitePeoplePresenter @AssistedInject constructor(
)
}
private fun CoroutineScope.sendInvites(selectedUsers: List<MatrixUser>) = launch {
private fun CoroutineScope.sendInvites(
room: JoinedRoom,
selectedUsers: List<MatrixUser>,
) = launch {
val anyInviteFailed = selectedUsers
.map { room.inviteUserById(it.userId) }
.any { it.isFailure }
@ -186,7 +210,10 @@ class DefaultInvitePeoplePresenter @AssistedInject constructor(
}.launchIn(this)
}
private suspend fun fetchMembers(roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>) {
private suspend fun fetchMembers(
room: JoinedRoom,
roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>
) {
suspend {
room.filterMembers("", coroutineDispatchers.io).toImmutableList()
}.runCatchingUpdatingState(roomMembers)

View file

@ -9,11 +9,13 @@ package io.element.android.features.invitepeople.impl
import io.element.android.features.invitepeople.api.InvitePeopleEvents
import io.element.android.features.invitepeople.api.InvitePeopleState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
data class DefaultInvitePeopleState(
val room: AsyncData<Unit>,
override val canInvite: Boolean,
val searchQuery: String,
val showSearchLoader: Boolean,

View file

@ -8,6 +8,7 @@
package io.element.android.features.invitepeople.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUser
@ -66,6 +67,7 @@ internal class DefaultInvitePeopleStateProvider : PreviewParameterProvider<Defau
),
showSearchLoader = true,
),
aDefaultInvitePeopleState(room = AsyncData.Failure(Exception("Room not found"))),
)
}
@ -84,6 +86,7 @@ private fun anInvitableUser(
)
private fun aDefaultInvitePeopleState(
room: AsyncData<Unit> = AsyncData.Success(Unit),
canInvite: Boolean = false,
searchQuery: String = "",
searchResults: SearchBarResultState<ImmutableList<InvitableUser>> = SearchBarResultState.Initial(),
@ -92,6 +95,7 @@ private fun aDefaultInvitePeopleState(
showSearchLoader: Boolean = false,
): DefaultInvitePeopleState {
return DefaultInvitePeopleState(
room = room,
canInvite = canInvite,
searchQuery = searchQuery,
searchResults = searchResults,

View file

@ -8,19 +8,24 @@
package io.element.android.features.invitepeople.impl
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.components.async.AsyncFailure
import io.element.android.libraries.designsystem.components.async.AsyncLoading
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview
@ -42,9 +47,39 @@ import kotlinx.collections.immutable.ImmutableList
fun InvitePeopleView(
state: DefaultInvitePeopleState,
modifier: Modifier = Modifier,
) {
when (state.room) {
is AsyncData.Failure -> InvitePeopleViewError(state.room.error, modifier)
AsyncData.Uninitialized,
is AsyncData.Loading,
is AsyncData.Success -> InvitePeopleContentView(state, modifier)
}
}
@Composable
private fun InvitePeopleViewError(
error: Throwable,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
AsyncFailure(
throwable = error,
onRetry = null,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
}
@Composable
private fun InvitePeopleContentView(
state: DefaultInvitePeopleState,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.fillMaxWidth(),
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
InvitePeopleSearchBar(