Skip to content

Commit

Permalink
Add Sign Up screen skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
toluo-stripe committed Sep 27, 2024
1 parent 39d7507 commit 37fe570
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 35 deletions.
22 changes: 19 additions & 3 deletions link/src/main/java/com/stripe/android/link/LinkActivity.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.stripe.android.link

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.LaunchedEffect
import androidx.core.os.bundleOf
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.stripe.android.link.ui.cardedit.CardEditScreen
import com.stripe.android.link.ui.paymentmenthod.PaymentMethodScreen
import com.stripe.android.link.ui.signup.SignUpScreen
import com.stripe.android.link.ui.signup.SignUpViewModel
import com.stripe.android.link.ui.verification.VerificationScreen
import com.stripe.android.link.ui.wallet.WalletScreen

Expand All @@ -35,8 +40,15 @@ internal class LinkActivity : AppCompatActivity() {
viewModel.effect.collect { effect ->
when (effect) {
LinkEffect.GoBack -> navController.popBackStack()
is LinkEffect.NavigateTo -> {
navController.navigate(effect.screen.route)
is LinkEffect.SendResult -> {
val bundle = bundleOf(
LinkActivityContract.EXTRA_RESULT to LinkActivityContract.Result(effect.result)
)
this@LinkActivity.setResult(
Activity.RESULT_OK,
Intent().putExtras(bundle)
)
this@LinkActivity.finish()
}
}
}
Expand All @@ -47,7 +59,11 @@ internal class LinkActivity : AppCompatActivity() {
startDestination = LinkScreen.SignUp.route
) {
composable(LinkScreen.SignUp.route) {
SignUpScreen()
val vm = viewModel<SignUpViewModel>()
SignUpScreen(
viewModel = vm,
navController = navController
)
}

composable(LinkScreen.Verification.route) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ class LinkActivityContract @Inject internal constructor(
data class Result(
val linkResult: LinkActivityResult
)

companion object {
const val EXTRA_RESULT =
"com.stripe.android.link.LinkActivityContract.extra_result"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,15 @@ internal class LinkActivityViewModel : LinkViewModel<LinkState, LinkAction, Link
initialState = LinkState,
) {
override fun actionToResult(action: LinkAction): Flow<LinkResult> {
// These are placeholder actions, they may be removed
return when (action) {
LinkAction.BackPressed -> handleBackPressed()
LinkAction.WalletClicked -> handleWalletClicked()
}
}

private fun handleBackPressed(): Flow<LinkResult> {
return flowOf(LinkResult.SendEffect(LinkEffect.GoBack))
}

private fun handleWalletClicked(): Flow<LinkResult> {
return flowOf(
value = LinkResult.SendEffect(
effect = LinkEffect.NavigateTo(
screen = LinkScreen.Wallet
)
)
)
}

override fun resultToState(currentState: LinkState, result: LinkResult) = currentState

override fun resultToEffect(result: LinkResult): LinkEffect? {
Expand Down
3 changes: 1 addition & 2 deletions link/src/main/java/com/stripe/android/link/LinkState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ internal object LinkState

internal sealed interface LinkAction {
data object BackPressed : LinkAction
data object WalletClicked : LinkAction
}

internal sealed interface LinkResult {
Expand All @@ -13,5 +12,5 @@ internal sealed interface LinkResult {

internal sealed interface LinkEffect {
data object GoBack : LinkEffect
data class NavigateTo(val screen: LinkScreen) : LinkEffect
data class SendResult(val result: LinkActivityResult) : LinkEffect
}
2 changes: 0 additions & 2 deletions link/src/main/java/com/stripe/android/link/LinkViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

// This will be used as a base for all Link screen viewmodels to create a consistent architecture
// There should be no more logic here besides updating state and sending effects
internal abstract class LinkViewModel<State, Action, Result, Effect>(
initialState: State,
) : ViewModel() {
Expand Down
18 changes: 18 additions & 0 deletions link/src/main/java/com/stripe/android/link/ui/signup/Model.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stripe.android.link.ui.signup

data class SignUpViewState(
val loading: Boolean = false
)

sealed interface SignUpAction {
data object SignUpClicked : SignUpAction
}

sealed interface SignUpResult {
data object ShowLoader : SignUpResult
data class SendEffect(val effect: SignUpEffect) : SignUpResult
}

sealed interface SignUpEffect {
data object NavigateToWallet : SignUpEffect
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package com.stripe.android.link.ui.signup

import androidx.compose.material.Text
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.navigation.NavHostController
import com.stripe.android.link.LinkScreen
import com.stripe.android.uicore.utils.collectAsState

@Composable
internal fun SignUpScreen() {
Text(text = "SignUpScreen")
internal fun SignUpScreen(
viewModel: SignUpViewModel,
navController: NavHostController
) {
LaunchedEffect("SignUpEffects") {
viewModel.effect.collect { effect ->
when (effect) {
SignUpEffect.NavigateToWallet -> navController.navigate(LinkScreen.Wallet.route)
}
}
}
val state by viewModel.state.collectAsState()
AnimatedVisibility(visible = state.loading) {
CircularProgressIndicator()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stripe.android.link.ui.signup

import com.stripe.android.link.LinkViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

internal class SignUpViewModel : LinkViewModel<SignUpViewState, SignUpAction, SignUpResult, SignUpEffect>(
initialState = SignUpViewState()
) {
override fun actionToResult(action: SignUpAction): Flow<SignUpResult> {
return when (action) {
SignUpAction.SignUpClicked -> handleSignUpClicked()
}
}

private fun handleSignUpClicked() = flow {
emit(SignUpResult.ShowLoader)
emit(
value = SignUpResult.SendEffect(
effect = SignUpEffect.NavigateToWallet
)
)
}

override fun resultToState(currentState: SignUpViewState, result: SignUpResult): SignUpViewState {
return when (result) {
is SignUpResult.SendEffect -> currentState
SignUpResult.ShowLoader -> {
currentState.copy(loading = true)
}
}
}

override fun resultToEffect(result: SignUpResult): SignUpEffect? {
return when (result) {
is SignUpResult.SendEffect -> result.effect
SignUpResult.ShowLoader -> null
}
}
}
13 changes: 0 additions & 13 deletions link/src/test/java/com/stripe/android/link/LinkActivityTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,6 @@ internal class LinkActivityTest {
}
}

@Test
fun `test that navigator navigates to wallet on wallet clicked`() {
val vm = LinkActivityViewModel()
val scenario = activityScenario(viewModel = vm)
scenario.launchTest {
vm.handleAction(LinkAction.WalletClicked)

composeTestRule.waitForIdle()

verify(navHostController).navigate(LinkScreen.Wallet.route)
}
}

private fun InjectableActivityScenario<LinkActivity>.launchTest(
startIntent: Intent = Intent(context, LinkActivity::class.java),
block: (LinkActivity) -> Unit
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.stripe.android.link.ui.signup

import app.cash.turbine.test
import com.google.common.truth.Truth
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class SignUpViewModelTest {
private val dispatcher = UnconfinedTestDispatcher()
private val vm = SignUpViewModel()

@Before
fun setUp() {
Dispatchers.setMain(dispatcher)
}

@After
fun tearDown() {
Dispatchers.resetMain()
}

@Test
fun `test that viewmodel has correct initial state`() = runTest(dispatcher) {
vm.state.test {
Truth.assertThat(awaitItem()).isEqualTo(SignUpViewState())
}
}

@Test
fun `test that correct effect is emitted on successful sign up`() = runTest(dispatcher) {
vm.effect.test {
vm.handleAction(SignUpAction.SignUpClicked)
Truth.assertThat(awaitItem()).isEqualTo(SignUpEffect.NavigateToWallet)
}
}
}

0 comments on commit 37fe570

Please sign in to comment.