Skip to content

Comments

Implement Filtering UI #120

Open
RyanCheung555 wants to merge 28 commits intomainfrom
ryan/filtering
Open

Implement Filtering UI #120
RyanCheung555 wants to merge 28 commits intomainfrom
ryan/filtering

Conversation

@RyanCheung555
Copy link
Contributor

Overview

  • Added the UI for a filter button and filtering actions

Changes Made

  • Added a clickable filter button that pulls up an interactive bottom sheet to select filters
  • When filters are selected, their respective labels appear next to the filter button for clarity

Test Coverage

  • Tested on Medium Phone emulator
  • Run on ecosystem build
  • Simple interaction tests with filter button and verifying appearance

Next Steps (delete if not applicable)

  • Add filter functionality

Screenshots (delete if not applicable)

The last button press was 'cancel' (e.g. do nothing)

Filter UI Screen Recording
filter_ui_demo.webm

RyanCheung555 and others added 23 commits February 7, 2026 02:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements the UI components for a filtering feature in the favorites section. The PR adds interactive filter buttons and bottom sheets that allow users to select location type filters (Gyms, Eateries, Libraries, Printers, Other), with selected filters displayed as removable tags. The actual filtering functionality is intentionally deferred to a future PR, as stated in the "Next Steps" section.

Changes:

  • Added UI components for filter button, filter row with removable tags, and modal bottom sheet for filter selection
  • Introduced filter state management in HomeViewModel to track selected and applied filters
  • Created new drawable resources for filter icons representing different location types

Reviewed changes

