knock requests : rework knock requests service to avoid reloading of data (and weird ui glitch because of them)

This commit is contained in:
ganfra 2024-12-18 20:24:29 +01:00
parent 69307f7c62
commit 92380ef3e4
12 changed files with 127 additions and 100 deletions

View file

@ -19,11 +19,6 @@ import io.element.android.features.knockrequests.impl.data.KnockRequestsService
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.core.extensions.firstIfSingle import io.element.android.libraries.core.extensions.firstIfSingle
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.ui.room.canHandleKnockRequestsAsState
import io.element.android.libraries.matrix.ui.room.canInviteAsState
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -33,10 +28,8 @@ import javax.inject.Inject
private const val ACCEPT_ERROR_DISPLAY_DURATION = 1500L private const val ACCEPT_ERROR_DISPLAY_DURATION = 1500L
class KnockRequestsBannerPresenter @Inject constructor( class KnockRequestsBannerPresenter @Inject constructor(
private val room: MatrixRoom,
private val knockRequestsService: KnockRequestsService, private val knockRequestsService: KnockRequestsService,
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
private val featureFlagService: FeatureFlagService,
) : Presenter<KnockRequestsBannerState> { ) : Presenter<KnockRequestsBannerState> {
@Composable @Composable
override fun present(): KnockRequestsBannerState { override fun present(): KnockRequestsBannerState {
@ -48,15 +41,12 @@ class KnockRequestsBannerPresenter @Inject constructor(
} }
}.collectAsState() }.collectAsState()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val permissions by knockRequestsService.permissionsFlow.collectAsState()
val canAccept by room.canInviteAsState(syncUpdateFlow.value)
val canHandleKnockRequests by room.canHandleKnockRequestsAsState(syncUpdateFlow.value)
val showAcceptError = remember { mutableStateOf(false) } val showAcceptError = remember { mutableStateOf(false) }
val isKnockRequestsEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(false)
val shouldShowBanner by remember { val shouldShowBanner by remember {
derivedStateOf { derivedStateOf {
isKnockRequestsEnabled && canHandleKnockRequests && knockRequests.isNotEmpty() permissions.canHandle && knockRequests.isNotEmpty()
} }
} }
@ -79,7 +69,7 @@ class KnockRequestsBannerPresenter @Inject constructor(
return KnockRequestsBannerState( return KnockRequestsBannerState(
knockRequests = knockRequests, knockRequests = knockRequests,
displayAcceptError = showAcceptError.value, displayAcceptError = showAcceptError.value,
canAccept = canAccept, canAccept = permissions.canAccept,
isVisible = shouldShowBanner, isVisible = shouldShowBanner,
eventSink = ::handleEvents, eventSink = ::handleEvents,
) )

View file

@ -0,0 +1,32 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.knockrequests.impl.data
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.powerlevels.canBan
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
import io.element.android.libraries.matrix.api.room.powerlevels.canKick
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
data class KnockRequestPermissions(
val canAccept: Boolean,
val canDecline: Boolean,
val canBan: Boolean,
) {
val canHandle = canAccept || canDecline || canBan
}
fun MatrixRoom.knockRequestPermissionsFlow(): Flow<KnockRequestPermissions> {
return syncUpdateFlow.map {
val canAccept = canInvite().getOrDefault(false)
val canDecline = canKick().getOrDefault(false)
val canBan = canBan().getOrDefault(false)
KnockRequestPermissions(canAccept, canDecline, canBan)
}
}

View file

@ -12,23 +12,23 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.knock.KnockRequest
class KnockRequestWrapper( class KnockRequestWrapper(
private val knockRequest: KnockRequest, private val inner: KnockRequest,
dateFormatter: (Long?) -> String? = { null } dateFormatter: (Long?) -> String? = { null }
) : KnockRequestPresentable { ) : KnockRequestPresentable {
override val eventId: EventId = knockRequest.eventId override val eventId: EventId = inner.eventId
override val userId: UserId = knockRequest.userId override val userId: UserId = inner.userId
override val displayName: String? = knockRequest.displayName override val displayName: String? = inner.displayName
override val avatarUrl: String? = knockRequest.avatarUrl override val avatarUrl: String? = inner.avatarUrl
override val reason: String? = knockRequest.reason?.trim() override val reason: String? = inner.reason?.trim()
override val formattedDate: String? = dateFormatter(knockRequest.timestamp) override val formattedDate: String? = dateFormatter(inner.timestamp)
val isSeen: Boolean = knockRequest.isSeen val isSeen: Boolean = inner.isSeen
suspend fun accept(): Result<Unit> = knockRequest.accept() suspend fun accept(): Result<Unit> = inner.accept()
suspend fun decline(reason: String?): Result<Unit> = knockRequest.decline(reason) suspend fun decline(reason: String?): Result<Unit> = inner.decline(reason)
suspend fun declineAndBan(reason: String?): Result<Unit> = knockRequest.declineAndBan(reason) suspend fun declineAndBan(reason: String?): Result<Unit> = inner.declineAndBan(reason)
suspend fun markAsSeen(): Result<Unit> = knockRequest.markAsSeen() suspend fun markAsSeen(): Result<Unit> = inner.markAsSeen()
} }

View file

@ -12,6 +12,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
@Module @Module
@ -19,7 +21,12 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
object KnockRequestsModule { object KnockRequestsModule {
@Provides @Provides
@SingleIn(RoomScope::class) @SingleIn(RoomScope::class)
fun knockRequestsService(room: MatrixRoom): KnockRequestsService { fun knockRequestsService(room: MatrixRoom, featureFlagService: FeatureFlagService): KnockRequestsService {
return KnockRequestsService(room.knockRequestsFlow, room.roomCoroutineScope) return KnockRequestsService(
knockRequestsFlow = room.knockRequestsFlow,
permissionsFlow = room.knockRequestPermissionsFlow(),
isKnockFeatureEnabledFlow = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock),
coroutineScope = room.roomCoroutineScope
)
} }
} }

View file

@ -10,6 +10,7 @@ package io.element.android.features.knockrequests.impl.data
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.knock.KnockRequest
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -19,27 +20,40 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
class KnockRequestsService( class KnockRequestsService(
knockRequestsFlow: Flow<List<KnockRequest>>, knockRequestsFlow: Flow<List<KnockRequest>>,
permissionsFlow: Flow<KnockRequestPermissions>,
isKnockFeatureEnabledFlow: Flow<Boolean>,
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
) { ) {
// Keep track of the knock requests that have been handled, so we don't have to wait for sync to remove them. // Keep track of the knock requests that have been handled, so we don't have to wait for sync to remove them.
private val handledKnockRequestIds = MutableStateFlow<Set<EventId>>(emptySet()) private val handledKnockRequestIds = MutableStateFlow<Set<EventId>>(emptySet())
val knockRequestsFlow = combine( val knockRequestsFlow = combine(
knockRequestsFlow.wrapped(), isKnockFeatureEnabledFlow,
knockRequestsFlow,
handledKnockRequestIds, handledKnockRequestIds,
) { knockRequests, handledKnockIds -> ) { isKnockEnabled, knockRequests, handledKnockIds ->
val presentableKnockRequests = knockRequests if (!isKnockEnabled) {
.filter { it.eventId !in handledKnockIds } AsyncData.Success(persistentListOf())
.toImmutableList() } else {
AsyncData.Success(presentableKnockRequests) val presentableKnockRequests = knockRequests
.filter { it.eventId !in handledKnockIds }
.map { inner -> KnockRequestWrapper(inner) }
.toImmutableList()
AsyncData.Success(presentableKnockRequests)
}
}.stateIn(coroutineScope, SharingStarted.Lazily, AsyncData.Loading()) }.stateIn(coroutineScope, SharingStarted.Lazily, AsyncData.Loading())
val permissionsFlow = permissionsFlow.stateIn(
scope = coroutineScope,
started = SharingStarted.Lazily,
initialValue = KnockRequestPermissions(canAccept = false, canDecline = false, canBan = false)
)
private fun knockRequestsList() = knockRequestsFlow.value.dataOrNull().orEmpty() private fun knockRequestsList() = knockRequestsFlow.value.dataOrNull().orEmpty()
private fun getKnockRequestById(eventId: EventId): KnockRequestWrapper? { private fun getKnockRequestById(eventId: EventId): KnockRequestWrapper? {
@ -128,8 +142,4 @@ class KnockRequestsService(
} }
private fun knockRequestNotFoundResult() = Result.failure<Unit>(IllegalArgumentException("Knock request not found")) private fun knockRequestNotFoundResult() = Result.failure<Unit>(IllegalArgumentException("Knock request not found"))
private fun Flow<List<KnockRequest>>.wrapped() = map { knockRequests ->
knockRequests.map { KnockRequestWrapper(it) }
}
} }

View file

@ -20,16 +20,11 @@ import io.element.android.features.knockrequests.impl.data.KnockRequestsService
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.ui.room.canBanAsState
import io.element.android.libraries.matrix.ui.room.canInviteAsState
import io.element.android.libraries.matrix.ui.room.canKickAsState
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class KnockRequestsListPresenter @Inject constructor( class KnockRequestsListPresenter @Inject constructor(
private val room: MatrixRoom,
private val knockRequestsService: KnockRequestsService, private val knockRequestsService: KnockRequestsService,
) : Presenter<KnockRequestsListState> { ) : Presenter<KnockRequestsListState> {
@Composable @Composable
@ -37,11 +32,7 @@ class KnockRequestsListPresenter @Inject constructor(
val asyncAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) } val asyncAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
var actionTarget by remember { mutableStateOf<KnockRequestsActionTarget>(KnockRequestsActionTarget.None) } var actionTarget by remember { mutableStateOf<KnockRequestsActionTarget>(KnockRequestsActionTarget.None) }
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val permissions by knockRequestsService.permissionsFlow.collectAsState()
val canBan by room.canBanAsState(syncUpdateFlow.value)
val canDecline by room.canKickAsState(syncUpdateFlow.value)
val canAccept by room.canInviteAsState(syncUpdateFlow.value)
val knockRequests by knockRequestsService.knockRequestsFlow.collectAsState() val knockRequests by knockRequestsService.knockRequestsFlow.collectAsState()
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -79,10 +70,8 @@ class KnockRequestsListPresenter @Inject constructor(
return KnockRequestsListState( return KnockRequestsListState(
knockRequests = knockRequests, knockRequests = knockRequests,
actionTarget = actionTarget, actionTarget = actionTarget,
permissions = permissions,
asyncAction = asyncAction.value, asyncAction = asyncAction.value,
canAccept = canAccept,
canDecline = canDecline,
canBan = canBan,
eventSink = ::handleEvents eventSink = ::handleEvents
) )
} }

View file

@ -8,6 +8,7 @@
package io.element.android.features.knockrequests.impl.list package io.element.android.features.knockrequests.impl.list
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import io.element.android.features.knockrequests.impl.data.KnockRequestPermissions
import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
@ -17,12 +18,10 @@ data class KnockRequestsListState(
val knockRequests: AsyncData<ImmutableList<KnockRequestPresentable>>, val knockRequests: AsyncData<ImmutableList<KnockRequestPresentable>>,
val actionTarget: KnockRequestsActionTarget, val actionTarget: KnockRequestsActionTarget,
val asyncAction: AsyncAction<Unit>, val asyncAction: AsyncAction<Unit>,
val canAccept: Boolean, val permissions: KnockRequestPermissions,
val canDecline: Boolean,
val canBan: Boolean,
val eventSink: (KnockRequestsListEvents) -> Unit, val eventSink: (KnockRequestsListEvents) -> Unit,
) { ) {
val canAcceptAll = canAccept && knockRequests is AsyncData.Success && knockRequests.data.size > 1 val canAcceptAll = permissions.canAccept && knockRequests is AsyncData.Success && knockRequests.data.size > 1
} }
@Immutable @Immutable

View file

@ -8,6 +8,7 @@
package io.element.android.features.knockrequests.impl.list package io.element.android.features.knockrequests.impl.list
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.knockrequests.impl.data.KnockRequestPermissions
import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable
import io.element.android.features.knockrequests.impl.data.aKnockRequest import io.element.android.features.knockrequests.impl.data.aKnockRequest
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
@ -82,7 +83,11 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider<KnockReques
aKnockRequest() aKnockRequest()
) )
), ),
canAccept = false, permissions = KnockRequestPermissions(
canAccept = false,
canDecline = true,
canBan = true,
),
actionTarget = KnockRequestsActionTarget.AcceptAll, actionTarget = KnockRequestsActionTarget.AcceptAll,
asyncAction = AsyncAction.Failure(Throwable("Failed to accept all")), asyncAction = AsyncAction.Failure(Throwable("Failed to accept all")),
), ),
@ -92,7 +97,11 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider<KnockReques
aKnockRequest() aKnockRequest()
) )
), ),
canDecline = false, permissions = KnockRequestPermissions(
canAccept = true,
canDecline = false,
canBan = true,
),
), ),
aKnockRequestsListState( aKnockRequestsListState(
knockRequests = AsyncData.Success( knockRequests = AsyncData.Success(
@ -100,8 +109,11 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider<KnockReques
aKnockRequest() aKnockRequest()
) )
), ),
canAccept = false, permissions = KnockRequestPermissions(
canDecline = false, canAccept = false,
canDecline = false,
canBan = true,
),
), ),
aKnockRequestsListState( aKnockRequestsListState(
knockRequests = AsyncData.Success( knockRequests = AsyncData.Success(
@ -109,7 +121,11 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider<KnockReques
aKnockRequest() aKnockRequest()
) )
), ),
canBan = false, permissions = KnockRequestPermissions(
canAccept = true,
canDecline = true,
canBan = false,
),
), ),
) )
} }
@ -118,16 +134,16 @@ fun aKnockRequestsListState(
knockRequests: AsyncData<ImmutableList<KnockRequestPresentable>> = AsyncData.Success(persistentListOf()), knockRequests: AsyncData<ImmutableList<KnockRequestPresentable>> = AsyncData.Success(persistentListOf()),
actionTarget: KnockRequestsActionTarget = KnockRequestsActionTarget.None, actionTarget: KnockRequestsActionTarget = KnockRequestsActionTarget.None,
asyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized, asyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
canAccept: Boolean = true, permissions: KnockRequestPermissions = KnockRequestPermissions(
canDecline: Boolean = true, canAccept = true,
canBan: Boolean = true, canDecline = true,
canBan = true,
),
eventSink: (KnockRequestsListEvents) -> Unit = {}, eventSink: (KnockRequestsListEvents) -> Unit = {},
) = KnockRequestsListState( ) = KnockRequestsListState(
knockRequests = knockRequests, knockRequests = knockRequests,
actionTarget = actionTarget, actionTarget = actionTarget,
asyncAction = asyncAction, asyncAction = asyncAction,
canAccept = canAccept, permissions = permissions,
canDecline = canDecline,
canBan = canBan,
eventSink = eventSink, eventSink = eventSink,
) )

