Skip to content

Commit 4ea3909

Browse files
Merge branch 'main' into andrew/capacity-reminders-networking
2 parents f5fe89c + 4180586 commit 4ea3909

26 files changed

Lines changed: 1749 additions & 69 deletions

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,4 @@ lint/tmp/
8787

8888
secrets.properties
8989
google-services.json
90-
91-
.DS_Store
90+
.DS_Store

app/src/main/java/com/cornellappdev/uplift/ui/MainNavigationWrapper.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ import com.cornellappdev.uplift.ui.screens.gyms.HomeScreen
3434
import com.cornellappdev.uplift.ui.screens.onboarding.ProfileCreationScreen
3535
import com.cornellappdev.uplift.ui.screens.onboarding.SignInPromptScreen
3636
import com.cornellappdev.uplift.ui.screens.reminders.CapacityReminderScreen
37+
import com.cornellappdev.uplift.ui.screens.profile.ProfileScreen
38+
import com.cornellappdev.uplift.ui.screens.profile.SettingsScreen
39+
import com.cornellappdev.uplift.ui.screens.reminders.MainReminderScreen
3740
import com.cornellappdev.uplift.ui.screens.report.ReportIssueScreen
3841
import com.cornellappdev.uplift.ui.screens.report.ReportSubmittedScreen
3942
import com.cornellappdev.uplift.ui.viewmodels.classes.ClassDetailViewModel
@@ -79,6 +82,7 @@ fun MainNavigationWrapper(
7982
val items = listOf(
8083
BottomNavScreens.Home,
8184
BottomNavScreens.Classes,
85+
BottomNavScreens.Profile
8286
// TODO: Add new items when activities and profile are implemented.
8387
)
8488

@@ -214,6 +218,14 @@ fun MainNavigationWrapper(
214218
}
215219
composable<UpliftRootRoute.CapacityReminders>{
216220
CapacityReminderScreen()
221+
composable<UpliftRootRoute.Profile> {
222+
ProfileScreen()
223+
}
224+
composable<UpliftRootRoute.Reminders> {
225+
MainReminderScreen()
226+
}
227+
composable<UpliftRootRoute.Settings> {
228+
SettingsScreen()
217229
}
218230
composable<UpliftRootRoute.Sports> {}
219231
composable<UpliftRootRoute.Favorites> {}
@@ -253,9 +265,15 @@ sealed class UpliftRootRoute {
253265
@Serializable
254266
data object CapacityReminders : UpliftRootRoute()
255267

268+
@Serializable
269+
data object Reminders : UpliftRootRoute()
270+
256271
@Serializable
257272
data object ReportIssue : UpliftRootRoute()
258273

259274
@Serializable
260275
data object ReportSuccess : UpliftRootRoute()
276+
277+
@Serializable
278+
data object Settings : UpliftRootRoute()
261279
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.cornellappdev.uplift.ui.components.general
2+
3+
import androidx.compose.material.TabRowDefaults.Divider
4+
import androidx.compose.material3.Tab
5+
import androidx.compose.material3.TabRow
6+
import androidx.compose.material3.TabRowDefaults.SecondaryIndicator
7+
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
8+
import androidx.compose.material3.Text
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.getValue
11+
import androidx.compose.runtime.mutableStateOf
12+
import androidx.compose.runtime.remember
13+
import androidx.compose.runtime.setValue
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.graphics.Color
16+
import androidx.compose.ui.text.font.FontWeight
17+
import androidx.compose.ui.tooling.preview.Preview
18+
import androidx.compose.ui.unit.dp
19+
import androidx.compose.ui.unit.sp
20+
import com.cornellappdev.uplift.util.GRAY01
21+
import com.cornellappdev.uplift.util.GRAY04
22+
import com.cornellappdev.uplift.util.PRIMARY_BLACK
23+
import com.cornellappdev.uplift.util.PRIMARY_YELLOW
24+
import com.cornellappdev.uplift.util.montserratFamily
25+
26+
@Composable
27+
fun UpliftTabRow(tabIndex: Int, tabs: List<String>, onTabChange: (Int) -> Unit = {}) {
28+
TabRow(
29+
selectedTabIndex = tabIndex,
30+
containerColor = Color.White,
31+
indicator = { tabPositions ->
32+
SecondaryIndicator(
33+
Modifier.tabIndicatorOffset(tabPositions[tabIndex]),
34+
height = 2.dp,
35+
color = PRIMARY_YELLOW
36+
)
37+
},
38+
divider = {
39+
Divider(
40+
color = GRAY01,
41+
thickness = 1.dp,
42+
)
43+
}
44+
) {
45+
tabs.forEachIndexed { index, title ->
46+
Tab(
47+
text = {
48+
Text(
49+
text = title,
50+
color = if (tabIndex == index) PRIMARY_BLACK else GRAY04,
51+
fontFamily = montserratFamily,
52+
fontSize = 12.sp,
53+
fontWeight = FontWeight.Bold
54+
)
55+
},
56+
selected = tabIndex == index,
57+
onClick = { onTabChange(index) },
58+
selectedContentColor = GRAY01,
59+
)
60+
}
61+
}
62+
}
63+
64+
@Preview
65+
@Composable
66+
private fun UpliftTabRowPreview() {
67+
var tabIndex by remember { mutableStateOf(0) }
68+
UpliftTabRow(
69+
tabIndex = tabIndex,
70+
tabs = listOf("Gym", "Classes"),
71+
onTabChange = {
72+
tabIndex = it
73+
}
74+
)
75+
}

app/src/main/java/com/cornellappdev/uplift/ui/components/onboarding/PhotoPicker.kt

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,45 +24,80 @@ import androidx.compose.runtime.setValue
2424
import androidx.compose.ui.Alignment
2525
import androidx.compose.ui.Modifier
2626
import androidx.compose.ui.draw.clip
27+
import androidx.compose.ui.geometry.Offset
2728
import androidx.compose.ui.graphics.Color
2829
import androidx.compose.ui.layout.ContentScale
2930
import androidx.compose.ui.res.painterResource
3031
import androidx.compose.ui.tooling.preview.Preview
32+
import androidx.compose.ui.unit.Dp
3133
import androidx.compose.ui.unit.dp
3234
import coil.compose.rememberAsyncImagePainter
3335
import com.cornellappdev.uplift.util.GRAY01
3436
import com.cornellappdev.uplift.R
3537

36-
/**
37-
* Component that displays a camera icon for the user to select a photo for their profile
38-
* @param onPhotoSelected: function to call when the user selects a photo. Takes in uri parameter
39-
*/
38+
data class PhotoPickerSizes(
39+
val outerCircleSize: Dp,
40+
val innerCircleSize: Dp,
41+
val cameraIconOffset: Offset
42+
43+
)
44+
45+
enum class ScreenType {
46+
ONBOARDING,
47+
PROFILE
48+
}
49+
50+
fun getPhotoPickerSizes(screenType: ScreenType): PhotoPickerSizes {
51+
return when (screenType) {
52+
ScreenType.ONBOARDING -> PhotoPickerSizes(
53+
outerCircleSize = 160.dp,
54+
innerCircleSize = 144.dp,
55+
cameraIconOffset = Offset(
56+
x = 60.dp.value,
57+
y = 60.dp.value
58+
)
59+
)
60+
61+
ScreenType.PROFILE -> PhotoPickerSizes(
62+
outerCircleSize = 98.dp,
63+
innerCircleSize = 88.dp,
64+
cameraIconOffset = Offset(
65+
x = 36.dp.value,
66+
y = 36.dp.value
67+
)
68+
)
69+
}
70+
}
71+
4072
@Composable
41-
fun PhotoPicker(imageUri: Uri? = null, onPhotoSelected: (Uri) -> Unit) {
73+
fun PhotoPicker(
74+
imageUri: Uri? = null,
75+
onPhotoSelected: (Uri) -> Unit,
76+
screenType: ScreenType = ScreenType.ONBOARDING
77+
) {
78+
val sizes = getPhotoPickerSizes(screenType)
79+
4280
// State to store the selected image URI
4381
var selectedImageUri by rememberSaveable { mutableStateOf(imageUri) }
4482

4583
// Registers a photo picker activity launcher in single-select mode.
4684
val pickMedia =
4785
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
48-
// Callback is invoked after the user selects a media item or closes the
49-
// photo picker.
5086
if (uri != null) {
5187
selectedImageUri = uri
5288
onPhotoSelected(uri)
5389
}
5490
}
5591

5692
Box(
57-
modifier = Modifier.size(160.dp), contentAlignment = Alignment.Center
93+
modifier = Modifier.size(sizes.outerCircleSize), contentAlignment = Alignment.Center
5894
) {
5995
ElevatedCard(
6096
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
6197
colors = CardDefaults.cardColors(containerColor = Color.White),
6298
shape = CircleShape,
63-
modifier = Modifier.size(160.dp),
99+
modifier = Modifier.size(sizes.outerCircleSize),
64100
onClick = {
65-
// Launch the photo picker and let the user choose only images.
66101
pickMedia.launch(
67102
PickVisualMediaRequest(
68103
ActivityResultContracts.PickVisualMedia.ImageOnly
@@ -74,20 +109,19 @@ fun PhotoPicker(imageUri: Uri? = null, onPhotoSelected: (Uri) -> Unit) {
74109
modifier = Modifier.fillMaxSize(),
75110
contentAlignment = Alignment.Center
76111
) {
77-
// Check if a photo is selected, and display it, otherwise show the camera icon
78112
if (selectedImageUri != null) {
79113
Image(
80114
painter = rememberAsyncImagePainter(selectedImageUri),
81115
contentDescription = null,
82116
contentScale = ContentScale.Crop,
83117
modifier = Modifier
84-
.size(144.dp)
118+
.size(sizes.innerCircleSize)
85119
.clip(CircleShape)
86120
)
87121
} else {
88122
Box(
89123
modifier = Modifier
90-
.size(144.dp)
124+
.size(sizes.innerCircleSize)
91125
.clip(CircleShape)
92126
.background(GRAY01),
93127
) {
@@ -102,12 +136,15 @@ fun PhotoPicker(imageUri: Uri? = null, onPhotoSelected: (Uri) -> Unit) {
102136
}
103137
}
104138
}
105-
// If a photo is selected, display the camera icon on top of the photo
106-
if (selectedImageUri != null) {
139+
if (selectedImageUri != null || screenType == ScreenType.PROFILE) {
107140
Image(
108141
painter = painterResource(id = R.drawable.ic_camera_circle),
109142
contentDescription = null,
110-
modifier = Modifier.offset(60.dp, 64.dp)
143+
modifier = Modifier
144+
.offset(
145+
sizes.cameraIconOffset.x.dp,
146+
sizes.cameraIconOffset.y.dp
147+
)
111148
)
112149
}
113150
}
@@ -121,6 +158,9 @@ private fun PhotoPickerPreview() {
121158
horizontalAlignment = Alignment.CenterHorizontally,
122159
verticalArrangement = Arrangement.Center
123160
) {
124-
PhotoPicker {}
161+
PhotoPicker(
162+
imageUri = null,
163+
onPhotoSelected = { }
164+
)
125165
}
126166
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.cornellappdev.uplift.ui.components.profile
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.height
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.tooling.preview.Preview
12+
import androidx.compose.ui.unit.dp
13+
14+
@Composable
15+
fun GoalsSection(
16+
workoutsCompleted: Int,
17+
workoutGoal: Int,
18+
daysOfMonth: List<Int>,
19+
completedDays: List<Boolean>,
20+
onClick: () -> Unit
21+
) {
22+
Column(
23+
modifier = Modifier.fillMaxWidth(),
24+
horizontalAlignment = Alignment.CenterHorizontally,
25+
) {
26+
SectionTitleText("My Goals", onClick)
27+
Spacer(modifier = Modifier.height(12.dp))
28+
WorkoutProgressArc(
29+
workoutsCompleted,
30+
workoutGoal
31+
)
32+
Spacer(modifier = Modifier.height(16.dp))
33+
WeeklyProgressTracker(
34+
daysOfMonth,
35+
completedDays
36+
)
37+
}
38+
}
39+
40+
@Preview(showBackground = true)
41+
@Composable
42+
private fun GoalsSectionPreview() {
43+
val daysOfMonth = (25..31).toList()
44+
val completedDays = listOf(false, false, true, false, true, false, false)
45+
GoalsSection(
46+
workoutsCompleted = 3,
47+
workoutGoal = 5,
48+
daysOfMonth = daysOfMonth,
49+
completedDays = completedDays,
50+
onClick = {}
51+
)
52+
}

0 commit comments

Comments
 (0)