Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Comment thread
EmilJiang marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.cornellappdev.score.screen

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.cornellappdev.score.R
import com.cornellappdev.score.components.NavigationHeader
import com.cornellappdev.score.model.GenderDivision
import com.cornellappdev.score.model.ProfileFavorite
import com.cornellappdev.score.model.Sport
import com.cornellappdev.score.theme.*

private val favoriteSportComparator = compareBy<ProfileFavorite>(
{ it.sport.displayName },
{ it.gender == GenderDivision.FEMALE }
)

private fun selectedFavoritesFirst(favorites: List<ProfileFavorite>): Comparator<ProfileFavorite> =
compareBy<ProfileFavorite> { it !in favorites }.then(favoriteSportComparator)

// Use the complete icon catalog, independent of sports with games in the current season.
private val favoriteSportOptions = Sport.entries.flatMap { sport ->
// The profile design includes both golf divisions, unlike the schedule catalog.
val divisions = if (sport.gender == GenderDivision.ALL || sport == Sport.GOLF) {
listOf(GenderDivision.MALE, GenderDivision.FEMALE)
} else listOf(sport.gender)
divisions.map { ProfileFavorite(sport, it) }
}.sortedWith(favoriteSportComparator)

@Composable
fun EditFavoriteSportsScreen(
favorites: List<ProfileFavorite>,
onToggleFavorite: (ProfileFavorite) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
var gender by rememberSaveable { mutableStateOf(GenderDivision.ALL) }
val options = (favorites + favoriteSportOptions).distinct()
.filter { gender == GenderDivision.ALL || it.gender == gender }
.sortedWith(selectedFavoritesFirst(favorites))

Column(modifier.fillMaxSize().background(Color.White)) {
NavigationHeader(stringResource(R.string.edit_favorite_sports), onBack)
Row(
Modifier.padding(horizontal = 24.dp, vertical = 32.dp)
.fillMaxWidth().border(1.dp, GrayLight, CircleShape)
.padding(6.dp).selectableGroup()
) {
GenderDivision.entries.forEach { division ->
val selected = gender == division
Box(
Modifier.weight(1f).clip(CircleShape)
.background(if (selected) CrimsonPrimary else Color.Transparent)
.selectable(selected, role = Role.Tab, onClick = { gender = division })
.padding(vertical = 8.dp),
contentAlignment = Alignment.Center
) {
Text(
division.displayName,
style = if (selected) Style.bodySemibold else Style.bodyMedium,
color = if (selected) Color.White else GrayMedium
)
}
}
}
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier.weight(1f),
contentPadding = PaddingValues(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 32.dp),
horizontalArrangement = Arrangement.spacedBy(24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
items(options, key = { "${it.sport.name}:${it.gender.name}" }) { favorite ->
FavoriteSportOption(favorite, favorite in favorites) { onToggleFavorite(favorite) }
}
}
}
}