View file

@ -126,9 +126,9 @@ private fun KnockRequestsListContent(
} else { } else {
KnockRequestsList( KnockRequestsList(
knockRequests = knockRequests, knockRequests = knockRequests,
canAccept = state.canAccept, canAccept = state.permissions.canAccept,
canDecline = state.canDecline, canDecline = state.permissions.canDecline,
canBan = state.canBan, canBan = state.permissions.canBan,
onAcceptClick = ::onAcceptClick, onAcceptClick = ::onAcceptClick,
onDeclineClick = ::onDeclineClick, onDeclineClick = ::onDeclineClick,
onBanClick = ::onBanClick, onBanClick = ::onBanClick,

View file

@ -8,14 +8,12 @@
package io.element.android.features.knockrequests.impl.banner package io.element.android.features.knockrequests.impl.banner
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.knockrequests.impl.data.KnockRequestPermissions
import io.element.android.features.knockrequests.impl.data.KnockRequestsService import io.element.android.features.knockrequests.impl.data.KnockRequestsService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.knock.KnockRequest
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.A_USER_ID_3 import io.element.android.libraries.matrix.test.A_USER_ID_3
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.knock.FakeKnockRequest import io.element.android.libraries.matrix.test.room.knock.FakeKnockRequest
import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.assert
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -234,19 +232,12 @@ private fun TestScope.createKnockRequestsBannerPresenter(
): KnockRequestsBannerPresenter { ): KnockRequestsBannerPresenter {
val knockRequestsService = KnockRequestsService( val knockRequestsService = KnockRequestsService(
knockRequestsFlow = knockRequestsFlow, knockRequestsFlow = knockRequestsFlow,
coroutineScope = backgroundScope coroutineScope = backgroundScope,
) isKnockFeatureEnabledFlow = flowOf(isFeatureEnabled),
val featureFlagService = FakeFeatureFlagService( permissionsFlow = flowOf(KnockRequestPermissions(canAcceptKnockRequests, canAcceptKnockRequests, canAcceptKnockRequests)),
initialState = mapOf(
FeatureFlags.Knock.key to isFeatureEnabled
)
) )
return KnockRequestsBannerPresenter( return KnockRequestsBannerPresenter(
room = FakeMatrixRoom(
canInviteResult = { Result.success(canAcceptKnockRequests) }
),
knockRequestsService = knockRequestsService, knockRequestsService = knockRequestsService,
appCoroutineScope = this, appCoroutineScope = this,
featureFlagService = featureFlagService
) )
} }

View file

@ -10,13 +10,13 @@
package io.element.android.features.knockrequests.impl.list package io.element.android.features.knockrequests.impl.list
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.knockrequests.impl.data.KnockRequestPermissions
import io.element.android.features.knockrequests.impl.data.KnockRequestsService import io.element.android.features.knockrequests.impl.data.KnockRequestsService
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.knock.KnockRequest
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.knock.FakeKnockRequest import io.element.android.libraries.matrix.test.room.knock.FakeKnockRequest
import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.assert
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -35,15 +35,15 @@ class KnockRequestsListPresenterTest {
presenter.test { presenter.test {
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.knockRequests).isInstanceOf(AsyncData.Loading::class.java) assertThat(state.knockRequests).isInstanceOf(AsyncData.Loading::class.java)
assertThat(state.canAccept).isFalse() assertThat(state.permissions.canAccept).isFalse()
assertThat(state.canDecline).isFalse() assertThat(state.permissions.canDecline).isFalse()
assertThat(state.canBan).isFalse() assertThat(state.permissions.canBan).isFalse()
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.knockRequests).isInstanceOf(AsyncData.Loading::class.java) assertThat(state.knockRequests).isInstanceOf(AsyncData.Loading::class.java)
assertThat(state.canAccept).isTrue() assertThat(state.permissions.canAccept).isTrue()
assertThat(state.canDecline).isTrue() assertThat(state.permissions.canDecline).isTrue()
assertThat(state.canBan).isTrue() assertThat(state.permissions.canBan).isTrue()
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.knockRequests).isInstanceOf(AsyncData.Success::class.java) assertThat(state.knockRequests).isInstanceOf(AsyncData.Success::class.java)
@ -293,18 +293,12 @@ class KnockRequestsListPresenterTest {
canBan: Boolean = true, canBan: Boolean = true,
knockRequestsFlow: Flow<List<KnockRequest>> = flowOf(emptyList()) knockRequestsFlow: Flow<List<KnockRequest>> = flowOf(emptyList())
): KnockRequestsListPresenter { ): KnockRequestsListPresenter {
val room = FakeMatrixRoom(
canInviteResult = { Result.success(canAccept) },
canKickResult = { Result.success(canDecline) },
canBanResult = { Result.success(canBan) }
)
val knockRequestsService = KnockRequestsService( val knockRequestsService = KnockRequestsService(
knockRequestsFlow = knockRequestsFlow, knockRequestsFlow = knockRequestsFlow,
coroutineScope = backgroundScope coroutineScope = backgroundScope,
) isKnockFeatureEnabledFlow = flowOf(true),
return KnockRequestsListPresenter( permissionsFlow = flowOf(KnockRequestPermissions(canAccept, canDecline, canBan)),
room = room,
knockRequestsService = knockRequestsService,
) )
return KnockRequestsListPresenter(knockRequestsService = knockRequestsService)
} }
} }

View file

@ -48,7 +48,6 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch