EditUserProfileEvents -> EditUserProfileEvent

This commit is contained in:
Benoit Marty 2025-12-03 17:04:53 +01:00 committed by Benoit Marty
parent 766e97dcc1
commit e6dc2501a5
7 changed files with 71 additions and 71 deletions

View file

@ -10,10 +10,10 @@ package io.element.android.features.preferences.impl.user.editprofile
import io.element.android.libraries.matrix.ui.media.AvatarAction
sealed interface EditUserProfileEvents {
data class HandleAvatarAction(val action: AvatarAction) : EditUserProfileEvents
data class UpdateDisplayName(val name: String) : EditUserProfileEvents
data object Exit : EditUserProfileEvents
data object Save : EditUserProfileEvents
data object CloseDialog : EditUserProfileEvents
sealed interface EditUserProfileEvent {
data class HandleAvatarAction(val action: AvatarAction) : EditUserProfileEvent
data class UpdateDisplayName(val name: String) : EditUserProfileEvent
data object Exit : EditUserProfileEvent
data object Save : EditUserProfileEvent
data object CloseDialog : EditUserProfileEvent
}

View file

@ -112,15 +112,15 @@ class EditUserProfilePresenter(
!userDisplayName.isNullOrBlank() && hasProfileChanged
}
fun handleEvent(event: EditUserProfileEvents) {
fun handleEvent(event: EditUserProfileEvent) {
when (event) {
is EditUserProfileEvents.Save -> localCoroutineScope.saveChanges(
is EditUserProfileEvent.Save -> localCoroutineScope.saveChanges(
name = userDisplayName,
avatarUri = userAvatarUri?.toUri(),
currentUser = matrixUser,
action = saveAction,
)
is EditUserProfileEvents.HandleAvatarAction -> {
is EditUserProfileEvent.HandleAvatarAction -> {
when (event.action) {
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
@ -135,8 +135,8 @@ class EditUserProfilePresenter(
}
}
}
is EditUserProfileEvents.UpdateDisplayName -> userDisplayName = event.name
EditUserProfileEvents.Exit -> {
is EditUserProfileEvent.UpdateDisplayName -> userDisplayName = event.name
EditUserProfileEvent.Exit -> {
when (saveAction.value) {
is AsyncAction.Confirming -> {
// Close the dialog right now
@ -157,7 +157,7 @@ class EditUserProfilePresenter(
}
}
}
EditUserProfileEvents.CloseDialog -> saveAction.value = AsyncAction.Uninitialized
EditUserProfileEvent.CloseDialog -> saveAction.value = AsyncAction.Uninitialized
}
}

View file

@ -22,5 +22,5 @@ data class EditUserProfileState(
val saveButtonEnabled: Boolean,
val saveAction: AsyncAction<Unit>,
val cameraPermissionState: PermissionsState,
val eventSink: (EditUserProfileEvents) -> Unit
val eventSink: (EditUserProfileEvent) -> Unit
)

View file

@ -33,7 +33,7 @@ fun aEditUserProfileState(
saveButtonEnabled: Boolean = true,
saveAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false),
eventSink: (EditUserProfileEvents) -> Unit = {},
eventSink: (EditUserProfileEvent) -> Unit = {},
) = EditUserProfileState(
userId = userId,
displayName = displayName,

View file

@ -68,7 +68,7 @@ fun EditUserProfileView(
fun onBackClick() {
focusManager.clearFocus()
state.eventSink(EditUserProfileEvents.Exit)
state.eventSink(EditUserProfileEvent.Exit)
}
BackHandler(
@ -87,7 +87,7 @@ fun EditUserProfileView(
enabled = state.saveButtonEnabled,
onClick = {
focusManager.clearFocus()
state.eventSink(EditUserProfileEvents.Save)
state.eventSink(EditUserProfileEvent.Save)
},
)
}
@ -125,7 +125,7 @@ fun EditUserProfileView(
value = state.displayName,
placeholder = stringResource(CommonStrings.common_room_name_placeholder),
singleLine = true,
onValueChange = { state.eventSink(EditUserProfileEvents.UpdateDisplayName(it)) },
onValueChange = { state.eventSink(EditUserProfileEvent.UpdateDisplayName(it)) },
)
}
@ -133,7 +133,7 @@ fun EditUserProfileView(
actions = state.avatarActions,
isVisible = isAvatarActionsSheetVisible.value,
onDismiss = { isAvatarActionsSheetVisible.value = false },
onSelectAction = { state.eventSink(EditUserProfileEvents.HandleAvatarAction(it)) }
onSelectAction = { state.eventSink(EditUserProfileEvent.HandleAvatarAction(it)) }
)
AsyncActionView(
@ -147,9 +147,9 @@ fun EditUserProfileView(
when (confirming) {
is AsyncAction.ConfirmingCancellation -> {
SaveChangesDialog(
onSaveClick = { state.eventSink(EditUserProfileEvents.Save) },
onDiscardClick = { state.eventSink(EditUserProfileEvents.Exit) },
onDismiss = { state.eventSink(EditUserProfileEvents.CloseDialog) },
onSaveClick = { state.eventSink(EditUserProfileEvent.Save) },
onDiscardClick = { state.eventSink(EditUserProfileEvent.Exit) },
onDismiss = { state.eventSink(EditUserProfileEvent.CloseDialog) },
)
}
}
@ -157,7 +157,7 @@ fun EditUserProfileView(
onSuccess = { onEditProfileSuccess() },
errorTitle = { stringResource(R.string.screen_edit_profile_error_title) },
errorMessage = { stringResource(R.string.screen_edit_profile_error) },
onErrorDismiss = { state.eventSink(EditUserProfileEvents.CloseDialog) },
onErrorDismiss = { state.eventSink(EditUserProfileEvent.CloseDialog) },
)
}
PermissionsView(

View file

@ -124,7 +124,7 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.Exit)
initialState.eventSink(EditUserProfileEvent.Exit)
closeLambda.assertions().isCalledOnce()
}
}
@ -139,21 +139,21 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("New name"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("New name"))
val withUpdatedName = awaitItem()
withUpdatedName.eventSink(EditUserProfileEvents.Exit)
withUpdatedName.eventSink(EditUserProfileEvent.Exit)
val withConfirmation = awaitItem()
assertThat(withConfirmation.saveAction).isEqualTo(AsyncAction.ConfirmingCancellation)
// Cancel
withConfirmation.eventSink(EditUserProfileEvents.CloseDialog)
withConfirmation.eventSink(EditUserProfileEvent.CloseDialog)
val afterCancel = awaitItem()
assertThat(afterCancel.saveAction).isEqualTo(AsyncAction.Uninitialized)
// Try again and confirm
afterCancel.eventSink(EditUserProfileEvents.Exit)
afterCancel.eventSink(EditUserProfileEvent.Exit)
val withConfirmation2 = awaitItem()
assertThat(withConfirmation2.saveAction).isEqualTo(AsyncAction.ConfirmingCancellation)
closeLambda.assertions().isNeverCalled()
withConfirmation2.eventSink(EditUserProfileEvents.Exit)
withConfirmation2.eventSink(EditUserProfileEvent.Exit)
// Dialog is closed
val finalState = awaitItem()
assertThat(finalState.saveAction).isEqualTo(AsyncAction.Uninitialized)
@ -174,17 +174,17 @@ class EditUserProfilePresenterTest {
val initialState = awaitItem()
assertThat(initialState.displayName).isEqualTo("Name")
assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL)
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name II"))
awaitItem().apply {
assertThat(displayName).isEqualTo("Name II")
assertThat(userAvatarUrl).isEqualTo(AN_AVATAR_URL)
}
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name III"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name III"))
awaitItem().apply {
assertThat(displayName).isEqualTo("Name III")
assertThat(userAvatarUrl).isEqualTo(AN_AVATAR_URL)
}
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.Remove))
awaitItem().apply {
assertThat(displayName).isEqualTo("Name III")
assertThat(userAvatarUrl).isNull()
@ -205,7 +205,7 @@ class EditUserProfilePresenterTest {
presenter.test {
val initialState = awaitItem()
assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL)
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
awaitItem().apply {
assertThat(userAvatarUrl).isEqualTo(ANOTHER_AVATAR_URL)
}
@ -229,7 +229,7 @@ class EditUserProfilePresenterTest {
val initialState = awaitItem()
assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL)
assertThat(initialState.cameraPermissionState.permissionGranted).isFalse()
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.TakePhoto))
val stateWithAskingPermission = awaitItem()
assertThat(stateWithAskingPermission.cameraPermissionState.showDialog).isTrue()
fakePermissionsPresenter.setPermissionGranted()
@ -239,7 +239,7 @@ class EditUserProfilePresenterTest {
assertThat(stateWithNewAvatar.userAvatarUrl).isEqualTo(ANOTHER_AVATAR_URL)
// Do it again, no permission is requested
fakePickerProvider.givenResult(userAvatarUri)
stateWithNewAvatar.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
stateWithNewAvatar.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.TakePhoto))
val stateWithNewAvatar2 = awaitItem()
assertThat(stateWithNewAvatar2.userAvatarUrl).isEqualTo(AN_AVATAR_URL)
deleteCallback.assertions().isCalledExactly(2).withSequence(
@ -264,22 +264,22 @@ class EditUserProfilePresenterTest {
val initialState = awaitItem()
assertThat(initialState.saveButtonEnabled).isFalse()
// Once a change is made, the save button is enabled
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name II"))
awaitItem().apply {
assertThat(saveButtonEnabled).isTrue()
}
// If it's reverted then the save disables again
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name"))
awaitItem().apply {
assertThat(saveButtonEnabled).isFalse()
}
// Make a change...
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.Remove))
awaitItem().apply {
assertThat(saveButtonEnabled).isTrue()
}
// Revert it...
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
awaitItem().apply {
assertThat(saveButtonEnabled).isFalse()
}
@ -305,22 +305,22 @@ class EditUserProfilePresenterTest {
val initialState = awaitItem()
assertThat(initialState.saveButtonEnabled).isFalse()
// Once a change is made, the save button is enabled
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name II"))
awaitItem().apply {
assertThat(saveButtonEnabled).isTrue()
}
// If it's reverted then the save disables again
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name"))
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("Name"))
awaitItem().apply {
assertThat(saveButtonEnabled).isFalse()
}
// Make a change...
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
awaitItem().apply {
assertThat(saveButtonEnabled).isTrue()
}
// Revert it...
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.Remove))
awaitItem().apply {
assertThat(saveButtonEnabled).isFalse()
}
@ -344,9 +344,9 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("New name"))
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("New name"))
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.Remove))
initialState.eventSink(EditUserProfileEvent.Save)
consumeItemsUntilPredicate { matrixClient.setDisplayNameCalled && matrixClient.removeAvatarCalled && !matrixClient.uploadAvatarCalled }
assertThat(matrixClient.setDisplayNameCalled).isTrue()
assertThat(matrixClient.removeAvatarCalled).isTrue()
@ -365,8 +365,8 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(" Name "))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName(" Name "))
initialState.eventSink(EditUserProfileEvent.Save)
consumeItemsUntilTimeout()
assertThat(matrixClient.setDisplayNameCalled).isFalse()
assertThat(matrixClient.uploadAvatarCalled).isFalse()
@ -384,8 +384,8 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName(""))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName(""))
initialState.eventSink(EditUserProfileEvent.Save)
assertThat(matrixClient.setDisplayNameCalled).isFalse()
assertThat(matrixClient.uploadAvatarCalled).isFalse()
assertThat(matrixClient.removeAvatarCalled).isFalse()
@ -407,8 +407,8 @@ class EditUserProfilePresenterTest {
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.Save)
consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled }
assertThat(matrixClient.uploadAvatarCalled).isTrue()
}
@ -429,8 +429,8 @@ class EditUserProfilePresenterTest {
fakeMediaPreProcessor.givenResult(Result.failure(RuntimeException("Oh no")))
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.Save)
skipItems(2)
assertThat(matrixClient.uploadAvatarCalled).isFalse()
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
@ -443,7 +443,7 @@ class EditUserProfilePresenterTest {
val matrixClient = FakeMatrixClient().apply {
givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
}
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.UpdateDisplayName("New name"))
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.UpdateDisplayName("New name"))
}
@Test
@ -452,7 +452,7 @@ class EditUserProfilePresenterTest {
val matrixClient = FakeMatrixClient().apply {
givenRemoveAvatarResult(Result.failure(RuntimeException("!")))
}
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove))
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.Remove))
}
@Test
@ -462,7 +462,7 @@ class EditUserProfilePresenterTest {
val matrixClient = FakeMatrixClient().apply {
givenUploadAvatarResult(Result.failure(RuntimeException("!")))
}
saveAndAssertFailure(user, matrixClient, EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
}
@Test
@ -475,16 +475,16 @@ class EditUserProfilePresenterTest {
val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("foo"))
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo"))
initialState.eventSink(EditUserProfileEvent.Save)
skipItems(2)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(EditUserProfileEvents.CloseDialog)
initialState.eventSink(EditUserProfileEvent.CloseDialog)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
}
}
private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvents) {
private suspend fun saveAndAssertFailure(matrixUser: MatrixUser, matrixClient: MatrixClient, event: EditUserProfileEvent) {
val presenter = createEditUserProfilePresenter(
matrixUser = matrixUser,
matrixClient = matrixClient,
@ -495,7 +495,7 @@ class EditUserProfilePresenterTest {
presenter.test {
val initialState = awaitItem()
initialState.eventSink(event)
initialState.eventSink(EditUserProfileEvents.Save)
initialState.eventSink(EditUserProfileEvent.Save)
skipItems(1)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)

View file

@ -34,19 +34,19 @@ class EditUserProfileViewTest {
@Test
fun `clicking on back emits the expected event`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>()
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
aEditUserProfileState(
eventSink = eventsRecorder,
),
)
rule.pressBack()
eventsRecorder.assertSingle(EditUserProfileEvents.Exit)
eventsRecorder.assertSingle(EditUserProfileEvent.Exit)
}
@Test
fun `clicking on save from the exit confirmation dialog emits the expected event`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>()
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.ConfirmingCancellation,
@ -54,12 +54,12 @@ class EditUserProfileViewTest {
),
)
rule.clickOn(CommonStrings.action_save, inDialog = true)
eventsRecorder.assertSingle(EditUserProfileEvents.Save)
eventsRecorder.assertSingle(EditUserProfileEvent.Save)
}
@Test
fun `clicking on discard exit emits the expected event`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>()
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.ConfirmingCancellation,
@ -67,12 +67,12 @@ class EditUserProfileViewTest {
),
)
rule.clickOn(CommonStrings.action_discard)
eventsRecorder.assertSingle(EditUserProfileEvents.Exit)
eventsRecorder.assertSingle(EditUserProfileEvent.Exit)
}
@Test
fun `clicking on save emits the expected event`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>()
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
aEditUserProfileState(
saveButtonEnabled = true,
@ -81,12 +81,12 @@ class EditUserProfileViewTest {
),
)
rule.clickOn(CommonStrings.action_save)
eventsRecorder.assertSingle(EditUserProfileEvents.Save)
eventsRecorder.assertSingle(EditUserProfileEvent.Save)
}
@Test
fun `clicking on avatar opens the bottom sheet dialog`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>()
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
val actions = listOf(
AvatarAction.TakePhoto,
AvatarAction.ChoosePhoto,
@ -110,7 +110,7 @@ class EditUserProfileViewTest {
@Test
fun `success invokes the expected callback`() {
val eventsRecorder = EventsRecorder<EditUserProfileEvents>(expectEvents = false)
val eventsRecorder = EventsRecorder<EditUserProfileEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setEditUserProfileView(
aEditUserProfileState(