Copilot reviewed 18 out of 24 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
app/src/main/res/drawable/*.xml New vector drawable icons for different location type filters (printer, other, library, gym, eatery, and main filter icon)
app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FavoritesFilterSheetState.kt New enum class defining the available filter types with their icons and labels
app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt Added trailing comma to last enum entry for consistency
app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt Added state management for filter sheet visibility and selected/applied filters
app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt Added MutedTransitBlue color for active filter background
app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt Integrated filter sheet state into the home screen
app/src/main/java/com/cornellappdev/transit/ui/components/home/FilterRow.kt New component displaying the filter button and selected filter tags in a flow row
app/src/main/java/com/cornellappdev/transit/ui/components/home/FilterButton.kt New button component that triggers the filter bottom sheet
app/src/main/java/com/cornellappdev/transit/ui/components/home/FavoritesFilterSheetItem.kt New component for individual filter options in the bottom sheet grid
app/src/main/java/com/cornellappdev/transit/ui/components/home/FavoritesFilterBottomSheet.kt Modal bottom sheet component displaying filter options with cancel/apply actions
app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt Integrated filter UI into the favorites view with conditional rendering
app/src/main/java/com/cornellappdev/transit/ui/components/home/RoundedImagePlaceCard.kt Removed horizontal and vertical padding to allow for spacing in parent component
app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt Added vertical content padding parameter
.DS_Store, app/.DS_Store, app/src/.DS_Store, app/src/main/.DS_Store, app/src/main/res/.DS_Store, app/src/main/res/drawable/.DS_Store macOS system files that should not be committed

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

onRemoveFilter = {}
)


Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two blank lines at the end of this preview function. This is inconsistent with the codebase style, where preview functions typically have only one blank line after the closing brace (see line 98 or other preview functions in the codebase).

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +98

}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line after the closing brace. This is inconsistent with the codebase style where preview functions typically have only one blank line after the closing brace (see other preview functions in the codebase).

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +77
color = if(isActive) TransitBlue else MetadataGray,
shape = RoundedCornerShape(8.dp)
)
.background(
color = if(isActive) MutedTransitBlue
else Color.Transparent,
shape = RoundedCornerShape(8.dp)
)
.clickable(onClick = itemOnClick)
.padding(vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painterResource(id = iconId),
contentDescription = label,
modifier = Modifier
.size(34.dp),
tint = if(isActive) TransitBlue else Color.Unspecified
)

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

Text(
label,
color = if(isActive) TransitBlue else MetadataGray,
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after 'if' keyword. The codebase consistently uses 'if (' with a space (see examples in BottomSheetFilterItem.kt:41, FilterRow.kt:48, EcosystemBottomSheetContent.kt:116). Please change 'if(isActive)' to 'if (isActive)' for consistency with the codebase style.

Copilot uses AI. Check for mistakes.

@Preview(showBackground = true)
@Composable
private fun InactiveFavoritesFilterSheetItemInactive() {
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preview function name 'InactiveFavoritesFilterSheetItemInactive' is inconsistent with the preview naming convention used elsewhere in the codebase. Based on other preview functions like 'PreviewBottomSheetFilterItem' and 'InactivePreviewBottomSheetFilterItem' (from BottomSheetFilterItem.kt), the name should be 'InactivePreviewFavoritesFilterSheetItem' or 'PreviewFavoritesFilterSheetItemInactive' to maintain consistency.

Suggested change
private fun InactiveFavoritesFilterSheetItemInactive() {
private fun PreviewFavoritesFilterSheetItemInactive() {

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +109

}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line after the closing brace. This is inconsistent with the codebase style where preview functions typically have only one blank line after the closing brace (see other preview functions in the codebase).

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +29

) {
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line in function signature. There's an unnecessary blank line between the parameter list and the closing brace. Remove line 28 for cleaner code formatting.

Suggested change
) {
) {

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^

@AndrewCheung360
Copy link
Member

Remove the .DS_Store files

showFilterSheet: Boolean,
onFilterSheetShow: () -> Unit,
onAddFavoritesClick: () -> Unit,
homeViewModel: HomeViewModel = hiltViewModel(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be passing view models to composables, they should ideally only be used on the screen composable. You should just add whatever fields or functions you need from the vm to this composable as parameters

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are too many parameters, you can consider grouping some together into a data class

)
}

val selectedFilters by homeViewModel.selectedFavoritesFilters.collectAsStateWithLifecycle()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above comment, but this should be passed in instead of using the home vm here

if (showFilterSheet) {
ModalBottomSheet(
onDismissRequest = {
homeViewModel.cancelFavoritesFilters()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above for all functions you are using from home view model

when (currentFilter) {
FilterState.FAVORITES -> {
favoriteList(favorites, navigateToPlace, onAddFavoritesClick)
val appliedFilters by homeViewModel.appliedFavoritesFilters.collectAsStateWithLifecycle()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

)
}
}
val isFilterBarHidden = currentFilter == FilterState.FAVORITES && appliedFilters.isEmpty()
Copy link
Member

@AndrewCheung360 AndrewCheung360 Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: might be worth abstracting to vm or using some sort of derived value from the data class

import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be using material3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check other imports below for same reason

.padding(horizontal = 24.dp, vertical = 24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: If you guys set up a typography system based on figma design system, might be easier to just use the typography style instead of manually including all the font styling all the time

text: String,
modifier : Modifier = Modifier
){
val isCancel = text == "Cancel"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a better way than this? Maybe just have an optional parameter?

}

@Composable
private fun FooterButton(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These button components can usually be boiled down to having one central reusable button component for the app that takes in various parameters when needed to customize it


}

@Preview(showBackground = true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you can use preview parameters instead of making 2 separate previews

modifier: Modifier = Modifier
) {
FlowRow(
modifier = modifier,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add animateContentSize for more smooth animation when adding or removing filters

containerColor = Color.Transparent,
contentColor = SecondaryText
),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think vertical 0.dp does anything but test it out to be safe

FilterLabel(
text = filter.label,
onRemove = { onRemoveFilter(filter) },
modifier = Modifier.align(Alignment.CenterVertically)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This modifier might not be necessary. Pretty sure there is a way to do center vertically in the vertical arrangement + spaced by at the same time

) {
FilterButton(
onFilterClick = onFilterClick,
modifier = Modifier.align(Alignment.CenterVertically)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if necessary, see below comment

var searchActive by remember { mutableStateOf(false) }

// Favorite filter bottom sheet state
val showFilterSheet by homeViewModel.showFilterSheet.collectAsState()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be using collectAsStateWithLifecycle ideally

}

fun removeAppliedFilter(filter: FavoritesFilterSheetState) {
_selectedFavoritesFilters.value = _selectedFavoritesFilters.value - filter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you can use -=

@@ -90,6 +90,13 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: could be worth separating filter logic into separate vm or file for better separation and organization

_showAddFavoritesSheet.value = show
}

val favoritesFilterList = listOf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For constants like these, we can create some sort of specific constants file somewhere else instead of using home vm to store it

*/
val defaultIthaca = LatLng(42.44, -76.50)

val filterList = listOf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as below comment about constants in separate file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants