-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compare menus #148
Compare menus #148
Changes from 16 commits
134051d
93328c7
3c02335
7fc7e16
278a168
b954171
15f896a
8442cea
2f187b6
387637f
2155809
f8500bb
4a8e2d6
d99096b
6541231
c308810
90379e0
201ace2
2d4740e
a91b9bd
878d5b6
d5106bb
4f2ad53
9acb865
f67c277
bafe771
ce85db8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
package com.cornellappdev.android.eatery.ui.components.comparemenus | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.border | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxHeight | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.foundation.lazy.items | ||
import androidx.compose.foundation.shape.CircleShape | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material.Button | ||
import androidx.compose.material.ButtonDefaults | ||
import androidx.compose.material.Icon | ||
import androidx.compose.material.IconButton | ||
import androidx.compose.material.Text | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.filled.Check | ||
import androidx.compose.material.icons.filled.Close | ||
import androidx.compose.material.icons.outlined.Schedule | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberCoroutineScope | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.unit.dp | ||
import androidx.hilt.navigation.compose.hiltViewModel | ||
import androidx.lifecycle.ViewModel | ||
import com.cornellappdev.android.eatery.data.models.Eatery | ||
import com.cornellappdev.android.eatery.ui.components.general.CompareFilterRow | ||
import com.cornellappdev.android.eatery.ui.components.general.Filter | ||
import com.cornellappdev.android.eatery.ui.components.general.FilterRow | ||
import com.cornellappdev.android.eatery.ui.components.home.MainLoadingItem | ||
import com.cornellappdev.android.eatery.ui.theme.EateryBlue | ||
import com.cornellappdev.android.eatery.ui.theme.EateryBlueTypography | ||
import com.cornellappdev.android.eatery.ui.theme.GrayFive | ||
import com.cornellappdev.android.eatery.ui.theme.GrayTwo | ||
import com.cornellappdev.android.eatery.ui.theme.GrayZero | ||
import com.cornellappdev.android.eatery.ui.viewmodels.CompareMenusBotViewModel | ||
import com.cornellappdev.android.eatery.ui.viewmodels.CompareMenusViewModel | ||
import com.cornellappdev.android.eatery.ui.viewmodels.HomeViewModel | ||
import com.cornellappdev.android.eatery.ui.viewmodels.state.CompareMenusUIState | ||
import com.cornellappdev.android.eatery.ui.viewmodels.state.EateryApiResponse | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.launch | ||
|
||
@Composable | ||
fun CompareMenusBotSheet( | ||
onDismiss: () -> Unit, | ||
onCompareMenusClick: (selectedEateries : List<Eatery>) -> Unit, | ||
compareMenusBotViewModel: CompareMenusBotViewModel = hiltViewModel() | ||
zachseidner1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) { | ||
val compareMenusUIState by compareMenusBotViewModel.compareMenusUiState.collectAsState() | ||
val filters = compareMenusUIState.filters | ||
val selectedEateries = compareMenusUIState.selected | ||
val eateries = compareMenusUIState.eateries | ||
|
||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(top = 14.dp) | ||
.padding(16.dp), | ||
horizontalAlignment = Alignment.Start | ||
) { | ||
Row( | ||
modifier = Modifier.fillMaxWidth(), | ||
horizontalArrangement = Arrangement.SpaceBetween | ||
) { | ||
Text( | ||
text = "Compare Menus", style = EateryBlueTypography.h4, | ||
color = Color.Black | ||
) | ||
IconButton( | ||
onClick = { | ||
compareMenusBotViewModel.resetSelected() | ||
onDismiss() | ||
}, | ||
modifier = Modifier | ||
.size(40.dp) | ||
.background(color = GrayZero, shape = CircleShape) | ||
) { | ||
Icon( | ||
Icons.Default.Close, | ||
contentDescription = Icons.Default.Close.name, | ||
tint = Color.Black | ||
) | ||
} | ||
} | ||
|
||
CompareFilterRow( | ||
currentFiltersSelected = filters, | ||
onPaymentMethodsClicked = {}, | ||
onFilterClicked = { filter -> | ||
if (filters.contains(filter)) { | ||
compareMenusBotViewModel.removeFilterCM(filter) | ||
} else { | ||
compareMenusBotViewModel.addFilterCM(filter) | ||
} | ||
}) | ||
|
||
Spacer(modifier = Modifier.height(12.dp)) | ||
|
||
Box( | ||
modifier = Modifier | ||
.background(Color.White) | ||
.fillMaxHeight(0.4f) | ||
.fillMaxWidth() | ||
) { | ||
LazyColumn { | ||
items(eateries) { eatery -> | ||
Row( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you abstract this component into a private composable like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ya good point! There's a lot of indentation here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's generally good to take out the header, content, and bottom portions into separate composables for readability. Veronica discovered this feature where you just highlight a bunch of your function and hit |
||
modifier = Modifier.fillMaxWidth().clickable { | ||
if (selectedEateries.contains(eatery)) { | ||
compareMenusBotViewModel.removeSelected(eatery) | ||
} else { | ||
compareMenusBotViewModel.addSelected(eatery) | ||
} | ||
}, | ||
verticalAlignment = Alignment.CenterVertically, | ||
|
||
) { | ||
IconButton( | ||
onClick = { | ||
if (selectedEateries.contains(eatery)) { | ||
compareMenusBotViewModel.removeSelected(eatery) | ||
} else { | ||
compareMenusBotViewModel.addSelected(eatery) | ||
} | ||
}, | ||
modifier = Modifier.align(Alignment.CenterVertically) | ||
) { | ||
if (selectedEateries.contains(eatery)) { | ||
Box( | ||
contentAlignment = Alignment.Center, | ||
modifier = Modifier | ||
.size(26.dp) | ||
.background(Color.Black, CircleShape) | ||
) { | ||
Icon( | ||
imageVector = Icons.Default.Check, | ||
contentDescription = "Selected", | ||
tint = Color.White, | ||
modifier = Modifier.size(24.dp) | ||
) | ||
} | ||
} else { | ||
Box( | ||
contentAlignment = Alignment.Center, | ||
modifier = Modifier | ||
.size(26.dp) | ||
.background(Color.White, CircleShape) | ||
.border(2.dp, Color.Black, CircleShape) | ||
) { | ||
} | ||
} | ||
} | ||
eatery.name?.let { | ||
Text( | ||
text = it, style = EateryBlueTypography.body1, | ||
color = Color.Black | ||
) | ||
} | ||
} | ||
|
||
|
||
} | ||
} | ||
} | ||
|
||
Spacer(modifier = Modifier.height(12.dp)) | ||
val coroutineScope = rememberCoroutineScope() | ||
Button( | ||
onClick = { | ||
if (selectedEateries.size < 2){ | ||
|
||
} | ||
else{ | ||
coroutineScope.launch { | ||
delay(100) | ||
onCompareMenusClick(compareMenusUIState.selected) | ||
} | ||
} | ||
}, | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.height(48.dp) | ||
.align(Alignment.CenterHorizontally), | ||
shape = RoundedCornerShape(100), | ||
colors = ButtonDefaults.buttonColors( | ||
backgroundColor = if (selectedEateries.size < 2) GrayTwo else EateryBlue, | ||
contentColor = if (selectedEateries.size < 2) GrayZero else Color.White | ||
) | ||
) { | ||
Text( | ||
text = if (selectedEateries.size < 2) "Select at least ${2 - selectedEateries.size} more" | ||
else "Compare ${selectedEateries.size} now", | ||
style = EateryBlueTypography.h5, | ||
color = Color.White | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.cornellappdev.android.eatery.ui.components.comparemenus | ||
|
||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.FloatingActionButton | ||
import androidx.compose.material.Icon | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.unit.dp | ||
import com.cornellappdev.android.eatery.R | ||
import com.cornellappdev.android.eatery.ui.theme.EateryBlue | ||
|
||
@Composable | ||
fun CompareMenusFAB( | ||
modifier: Modifier = Modifier, | ||
onClick: () -> Unit | ||
) { | ||
FloatingActionButton( | ||
onClick = { onClick() }, | ||
modifier = modifier | ||
.padding(16.dp), | ||
backgroundColor = EateryBlue | ||
) { | ||
Icon( | ||
painter = painterResource(id = R.drawable.ic_compare_menus), | ||
"Floating action button.", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: could you label this with "Compare menus" for accessibility |
||
tint = Color.White | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,8 +61,9 @@ fun EateryDetailsStickyHeader( | |
filterText: String, | ||
fullMenuList: MutableList<String>, | ||
listState: LazyListState, | ||
eateryDetailViewModel: EateryDetailViewModel = hiltViewModel(), | ||
onItemClick: (Int) -> Unit | ||
startIndex : Int, | ||
onItemClick: (Int) -> Unit, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, love the removal of viewmodel. |
||
|
||
) { | ||
val rowState = rememberLazyListState() | ||
val rowCoroutine = rememberCoroutineScope() | ||
|
@@ -71,7 +72,8 @@ fun EateryDetailsStickyHeader( | |
category, | ||
listState, | ||
nextEvent, | ||
fullMenuList | ||
fullMenuList, | ||
startIndex | ||
) | ||
} | ||
val selectedIndex: Int = | ||
|
@@ -90,52 +92,7 @@ fun EateryDetailsStickyHeader( | |
modifier = Modifier | ||
.fillMaxWidth() | ||
.background(Color.White) | ||
.padding(top = 48.dp, bottom = 12.dp) | ||
) { | ||
Box( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.height(40.dp) | ||
) { | ||
Text( | ||
modifier = Modifier | ||
.height(26.dp) | ||
.widthIn(0.dp, 280.dp) | ||
.align(Alignment.Center), | ||
textAlign = TextAlign.Center, | ||
text = eatery.name ?: "Loading...", | ||
maxLines = 1, | ||
overflow = TextOverflow.Ellipsis, | ||
color = Color.Black, | ||
style = TextStyle( | ||
fontWeight = FontWeight.SemiBold, | ||
fontSize = 20.sp | ||
) | ||
) | ||
|
||
Button( | ||
onClick = { eateryDetailViewModel.toggleFavorite() }, | ||
modifier = Modifier | ||
.align(Alignment.CenterEnd) | ||
.padding(end = 16.dp) | ||
.size(40.dp), | ||
contentPadding = PaddingValues(6.dp), | ||
shape = CircleShape, | ||
colors = ButtonDefaults.buttonColors( | ||
backgroundColor = Color.White, | ||
), | ||
elevation = ButtonDefaults.elevation( | ||
defaultElevation = 0.dp, | ||
pressedElevation = 0.dp | ||
) | ||
) { | ||
Icon( | ||
imageVector = if (eateryDetailViewModel.isFavorite) Icons.Filled.Star else Icons.Outlined.StarOutline, | ||
tint = if (eateryDetailViewModel.isFavorite) Yellow else GrayFive, | ||
contentDescription = null | ||
) | ||
} | ||
} | ||
|
||
Spacer(modifier = Modifier.height(6.dp)) | ||
|
||
|
@@ -167,7 +124,8 @@ fun EateryDetailsStickyHeader( | |
category, | ||
listState, | ||
nextEvent, | ||
fullMenuList | ||
fullMenuList, | ||
startIndex | ||
) | ||
CategoryItem( | ||
category.category ?: "Category", | ||
|
@@ -192,6 +150,7 @@ fun EateryDetailsStickyHeader( | |
} | ||
} | ||
} | ||
Spacer(modifier = Modifier.height(6.dp)) | ||
} | ||
|
||
Divider( | ||
|
@@ -245,12 +204,13 @@ fun highlightCategory( | |
category: MenuCategory, | ||
listState: LazyListState, | ||
nextEvent: Event?, | ||
fullMenuList: MutableList<String> | ||
fullMenuList: MutableList<String>, | ||
startIndex: Int | ||
): Boolean { | ||
val firstVisibleState = remember { derivedStateOf { listState.firstVisibleItemIndex } } | ||
// Note: - 5 here assumes that there are 5 UI elements above the menu, which is true currently. | ||
// If that changes, this must be tweaked. | ||
val firstMenuItemIndex = firstVisibleState.value - 5 | ||
val firstMenuItemIndex = firstVisibleState.value - startIndex | ||
|
||
if (firstMenuItemIndex >= 0 && firstMenuItemIndex < fullMenuList.size) { | ||
val item = fullMenuList[firstMenuItemIndex] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you map this to a list of IDs anyway, this should be
(selectedEateryIds: List<Int>) -> Unit
.