From fd85ad0f1bf6b46f273afba56a8e054b7e9cc8bd Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 22 Sep 2022 15:06:23 +0200 Subject: [PATCH] Add Init action + corresponding initialized event --- .../devices/v2/rename/RenameSessionAction.kt | 1 + .../v2/rename/RenameSessionFragment.kt | 16 +++--- .../v2/rename/RenameSessionViewEvent.kt | 1 + .../v2/rename/RenameSessionViewModel.kt | 40 +++++++++---- .../v2/rename/RenameSessionViewModelTest.kt | 56 +++++++++++++++---- 5 files changed, 84 insertions(+), 30 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionAction.kt index d175d1ea4a..c60bc7df14 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionAction.kt @@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.rename import im.vector.app.core.platform.VectorViewModelAction sealed class RenameSessionAction : VectorViewModelAction { + object InitWithLastEditedName : RenameSessionAction() object SaveModifications : RenameSessionAction() data class EditLocally(val editedName: String) : RenameSessionAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionFragment.kt index 8e98927cff..df92bee100 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionFragment.kt @@ -40,8 +40,6 @@ class RenameSessionFragment : @Inject lateinit var viewNavigator: RenameSessionViewNavigator - private var renameEditTextInitialized = false - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSessionRenameBinding { return FragmentSessionRenameBinding.inflate(inflater, container, false) } @@ -52,6 +50,7 @@ class RenameSessionFragment : initToolbar() initEditText() initSaveButton() + initWithLastEditedName() } private fun initToolbar() { @@ -72,9 +71,17 @@ class RenameSessionFragment : } } + private fun initWithLastEditedName() { + viewModel.handle(RenameSessionAction.InitWithLastEditedName) + } + private fun observeViewEvents() { viewModel.observeViewEvents { when (it) { + is RenameSessionViewEvent.Initialized -> { + views.renameSessionEditText.setText(it.deviceName) + views.renameSessionEditText.setSelection(views.renameSessionEditText.length()) + } is RenameSessionViewEvent.SessionRenamed -> { viewNavigator.goBack(requireActivity()) } @@ -86,11 +93,6 @@ class RenameSessionFragment : } override fun invalidate() = withState(viewModel) { state -> - if (renameEditTextInitialized.not()) { - views.renameSessionEditText.setText(state.editedDeviceName) - views.renameSessionEditText.setSelection(views.renameSessionEditText.length()) - renameEditTextInitialized = true - } views.renameSessionSave.isEnabled = state.editedDeviceName.isNotEmpty() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewEvent.kt index e18ad4361a..fd40412547 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewEvent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewEvent.kt @@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.rename import im.vector.app.core.platform.VectorViewEvents sealed class RenameSessionViewEvent : VectorViewEvents { + data class Initialized(val deviceName: String) : RenameSessionViewEvent() object SessionRenamed : RenameSessionViewEvent() data class Failure(val throwable: Throwable) : RenameSessionViewEvent() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModel.kt index 0c2d06a027..22170fc702 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices.v2.rename +import androidx.annotation.VisibleForTesting import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -24,8 +25,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.settings.devices.v2.overview.GetDeviceFullInfoUseCase -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch class RenameSessionViewModel @AssistedInject constructor( @@ -41,23 +41,41 @@ class RenameSessionViewModel @AssistedInject constructor( override fun create(initialState: RenameSessionViewState): RenameSessionViewModel } - init { - observeSessionInfo(initialState.deviceId) - } - - private fun observeSessionInfo(deviceId: String) { - getDeviceFullInfoUseCase.execute(deviceId) - .onEach { setState { copy(editedDeviceName = it.deviceInfo.displayName.orEmpty()) } } - .launchIn(viewModelScope) - } + @VisibleForTesting + var hasRetrievedOriginalDeviceName = false override fun handle(action: RenameSessionAction) { when (action) { + is RenameSessionAction.InitWithLastEditedName -> handleInitWithLastEditedName() is RenameSessionAction.EditLocally -> handleEditLocally(action.editedName) is RenameSessionAction.SaveModifications -> handleSaveModifications() } } + private fun handleInitWithLastEditedName() = withState { state -> + if (hasRetrievedOriginalDeviceName) { + postInitEvent() + } else { + hasRetrievedOriginalDeviceName = true + viewModelScope.launch { + setStateWithOriginalDeviceName(state.deviceId) + postInitEvent() + } + } + } + + private suspend fun setStateWithOriginalDeviceName(deviceId: String) { + getDeviceFullInfoUseCase.execute(deviceId) + .firstOrNull() + ?.let { deviceFullInfo -> + setState { copy(editedDeviceName = deviceFullInfo.deviceInfo.displayName.orEmpty()) } + } + } + + private fun postInitEvent() = withState { state -> + _viewEvents.post(RenameSessionViewEvent.Initialized(state.editedDeviceName)) + } + private fun handleEditLocally(editedName: String) { setState { copy(editedDeviceName = editedName) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModelTest.kt index e6b218c476..c14f2f3526 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionViewModelTest.kt @@ -51,38 +51,72 @@ class RenameSessionViewModelTest { ) @Test - fun `given the viewModel has been initialized then viewState is updated with current session name`() { + fun `given the original device name has not been retrieved when handling init with last edited name action then view state and view events are updated`() { // Given givenSessionWithName(A_SESSION_NAME) + val action = RenameSessionAction.InitWithLastEditedName val expectedState = RenameSessionViewState( deviceId = A_SESSION_ID, editedDeviceName = A_SESSION_NAME, ) + val expectedEvent = RenameSessionViewEvent.Initialized( + deviceName = A_SESSION_NAME, + ) + val viewModel = createViewModel() + viewModel.hasRetrievedOriginalDeviceName = false // When - val viewModel = createViewModel() + val viewModelTest = viewModel.test() + viewModel.handle(action) // Then - viewModel.test() - .assertLatestState { state -> state == expectedState } + viewModelTest.assertLatestState { state -> state == expectedState } + .assertEvent { event -> event == expectedEvent } .finish() verify { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } } + @Test + fun `given the original device name has been retrieved when handling init with last edited name action then view state and view events are updated`() { + // Given + val action = RenameSessionAction.InitWithLastEditedName + val expectedState = RenameSessionViewState( + deviceId = A_SESSION_ID, + editedDeviceName = AN_EDITED_SESSION_NAME, + ) + val expectedEvent = RenameSessionViewEvent.Initialized( + deviceName = AN_EDITED_SESSION_NAME, + ) + val viewModel = createViewModel() + viewModel.handle(RenameSessionAction.EditLocally(AN_EDITED_SESSION_NAME)) + viewModel.hasRetrievedOriginalDeviceName = true + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest.assertLatestState { state -> state == expectedState } + .assertEvent { event -> event == expectedEvent } + .finish() + verify(inverse = true) { + getDeviceFullInfoUseCase.execute(A_SESSION_ID) + } + } + @Test fun `given a new edited name when handling edit name locally action then view state is updated accordingly`() { // Given - givenSessionWithName(A_SESSION_NAME) val action = RenameSessionAction.EditLocally(AN_EDITED_SESSION_NAME) val expectedState = RenameSessionViewState( deviceId = A_SESSION_ID, editedDeviceName = AN_EDITED_SESSION_NAME, ) + val viewModel = createViewModel() // When - val viewModel = createViewModel() val viewModelTest = viewModel.test() viewModel.handle(action) @@ -95,12 +129,11 @@ class RenameSessionViewModelTest { @Test fun `given current edited name when handling save modifications action with success then correct view event is posted`() { // Given - givenSessionWithName(A_SESSION_NAME) - coEvery { renameSessionUseCase.execute(A_SESSION_ID, A_SESSION_NAME) } returns Result.success(Unit) + coEvery { renameSessionUseCase.execute(A_SESSION_ID, any()) } returns Result.success(Unit) val action = RenameSessionAction.SaveModifications + val viewModel = createViewModel() // When - val viewModel = createViewModel() val viewModelTest = viewModel.test() viewModel.handle(action) @@ -113,13 +146,12 @@ class RenameSessionViewModelTest { @Test fun `given current edited name when handling save modifications action with error then correct view event is posted`() { // Given - givenSessionWithName(A_SESSION_NAME) val error = Exception() - coEvery { renameSessionUseCase.execute(A_SESSION_ID, A_SESSION_NAME) } returns Result.failure(error) + coEvery { renameSessionUseCase.execute(A_SESSION_ID, any()) } returns Result.failure(error) val action = RenameSessionAction.SaveModifications + val viewModel = createViewModel() // When - val viewModel = createViewModel() val viewModelTest = viewModel.test() viewModel.handle(action)