UserProfileEvents -> UserProfileEvent

This commit is contained in:
Benoit Marty
2026-08-25 10:28:49 +02:00
parent 51a4704854
commit 650f20d6ba
12 changed files with 91 additions and 91 deletions
@@ -17,7 +17,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.api.UserProfilePresenterFactory
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
@@ -112,12 +112,12 @@ class RoomMemberDetailsPresenter(
} }
} }
fun handleEvent(event: UserProfileEvents) { fun handleEvent(event: UserProfileEvent) {
when (event) { when (event) {
UserProfileEvents.WithdrawVerification -> coroutineScope.launch { UserProfileEvent.WithdrawVerification -> coroutineScope.launch {
encryptionService.withdrawVerification(roomMemberId) encryptionService.withdrawVerification(roomMemberId)
} }
is UserProfileEvents.CopyToClipboard -> { is UserProfileEvent.CopyToClipboard -> {
clipboardHelper.copyPlainText(event.text) clipboardHelper.copyPlainText(event.text)
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
} }
@@ -11,7 +11,7 @@ package io.element.android.features.roomdetails.impl.members.details
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomdetails.impl.aJoinedRoom import io.element.android.features.roomdetails.impl.aJoinedRoom
import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.aRoomMember
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.features.userprofile.api.UserProfilePresenterFactory
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.features.userprofile.shared.aUserProfileState
@@ -348,7 +348,7 @@ class RoomMemberDetailsPresenterTest {
consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION } consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION }
initialState.eventSink(UserProfileEvents.WithdrawVerification) initialState.eventSink(UserProfileEvent.WithdrawVerification)
withdrawVerificationResult.assertions().isCalledOnce() withdrawVerificationResult.assertions().isCalledOnce()
} }
} }
@@ -8,13 +8,13 @@
package io.element.android.features.userprofile.api package io.element.android.features.userprofile.api
sealed interface UserProfileEvents { sealed interface UserProfileEvent {
data object StartDM : UserProfileEvents data object StartDM : UserProfileEvent
data object ClearStartDMState : UserProfileEvents data object ClearStartDMState : UserProfileEvent
data class BlockUser(val needsConfirmation: Boolean = false) : UserProfileEvents data class BlockUser(val needsConfirmation: Boolean = false) : UserProfileEvent
data class UnblockUser(val needsConfirmation: Boolean = false) : UserProfileEvents data class UnblockUser(val needsConfirmation: Boolean = false) : UserProfileEvent
data object ClearBlockUserError : UserProfileEvents data object ClearBlockUserError : UserProfileEvent
data object ClearConfirmationDialog : UserProfileEvents data object ClearConfirmationDialog : UserProfileEvent
data object WithdrawVerification : UserProfileEvents data object WithdrawVerification : UserProfileEvent
data class CopyToClipboard(val text: String) : UserProfileEvents data class CopyToClipboard(val text: String) : UserProfileEvent
} }
@@ -28,7 +28,7 @@ data class UserProfileState(
val canCall: Boolean, val canCall: Boolean,
val snackbarMessage: SnackbarMessage?, val snackbarMessage: SnackbarMessage?,
val displayedStatus: DisplayedStatus?, val displayedStatus: DisplayedStatus?,
val eventSink: (UserProfileEvents) -> Unit val eventSink: (UserProfileEvent) -> Unit
) { ) {
enum class ConfirmationDialog { enum class ConfirmationDialog {
Block, Block,
@@ -23,7 +23,7 @@ import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.features.startchat.api.StartDMAction import io.element.android.features.startchat.api.StartDMAction
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
@@ -101,9 +101,9 @@ class UserProfilePresenter(
} }
val userProfile by produceState<MatrixUser?>(null) { value = client.getProfile(userId).getOrNull() } val userProfile by produceState<MatrixUser?>(null) { value = client.getProfile(userId).getOrNull() }
fun handleEvent(event: UserProfileEvents) { fun handleEvent(event: UserProfileEvent) {
when (event) { when (event) {
is UserProfileEvents.BlockUser -> { is UserProfileEvent.BlockUser -> {
if (event.needsConfirmation) { if (event.needsConfirmation) {
confirmationDialog = ConfirmationDialog.Block confirmationDialog = ConfirmationDialog.Block
} else { } else {
@@ -111,7 +111,7 @@ class UserProfilePresenter(
coroutineScope.blockUser(isBlocked) coroutineScope.blockUser(isBlocked)
} }
} }
is UserProfileEvents.UnblockUser -> { is UserProfileEvent.UnblockUser -> {
if (event.needsConfirmation) { if (event.needsConfirmation) {
confirmationDialog = ConfirmationDialog.Unblock confirmationDialog = ConfirmationDialog.Unblock
} else { } else {
@@ -119,11 +119,11 @@ class UserProfilePresenter(
coroutineScope.unblockUser(isBlocked) coroutineScope.unblockUser(isBlocked)
} }
} }
UserProfileEvents.ClearConfirmationDialog -> confirmationDialog = null UserProfileEvent.ClearConfirmationDialog -> confirmationDialog = null
UserProfileEvents.ClearBlockUserError -> { UserProfileEvent.ClearBlockUserError -> {
isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse()) isBlocked.value = AsyncData.Success(isBlocked.value.dataOrNull().orFalse())
} }
UserProfileEvents.StartDM -> { UserProfileEvent.StartDM -> {
coroutineScope.launch { coroutineScope.launch {
startDMAction.execute( startDMAction.execute(
matrixUser = userProfile ?: MatrixUser(userId), matrixUser = userProfile ?: MatrixUser(userId),
@@ -132,12 +132,12 @@ class UserProfilePresenter(
) )
} }
} }
UserProfileEvents.ClearStartDMState -> { UserProfileEvent.ClearStartDMState -> {
startDmActionState.value = AsyncAction.Uninitialized startDmActionState.value = AsyncAction.Uninitialized
} }
// Do nothing for other event as they are handled by the RoomMemberDetailsPresenter if needed // Do nothing for other event as they are handled by the RoomMemberDetailsPresenter if needed
UserProfileEvents.WithdrawVerification, UserProfileEvent.WithdrawVerification,
is UserProfileEvents.CopyToClipboard -> Unit is UserProfileEvent.CopyToClipboard -> Unit
} }
} }
@@ -18,7 +18,7 @@ import io.element.android.features.enterprise.test.FakeSessionEnterpriseService
import io.element.android.features.invitepeople.test.FakeStartDMAction import io.element.android.features.invitepeople.test.FakeStartDMAction
import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser
import io.element.android.features.startchat.api.StartDMAction import io.element.android.features.startchat.api.StartDMAction
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
import io.element.android.features.userprofile.impl.root.UserProfilePresenter import io.element.android.features.userprofile.impl.root.UserProfilePresenter
@@ -182,12 +182,12 @@ class UserProfilePresenterTest {
val presenter = createUserProfilePresenter() val presenter = createUserProfilePresenter()
presenter.test { presenter.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = true))
val dialogState = awaitItem() val dialogState = awaitItem()
assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Block) assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Block)
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) dialogState.eventSink(UserProfileEvent.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull() assertThat(awaitItem().displayConfirmationDialog).isNull()
} }
} }
@@ -202,12 +202,12 @@ class UserProfilePresenterTest {
) )
presenter.test { presenter.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
ignoredUsersFlow.emit(persistentListOf(A_USER_ID)) ignoredUsersFlow.emit(persistentListOf(A_USER_ID))
assertThat(awaitItem().isBlocked.dataOrNull()).isTrue() assertThat(awaitItem().isBlocked.dataOrNull()).isTrue()
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
ignoredUsersFlow.emit(persistentListOf()) ignoredUsersFlow.emit(persistentListOf())
assertThat(awaitItem().isBlocked.dataOrNull()).isFalse() assertThat(awaitItem().isBlocked.dataOrNull()).isFalse()
@@ -222,12 +222,12 @@ class UserProfilePresenterTest {
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
val initialState = awaitFirstItem(count = 2) val initialState = awaitFirstItem(count = 2)
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvent.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION)
// Clear error // Clear error
initialState.eventSink(UserProfileEvents.ClearBlockUserError) initialState.eventSink(UserProfileEvent.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false)) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(false))
} }
} }
@@ -240,12 +240,12 @@ class UserProfilePresenterTest {
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
val initialState = awaitFirstItem(count = 2) val initialState = awaitFirstItem(count = 2)
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false)) initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue() assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem() val errorState = awaitItem()
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION) assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(AN_EXCEPTION)
// Clear error // Clear error
initialState.eventSink(UserProfileEvents.ClearBlockUserError) initialState.eventSink(UserProfileEvent.ClearBlockUserError)
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true)) assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true))
} }
} }
@@ -255,12 +255,12 @@ class UserProfilePresenterTest {
val presenter = createUserProfilePresenter() val presenter = createUserProfilePresenter()
presenter.test { presenter.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) initialState.eventSink(UserProfileEvent.UnblockUser(needsConfirmation = true))
val dialogState = awaitItem() val dialogState = awaitItem()
assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Unblock) assertThat(dialogState.displayConfirmationDialog).isEqualTo(UserProfileState.ConfirmationDialog.Unblock)
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) dialogState.eventSink(UserProfileEvent.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull() assertThat(awaitItem().displayConfirmationDialog).isNull()
} }
} }
@@ -279,7 +279,7 @@ class UserProfilePresenterTest {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
val matrixUser = MatrixUser(UserId("@alice:server.org")) val matrixUser = MatrixUser(UserId("@alice:server.org"))
initialState.eventSink(UserProfileEvents.StartDM) initialState.eventSink(UserProfileEvent.StartDM)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult) assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
executeResult.assertions().isCalledOnce().with( executeResult.assertions().isCalledOnce().with(
@@ -287,7 +287,7 @@ class UserProfilePresenterTest {
value(false), value(false),
any(), any(),
) )
state.eventSink(UserProfileEvents.ClearStartDMState) state.eventSink(UserProfileEvent.ClearStartDMState)
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.startDmActionState.isUninitialized()).isTrue() assertThat(state.startDmActionState.isUninitialized()).isTrue()
@@ -309,7 +309,7 @@ class UserProfilePresenterTest {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
val matrixUser = MatrixUser(UserId("@alice:server.org")) val matrixUser = MatrixUser(UserId("@alice:server.org"))
initialState.eventSink(UserProfileEvents.StartDM) initialState.eventSink(UserProfileEvent.StartDM)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.startDmActionState).isEqualTo(startDMSuccessResult) assertThat(state.startDmActionState).isEqualTo(startDMSuccessResult)
executeResult.assertions().isCalledOnce().with( executeResult.assertions().isCalledOnce().with(
@@ -335,7 +335,7 @@ class UserProfilePresenterTest {
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
initialState.eventSink(UserProfileEvents.StartDM) initialState.eventSink(UserProfileEvent.StartDM)
val confirmingState = awaitItem() val confirmingState = awaitItem()
assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult) assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult)
executeResult.assertions().isCalledOnce().with( executeResult.assertions().isCalledOnce().with(
@@ -344,7 +344,7 @@ class UserProfilePresenterTest {
any(), any(),
) )
// Cancelling should not create the DM // Cancelling should not create the DM
confirmingState.eventSink(UserProfileEvents.ClearStartDMState) confirmingState.eventSink(UserProfileEvent.ClearStartDMState)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.startDmActionState.isUninitialized()).isTrue() assertThat(finalState.startDmActionState.isUninitialized()).isTrue()
executeResult.assertions().isCalledExactly(1) executeResult.assertions().isCalledExactly(1)
@@ -365,7 +365,7 @@ class UserProfilePresenterTest {
}.test { }.test {
val initialState = awaitFirstItem() val initialState = awaitFirstItem()
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
initialState.eventSink(UserProfileEvents.StartDM) initialState.eventSink(UserProfileEvent.StartDM)
val confirmingState = awaitItem() val confirmingState = awaitItem()
assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult) assertThat(confirmingState.startDmActionState).isEqualTo(startDMConfirmationResult)
executeResult.assertions().isCalledOnce().with( executeResult.assertions().isCalledOnce().with(
@@ -374,7 +374,7 @@ class UserProfilePresenterTest {
any(), any(),
) )
// Start DM again should invoke the action with createIfDmDoesNotExist = true // Start DM again should invoke the action with createIfDmDoesNotExist = true
confirmingState.eventSink(UserProfileEvents.StartDM) confirmingState.eventSink(UserProfileEvent.StartDM)
executeResult.assertions().isCalledExactly(2).withSequence( executeResult.assertions().isCalledExactly(2).withSequence(
listOf(value(matrixUser), value(false), any()), listOf(value(matrixUser), value(false), any()),
listOf(value(matrixUser), value(true), any()), listOf(value(matrixUser), value(true), any()),
@@ -10,7 +10,7 @@ package io.element.android.features.userprofile.shared
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
@@ -50,7 +50,7 @@ fun aUserProfileState(
canCall: Boolean = false, canCall: Boolean = false,
snackbarMessage: SnackbarMessage? = null, snackbarMessage: SnackbarMessage? = null,
displayedStatus: DisplayedStatus? = null, displayedStatus: DisplayedStatus? = null,
eventSink: (UserProfileEvents) -> Unit = {}, eventSink: (UserProfileEvent) -> Unit = {},
) = UserProfileState( ) = UserProfileState(
userId = userId, userId = userId,
userName = userName, userName = userName,
@@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs
@@ -83,15 +83,15 @@ fun UserProfileView(
openAvatarPreview(state.userName ?: state.userId.value, avatarUrl) openAvatarPreview(state.userName ?: state.userId.value, avatarUrl)
}, },
onUserIdClick = { onUserIdClick = {
state.eventSink(UserProfileEvents.CopyToClipboard(state.userId.value)) state.eventSink(UserProfileEvent.CopyToClipboard(state.userId.value))
}, },
withdrawVerificationClick = { state.eventSink(UserProfileEvents.WithdrawVerification) }, withdrawVerificationClick = { state.eventSink(UserProfileEvent.WithdrawVerification) },
) )
UserProfileMainActionsSection( UserProfileMainActionsSection(
isCurrentUser = state.isCurrentUser, isCurrentUser = state.isCurrentUser,
canCall = state.canCall, canCall = state.canCall,
onShareUser = onShareUser, onShareUser = onShareUser,
onStartDM = { state.eventSink(UserProfileEvents.StartDM) }, onStartDM = { state.eventSink(UserProfileEvent.StartDM) },
onCall = { intent -> state.dmRoomId?.let { onStartCall(it, intent) } } onCall = { intent -> state.dmRoomId?.let { onStartCall(it, intent) } }
) )
Spacer(modifier = Modifier.height(26.dp)) Spacer(modifier = Modifier.height(26.dp))
@@ -109,18 +109,18 @@ fun UserProfileView(
}, },
onSuccess = onOpenDm, onSuccess = onOpenDm,
errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) }, errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) },
onRetry = { state.eventSink(UserProfileEvents.StartDM) }, onRetry = { state.eventSink(UserProfileEvent.StartDM) },
onErrorDismiss = { state.eventSink(UserProfileEvents.ClearStartDMState) }, onErrorDismiss = { state.eventSink(UserProfileEvent.ClearStartDMState) },
confirmationDialog = { data -> confirmationDialog = { data ->
if (data is ConfirmingStartDmWithMatrixUser) { if (data is ConfirmingStartDmWithMatrixUser) {
CreateDmConfirmationBottomSheet( CreateDmConfirmationBottomSheet(
matrixUser = data.matrixUser, matrixUser = data.matrixUser,
isUserIdentityUnknown = data.isUserIdentityUnknown, isUserIdentityUnknown = data.isUserIdentityUnknown,
onSendInvite = { onSendInvite = {
state.eventSink(UserProfileEvents.StartDM) state.eventSink(UserProfileEvent.StartDM)
}, },
onDismiss = { onDismiss = {
state.eventSink(UserProfileEvents.ClearStartDMState) state.eventSink(UserProfileEvent.ClearStartDMState)
}, },
) )
} }
@@ -10,7 +10,7 @@ package io.element.android.features.userprofile.shared.blockuser
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.R
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
@@ -23,24 +23,24 @@ fun BlockUserDialogs(state: UserProfileState) {
BlockConfirmationDialog( BlockConfirmationDialog(
onBlockAction = { onBlockAction = {
state.eventSink( state.eventSink(
UserProfileEvents.BlockUser( UserProfileEvent.BlockUser(
needsConfirmation = false needsConfirmation = false
) )
) )
}, },
onDismiss = { state.eventSink(UserProfileEvents.ClearConfirmationDialog) } onDismiss = { state.eventSink(UserProfileEvent.ClearConfirmationDialog) }
) )
} }
UserProfileState.ConfirmationDialog.Unblock -> { UserProfileState.ConfirmationDialog.Unblock -> {
UnblockConfirmationDialog( UnblockConfirmationDialog(
onUnblockAction = { onUnblockAction = {
state.eventSink( state.eventSink(
UserProfileEvents.UnblockUser( UserProfileEvent.UnblockUser(
needsConfirmation = false needsConfirmation = false
) )
) )
}, },
onDismiss = { state.eventSink(UserProfileEvents.ClearConfirmationDialog) } onDismiss = { state.eventSink(UserProfileEvent.ClearConfirmationDialog) }
) )
} }
} }
@@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.R
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
@@ -50,13 +50,13 @@ fun BlockUserSection(
if (isBlocked is AsyncData.Failure) { if (isBlocked is AsyncData.Failure) {
RetryDialog( RetryDialog(
content = stringResource(CommonStrings.error_unknown), content = stringResource(CommonStrings.error_unknown),
onDismiss = { state.eventSink(UserProfileEvents.ClearBlockUserError) }, onDismiss = { state.eventSink(UserProfileEvent.ClearBlockUserError) },
onRetry = { onRetry = {
val event = when (isBlocked.prevData) { val event = when (isBlocked.prevData) {
true -> UserProfileEvents.UnblockUser(needsConfirmation = false) true -> UserProfileEvent.UnblockUser(needsConfirmation = false)
false -> UserProfileEvents.BlockUser(needsConfirmation = false) false -> UserProfileEvent.BlockUser(needsConfirmation = false)
// null case Should not happen // null case Should not happen
null -> UserProfileEvents.ClearBlockUserError null -> UserProfileEvent.ClearBlockUserError
} }
state.eventSink(event) state.eventSink(event)
}, },
@@ -68,7 +68,7 @@ fun BlockUserSection(
private fun PreferenceBlockUser( private fun PreferenceBlockUser(
isBlocked: Boolean?, isBlocked: Boolean?,
isLoading: Boolean, isLoading: Boolean,
eventSink: (UserProfileEvents) -> Unit, eventSink: (UserProfileEvent) -> Unit,
) { ) {
val loadingCurrentValue = @Composable { _: Boolean -> val loadingCurrentValue = @Composable { _: Boolean ->
CircularProgressIndicator( CircularProgressIndicator(
@@ -82,7 +82,7 @@ private fun PreferenceBlockUser(
ListItem( ListItem(
content = { Text(stringResource(R.string.screen_dm_details_unblock_user)) }, content = { Text(stringResource(R.string.screen_dm_details_unblock_user)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
onClick = { if (!isLoading) eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) }, onClick = { if (!isLoading) eventSink(UserProfileEvent.UnblockUser(needsConfirmation = true)) },
trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null,
) )
} else { } else {
@@ -90,7 +90,7 @@ private fun PreferenceBlockUser(
content = { Text(stringResource(R.string.screen_dm_details_block_user)) }, content = { Text(stringResource(R.string.screen_dm_details_block_user)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
style = ListItemStyle.Destructive, style = ListItemStyle.Destructive,
onClick = { if (!isLoading) eventSink(UserProfileEvents.BlockUser(needsConfirmation = true)) }, onClick = { if (!isLoading) eventSink(UserProfileEvent.BlockUser(needsConfirmation = true)) },
trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null,
) )
} }
@@ -16,7 +16,7 @@ import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.R
@@ -89,7 +89,7 @@ class UserProfileViewTest : RobolectricTest() {
@Test @Test
fun `on Message clicked - the StartDm event is emitted`() = runAndroidComposeUiTest { fun `on Message clicked - the StartDm event is emitted`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
dmRoomId = A_ROOM_ID, dmRoomId = A_ROOM_ID,
@@ -97,7 +97,7 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(CommonStrings.action_message) clickOn(CommonStrings.action_message)
eventsRecorder.assertSingle(UserProfileEvents.StartDM) eventsRecorder.assertSingle(UserProfileEvent.StartDM)
} }
@Test @Test
@@ -131,19 +131,19 @@ class UserProfileViewTest : RobolectricTest() {
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
@Test @Test
fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
clickOn(R.string.screen_dm_details_block_user) clickOn(R.string.screen_dm_details_block_user)
eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = true)) eventsRecorder.assertSingle(UserProfileEvent.BlockUser(needsConfirmation = true))
} }
@Test @Test
fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block,
@@ -151,12 +151,12 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_dm_details_block_alert_action) clickOn(R.string.screen_dm_details_block_alert_action)
eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = false)) eventsRecorder.assertSingle(UserProfileEvent.BlockUser(needsConfirmation = false))
} }
@Test @Test
fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block,
@@ -164,13 +164,13 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(CommonStrings.action_cancel) clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog)
} }
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
@Test @Test
fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
isBlocked = AsyncData.Success(true), isBlocked = AsyncData.Success(true),
@@ -178,12 +178,12 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_dm_details_unblock_user) clickOn(R.string.screen_dm_details_unblock_user)
eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = true)) eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(needsConfirmation = true))
} }
@Test @Test
fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
isBlocked = AsyncData.Success(true), isBlocked = AsyncData.Success(true),
@@ -192,12 +192,12 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_dm_details_unblock_alert_action) clickOn(R.string.screen_dm_details_unblock_alert_action)
eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = false)) eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(needsConfirmation = false))
} }
@Test @Test
fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setUserProfileView( setUserProfileView(
state = aUserProfileState( state = aUserProfileState(
isBlocked = AsyncData.Success(true), isBlocked = AsyncData.Success(true),
@@ -206,7 +206,7 @@ class UserProfileViewTest : RobolectricTest() {
), ),
) )
clickOn(CommonStrings.action_cancel) clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog)
} }
@Test @Test
@@ -12,7 +12,7 @@ package io.element.android.features.userprofile.shared.blockuser
import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvent
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.R
import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.features.userprofile.shared.aUserProfileState
@@ -25,7 +25,7 @@ import org.junit.Test
class BlockUserDialogsTest : RobolectricTest() { class BlockUserDialogsTest : RobolectricTest() {
@Test @Test
fun `confirm block user emit expected Event`() = runAndroidComposeUiTest { fun `confirm block user emit expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setContent { setContent {
BlockUserDialogs( BlockUserDialogs(
state = aUserProfileState( state = aUserProfileState(
@@ -35,12 +35,12 @@ class BlockUserDialogsTest : RobolectricTest() {
) )
} }
clickOn(R.string.screen_dm_details_block_alert_action) clickOn(R.string.screen_dm_details_block_alert_action)
eventsRecorder.assertSingle(UserProfileEvents.BlockUser(false)) eventsRecorder.assertSingle(UserProfileEvent.BlockUser(false))
} }
@Test @Test
fun `cancel block user emit expected Event`() = runAndroidComposeUiTest { fun `cancel block user emit expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setContent { setContent {
BlockUserDialogs( BlockUserDialogs(
state = aUserProfileState( state = aUserProfileState(
@@ -50,12 +50,12 @@ class BlockUserDialogsTest : RobolectricTest() {
) )
} }
clickOn(CommonStrings.action_cancel) clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog)
} }
@Test @Test
fun `confirm unblock user emit expected Event`() = runAndroidComposeUiTest { fun `confirm unblock user emit expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setContent { setContent {
BlockUserDialogs( BlockUserDialogs(
state = aUserProfileState( state = aUserProfileState(
@@ -65,12 +65,12 @@ class BlockUserDialogsTest : RobolectricTest() {
) )
} }
clickOn(R.string.screen_dm_details_unblock_alert_action) clickOn(R.string.screen_dm_details_unblock_alert_action)
eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(false)) eventsRecorder.assertSingle(UserProfileEvent.UnblockUser(false))
} }
@Test @Test
fun `cancel unblock user emit expected Event`() = runAndroidComposeUiTest { fun `cancel unblock user emit expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvent>()
setContent { setContent {
BlockUserDialogs( BlockUserDialogs(
state = aUserProfileState( state = aUserProfileState(
@@ -80,6 +80,6 @@ class BlockUserDialogsTest : RobolectricTest() {
) )
} }
clickOn(CommonStrings.action_cancel) clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) eventsRecorder.assertSingle(UserProfileEvent.ClearConfirmationDialog)
} }
} }