From e5bfdd0fa77611d345d281c3bf189e4e714b7b71 Mon Sep 17 00:00:00 2001 From: Caleb Derosier <125901828+caleb-livefront@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:42:56 -0700 Subject: [PATCH] BIT-810: Implement resend notification (#803) --- .../loginwithdevice/LoginWithDeviceScreen.kt | 30 ++++++++++++++----- .../LoginWithDeviceViewModel.kt | 21 +++++++++++-- .../LoginWithDeviceScreenTest.kt | 1 + .../LoginWithDeviceViewModelTest.kt | 27 +++++++++++------ 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreen.kt index 7bb60ba81..6559dc28b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreen.kt @@ -4,12 +4,14 @@ import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator @@ -198,14 +200,28 @@ private fun LoginWithDeviceScreenContent( Spacer(modifier = Modifier.height(24.dp)) - BitwardenClickableText( + Column( modifier = Modifier - .padding(horizontal = 16.dp) - .semantics { testTag = "ResendNotificationButton" } - .fillMaxWidth(), - label = stringResource(id = R.string.resend_notification), - onClick = onResendNotificationClick, - ) + .defaultMinSize(minHeight = 32.dp) + .align(Alignment.Start), + ) { + if (state.isResendNotificationLoading) { + CircularProgressIndicator( + modifier = Modifier + .padding(horizontal = 64.dp) + .size(size = 16.dp), + ) + } else { + BitwardenClickableText( + modifier = Modifier + .padding(horizontal = 16.dp) + .semantics { testTag = "ResendNotificationButton" } + .fillMaxWidth(), + label = stringResource(id = R.string.resend_notification), + onClick = onResendNotificationClick, + ) + } + } Spacer(modifier = Modifier.height(24.dp)) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModel.kt index fd1798429..076fe52e5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModel.kt @@ -52,8 +52,7 @@ class LoginWithDeviceViewModel @Inject constructor( } private fun handleResendNotificationClicked() { - // TODO BIT-810: implement Resend Notification button - sendEvent(LoginWithDeviceEvent.ShowToast("Not yet implemented.")) + sendNewAuthRequest() } private fun handleViewAllLogInOptionsClicked() { @@ -69,6 +68,7 @@ class LoginWithDeviceViewModel @Inject constructor( it.copy( viewState = LoginWithDeviceState.ViewState.Content( fingerprintPhrase = action.result.authRequest.fingerprint, + isResendNotificationLoading = false, ), ) } @@ -88,6 +88,7 @@ class LoginWithDeviceViewModel @Inject constructor( } private fun sendNewAuthRequest() { + setIsResendNotificationLoading(true) viewModelScope.launch { trySendAction( LoginWithDeviceAction.Internal.NewAuthRequestResultReceive( @@ -98,6 +99,21 @@ class LoginWithDeviceViewModel @Inject constructor( ) } } + + private fun setIsResendNotificationLoading(isLoading: Boolean) { + when (val viewState = mutableStateFlow.value.viewState) { + is LoginWithDeviceState.ViewState.Content -> { + mutableStateFlow.update { + it.copy( + viewState = viewState.copy( + isResendNotificationLoading = isLoading, + ), + ) + } + } + else -> Unit + } + } } /** @@ -139,6 +155,7 @@ data class LoginWithDeviceState( @Parcelize data class Content( val fingerprintPhrase: String, + val isResendNotificationLoading: Boolean, ) : ViewState() } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt index 76b4f4d55..3680028d1 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceScreenTest.kt @@ -110,6 +110,7 @@ class LoginWithDeviceScreenTest : BaseComposeTest() { emailAddress = EMAIL, viewState = LoginWithDeviceState.ViewState.Content( fingerprintPhrase = "alabster-drinkable-mystified-rapping-irrigate", + isResendNotificationLoading = false, ), ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModelTest.kt index 75cc260de..13309bf17 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/loginwithdevice/LoginWithDeviceViewModelTest.kt @@ -45,6 +45,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() { emailAddress = newEmail, viewState = LoginWithDeviceState.ViewState.Content( fingerprintPhrase = FINGERPRINT, + isResendNotificationLoading = false, ), ) val viewModel = createViewModel(state) @@ -69,16 +70,22 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() { } @Test - fun `ResendNotificationClick should emit ShowToast`() = runTest { + fun `ResendNotificationClick should create new auth request and update state`() = runTest { + val newFingerprint = "newFingerprint" + coEvery { + authRepository.createAuthRequest(EMAIL) + } returns AuthRequestResult.Success(AUTH_REQUEST.copy(fingerprint = newFingerprint)) val viewModel = createViewModel() - viewModel.eventFlow.test { - viewModel.actionChannel.trySend(LoginWithDeviceAction.ResendNotificationClick) - assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) - assertEquals( - LoginWithDeviceEvent.ShowToast("Not yet implemented."), - awaitItem(), - ) - } + viewModel.actionChannel.trySend(LoginWithDeviceAction.ResendNotificationClick) + assertEquals( + DEFAULT_STATE.copy( + viewState = LoginWithDeviceState.ViewState.Content( + fingerprintPhrase = newFingerprint, + isResendNotificationLoading = false, + ), + ), + viewModel.stateFlow.value, + ) } @Test @@ -112,6 +119,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() { DEFAULT_STATE.copy( viewState = LoginWithDeviceState.ViewState.Content( fingerprintPhrase = newFingerprint, + isResendNotificationLoading = false, ), ), viewModel.stateFlow.value, @@ -152,6 +160,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() { emailAddress = EMAIL, viewState = LoginWithDeviceState.ViewState.Content( fingerprintPhrase = FINGERPRINT, + isResendNotificationLoading = false, ), ) private val AUTH_REQUEST = AuthRequest(