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

@ -26,16 +26,13 @@ import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.coroutines.runBlocking
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
class CreateRoomFlowNode @AssistedInject constructor( class CreateRoomFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val client: MatrixClient,
) : BaseFlowNode<CreateRoomFlowNode.NavTarget>( ) : BaseFlowNode<CreateRoomFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.ConfigureRoom, initialElement = NavTarget.ConfigureRoom,
@ -59,8 +56,7 @@ class CreateRoomFlowNode @AssistedInject constructor(
createNode<ConfigureRoomNode>(buildContext, plugins = listOf(callback)) createNode<ConfigureRoomNode>(buildContext, plugins = listOf(callback))
} }
is NavTarget.AddPeople -> { is NavTarget.AddPeople -> {
val joinedRoom = runBlocking { client.getJoinedRoom(navTarget.roomId) } ?: error("Room not found") val inputs = AddPeopleNode.Inputs(navTarget.roomId)
val inputs = AddPeopleNode.Inputs(joinedRoom)
val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback { val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback {
override fun onFinish() { override fun onFinish() {
onRoomCreated(navTarget.roomId) onRoomCreated(navTarget.roomId)

View file

@ -21,7 +21,7 @@ import io.element.android.features.invitepeople.api.InvitePeopleRenderer
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.core.RoomId
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
class AddPeopleNode @AssistedInject constructor( class AddPeopleNode @AssistedInject constructor(
@ -31,7 +31,7 @@ class AddPeopleNode @AssistedInject constructor(
private val invitePeopleRenderer: InvitePeopleRenderer, private val invitePeopleRenderer: InvitePeopleRenderer,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
data class Inputs( data class Inputs(
val joinedRoom: JoinedRoom val roomId: RoomId,
) : NodeInputs ) : NodeInputs
interface Callback : Plugin { interface Callback : Plugin {
@ -42,8 +42,11 @@ class AddPeopleNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onFinish() } plugins<Callback>().forEach { it.onFinish() }
} }
private val joinedRoom = inputs<Inputs>().joinedRoom private val roomId = inputs<Inputs>().roomId
private val invitePeoplePresenter = invitePeoplePresenterFactory.create(joinedRoom) private val invitePeoplePresenter = invitePeoplePresenterFactory.create(
joinedRoom = null,
roomId = roomId,
)
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View file

@ -8,10 +8,14 @@
package io.element.android.features.invitepeople.api package io.element.android.features.invitepeople.api
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
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.JoinedRoom
interface InvitePeoplePresenter : Presenter<InvitePeopleState> { interface InvitePeoplePresenter : Presenter<InvitePeopleState> {
interface Factory { interface Factory {
fun create(room: JoinedRoom): InvitePeoplePresenter fun create(
joinedRoom: JoinedRoom?,
roomId: RoomId,
): InvitePeoplePresenter
} }
} }

View file

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

View file

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

View file

@ -8,19 +8,24 @@
package io.element.android.features.invitepeople.impl package io.element.android.features.invitepeople.impl
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme 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.async.AsyncLoading
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
@ -42,9 +47,39 @@ import kotlinx.collections.immutable.ImmutableList
fun InvitePeopleView( fun InvitePeopleView(
state: DefaultInvitePeopleState, state: DefaultInvitePeopleState,
modifier: Modifier = Modifier, 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( Column(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
InvitePeopleSearchBar( InvitePeopleSearchBar(

View file

@ -10,15 +10,21 @@ package io.element.android.features.invitepeople.impl
import app.cash.turbine.ReceiveTurbine import app.cash.turbine.ReceiveTurbine
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.invitepeople.api.InvitePeopleEvents import io.element.android.features.invitepeople.api.InvitePeopleEvents
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembersState
import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.libraries.matrix.test.room.aRoomMemberList import io.element.android.libraries.matrix.test.room.aRoomMemberList
@ -54,7 +60,7 @@ internal class DefaultInvitePeoplePresenterTest {
val presenter = createDefaultInvitePeoplePresenter() val presenter = createDefaultInvitePeoplePresenter()
presenter.test { presenter.test {
val initialState = awaitItemAsDefault() val initialState = awaitItemAsDefault()
assertThat(initialState.room).isEqualTo(AsyncData.Success(Unit))
assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.Initial::class.java) assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.Initial::class.java)
assertThat(initialState.isSearchActive).isFalse() assertThat(initialState.isSearchActive).isFalse()
assertThat(initialState.canInvite).isFalse() assertThat(initialState.canInvite).isFalse()
@ -452,6 +458,40 @@ internal class DefaultInvitePeoplePresenterTest {
} }
} }
@Test
fun `present - when joinedRoom is not provided, it is retrieved on the MatrixClient`() = runTest {
val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, FakeJoinedRoom())
}
val presenter = createDefaultInvitePeoplePresenter(
joinedRoom = null,
roomId = A_ROOM_ID,
matrixClient = matrixClient,
)
presenter.test {
val initialState = awaitItemAsDefault()
assertThat(initialState.room.isLoading()).isTrue()
val finalState = awaitItemAsDefault()
assertThat(finalState.room).isEqualTo(AsyncData.Success(Unit))
}
}
@Test
fun `present - when joinedRoom is not provided, it is retrieved on the MatrixClient - error case`() = runTest {
val matrixClient = FakeMatrixClient()
val presenter = createDefaultInvitePeoplePresenter(
joinedRoom = null,
roomId = A_ROOM_ID,
matrixClient = matrixClient,
)
presenter.test {
val initialState = awaitItemAsDefault()
assertThat(initialState.room.isLoading()).isTrue()
val finalState = awaitItemAsDefault()
assertThat(finalState.room.errorOrNull()?.message).isEqualTo("Room not found")
}
}
private suspend fun FakeUserRepository.emitStateWithUsers( private suspend fun FakeUserRepository.emitStateWithUsers(
users: List<MatrixUser>, users: List<MatrixUser>,
isSearching: Boolean = false isSearching: Boolean = false
@ -484,19 +524,24 @@ private suspend fun <T> ReceiveTurbine<T>.awaitItemAsDefault(): DefaultInvitePeo
fun TestScope.createDefaultInvitePeoplePresenter( fun TestScope.createDefaultInvitePeoplePresenter(
roomMembersState: RoomMembersState = RoomMembersState.Ready(aRoomMemberList()), roomMembersState: RoomMembersState = RoomMembersState.Ready(aRoomMemberList()),
inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() }, inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() },
joinedRoom: JoinedRoom? = FakeJoinedRoom(
inviteUserResult = inviteUserResult,
).apply {
givenRoomMembersState(roomMembersState)
},
roomId: RoomId = A_ROOM_ID,
userRepository: UserRepository = FakeUserRepository(), userRepository: UserRepository = FakeUserRepository(),
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
appErrorStateService: AppErrorStateService = FakeAppErrorStateService(), appErrorStateService: AppErrorStateService = FakeAppErrorStateService(),
matrixClient: MatrixClient = FakeMatrixClient(),
): DefaultInvitePeoplePresenter { ): DefaultInvitePeoplePresenter {
return DefaultInvitePeoplePresenter( return DefaultInvitePeoplePresenter(
room = FakeJoinedRoom( joinedRoom = joinedRoom,
inviteUserResult = inviteUserResult, roomId = roomId,
).apply {
givenRoomMembersState(roomMembersState)
},
userRepository = userRepository, userRepository = userRepository,
coroutineDispatchers = coroutineDispatchers, coroutineDispatchers = coroutineDispatchers,
coroutineScope = backgroundScope, sessionCoroutineScope = backgroundScope,
appErrorStateService = appErrorStateService, appErrorStateService = appErrorStateService,
matrixClient = matrixClient,
) )
} }

View file

@ -40,7 +40,10 @@ class RoomInviteMembersNode @AssistedInject constructor(
) )
} }
private val invitePeoplePresenter = invitePeoplePresenterFactory.create(room) private val invitePeoplePresenter = invitePeoplePresenterFactory.create(
joinedRoom = room,
roomId = room.roomId,
)
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View file

@ -160,3 +160,17 @@ suspend inline fun <T> runUpdatingState(
} }
) )
} }
inline fun <T, R> AsyncData<T>.map(
transform: (T?) -> R,
): AsyncData<R> {
return when (this) {
is AsyncData.Failure -> AsyncData.Failure(
error = error,
prevData = transform(prevData)
)
is AsyncData.Loading -> AsyncData.Loading(transform(prevData))
is AsyncData.Success -> AsyncData.Success(transform(data))
AsyncData.Uninitialized -> AsyncData.Uninitialized
}
}