@Composable
private fun FavoriteSportOption(favorite: ProfileFavorite, selected: Boolean, onClick: () -> Unit) {
val shape = RoundedCornerShape(20.dp)
Column(
Modifier.fillMaxWidth()
.semantics(mergeDescendants = true) {
contentDescription = "${favorite.gender.displayName} ${favorite.sport.displayName}"
}
.toggleable(selected, role = Role.Checkbox, onValueChange = { onClick() }),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(Modifier.fillMaxWidth().aspectRatio(1f), contentAlignment = Alignment.Center) {
Box(
Modifier.fillMaxSize()
.background(if (selected) GreenBackground else GrayStroke, shape)
.border(
if (selected) 3.dp else 1.dp,
if (selected) saturatedGreen else GrayStroke,
shape
),
contentAlignment = Alignment.Center
) {
Icon(
painterResource(favorite.sport.emptyIcon), null, Modifier.size(45.dp),
tint = if (selected) saturatedGreen else GrayPrimary
)
}
if (selected) {
Box(
Modifier.align(Alignment.TopStart).offset(x = (-5).dp, y = (-5).dp)
.size(32.dp).background(CrimsonPrimary, CircleShape),
contentAlignment = Alignment.Center
) {
Icon(
painterResource(R.drawable.ic_round_minus),
null,
Modifier.size(24.dp),
tint = Color.White
)
}
}
}
Spacer(Modifier.height(8.dp))
Text(
favorite.sport.displayName, style = Style.bodyMedium, color = GrayPrimary,
textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(2.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
Icon(
painterResource(if (favorite.gender == GenderDivision.FEMALE) R.drawable.ic_gender_women else R.drawable.ic_gender_men),
null, Modifier.size(18.dp), tint = Color.Unspecified
)
Text(
if (favorite.gender == GenderDivision.FEMALE) "W" else "M",
style = Style.labelsNormal,
color = GrayPrimary
)
}
}
}

@Preview(showBackground = true, widthDp = 393, heightDp = 805)
@Composable
private fun EditFavoriteSportsScreenPreview() {
EditFavoriteSportsScreen(sampleProfileData.favorites, {}, {})
}
41 changes: 36 additions & 5 deletions app/src/main/java/com/cornellappdev/score/screen/ProfileScreen.kt
Comment thread
EmilJiang marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -50,6 +51,7 @@ import androidx.compose.ui.unit.sp
import com.cornellappdev.score.R
import com.cornellappdev.score.components.highlights.ArticleHighlightCard
import com.cornellappdev.score.components.highlights.VideoHighlightCard
import com.cornellappdev.score.model.Sport
import com.cornellappdev.score.model.ProfileData
import com.cornellappdev.score.model.ProfileFavorite
import com.cornellappdev.score.model.GenderDivision
Expand All @@ -72,25 +74,54 @@ fun ProfileScreen(
var selectedPicture by rememberSaveable { mutableStateOf(ProfilePicture.TENNIS) }
var editingPicture by rememberSaveable { mutableStateOf(false) }

BackHandler(enabled = editingPicture) { editingPicture = false }
var editingSports by rememberSaveable { mutableStateOf(false) }
var favorites by rememberSaveable(
data.favorites,
stateSaver = listSaver(
save = { list -> list.map { "${it.sport.name}:${it.gender.name}" } },
restore = { list ->
list.map {
val (sport, gender) = it.split(":")
ProfileFavorite(Sport.valueOf(sport), GenderDivision.valueOf(gender))
}
}
)
) { mutableStateOf(data.favorites) }

BackHandler(enabled = editingPicture || editingSports) {
editingPicture = false
editingSports = false
}
Column(modifier.fillMaxSize().background(Color.White)) {
Box(Modifier.weight(1f)) {
if (editingPicture) {
if (editingSports) {
EditFavoriteSportsScreen(
favorites = favorites,
onToggleFavorite = { favorite ->
favorites =
if (favorite in favorites) favorites - favorite else favorites + favorite
},
onBack = { editingSports = false }
)
} else if (editingPicture) {
EditProfileScreen(
selectedPicture,
{ selectedPicture = it },
{ editingPicture = false })
} else {
ProfileContent(
data = data,
data = data.copy(favorites = favorites),
selectedPicture = selectedPicture,
onEditProfilePicture = { editingPicture = true },
onEditSports = onEditSports,
onEditSports = {
editingSports = true
onEditSports()
},
onShowAllBookmarks = onShowAllBookmarks
)
}
}
ProfileTabBar()
if (!editingSports) ProfileTabBar()
}
}

Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/com/cornellappdev/score/theme/Color.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ val GrayStroke = Color(0xFFEAEAEA)
val GreenAccent = Color(0xFF247211)
val RedAccent = Color(0xFFC41616)
val GreenLight = Color(0xFF88D598)
val GreenBackground = Color(0xFFF6FFF3)
val BlueLight = Color(0XFF5AA8D5)
val CrimsonPrimary = Color(0xFFA5210D)
val PeachPrimary = Color(0xFFE1A69F)
Expand All @@ -25,4 +26,4 @@ val PennBlue = Color(0x66021E59)
val PrincetonOrange = Color(0x66FF6000)

val Wash = Color(0xFFF4F4F4)
val Stroke = Color(0xFFD6D6D6)
val Stroke = Color(0xFFD6D6D6)
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
<string name="profile_schedule">Schedule</string>
<string name="profile_scores">Scores</string>
<string name="profile_highlights">Highlights</string>
<string name="edit_favorite_sports">Edit Favorite Sports</string>
</resources>
Loading