Feature : Report room (#4654)
* feature (report room) : introduce all presentation classes. * feature (report room) : branch entry point in the room list * refactor (matrix ui) : move some code from appnav to matrix ui * feature (report room) : add api on room * feature (report room) : adjust ui * feature (report room) : branch api * feature (decline invite and block) : move things around and introduce presentation classes * feature (decline invite and block) : continue to move things * feature (report room) : remove reference to "conversation" for now * feature (report room) : add report room action to room detail screen * feature (report room) : enabled button state * feature (report room) : improve code and reuse * feature (report room) : add feature flag * feature (report room) : change feature flag to static bool * feature (report room) : add tests * feature (report room) : fix ui with new api on ListItem * feature (report room) : clean up and add more tests. * Update screenshots * feature (report room) : more test and fix issue * feature (report room) : update strings * feature (report room) : fix konsist preview * feature (report room) : disable feature * Update screenshots * var -> val * Improve preview of AcceptDeclineInviteView * Improve preview consistency * Add missing test on DismissErrorAndHideContent * Update screenshots * Add missing tests --------- Co-authored-by: ElementBot <android@element.io> Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
parent
e502eb1971
commit
0b83e66733
229 changed files with 3995 additions and 1210 deletions
|
|
@ -18,7 +18,7 @@ import javax.inject.Inject
|
|||
@ContributesBinding(AppScope::class)
|
||||
class DefaultJoinRoomEntryPoint @Inject constructor() : JoinRoomEntryPoint {
|
||||
override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: JoinRoomEntryPoint.Inputs): Node {
|
||||
return parentNode.createNode<JoinRoomNode>(
|
||||
return parentNode.createNode<JoinRoomFlowNode>(
|
||||
buildContext = buildContext,
|
||||
plugins = listOf(inputs)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
|
||||
sealed interface JoinRoomEvents {
|
||||
data object RetryFetchingContent : JoinRoomEvents
|
||||
data object DismissErrorAndHideContent : JoinRoomEvents
|
||||
|
|
@ -16,6 +18,6 @@ sealed interface JoinRoomEvents {
|
|||
data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvents
|
||||
data class UpdateKnockMessage(val message: String) : JoinRoomEvents
|
||||
data object ClearActionStates : JoinRoomEvents
|
||||
data object AcceptInvite : JoinRoomEvents
|
||||
data class DeclineInvite(val blockUser: Boolean) : JoinRoomEvents
|
||||
data class AcceptInvite(val inviteData: InviteData) : JoinRoomEvents
|
||||
data class DeclineInvite(val inviteData: InviteData, val blockUser: Boolean) : JoinRoomEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.node.node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView
|
||||
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
|
||||
import io.element.android.features.joinroom.api.JoinRoomEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
class JoinRoomFlowNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: JoinRoomPresenter.Factory,
|
||||
private val acceptDeclineInviteView: AcceptDeclineInviteView,
|
||||
private val declineAndBlockEntryPoint: DeclineInviteAndBlockEntryPoint
|
||||
) : BaseFlowNode<JoinRoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
),
|
||||
buildContext = buildContext,
|
||||
plugins = plugins
|
||||
) {
|
||||
private val inputs: JoinRoomEntryPoint.Inputs = inputs()
|
||||
private val presenter = presenterFactory.create(
|
||||
inputs.roomId,
|
||||
inputs.roomIdOrAlias,
|
||||
inputs.roomDescription,
|
||||
inputs.serverNames,
|
||||
inputs.trigger,
|
||||
)
|
||||
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
data object Root : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class DeclineInviteAndBlockUser(val inviteData: InviteData) : NavTarget
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
is NavTarget.DeclineInviteAndBlockUser -> declineAndBlockEntryPoint.createNode(this, buildContext, navTarget.inviteData)
|
||||
NavTarget.Root -> rootNode(buildContext)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
BackstackView(modifier)
|
||||
}
|
||||
|
||||
private fun rootNode(buildContext: BuildContext): Node {
|
||||
return node(buildContext) { modifier ->
|
||||
val state = presenter.present()
|
||||
JoinRoomView(
|
||||
state = state,
|
||||
onBackClick = ::navigateUp,
|
||||
onJoinSuccess = ::navigateUp,
|
||||
onForgetSuccess = ::navigateUp,
|
||||
onCancelKnockSuccess = {},
|
||||
onKnockSuccess = {},
|
||||
onDeclineInviteAndBlockUser = {
|
||||
backstack.push(
|
||||
NavTarget.DeclineInviteAndBlockUser(it)
|
||||
)
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
acceptDeclineInviteView.Render(
|
||||
state = state.acceptDeclineInviteState,
|
||||
onAcceptInviteSuccess = {},
|
||||
onDeclineInviteSuccess = {},
|
||||
modifier = Modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteView
|
||||
import io.element.android.features.joinroom.api.JoinRoomEntryPoint
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
class JoinRoomNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: JoinRoomPresenter.Factory,
|
||||
private val acceptDeclineInviteView: AcceptDeclineInviteView,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
private val inputs: JoinRoomEntryPoint.Inputs = inputs()
|
||||
private val presenter = presenterFactory.create(
|
||||
inputs.roomId,
|
||||
inputs.roomIdOrAlias,
|
||||
inputs.roomDescription,
|
||||
inputs.serverNames,
|
||||
inputs.trigger,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
JoinRoomView(
|
||||
state = state,
|
||||
onBackClick = ::navigateUp,
|
||||
onJoinSuccess = ::navigateUp,
|
||||
onForgetSuccess = ::navigateUp,
|
||||
onCancelKnockSuccess = {},
|
||||
onKnockSuccess = {},
|
||||
modifier = modifier
|
||||
)
|
||||
acceptDeclineInviteView.Render(
|
||||
state = state.acceptDeclineInviteState,
|
||||
onAcceptInvite = {},
|
||||
onDeclineInvite = {},
|
||||
modifier = Modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,10 +23,11 @@ import androidx.compose.runtime.setValue
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.appconfig.MatrixConfiguration
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.toInviteData
|
||||
import io.element.android.features.joinroom.impl.di.CancelKnockRoom
|
||||
import io.element.android.features.joinroom.impl.di.ForgetRoom
|
||||
import io.element.android.features.joinroom.impl.di.KnockRoom
|
||||
|
|
@ -170,16 +171,14 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
when (event) {
|
||||
JoinRoomEvents.JoinRoom -> coroutineScope.joinRoom(joinAction)
|
||||
is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage)
|
||||
JoinRoomEvents.AcceptInvite -> {
|
||||
val inviteData = contentState.toInviteData()
|
||||
is JoinRoomEvents.AcceptInvite -> {
|
||||
acceptDeclineInviteState.eventSink(
|
||||
AcceptDeclineInviteEvents.AcceptInvite(inviteData)
|
||||
AcceptDeclineInviteEvents.AcceptInvite(event.inviteData)
|
||||
)
|
||||
}
|
||||
is JoinRoomEvents.DeclineInvite -> {
|
||||
val inviteData = contentState.toInviteData()
|
||||
acceptDeclineInviteState.eventSink(
|
||||
AcceptDeclineInviteEvents.DeclineInvite(invite = inviteData, blockUser = event.blockUser)
|
||||
AcceptDeclineInviteEvents.DeclineInvite(invite = event.inviteData, blockUser = event.blockUser, shouldConfirm = true)
|
||||
)
|
||||
}
|
||||
is JoinRoomEvents.CancelKnock -> coroutineScope.cancelKnockRoom(event.requiresConfirmation, cancelKnockAction)
|
||||
|
|
@ -213,6 +212,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
applicationName = buildMeta.applicationName,
|
||||
knockMessage = knockMessage,
|
||||
hideInviteAvatars = hideInviteAvatars,
|
||||
canReportRoom = MatrixConfiguration.CAN_REPORT_ROOM,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
|
@ -273,7 +273,12 @@ private fun RoomPreviewInfo.toContentState(senderMember: RoomMember?, reason: St
|
|||
roomType = roomType,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when (membership) {
|
||||
CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(senderMember?.toInviteSender())
|
||||
CurrentUserMembership.INVITED -> {
|
||||
JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = toInviteData(),
|
||||
inviteSender = senderMember?.toInviteSender()
|
||||
)
|
||||
}
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(senderMember?.toInviteSender(), reason)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
|
|
@ -317,7 +322,8 @@ internal fun RoomInfo.toContentState(
|
|||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when (currentUserMembership) {
|
||||
CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
||||
inviteSender = membershipSender?.toInviteSender()
|
||||
inviteData = toInviteData(),
|
||||
inviteSender = membershipSender?.toInviteSender(),
|
||||
)
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(
|
||||
banSender = membershipSender?.toInviteSender(),
|
||||
|
|
@ -340,23 +346,3 @@ private fun JoinRule?.toJoinAuthorisationStatus(): JoinAuthorisationStatus {
|
|||
else -> JoinAuthorisationStatus.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun ContentState.toInviteData(): InviteData? {
|
||||
return when (this) {
|
||||
is ContentState.Loaded -> {
|
||||
if (joinAuthorisationStatus is JoinAuthorisationStatus.IsInvited && joinAuthorisationStatus.inviteSender != null) {
|
||||
InviteData(
|
||||
roomId = roomId,
|
||||
// Note: name should not be null at this point, but use Id just in case...
|
||||
roomName = name ?: roomId.value,
|
||||
senderId = joinAuthorisationStatus.inviteSender.userId,
|
||||
isDm = isDm
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
|
|
@ -32,6 +33,7 @@ data class JoinRoomState(
|
|||
private val applicationName: String,
|
||||
val knockMessage: String,
|
||||
val hideInviteAvatars: Boolean,
|
||||
val canReportRoom: Boolean,
|
||||
val eventSink: (JoinRoomEvents) -> Unit
|
||||
) {
|
||||
val isJoinActionUnauthorized = joinAction is AsyncAction.Failure && joinAction.error is JoinRoomFailures.UnauthorizedJoin
|
||||
|
|
@ -95,7 +97,7 @@ sealed interface ContentState {
|
|||
sealed interface JoinAuthorisationStatus {
|
||||
data object None : JoinAuthorisationStatus
|
||||
data class IsSpace(val applicationName: String) : JoinAuthorisationStatus
|
||||
data class IsInvited(val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
||||
data class IsInvited(val inviteData: InviteData, val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
||||
data class IsBanned(val banSender: InviteSender?, val reason: String?) : JoinAuthorisationStatus
|
||||
data object IsKnocked : JoinAuthorisationStatus
|
||||
data object CanKnock : JoinAuthorisationStatus
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
|
|
@ -50,12 +51,20 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong", null))
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null))
|
||||
contentState = aLoadedContentState(
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = anInviteData(),
|
||||
inviteSender = null,
|
||||
)
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
numberOfMembers = 123,
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(anInviteSender()),
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = anInviteData(),
|
||||
inviteSender = anInviteSender(),
|
||||
),
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
|
|
@ -149,7 +158,7 @@ fun aLoadedContentState(
|
|||
isDm: Boolean = false,
|
||||
roomType: RoomType = RoomType.Room,
|
||||
roomAvatarUrl: String? = null,
|
||||
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown
|
||||
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown,
|
||||
) = ContentState.Loaded(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
|
|
@ -172,6 +181,7 @@ fun aJoinRoomState(
|
|||
cancelKnockAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
knockMessage: String = "",
|
||||
hideInviteAvatars: Boolean = false,
|
||||
canReportRoom: Boolean = true,
|
||||
eventSink: (JoinRoomEvents) -> Unit = {}
|
||||
) = JoinRoomState(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
|
|
@ -184,6 +194,7 @@ fun aJoinRoomState(
|
|||
applicationName = "AppName",
|
||||
knockMessage = knockMessage,
|
||||
hideInviteAvatars = hideInviteAvatars,
|
||||
canReportRoom = canReportRoom,
|
||||
eventSink = eventSink
|
||||
)
|
||||
|
||||
|
|
@ -199,5 +210,15 @@ internal fun anInviteSender(
|
|||
membershipChangeReason = membershipChangeReason,
|
||||
)
|
||||
|
||||
internal fun anInviteData(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
roomName: String = "Room name",
|
||||
isDm: Boolean = false,
|
||||
) = InviteData(
|
||||
roomId = roomId,
|
||||
roomName = roomName,
|
||||
isDm = isDm,
|
||||
)
|
||||
|
||||
private val A_ROOM_ID = RoomId("!exa:matrix.org")
|
||||
private val A_ROOM_ALIAS = RoomAlias("#exa:matrix.org")
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom
|
||||
|
|
@ -78,6 +79,7 @@ fun JoinRoomView(
|
|||
onKnockSuccess: () -> Unit,
|
||||
onForgetSuccess: () -> Unit,
|
||||
onCancelKnockSuccess: () -> Unit,
|
||||
onDeclineInviteAndBlockUser: (InviteData) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
|
|
@ -104,11 +106,15 @@ fun JoinRoomView(
|
|||
footer = {
|
||||
JoinRoomFooter(
|
||||
joinAuthorisationStatus = state.joinAuthorisationStatus,
|
||||
onAcceptInvite = {
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite)
|
||||
onAcceptInvite = { inviteData ->
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
},
|
||||
onDeclineInvite = { blockUser ->
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(blockUser))
|
||||
onDeclineInvite = { inviteData, blockUser ->
|
||||
if (state.canReportRoom && blockUser) {
|
||||
onDeclineInviteAndBlockUser(inviteData)
|
||||
} else {
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, blockUser = blockUser))
|
||||
}
|
||||
},
|
||||
onJoinRoom = {
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
|
|
@ -184,8 +190,8 @@ fun JoinRoomView(
|
|||
@Composable
|
||||
private fun JoinRoomFooter(
|
||||
joinAuthorisationStatus: JoinAuthorisationStatus,
|
||||
onAcceptInvite: () -> Unit,
|
||||
onDeclineInvite: (Boolean) -> Unit,
|
||||
onAcceptInvite: (InviteData) -> Unit,
|
||||
onDeclineInvite: (InviteData, Boolean) -> Unit,
|
||||
onJoinRoom: () -> Unit,
|
||||
onKnockRoom: () -> Unit,
|
||||
onCancelKnock: () -> Unit,
|
||||
|
|
@ -204,13 +210,13 @@ private fun JoinRoomFooter(
|
|||
ButtonRowMolecule(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||
OutlinedButton(
|
||||
text = stringResource(CommonStrings.action_decline),
|
||||
onClick = { onDeclineInvite(false) },
|
||||
onClick = { onDeclineInvite(joinAuthorisationStatus.inviteData, false) },
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_accept),
|
||||
onClick = onAcceptInvite,
|
||||
onClick = { onAcceptInvite(joinAuthorisationStatus.inviteData) },
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
|
|
@ -218,7 +224,7 @@ private fun JoinRoomFooter(
|
|||
Spacer(modifier = Modifier.height(24.dp))
|
||||
TextButton(
|
||||
text = stringResource(R.string.screen_join_room_decline_and_block_button_title),
|
||||
onClick = { onDeclineInvite(true) },
|
||||
onClick = { onDeclineInvite(joinAuthorisationStatus.inviteData, true) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
destructive = true
|
||||
)
|
||||
|
|
@ -585,5 +591,6 @@ internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class)
|
|||
onKnockSuccess = { },
|
||||
onForgetSuccess = { },
|
||||
onCancelKnockSuccess = { },
|
||||
onDeclineInviteAndBlockUser = { },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.joinroom.impl.JoinRoomPresenter
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ package io.element.android.features.joinroom.impl
|
|||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.toInviteData
|
||||
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
||||
import io.element.android.features.joinroom.impl.di.CancelKnockRoom
|
||||
import io.element.android.features.joinroom.impl.di.ForgetRoom
|
||||
|
|
@ -21,6 +23,8 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
|||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -46,6 +50,7 @@ import io.element.android.libraries.matrix.test.room.aRoomPreview
|
|||
import io.element.android.libraries.matrix.test.room.aRoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
|
|
@ -124,11 +129,12 @@ class JoinRoomPresenterTest {
|
|||
matrixClient = matrixClient,
|
||||
seenInvitesStore = seenInvitesStore,
|
||||
)
|
||||
val inviteData = roomSummary.info.toInviteData()
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(null))
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(inviteData, null))
|
||||
}
|
||||
// Check that the roomId is stored in the seen invites store
|
||||
assertThat(seenInvitesStore.seenRoomIds().first()).containsExactly(roomSummary.roomId)
|
||||
|
|
@ -144,6 +150,7 @@ class JoinRoomPresenterTest {
|
|||
joinedMembersCount = 5,
|
||||
inviter = inviter,
|
||||
)
|
||||
val inviteData = roomSummary.info.toInviteData()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
).apply {
|
||||
|
|
@ -157,7 +164,7 @@ class JoinRoomPresenterTest {
|
|||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(expectedInviteSender))
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(inviteData, expectedInviteSender))
|
||||
assertThat((state.contentState as ContentState.Loaded).numberOfMembers).isEqualTo(5)
|
||||
}
|
||||
}
|
||||
|
|
@ -208,6 +215,7 @@ class JoinRoomPresenterTest {
|
|||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val inviteData = roomSummary.info.toInviteData()
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
acceptDeclineInvitePresenter = acceptDeclinePresenter
|
||||
|
|
@ -216,16 +224,14 @@ class JoinRoomPresenterTest {
|
|||
skipItems(1)
|
||||
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite)
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(false))
|
||||
|
||||
val inviteData = state.contentState.toInviteData()
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
state.eventSink(JoinRoomEvents.DeclineInvite(inviteData, false))
|
||||
|
||||
assert(eventSinkRecorder)
|
||||
.isCalledExactly(2)
|
||||
.withSequence(
|
||||
listOf(value(AcceptDeclineInviteEvents.AcceptInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData))),
|
||||
listOf(value(AcceptDeclineInviteEvents.DeclineInvite(inviteData, blockUser = false, shouldConfirm = true))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -613,7 +619,7 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded`() = runTest {
|
||||
fun `present - when room is not known RoomPreview is loaded - membership null`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
|
|
@ -657,6 +663,193 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded - membership INVITED`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Loaded(
|
||||
roomId = A_ROOM_ID,
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = InviteData(
|
||||
roomId = A_ROOM_ID,
|
||||
roomName = "Room name",
|
||||
isDm = false,
|
||||
),
|
||||
inviteSender = InviteSender(
|
||||
userId = A_USER_ID_2,
|
||||
displayName = "Bob",
|
||||
avatarData = AvatarData(
|
||||
id = A_USER_ID_2.value,
|
||||
name = "Bob",
|
||||
size = AvatarSize.InviteSender,
|
||||
),
|
||||
membershipChangeReason = null,
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded - membership BANNED`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = null,
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = CurrentUserMembership.BANNED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Loaded(
|
||||
roomId = A_ROOM_ID,
|
||||
name = null,
|
||||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(
|
||||
banSender = InviteSender(
|
||||
userId = A_USER_ID_2,
|
||||
displayName = "Bob",
|
||||
avatarData = AvatarData(
|
||||
id = A_USER_ID_2.value,
|
||||
name = "Bob",
|
||||
size = AvatarSize.InviteSender,
|
||||
),
|
||||
membershipChangeReason = null,
|
||||
),
|
||||
reason = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded - membership KNOCKED`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = CurrentUserMembership.KNOCKED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Loaded(
|
||||
roomId = A_ROOM_ID,
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Private`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
|
|
@ -807,6 +1000,31 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error - dismiss`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(AN_EXCEPTION)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Failure(error = AN_EXCEPTION)
|
||||
)
|
||||
state.eventSink(JoinRoomEvents.DismissErrorAndHideContent)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(ContentState.Dismissing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
|
|
|
|||
|
|
@ -11,13 +11,19 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.test.anInviteData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.ensureCalledOnceWithParam
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
|
@ -141,40 +147,61 @@ class JoinRoomViewTest {
|
|||
@Test
|
||||
fun `clicking on Accept when JoinAuthorisationStatus is IsInvited emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val inviteData = anInviteData()
|
||||
rule.setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null)),
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, null)),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_accept)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.AcceptInvite)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.AcceptInvite(inviteData))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline when JoinAuthorisationStatus is IsInvited emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val inviteData = anInviteData()
|
||||
rule.setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null)),
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, null)),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_decline)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(false))
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
rule.setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null)),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and can report room, the expected callback is invoked`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
val inviteData = anInviteData()
|
||||
val joinRoomState = aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())),
|
||||
canReportRoom = true,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
rule.clickOn(R.string.screen_join_room_decline_and_block_button_title)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(true))
|
||||
ensureCalledOnceWithParam(inviteData) {
|
||||
rule.setJoinRoomView(
|
||||
state = joinRoomState,
|
||||
onDeclineInviteAndBlockUser = it,
|
||||
)
|
||||
rule.clickOn(R.string.screen_join_room_decline_and_block_button_title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and cant report room, emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
val inviteData = anInviteData()
|
||||
val joinRoomState = aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())),
|
||||
canReportRoom = false,
|
||||
eventSink = eventsRecorder,
|
||||
)
|
||||
rule.setJoinRoomView(state = joinRoomState,)
|
||||
rule.clickOn(R.string.screen_join_room_decline_and_block_button_title)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, true))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -242,6 +269,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinR
|
|||
onKnockSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onCancelKnockSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onForgetSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onDeclineInviteAndBlockUser: (InviteData) -> Unit = EnsureNeverCalledWithParam(),
|
||||
) {
|
||||
setContent {
|
||||
JoinRoomView(
|
||||
|
|
@ -251,6 +279,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinR
|
|||
onKnockSuccess = onKnockSuccess,
|
||||
onForgetSuccess = onForgetSuccess,
|
||||
onCancelKnockSuccess = onCancelKnockSuccess,
|
||||
onDeclineInviteAndBlockUser = onDeclineInviteAndBlockUser,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue