Skip to content

Commit

Permalink
CU-862k43hfa - Add circuit and decouple viewmodel from view
Browse files Browse the repository at this point in the history
  • Loading branch information
newmskywalker committed Jul 11, 2023
1 parent edabd57 commit 1bd7400
Show file tree
Hide file tree
Showing 30 changed files with 227 additions and 148 deletions.
3 changes: 3 additions & 0 deletions android/app-newm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ dependencies {
implementation(project(Modules.login))
implementation(project(Modules.shared))

implementation(Circuit.foundation)
implementation(Circuit.retained)

// implementation(Airbnb.showkase)
implementation(Google.activityCompose)
implementation(Google.androidxCore)
Expand Down
47 changes: 39 additions & 8 deletions android/app-newm/src/main/java/io/newm/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,64 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.slack.circuit.foundation.CircuitCompositionLocals
import com.slack.circuit.foundation.CircuitConfig
import com.slack.circuit.foundation.CircuitContent
import com.slack.circuit.retained.LocalRetainedStateRegistry
import com.slack.circuit.retained.continuityRetainedStateRegistry
import io.newm.core.theme.NewmTheme
import io.newm.feature.login.screen.*
import io.newm.feature.login.screen.createaccount.CreateAccountScreen
import io.newm.feature.login.screen.createaccount.CreateAccountPresenter
import io.newm.feature.login.screen.createaccount.CreateAccountUi
import io.newm.feature.login.screen.createaccount.CreateAccountViewModel
import io.newm.feature.login.screen.createaccount.EnterVerificationCodeScreen
import io.newm.feature.login.screen.createaccount.WhatShouldWeCallYouScreen
import io.newm.screens.Screen

class LoginActivity : ComponentActivity() {

// TODO inject
private val circuitConfig: CircuitConfig = CircuitConfig.Builder()
.addPresenterFactory { screen, navigator, _ ->
when (screen) {
is CreateAccountScreen -> CreateAccountPresenter()

else -> null
}
}
.addUiFactory { screen, _ ->
when (screen) {
is CreateAccountScreen -> CreateAccountUi()
else -> null
}
}.build()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
setContent {
NewmTheme(darkTheme = true) {
WelcomeToNewm(::launchHomeActivity)
CircuitDependencies {
WelcomeToNewm(::launchHomeActivity)
}
}
}
}

@Composable
private fun CircuitDependencies(
content: @Composable () -> Unit
) {
CircuitCompositionLocals(circuitConfig) {
CompositionLocalProvider(LocalRetainedStateRegistry provides continuityRetainedStateRegistry()) {
content()
}
}
}
Expand Down Expand Up @@ -60,13 +97,7 @@ fun WelcomeToNewm(
)
}
composable(Screen.Signup.route) {
CreateAccountScreen(
viewModel = signupViewModel,
onUserLoggedIn = onStartHomeActivity,
onNext = {
navController.navigate(Screen.WhatShouldWeCallYou.route)
},
)
CircuitContent(screen = CreateAccountScreen)
}
composable(Screen.WhatShouldWeCallYou.route) {
WhatShouldWeCallYouScreen(
Expand Down
4 changes: 4 additions & 0 deletions android/feature-login/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id(Plugins.androidLibrary)
kotlin(Plugins.android)
id(Plugins.parcelize)
id(Plugins.paparazzi)
}

Expand Down Expand Up @@ -40,6 +41,9 @@ dependencies {
implementation(project(Modules.coreUiUtils))
implementation(project(Modules.coreResources))

implementation(Circuit.foundation)
implementation(Circuit.retained)

implementation(Google.androidxCore)
implementation(Google.composeMaterial)
implementation(Google.composeUi)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.newm.feature.login.screen.createaccount

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.slack.circuit.retained.rememberRetained
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import io.newm.feature.login.screen.TextFieldState
import io.newm.feature.login.screen.createaccount.signupform.SignupFormUiEvent
import io.newm.feature.login.screen.password.ConfirmPasswordState
import io.newm.feature.login.screen.password.PasswordState

class CreateAccountPresenter : Presenter<CreateAccountUiState> {
@Composable
override fun present(): CreateAccountUiState {
val userEmail = rememberRetained { TextFieldState() }
val password = rememberRetained { PasswordState() }
val passwordConfirmation = rememberRetained { ConfirmPasswordState(password) }

return CreateAccountUiState.SignupForm(
emailState = userEmail,
passwordState = password,
passwordConfirmationState = passwordConfirmation,
) { event ->
when (event) {
SignupFormUiEvent.Next -> TODO()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,99 +1,7 @@
package io.newm.feature.login.screen.createaccount

import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.newm.feature.login.screen.email.Email
import io.newm.feature.login.screen.email.EmailState
import io.newm.feature.login.screen.password.Password
import io.newm.feature.login.screen.password.PasswordState
import io.newm.core.resources.R
import io.newm.core.ui.buttons.SecondaryButton
import io.newm.feature.login.screen.PreLoginArtistBackgroundContentTemplate
import com.slack.circuit.runtime.Screen
import kotlinx.parcelize.Parcelize

@Composable
fun CreateAccountScreen(
onUserLoggedIn: () -> Unit,
onNext: () -> Unit,
viewModel: CreateAccountViewModel
) {
val userState by viewModel.state.collectAsState()

CreateAccountScreen(
onUserLoggedIn = onUserLoggedIn,
onNext = onNext,
userState = userState,
setUserEmail = viewModel::setUserEmail,
setUserPassword = viewModel::setUserPassword,
setUserPasswordConfirmation = viewModel::setUserPasswordConfirmation,
requestCode = viewModel::requestCode,
)
}

@Composable
internal fun CreateAccountScreen(
onUserLoggedIn: () -> Unit,
onNext: () -> Unit,
userState: CreateAccountViewModel.SignupUserState,
setUserEmail: (String) -> Unit,
setUserPassword: (String) -> Unit,
setUserPasswordConfirmation: (String) -> Unit,
requestCode: () -> Unit,
) {
PreLoginArtistBackgroundContentTemplate {

LaunchedEffect(
key1 = userState.verificationRequested,
key2 = userState.verificationRequested
) {
if (userState.verificationRequested) {
onNext()
}
if (userState.isUserRegistered) {
onUserLoggedIn()
}
}

val focusRequester = remember { FocusRequester() }

val emailState = remember { EmailState() }
Email(emailState = emailState, onImeAction = { focusRequester.requestFocus() })

val passwordState1 = remember { PasswordState() }
Password(
label = stringResource(id = R.string.password),
passwordState = passwordState1,
onImeAction = {},
modifier = Modifier.focusRequester(focusRequester),
)

val passwordState2 = remember { PasswordState() }
Password(
label = stringResource(id = R.string.password),
passwordState = passwordState2,
onImeAction = {},
modifier = Modifier.focusRequester(focusRequester),
)

Spacer(modifier = Modifier.height(16.dp))

SecondaryButton(text = "Next") {
if (passwordState1.isValid && passwordState2.isValid
&& passwordState1.text == passwordState2.text || true
) {
setUserEmail("[email protected]")
setUserPassword("Password18")
setUserPasswordConfirmation("Password18")
requestCode()
}
}
}
}
@Parcelize
object CreateAccountScreen : Screen
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.newm.feature.login.screen.createaccount

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.slack.circuit.runtime.ui.Ui
import io.newm.feature.login.screen.createaccount.CreateAccountUiState.SignupForm
import io.newm.feature.login.screen.createaccount.signupform.SignUpFormUi

class CreateAccountUi : Ui<CreateAccountUiState> {
@Composable
override fun Content(state: CreateAccountUiState, modifier: Modifier) {
when (state) {
is SignupForm -> {
SignUpFormUi(state = state)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.newm.feature.login.screen.createaccount

import com.slack.circuit.runtime.CircuitUiState
import io.newm.feature.login.screen.TextFieldState
import io.newm.feature.login.screen.createaccount.signupform.SignupFormUiEvent

sealed interface CreateAccountUiState : CircuitUiState {
data class SignupForm(
val passwordConfirmationState: TextFieldState,
val passwordState: TextFieldState,
val emailState: TextFieldState,
val eventSink: (SignupFormUiEvent) -> Unit,
) : CreateAccountUiState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.newm.feature.login.screen.createaccount.signupform

import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.newm.feature.login.screen.email.Email
import io.newm.feature.login.screen.password.Password
import io.newm.core.resources.R
import io.newm.core.ui.buttons.SecondaryButton
import io.newm.feature.login.screen.PreLoginArtistBackgroundContentTemplate
import io.newm.feature.login.screen.createaccount.CreateAccountUiState.SignupForm

@Composable
fun SignUpFormUi(
state: SignupForm,
) {
val onEvent = state.eventSink
val focusRequester = remember { FocusRequester() }

PreLoginArtistBackgroundContentTemplate {
Email(
emailState = state.emailState,
onImeAction = { focusRequester.requestFocus() }
)

Password(
label = stringResource(id = R.string.password),
passwordState = state.passwordState,
onImeAction = {},
modifier = Modifier.focusRequester(focusRequester),
)

Password(
label = stringResource(id = R.string.password),
passwordState = state.passwordConfirmationState,
onImeAction = {},
modifier = Modifier.focusRequester(focusRequester),
)

Spacer(modifier = Modifier.height(16.dp))

SecondaryButton(text = "Next") {
onEvent(SignupFormUiEvent.Next)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.newm.feature.login.screen.createaccount.signupform

import com.slack.circuit.runtime.CircuitUiEvent

sealed interface SignupFormUiEvent : CircuitUiEvent {
object Next : SignupFormUiEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,26 @@ import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import io.newm.core.test.utils.SnapshotTest
import io.newm.core.test.utils.SnapshotTestConfiguration
import io.newm.feature.login.screen.createaccount.CreateAccountScreen
import io.newm.feature.login.screen.createaccount.CreateAccountViewModel
import io.newm.feature.login.screen.createaccount.CreateAccountUiState
import io.newm.feature.login.screen.createaccount.signupform.SignUpFormUi
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(TestParameterInjector::class)
class CreateAccountScreenTest(
class CreateAccountUiTest(
@TestParameter private val testConfiguration: SnapshotTestConfiguration,
) : SnapshotTest(testConfiguration) {

@Test
fun default() {
fun `default sign up form`() {
snapshot {
CreateAccountScreen(
userState = CreateAccountViewModel.SignupUserState(),
onNext = {},
onUserLoggedIn = {},
setUserPasswordConfirmation = {},
setUserPassword = {},
requestCode = {},
setUserEmail = {}
SignUpFormUi(
state = CreateAccountUiState.SignupForm(
passwordConfirmationState = TextFieldState(),
passwordState = TextFieldState(),
emailState = TextFieldState(),
eventSink = {},
)
)
}
}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1bd7400

Please sign in to comment.