change(invites) : add logic to decline invite and block a user

This commit is contained in:
ganfra 2025-02-27 21:09:47 +01:00
parent e2116e76f2
commit 82dc29b91c
13 changed files with 145 additions and 63 deletions

View file

@ -16,6 +16,7 @@ import im.vector.app.features.analytics.plan.JoinedRoom
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.ConfirmingDeclineInvite
import io.element.android.features.invite.api.response.InviteData
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
@ -43,15 +44,34 @@ class AcceptDeclineInvitePresenter @Inject constructor(
fun handleEvents(event: AcceptDeclineInviteEvents) {
when (event) {
is AcceptDeclineInviteEvents.AcceptInvite -> {
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
val inviteData = event.invite
if (inviteData == null) {
acceptedAction.value = AsyncAction.Failure(InvalidDataException())
} else {
localCoroutineScope.acceptInvite(inviteData.roomId, acceptedAction)
}
}
is AcceptDeclineInviteEvents.DeclineInvite -> {
declinedAction.value = ConfirmingDeclineInvite(event.invite)
val inviteData = event.invite
if (inviteData == null) {
declinedAction.value = AsyncAction.Failure(InvalidDataException())
} else {
declinedAction.value = ConfirmingDeclineInvite(inviteData, event.blockUser)
}
}
is InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite -> {
localCoroutineScope.declineInvite(event.roomId, declinedAction)
when (val declinedActionValue = declinedAction.value) {
is ConfirmingDeclineInvite -> {
localCoroutineScope.declineInvite(
inviteData = declinedActionValue.inviteData,
declinedAction = declinedAction,
blockUser = declinedActionValue.blockUser,
)
}
else -> Unit
}
}
is InternalAcceptDeclineInviteEvents.CancelDeclineInvite -> {
@ -92,13 +112,21 @@ class AcceptDeclineInvitePresenter @Inject constructor(
}
}
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<AsyncAction<RoomId>>) = launch {
private fun CoroutineScope.declineInvite(
inviteData: InviteData,
blockUser: Boolean,
declinedAction: MutableState<AsyncAction<RoomId>>,
) = launch {
suspend {
client.getPendingRoom(roomId)?.use {
client.getPendingRoom(inviteData.roomId)?.use {
it.leave().getOrThrow()
notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId)
}
roomId
val senderId = inviteData.senderId
if (blockUser && senderId != null) {
client.ignoreUser(senderId).getOrThrow()
}
notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, inviteData.roomId)
inviteData.roomId
}.runCatchingUpdatingState(declinedAction)
}
}

View file

@ -50,8 +50,9 @@ fun AcceptDeclineInviteView(
if (confirming is ConfirmingDeclineInvite) {
DeclineConfirmationDialog(
invite = confirming.inviteData,
blockUser = confirming.blockUser,
onConfirmClick = {
state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(confirming.inviteData.roomId))
state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite)
},
onDismissClick = {
state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite)
@ -66,29 +67,35 @@ fun AcceptDeclineInviteView(
@Composable
private fun DeclineConfirmationDialog(
invite: InviteData,
blockUser: Boolean,
onConfirmClick: () -> Unit,
onDismissClick: () -> Unit,
modifier: Modifier = Modifier
) {
val contentResource = if (invite.isDm) {
R.string.screen_invites_decline_direct_chat_message
} else {
R.string.screen_invites_decline_chat_message
val senderId = invite.senderId.value
val content = when {
blockUser -> stringResource(R.string.screen_join_room_decline_and_block_alert_message, senderId)
invite.isDm -> stringResource(R.string.screen_invites_decline_direct_chat_message, invite.roomName)
else -> stringResource(R.string.screen_invites_decline_chat_message, invite.roomName)
}
val titleResource = if (invite.isDm) {
R.string.screen_invites_decline_direct_chat_title
} else {
R.string.screen_invites_decline_chat_title
val title = when {
blockUser -> stringResource(R.string.screen_join_room_decline_and_block_alert_title)
invite.isDm -> stringResource(R.string.screen_invites_decline_direct_chat_title)
else -> stringResource(R.string.screen_invites_decline_chat_title)
}
val submitText = if (blockUser) {
stringResource(R.string.screen_join_room_decline_and_block_alert_confirmation)
} else {
stringResource(CommonStrings.action_decline)
}
ConfirmationDialog(
modifier = modifier,
content = stringResource(contentResource, invite.roomName),
title = stringResource(titleResource),
submitText = stringResource(CommonStrings.action_decline),
content = content,
title = title,
submitText = submitText,
cancelText = stringResource(CommonStrings.action_cancel),
onSubmitClick = onConfirmClick,
destructiveSubmit = blockUser,
onDismiss = onDismissClick,
)
}

View file

@ -8,10 +8,9 @@
package io.element.android.features.invite.impl.response
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
import io.element.android.libraries.matrix.api.core.RoomId
sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents {
data class ConfirmDeclineInvite(val roomId: RoomId) : InternalAcceptDeclineInviteEvents
data object ConfirmDeclineInvite : InternalAcceptDeclineInviteEvents
data object CancelDeclineInvite : InternalAcceptDeclineInviteEvents
data object DismissAcceptError : InternalAcceptDeclineInviteEvents
data object DismissDeclineError : InternalAcceptDeclineInviteEvents

View file

@ -0,0 +1,10 @@
/*
* Copyright 2025 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.invite.impl.response
class InvalidDataException : Exception()

View file

@ -17,10 +17,12 @@ 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.RoomIdOrAlias
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeRoomPreview
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
@ -93,7 +95,7 @@ class AcceptDeclineInvitePresenterTest {
awaitItem().also { state ->
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData))
state.eventSink(
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId)
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite()
)
}
assertThat(awaitItem().declineAction.isLoading()).isTrue()
@ -141,7 +143,7 @@ class AcceptDeclineInvitePresenterTest {
awaitItem().also { state ->
assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData))
state.eventSink(
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId)
InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite()
)
}
assertThat(awaitItem().declineAction.isLoading()).isTrue()
@ -237,12 +239,14 @@ class AcceptDeclineInvitePresenterTest {
private fun anInviteData(
roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME,
isDm: Boolean = false
isDm: Boolean = false,
senderId: UserId = A_USER_ID,
): InviteData {
return InviteData(
roomId = roomId,
roomName = name,
isDm = isDm
isDm = isDm,
senderId = senderId,
)
}