From c6de9459a374f24c0dd2aece7ceaa555e26b5169 Mon Sep 17 00:00:00 2001 From: Preston Williams Date: Mon, 23 Feb 2026 14:58:14 -0500 Subject: [PATCH 1/3] started goals page for onboarding --- .DS_Store | Bin 6148 -> 6148 bytes .../screens/onboarding/GoalsPromptScreen.kt | 96 ++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt diff --git a/.DS_Store b/.DS_Store index da07aa6853bb7b7687f6e861317c0d52c61a4ec7..8d2fcea67f1d759230e564672726f5dc907d3e5e 100644 GIT binary patch delta 126 zcmZoMXfc=|&e%4wP>hv>fq{WzVxfo(6OaJ{AexbZL4YASCn-Na2PDKiu~0(HghzREg2)o)i480OdutH3 diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt new file mode 100644 index 0000000..e6b3270 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt @@ -0,0 +1,96 @@ +package com.cornellappdev.uplift.ui.screens.onboarding + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.cornellappdev.uplift.ui.components.goalsetting.GoalSlider +import com.cornellappdev.uplift.util.GRAY01 +import com.cornellappdev.uplift.util.montserratFamily + +/** + * @param goalValue: value of the goal slider + * @param onGoalValueChange: callback for when the goal slider value is changed + * @return GoalPromptScreen composable + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun GoalPromptScreen( + /* TODO: Replace functions with viewmodel calls */ + goalValue: Float, + onGoalValueChange: (Float) -> Unit +) { + + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = "Set your goals.", + modifier = Modifier.padding(top = 20.dp), + fontSize = 24.sp, + fontFamily = montserratFamily, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Left + ) + }, + modifier = Modifier + .height(120.dp) + .shadow(elevation = 20.dp, ambientColor = GRAY01), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.White, + ) + + ) + }, + modifier = Modifier.fillMaxSize(), + ) { padding -> + Column( + modifier = Modifier + .background(color = Color.White) + .padding( + top = padding.calculateTopPadding(), + ) + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.SpaceBetween + ) { + /* Groups the goal slider and workout reminders together */ + Column { + GoalSlider(value = goalValue, onValueChange = onGoalValueChange) + + } + } + + } +} + + +@Preview(showBackground = true) +@Composable +fun GoalPromptScreenPreview() { + var sliderVal by remember { mutableFloatStateOf(1f) } + GoalPromptScreen(goalValue = sliderVal, onGoalValueChange = { sliderVal = it }) +} From ae1b752029ef3bbbcb488860d640ca07564704da Mon Sep 17 00:00:00 2001 From: Preston Williams Date: Mon, 23 Feb 2026 17:40:26 -0500 Subject: [PATCH 2/3] finished ui --- .../screens/onboarding/GoalsPromptScreen.kt | 72 ++++++++++++------- .../screens/onboarding/SignInPromptScreen.kt | 2 +- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt index e6b3270..f9316ff 100644 --- a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt +++ b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt @@ -2,8 +2,11 @@ package com.cornellappdev.uplift.ui.screens.onboarding import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -18,15 +21,17 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.credentials.Credential import com.cornellappdev.uplift.ui.components.goalsetting.GoalSlider +import com.cornellappdev.uplift.ui.components.onboarding.auth.LogInButton import com.cornellappdev.uplift.util.GRAY01 import com.cornellappdev.uplift.util.montserratFamily @@ -40,57 +45,72 @@ import com.cornellappdev.uplift.util.montserratFamily fun GoalPromptScreen( /* TODO: Replace functions with viewmodel calls */ goalValue: Float, - onGoalValueChange: (Float) -> Unit + onGoalValueChange: (Float) -> Unit, + onSignInWithGoogle: (credential: Credential) -> Unit, + onSkip: () -> Unit ) { - Scaffold( topBar = { - TopAppBar( - title = { + TopAppBar( + title = { + Box( + modifier = Modifier.fillMaxHeight(), + contentAlignment = Alignment.BottomStart + ) { Text( text = "Set your goals.", - modifier = Modifier.padding(top = 20.dp), fontSize = 24.sp, fontFamily = montserratFamily, fontWeight = FontWeight.Bold, - textAlign = TextAlign.Left + modifier = Modifier.padding(bottom = 16.dp) ) - }, - modifier = Modifier - .height(120.dp) - .shadow(elevation = 20.dp, ambientColor = GRAY01), - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.White, - ) - + } + }, + modifier = Modifier + .height(120.dp) + .shadow(elevation = 20.dp, ambientColor = GRAY01), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.White, ) + ) }, modifier = Modifier.fillMaxSize(), ) { padding -> Column( modifier = Modifier - .background(color = Color.White) - .padding( - top = padding.calculateTopPadding(), - ) + .padding(padding) .fillMaxSize() - .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.SpaceBetween + .background(color = Color.White), + horizontalAlignment = Alignment.CenterHorizontally ) { - /* Groups the goal slider and workout reminders together */ - Column { + Column( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { GoalSlider(value = goalValue, onValueChange = onGoalValueChange) + } + // Buttons pinned to the bottom + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + LogInButton { onSignInWithGoogle } + SkipButton { onSkip } } } - } } - @Preview(showBackground = true) @Composable fun GoalPromptScreenPreview() { var sliderVal by remember { mutableFloatStateOf(1f) } - GoalPromptScreen(goalValue = sliderVal, onGoalValueChange = { sliderVal = it }) + GoalPromptScreen(goalValue = sliderVal, onGoalValueChange = { sliderVal = it }, {}, {}) } diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/SignInPromptScreen.kt b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/SignInPromptScreen.kt index 4bf8778..3c7ae7a 100644 --- a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/SignInPromptScreen.kt +++ b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/SignInPromptScreen.kt @@ -112,7 +112,7 @@ private fun SignInPromptScreenContent( } @Composable -private fun SkipButton(onClick: () -> Unit) { +fun SkipButton(onClick: () -> Unit) { TextButton( onClick = onClick ) { From 4739034e29b17216aa23938a100caa4ed92418bc Mon Sep 17 00:00:00 2001 From: Preston Williams Date: Wed, 4 Mar 2026 12:26:17 -0500 Subject: [PATCH 3/3] added screen to onboarding flow --- .DS_Store | Bin 6148 -> 6148 bytes .../uplift/ui/MainNavigationWrapper.kt | 8 +++ .../screens/onboarding/GoalsPromptScreen.kt | 22 ++++--- .../onboarding/GoalsPromptViewModel.kt | 58 ++++++++++++++++++ .../onboarding/ProfileCreationViewModel.kt | 7 ++- 5 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/GoalsPromptViewModel.kt diff --git a/.DS_Store b/.DS_Store index 8d2fcea67f1d759230e564672726f5dc907d3e5e..9042e813efad8845e04a0c1b3c236620ba9a0d86 100644 GIT binary patch delta 127 zcmZoMXfc=|&e%S&P;8=}A|va>0Ba!8qvOfSki<~Rkjaq4kdjiIoRgHFpThtIYz*lP zMGT1yDL{!-WC;$2a)u0`L { ProfileCreationScreen() } + composable { + GoalPromptScreen() + } composable { CapacityReminderScreen() } @@ -329,6 +333,10 @@ sealed class UpliftRootRoute { @Serializable data object Profile : UpliftRootRoute() + @Serializable + data object GoalsPrompt : UpliftRootRoute() + + @Serializable data object CapacityReminders : UpliftRootRoute() diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt index f9316ff..bd73659 100644 --- a/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt +++ b/app/src/main/java/com/cornellappdev/uplift/ui/screens/onboarding/GoalsPromptScreen.kt @@ -30,8 +30,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.credentials.Credential +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.cornellappdev.uplift.ui.components.goalsetting.GoalSlider import com.cornellappdev.uplift.ui.components.onboarding.auth.LogInButton +import com.cornellappdev.uplift.ui.viewmodels.onboarding.GoalsPromptViewModel import com.cornellappdev.uplift.util.GRAY01 import com.cornellappdev.uplift.util.montserratFamily @@ -43,12 +46,11 @@ import com.cornellappdev.uplift.util.montserratFamily @OptIn(ExperimentalMaterial3Api::class) @Composable fun GoalPromptScreen( - /* TODO: Replace functions with viewmodel calls */ - goalValue: Float, - onGoalValueChange: (Float) -> Unit, - onSignInWithGoogle: (credential: Credential) -> Unit, - onSkip: () -> Unit + viewModel: GoalsPromptViewModel = hiltViewModel(), ) { + + val currentGoal by viewModel.goalValue.collectAsStateWithLifecycle() + Scaffold( topBar = { TopAppBar( @@ -90,7 +92,7 @@ fun GoalPromptScreen( .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { - GoalSlider(value = goalValue, onValueChange = onGoalValueChange) + GoalSlider(value = currentGoal, onValueChange = { viewModel.onGoalValueChange(it) }) } // Buttons pinned to the bottom @@ -101,8 +103,8 @@ fun GoalPromptScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { - LogInButton { onSignInWithGoogle } - SkipButton { onSkip } + LogInButton { viewModel.onSignInWithGoogle() } + SkipButton { viewModel.onSkip() } } } } @@ -111,6 +113,6 @@ fun GoalPromptScreen( @Preview(showBackground = true) @Composable fun GoalPromptScreenPreview() { - var sliderVal by remember { mutableFloatStateOf(1f) } - GoalPromptScreen(goalValue = sliderVal, onGoalValueChange = { sliderVal = it }, {}, {}) + var sliderVal by remember { mutableFloatStateOf(0f) } + GoalPromptScreen() } diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/GoalsPromptViewModel.kt b/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/GoalsPromptViewModel.kt new file mode 100644 index 0000000..7286587 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/GoalsPromptViewModel.kt @@ -0,0 +1,58 @@ +package com.cornellappdev.uplift.ui.viewmodels.onboarding + +import android.net.Uri +import android.util.Log +import androidx.lifecycle.viewModelScope +import com.cornellappdev.uplift.data.repositories.UserInfoRepository +import com.cornellappdev.uplift.ui.UpliftRootRoute +import com.cornellappdev.uplift.ui.nav.RootNavigationRepository +import com.cornellappdev.uplift.ui.viewmodels.UpliftViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +data class GoalsPromptUiState( + val name: String = "", + val imageUri: Uri? = null +) + +@HiltViewModel +class GoalsPromptViewModel @Inject constructor( + private val userInfoRepository: UserInfoRepository, + private val rootNavigationRepository: RootNavigationRepository, +) : UpliftViewModel(GoalsPromptUiState()) { + + private val _goalValue = MutableStateFlow(0f) + val goalValue: StateFlow = _goalValue.asStateFlow() + + init { + viewModelScope.launch { + // TODO: Change this later to reflect correct inputs + val user = userInfoRepository.getFirebaseUser() + val name = user?.displayName ?: "" + applyMutation { + copy(name = name) + } + } + } + + fun onGoalValueChange(newValue: Float) { + _goalValue.value = newValue + } + + // TODO: Change skip and google sign in to reflect correct behavior + fun onSkip() { + navigateToHome() + } + + fun onSignInWithGoogle() { + navigateToHome() + } + + fun navigateToHome() { + rootNavigationRepository.navigate(UpliftRootRoute.Home) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/ProfileCreationViewModel.kt b/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/ProfileCreationViewModel.kt index 8304a62..0332736 100644 --- a/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/ProfileCreationViewModel.kt +++ b/app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/ProfileCreationViewModel.kt @@ -38,7 +38,7 @@ class ProfileCreationViewModel @Inject constructor( val email = user?.email ?: "" val netId = email.substring(0, email.indexOf('@')) if (userInfoRepository.createUser(email, name, netId)) { - navigateToHome() + navigateToGoals() } else { //TODO: Add error handling Log.e("Error", "User not created") @@ -53,6 +53,11 @@ class ProfileCreationViewModel @Inject constructor( } } + private fun navigateToGoals() { + rootNavigationRepository.navigate(UpliftRootRoute.GoalsPrompt) + } + + // Possibly get rid of this function private fun navigateToHome() { rootNavigationRepository.navigate(UpliftRootRoute.Home) }