BIT-810: Implement resend notification (#803)

This commit is contained in:
Caleb Derosier 2024-01-26 14:42:56 -07:00 committed by Álison Fernandes
parent a7e393e325
commit e5bfdd0fa7
4 changed files with 61 additions and 18 deletions

View file

@ -4,12 +4,14 @@ import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@ -198,14 +200,28 @@ private fun LoginWithDeviceScreenContent(
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
BitwardenClickableText( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .defaultMinSize(minHeight = 32.dp)
.semantics { testTag = "ResendNotificationButton" } .align(Alignment.Start),
.fillMaxWidth(), ) {
label = stringResource(id = R.string.resend_notification), if (state.isResendNotificationLoading) {
onClick = onResendNotificationClick, 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)) Spacer(modifier = Modifier.height(24.dp))

View file

@ -52,8 +52,7 @@ class LoginWithDeviceViewModel @Inject constructor(
} }
private fun handleResendNotificationClicked() { private fun handleResendNotificationClicked() {
// TODO BIT-810: implement Resend Notification button sendNewAuthRequest()
sendEvent(LoginWithDeviceEvent.ShowToast("Not yet implemented."))
} }
private fun handleViewAllLogInOptionsClicked() { private fun handleViewAllLogInOptionsClicked() {
@ -69,6 +68,7 @@ class LoginWithDeviceViewModel @Inject constructor(
it.copy( it.copy(
viewState = LoginWithDeviceState.ViewState.Content( viewState = LoginWithDeviceState.ViewState.Content(
fingerprintPhrase = action.result.authRequest.fingerprint, fingerprintPhrase = action.result.authRequest.fingerprint,
isResendNotificationLoading = false,
), ),
) )
} }
@ -88,6 +88,7 @@ class LoginWithDeviceViewModel @Inject constructor(
} }
private fun sendNewAuthRequest() { private fun sendNewAuthRequest() {
setIsResendNotificationLoading(true)
viewModelScope.launch { viewModelScope.launch {
trySendAction( trySendAction(
LoginWithDeviceAction.Internal.NewAuthRequestResultReceive( 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 @Parcelize
data class Content( data class Content(
val fingerprintPhrase: String, val fingerprintPhrase: String,
val isResendNotificationLoading: Boolean,
) : ViewState() ) : ViewState()
} }
} }

View file

@ -110,6 +110,7 @@ class LoginWithDeviceScreenTest : BaseComposeTest() {
emailAddress = EMAIL, emailAddress = EMAIL,
viewState = LoginWithDeviceState.ViewState.Content( viewState = LoginWithDeviceState.ViewState.Content(
fingerprintPhrase = "alabster-drinkable-mystified-rapping-irrigate", fingerprintPhrase = "alabster-drinkable-mystified-rapping-irrigate",
isResendNotificationLoading = false,
), ),
) )
} }

View file

@ -45,6 +45,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() {
emailAddress = newEmail, emailAddress = newEmail,
viewState = LoginWithDeviceState.ViewState.Content( viewState = LoginWithDeviceState.ViewState.Content(
fingerprintPhrase = FINGERPRINT, fingerprintPhrase = FINGERPRINT,
isResendNotificationLoading = false,
), ),
) )
val viewModel = createViewModel(state) val viewModel = createViewModel(state)
@ -69,16 +70,22 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() {
} }
@Test @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() val viewModel = createViewModel()
viewModel.eventFlow.test { viewModel.actionChannel.trySend(LoginWithDeviceAction.ResendNotificationClick)
viewModel.actionChannel.trySend(LoginWithDeviceAction.ResendNotificationClick) assertEquals(
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) DEFAULT_STATE.copy(
assertEquals( viewState = LoginWithDeviceState.ViewState.Content(
LoginWithDeviceEvent.ShowToast("Not yet implemented."), fingerprintPhrase = newFingerprint,
awaitItem(), isResendNotificationLoading = false,
) ),
} ),
viewModel.stateFlow.value,
)
} }
@Test @Test
@ -112,6 +119,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() {
DEFAULT_STATE.copy( DEFAULT_STATE.copy(
viewState = LoginWithDeviceState.ViewState.Content( viewState = LoginWithDeviceState.ViewState.Content(
fingerprintPhrase = newFingerprint, fingerprintPhrase = newFingerprint,
isResendNotificationLoading = false,
), ),
), ),
viewModel.stateFlow.value, viewModel.stateFlow.value,
@ -152,6 +160,7 @@ class LoginWithDeviceViewModelTest : BaseViewModelTest() {
emailAddress = EMAIL, emailAddress = EMAIL,
viewState = LoginWithDeviceState.ViewState.Content( viewState = LoginWithDeviceState.ViewState.Content(
fingerprintPhrase = FINGERPRINT, fingerprintPhrase = FINGERPRINT,
isResendNotificationLoading = false,
), ),
) )
private val AUTH_REQUEST = AuthRequest( private val AUTH_REQUEST = AuthRequest(