From 3d8f85dd17042a700351aa6bd348a0309a4ed93e Mon Sep 17 00:00:00 2001 From: Ryan Cheung Date: Wed, 15 Apr 2026 18:23:25 -0400 Subject: [PATCH 1/4] feat: initialize hotspot tab in bottom sheet content --- .../home/EcosystemBottomSheetContent.kt | 9 +++++++++ .../ui/components/home/HomeScreenMarkers.kt | 4 ++++ .../transit/ui/screens/HomeScreen.kt | 2 ++ .../transit/ui/viewmodels/FilterState.kt | 1 + .../transit/ui/viewmodels/HomeViewModel.kt | 1 + app/src/main/res/drawable/hotspot_icon.xml | 16 ++++++++++++++++ app/src/main/res/drawable/hotspot_pin.xml | 12 ++++++++++++ 7 files changed, 45 insertions(+) create mode 100644 app/src/main/res/drawable/hotspot_icon.xml create mode 100644 app/src/main/res/drawable/hotspot_pin.xml diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt index 574aa375..08c63c3f 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt @@ -282,12 +282,20 @@ private fun BottomSheetFilteredContent( sanitizeLibraryAddress, ) } + + FilterState.HOTSPOTS -> { + hotspotList() + } } } } } } +private fun LazyListScope.hotspotList() { + infoItem("Hotspots coming soon") +} + /** * LazyList scoped enumeration of favorites for bottom sheet */ @@ -699,6 +707,7 @@ private fun PreviewEcosystemBottomSheet() { EcosystemBottomSheetContent( filters = listOf( FilterState.FAVORITES, + FilterState.HOTSPOTS, FilterState.GYMS, FilterState.EATERIES, FilterState.LIBRARIES, diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt index a58d8b62..79a2f690 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt @@ -64,6 +64,10 @@ fun HomeScreenMarkers(filterState: FilterState, favorites: Set, staticPla } } } + + FilterState.HOTSPOTS -> { + // Placeholder: hotspot markers are not wired yet. + } } } diff --git a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt index e3d58832..c92596da 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt @@ -299,6 +299,7 @@ fun HomeScreen( val gymsListState = rememberLazyListState() val eateriesListState = rememberLazyListState() val librariesListState = rememberLazyListState() + val hotspotsListState = rememberLazyListState() val printersListState = rememberLazyListState() fun listStateFor(filter: FilterState): LazyListState = when (filter) { @@ -306,6 +307,7 @@ fun HomeScreen( FilterState.GYMS -> gymsListState FilterState.EATERIES -> eateriesListState FilterState.LIBRARIES -> librariesListState + FilterState.HOTSPOTS -> hotspotsListState FilterState.PRINTERS -> printersListState } diff --git a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt index 2482fd0e..0c07b4c2 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt @@ -12,4 +12,5 @@ enum class FilterState(@DrawableRes val iconId: Int, val label: String) { GYMS(iconId = R.drawable.gym_icon, label = "Gyms"), EATERIES(iconId = R.drawable.eatery_icon, label = "Eateries"), LIBRARIES(iconId = R.drawable.library_icon, label = "Libraries"), + HOTSPOTS(iconId = R.drawable.hotspot_icon, label = "Hotspots"), } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt index 77ae02f2..3efacb63 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt @@ -93,6 +93,7 @@ class HomeViewModel @Inject constructor( val filterList = listOf( FilterState.FAVORITES, + FilterState.HOTSPOTS, FilterState.GYMS, FilterState.EATERIES, FilterState.LIBRARIES, diff --git a/app/src/main/res/drawable/hotspot_icon.xml b/app/src/main/res/drawable/hotspot_icon.xml new file mode 100644 index 00000000..16fae73d --- /dev/null +++ b/app/src/main/res/drawable/hotspot_icon.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/app/src/main/res/drawable/hotspot_pin.xml b/app/src/main/res/drawable/hotspot_pin.xml new file mode 100644 index 00000000..9bf734af --- /dev/null +++ b/app/src/main/res/drawable/hotspot_pin.xml @@ -0,0 +1,12 @@ + + + + From 699746a21bc1a659a725668fd6d23f35c244f633 Mon Sep 17 00:00:00 2001 From: Ryan Cheung Date: Tue, 21 Apr 2026 00:29:22 -0400 Subject: [PATCH 2/4] feat: initial commit for hotspot request form --- .../ui/components/home/AddFavoritesButton.kt | 63 ---- .../home/EcosystemBottomSheetContent.kt | 25 +- .../transit/ui/components/home/PillButton.kt | 77 +++++ .../ui/components/home/RequestHotspotSheet.kt | 280 ++++++++++++++++++ .../transit/ui/screens/HomeScreen.kt | 30 ++ .../transit/ui/viewmodels/HomeViewModel.kt | 6 + 6 files changed, 414 insertions(+), 67 deletions(-) delete mode 100644 app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt create mode 100644 app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt create mode 100644 app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt deleted file mode 100644 index b965588e..00000000 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.cornellappdev.transit.ui.components.home - -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -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.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.cornellappdev.transit.R -import com.cornellappdev.transit.ui.theme.SecondaryText -import com.cornellappdev.transit.ui.theme.robotoFamily - -@Composable -fun AddFavoritesButton( - onAddFavoritesClick: () -> Unit, - modifier: Modifier = Modifier -) { - Button( - onClick = onAddFavoritesClick, - colors = ButtonDefaults.buttonColors( - containerColor = Color.White, - contentColor = Color.Black - ), - modifier = modifier - .fillMaxWidth() - .height(40.dp), - ) { - - Icon( - painter = painterResource(R.drawable.ic_addition), - contentDescription = "Add Favorite", - tint = SecondaryText, - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Text( - text = "Add Favorites", - fontFamily = robotoFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 16.sp, - color = SecondaryText - ) - } -} - -@Preview -@Composable -private fun AddFavoritesButtonPreview() { - AddFavoritesButton( - onAddFavoritesClick = {}, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt index 8987ef3b..255b017b 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt @@ -85,6 +85,7 @@ fun EcosystemBottomSheetContent( showFilterSheet: Boolean, onFilterSheetShow: () -> Unit, onAddFavoritesClick: () -> Unit, + onRequestHotspotClick: () -> Unit = {}, selectedFilters: Set, appliedFilters: Set, favoritesFilterList: List, @@ -141,6 +142,7 @@ fun EcosystemBottomSheetContent( onFavoriteStarClick = onFavoriteStarClick, onFilterButtonClick = onFilterSheetShow, onAddFavoritesClick = onAddFavoritesClick, + onRequestHotspotClick = onRequestHotspotClick, appliedFilters = appliedFilters, onRemoveAppliedFilter = onRemoveAppliedFilter, operatingHoursToString = operatingHoursToString, @@ -178,6 +180,7 @@ private fun BottomSheetFilteredContent( onDetailsClick: (DetailedEcosystemPlace) -> Unit, onFavoriteStarClick: (Place) -> Unit, onAddFavoritesClick: () -> Unit, + onRequestHotspotClick: () -> Unit, onFilterButtonClick: () -> Unit, appliedFilters: Set, onRemoveAppliedFilter: (FavoritesFilterSheetState) -> Unit, @@ -284,7 +287,9 @@ private fun BottomSheetFilteredContent( } FilterState.HOTSPOTS -> { - hotspotList() + hotspotList( + onRequestHotspotClick = onRequestHotspotClick + ) } } } @@ -292,8 +297,16 @@ private fun BottomSheetFilteredContent( } } -private fun LazyListScope.hotspotList() { - infoItem("Hotspots coming soon") +private fun LazyListScope.hotspotList( + onRequestHotspotClick: () -> Unit, +) { + item { + Spacer(modifier = Modifier.height(8.dp)) + PillButton( + onClick = onRequestHotspotClick, + text = "Request Hotspot" + ) + } } /** @@ -317,7 +330,10 @@ private fun LazyListScope.favoriteList( ) { item { Spacer(modifier = Modifier.height(8.dp)) - AddFavoritesButton(onAddFavoritesClick = onAddFavoritesClick) + PillButton( + onClick = onAddFavoritesClick, + text = "Add Favorites", + ) } items( @@ -944,6 +960,7 @@ private fun PreviewBottomSheetFilteredContentFavorites() { onDetailsClick = {}, onFavoriteStarClick = {}, onAddFavoritesClick = {}, + onRequestHotspotClick = {}, onFilterButtonClick = {}, appliedFilters = setOf( FavoritesFilterSheetState.EATERIES, diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt new file mode 100644 index 00000000..fb970a54 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt @@ -0,0 +1,77 @@ +package com.cornellappdev.transit.ui.components.home + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +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.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.cornellappdev.transit.R +import com.cornellappdev.transit.ui.theme.SecondaryText +import com.cornellappdev.transit.ui.theme.robotoFamily + +@Composable +fun PillButton( + onClick: () -> Unit, + text: String, + modifier: Modifier = Modifier, + @DrawableRes iconResId: Int? = R.drawable.ic_addition, + contentDescription: String = text, + colors: ButtonColors = ButtonDefaults.buttonColors( + containerColor = Color.White, + contentColor = Color.Black + ), + iconTint: Color = SecondaryText, + textColor: Color = SecondaryText, + fontFamily: FontFamily = robotoFamily, + fontWeight: FontWeight = FontWeight.SemiBold, + fontSize: TextUnit = 16.sp, +) { + Button( + onClick = onClick, + colors = colors, + modifier = modifier + .fillMaxWidth() + .height(40.dp), + ) { + if (iconResId != null) { + Icon( + painter = painterResource(iconResId), + contentDescription = contentDescription, + tint = iconTint, + ) + Spacer(modifier = Modifier.width(8.dp)) + } + + Text( + text = text, + fontFamily = fontFamily, + fontWeight = fontWeight, + fontSize = fontSize, + color = textColor + ) + } +} + +@Preview +@Composable +private fun PillButtonPreview() { + PillButton( + onClick = {}, + text = "Add Favorites", + ) +} diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt new file mode 100644 index 00000000..6fd0caed --- /dev/null +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt @@ -0,0 +1,280 @@ +package com.cornellappdev.transit.ui.components.home + +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.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.outlined.KeyboardArrowDown +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.cornellappdev.transit.ui.theme.FavoritesYellow +import com.cornellappdev.transit.ui.theme.MetadataGray +import com.cornellappdev.transit.ui.theme.PrimaryText +import com.cornellappdev.transit.ui.theme.SecondaryText +import com.cornellappdev.transit.ui.theme.robotoFamily + +private val HotspotInputBackground = Color(0xFFF5F5F5) +private val HotspotBorder = Color(0xFFEAEFF4) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RequestHotspotSheet( + onDismiss: () -> Unit, + onSubmit: () -> Unit, + modifier: Modifier = Modifier, +) { + var name by remember { mutableStateOf("") } + var netId by remember { mutableStateOf("") } + var eventType by remember { mutableStateOf("") } + var description by remember { mutableStateOf("") } + var location by remember { mutableStateOf("") } + var eventTypeExpanded by remember { mutableStateOf(false) } + + val eventTypeOptions = listOf( + "Club Event", + "Performance", + "Study Session", + "Other", + ) + + Column( + modifier = modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(start = 24.dp, end = 24.dp, top = 24.dp, bottom = 32.dp), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Request a HotSpot", + fontFamily = robotoFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp, + color = PrimaryText + ) + + IconButton( + onClick = onDismiss, + modifier = Modifier + .background(color = HotspotInputBackground, shape = CircleShape) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Close request hotspot sheet", + tint = PrimaryText + ) + } + } + + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(18.dp) + ) { + LabeledInputField( + label = "Name", + value = name, + onValueChange = { name = it }, + placeholder = "Enter your Name", + ) + + LabeledInputField( + label = "NetID", + value = netId, + onValueChange = { netId = it }, + placeholder = "Enter your NetID", + ) + + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = "Type of event", + fontFamily = robotoFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp, + color = PrimaryText + ) + ExposedDropdownMenuBox( + expanded = eventTypeExpanded, + onExpandedChange = { eventTypeExpanded = !eventTypeExpanded }, + ) { + OutlinedTextField( + value = eventType, + onValueChange = {}, + readOnly = true, + singleLine = true, + placeholder = { + Text( + text = "Choose an option...", + color = MetadataGray, + fontFamily = robotoFamily, + fontSize = 16.sp, + ) + }, + trailingIcon = { + Icon( + imageVector = Icons.Outlined.KeyboardArrowDown, + contentDescription = "Choose event type", + tint = MetadataGray + ) + }, + colors = hotspotTextFieldColors(), + shape = RoundedCornerShape(10.dp), + modifier = Modifier + .menuAnchor() + .fillMaxWidth() + .height(52.dp) + ) + DropdownMenu( + expanded = eventTypeExpanded, + onDismissRequest = { eventTypeExpanded = false }, + ) { + eventTypeOptions.forEach { option -> + DropdownMenuItem( + text = { Text(option) }, + onClick = { + eventType = option + eventTypeExpanded = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding + ) + } + } + } + } + + LabeledInputField( + label = "Description", + value = description, + onValueChange = { description = it }, + placeholder = "Add a description...", + minLines = 5, + maxLines = 5 + ) + + LabeledInputField( + label = "Location", + value = location, + onValueChange = { location = it }, + placeholder = "e.g. Duffield Hall", + ) + + PillButton( + onClick = {}, + text = "Add Photos", + colors = androidx.compose.material3.ButtonDefaults.buttonColors( + containerColor = Color(0xFFEEEEEE), + contentColor = SecondaryText + ), + textColor = SecondaryText, + iconTint = SecondaryText + ) + } + + PillButton( + onClick = onSubmit, + text = "Submit", + iconResId = null, + colors = androidx.compose.material3.ButtonDefaults.buttonColors( + containerColor = FavoritesYellow, + contentColor = SecondaryText + ), + textColor = SecondaryText + ) + } +} + +@Composable +private fun LabeledInputField( + label: String, + value: String, + onValueChange: (String) -> Unit, + placeholder: String, + minLines: Int = 1, + maxLines: Int = 1, +) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = label, + fontFamily = robotoFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp, + color = PrimaryText + ) + OutlinedTextField( + value = value, + onValueChange = onValueChange, + placeholder = { + Text( + text = placeholder, + color = MetadataGray, + fontFamily = robotoFamily, + fontSize = 16.sp, + ) + }, + shape = RoundedCornerShape(10.dp), + minLines = minLines, + maxLines = maxLines, + colors = hotspotTextFieldColors(), + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Composable +private fun hotspotTextFieldColors() = OutlinedTextFieldDefaults.colors( + focusedTextColor = PrimaryText, + unfocusedTextColor = PrimaryText, + focusedContainerColor = HotspotInputBackground, + unfocusedContainerColor = HotspotInputBackground, + focusedBorderColor = HotspotBorder, + unfocusedBorderColor = HotspotBorder, + focusedPlaceholderColor = MetadataGray, + unfocusedPlaceholderColor = MetadataGray, + cursorColor = PrimaryText, +) + +@Preview(showBackground = true) +@Composable +private fun RequestHotspotSheetPreview() { + Box( + modifier = Modifier.background(Color.White) + ) { + RequestHotspotSheet( + onDismiss = {}, + onSubmit = {} + ) + } +} diff --git a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt index babb0270..547936a2 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt @@ -67,6 +67,7 @@ import com.cornellappdev.transit.ui.components.SearchSuggestions import com.cornellappdev.transit.ui.components.home.DetailedPlaceSheetContent import com.cornellappdev.transit.ui.components.home.EcosystemBottomSheetContent import com.cornellappdev.transit.ui.components.home.HomeScreenMarkers +import com.cornellappdev.transit.ui.components.home.RequestHotspotSheet import com.cornellappdev.transit.util.navigateSingleTop import com.cornellappdev.transit.ui.theme.DetailsHeaderGray import com.cornellappdev.transit.ui.theme.DividerGray @@ -135,10 +136,14 @@ fun HomeScreen( } val showAddFavoritesSheet by homeViewModel.showAddFavoritesSheet.collectAsStateWithLifecycle() + val showRequestHotspotSheet by homeViewModel.showRequestHotspotSheet.collectAsStateWithLifecycle() val addFavoritesSheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true ) + val requestHotspotSheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) //SheetState for FavoritesBottomSheet @@ -389,6 +394,9 @@ fun HomeScreen( onAddFavoritesClick = { homeViewModel.toggleAddFavoritesSheet(true) }, + onRequestHotspotClick = { + homeViewModel.toggleRequestHotspotSheet(true) + }, showFilterSheet = showFilterSheet, onFilterSheetShow = homeViewModel::openFilterSheet, selectedFilters = selectedFilters, @@ -491,6 +499,28 @@ fun HomeScreen( ) } } + + if (showRequestHotspotSheet) { + ModalBottomSheet( + onDismissRequest = { homeViewModel.toggleRequestHotspotSheet(false) }, + sheetState = requestHotspotSheetState, + shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp), + containerColor = Color(0xFFFDFDFD) + ) { + RequestHotspotSheet( + onDismiss = { homeViewModel.toggleRequestHotspotSheet(false) }, + onSubmit = { + homeViewModel.toggleRequestHotspotSheet(false) + // TODO: Connect to backend + Toast.makeText( + context, + "Hotspot request submitted", + Toast.LENGTH_SHORT + ).show() + } + ) + } + } } /** diff --git a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt index bf9e892d..e5e3e70f 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt @@ -183,6 +183,8 @@ class HomeViewModel @Inject constructor( private val _showAddFavoritesSheet = MutableStateFlow(false) val showAddFavoritesSheet: StateFlow = _showAddFavoritesSheet.asStateFlow() + private val _showRequestHotspotSheet = MutableStateFlow(false) + val showRequestHotspotSheet: StateFlow = _showRequestHotspotSheet.asStateFlow() val addSearchResultsFlow: StateFlow>> = unifiedSearchRepository.mergedSearchResults(_addSearchQuery) @@ -196,6 +198,10 @@ class HomeViewModel @Inject constructor( _showAddFavoritesSheet.value = show } + fun toggleRequestHotspotSheet(show: Boolean) { + _showRequestHotspotSheet.value = show + } + val favoritesFilterList = listOf( FavoritesFilterSheetState.GYMS, FavoritesFilterSheetState.EATERIES, From d8c30aab7401bb0be08f6c3b390cbf2b4517d54d Mon Sep 17 00:00:00 2001 From: Ryan Cheung Date: Tue, 21 Apr 2026 17:27:48 -0400 Subject: [PATCH 3/4] fix: minor fixes for hotspot sheet implementation --- .../ui/components/home/RequestHotspotSheet.kt | 25 ++++++++------- .../transit/ui/screens/HomeScreen.kt | 8 +++-- .../cornellappdev/transit/ui/theme/Color.kt | 32 ++++++++++--------- ...{hotspot_pin.xml => hotspot_event_pin.xml} | 0 4 files changed, 35 insertions(+), 30 deletions(-) rename app/src/main/res/drawable/{hotspot_pin.xml => hotspot_event_pin.xml} (100%) diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt index 6fd0caed..e916f666 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.outlined.KeyboardArrowDown +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox @@ -37,15 +38,15 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.cornellappdev.transit.ui.theme.DividerGray +import com.cornellappdev.transit.ui.theme.HotspotBorderGray import com.cornellappdev.transit.ui.theme.FavoritesYellow +import com.cornellappdev.transit.ui.theme.HotspotInputGray import com.cornellappdev.transit.ui.theme.MetadataGray import com.cornellappdev.transit.ui.theme.PrimaryText import com.cornellappdev.transit.ui.theme.SecondaryText import com.cornellappdev.transit.ui.theme.robotoFamily -private val HotspotInputBackground = Color(0xFFF5F5F5) -private val HotspotBorder = Color(0xFFEAEFF4) - @OptIn(ExperimentalMaterial3Api::class) @Composable fun RequestHotspotSheet( @@ -90,7 +91,7 @@ fun RequestHotspotSheet( IconButton( onClick = onDismiss, modifier = Modifier - .background(color = HotspotInputBackground, shape = CircleShape) + .background(color = HotspotInputGray, shape = CircleShape) ) { Icon( imageVector = Icons.Default.Close, @@ -102,7 +103,7 @@ fun RequestHotspotSheet( Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(18.dp) + verticalArrangement = Arrangement.spacedBy(16.dp) ) { LabeledInputField( label = "Name", @@ -194,8 +195,8 @@ fun RequestHotspotSheet( PillButton( onClick = {}, text = "Add Photos", - colors = androidx.compose.material3.ButtonDefaults.buttonColors( - containerColor = Color(0xFFEEEEEE), + colors = ButtonDefaults.buttonColors( + containerColor = DividerGray, contentColor = SecondaryText ), textColor = SecondaryText, @@ -207,7 +208,7 @@ fun RequestHotspotSheet( onClick = onSubmit, text = "Submit", iconResId = null, - colors = androidx.compose.material3.ButtonDefaults.buttonColors( + colors = ButtonDefaults.buttonColors( containerColor = FavoritesYellow, contentColor = SecondaryText ), @@ -257,10 +258,10 @@ private fun LabeledInputField( private fun hotspotTextFieldColors() = OutlinedTextFieldDefaults.colors( focusedTextColor = PrimaryText, unfocusedTextColor = PrimaryText, - focusedContainerColor = HotspotInputBackground, - unfocusedContainerColor = HotspotInputBackground, - focusedBorderColor = HotspotBorder, - unfocusedBorderColor = HotspotBorder, + focusedContainerColor = HotspotInputGray, + unfocusedContainerColor = HotspotInputGray, + focusedBorderColor = HotspotBorderGray, + unfocusedBorderColor = HotspotBorderGray, focusedPlaceholderColor = MetadataGray, unfocusedPlaceholderColor = MetadataGray, cursorColor = PrimaryText, diff --git a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt index 547936a2..adc159c4 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt @@ -68,6 +68,7 @@ import com.cornellappdev.transit.ui.components.home.DetailedPlaceSheetContent import com.cornellappdev.transit.ui.components.home.EcosystemBottomSheetContent import com.cornellappdev.transit.ui.components.home.HomeScreenMarkers import com.cornellappdev.transit.ui.components.home.RequestHotspotSheet +import com.cornellappdev.transit.ui.theme.CardBackground import com.cornellappdev.transit.util.navigateSingleTop import com.cornellappdev.transit.ui.theme.DetailsHeaderGray import com.cornellappdev.transit.ui.theme.DividerGray @@ -505,7 +506,8 @@ fun HomeScreen( onDismissRequest = { homeViewModel.toggleRequestHotspotSheet(false) }, sheetState = requestHotspotSheetState, shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp), - containerColor = Color(0xFFFDFDFD) + containerColor = CardBackground, + modifier = Modifier.fillMaxSize() ) { RequestHotspotSheet( onDismiss = { homeViewModel.toggleRequestHotspotSheet(false) }, @@ -517,7 +519,8 @@ fun HomeScreen( "Hotspot request submitted", Toast.LENGTH_SHORT ).show() - } + }, + modifier = Modifier.fillMaxSize() ) } } @@ -647,4 +650,3 @@ private fun HomeScreenSearchBar( } } - diff --git a/app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt b/app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt index 624fea22..da916076 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt @@ -6,27 +6,29 @@ val Purple80 = Color(0xFFD0BCFF) val PurpleGray80 = Color(0xFFCCC2DC) val Pink80 = Color(0xFFEFB8C8) -val Purple40 = Color(0xFF6650a4) -val PurpleGray40 = Color(0xFF625b71) +val Purple40 = Color(0xFF6650A4) +val PurpleGray40 = Color(0xFF625B71) val Pink40 = Color(0xFF7D5260) -val TransitBlue = Color(0xff079ddc) +val TransitBlue = Color(0xFF079DDC) val MutedTransitBlue = Color(0xFFD6F3FF) -val PrimaryText = Color(0xff212121) -val TextButtonGray = Color(0xff6A737D) -val SecondaryText = Color(0xff616161) -val MetadataGray = Color(0xff9e9e9e) -val DividerGray = Color(0xffeeeeee) -val IconGray = Color(0xffbdbdbd) -val LiveGreen = Color(0xff1BAF5D) -val LateRed = Color(0xffD82D4D) - -val DetailsHeaderGray = Color(0xfff5f5f5) -val DetailsDividerGray = Color(0xffc6c6c8) +val PrimaryText = Color(0xFF212121) +val TextButtonGray = Color(0xFF6A737D) +val SecondaryText = Color(0xFF616161) +val MetadataGray = Color(0xFF9E9E9E) +val DividerGray = Color(0xFFEEEEEE) +val IconGray = Color(0xFFBDBDBD) +val LiveGreen = Color(0xFF1BAF5D) +val LateRed = Color(0xFFD82D4D) +val CardBackground = Color(0xFFFDFDFD) +val HotspotInputGray = Color(0xFFF5F5F5) +val HotspotBorderGray = Color(0xFFEAEFF4) +val DetailsHeaderGray = Color(0xFFF5F5F5) +val DetailsDividerGray = Color(0xFFC6C6C8) val FavoritesYellow = Color(0xFFFEC50E) -val FavoritesDividerGray = Color(0xffDADADA) +val FavoritesDividerGray = Color(0xFFDADADA) val Gray02 = Color(0xFFA5A5A5) val Gray04 = Color(0xFF707070) diff --git a/app/src/main/res/drawable/hotspot_pin.xml b/app/src/main/res/drawable/hotspot_event_pin.xml similarity index 100% rename from app/src/main/res/drawable/hotspot_pin.xml rename to app/src/main/res/drawable/hotspot_event_pin.xml From 2f866d3124eaa7f1893486ecd307e64acb1f21d3 Mon Sep 17 00:00:00 2001 From: Ryan Cheung Date: Thu, 23 Apr 2026 02:17:44 -0400 Subject: [PATCH 4/4] fix: improve dropdown menu ui and add hotspot markers to map when in hotspot section --- .../ui/components/home/HomeScreenMarkers.kt | 12 ++++++++- .../ui/components/home/RequestHotspotSheet.kt | 25 +++++++++++++++---- .../res/drawable/hotspot_fun_spot_pin.xml | 19 ++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/hotspot_fun_spot_pin.xml diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt index 2e91ad91..4902ca86 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt @@ -26,6 +26,10 @@ fun HomeScreenMarkers( staticPlaces: StaticPlaces, onPlaceClick: (Place) -> Unit ) { + val placeholderHotspotMarkers = listOf( + LatLng(42.4441, -76.4837) to R.drawable.hotspot_event_pin, + LatLng(42.4475, -76.4852) to R.drawable.hotspot_fun_spot_pin + ) when (filterState) { FilterState.FAVORITES -> { @@ -91,7 +95,13 @@ fun HomeScreenMarkers( } FilterState.HOTSPOTS -> { - // Placeholder: hotspot markers are not wired yet. + placeholderHotspotMarkers.forEach { (position, iconRes) -> + LocationMarker( + position = position, + iconRes = iconRes, + onClick = {} + ) + } } } } diff --git a/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt index e916f666..a3220cb7 100644 --- a/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt +++ b/app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -33,9 +34,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.cornellappdev.transit.ui.theme.DividerGray @@ -60,12 +63,12 @@ fun RequestHotspotSheet( var description by remember { mutableStateOf("") } var location by remember { mutableStateOf("") } var eventTypeExpanded by remember { mutableStateOf(false) } + var eventTypeFieldWidth by remember { mutableStateOf(0.dp) } + val density = LocalDensity.current val eventTypeOptions = listOf( - "Club Event", - "Performance", - "Study Session", - "Other", + "Pop-up Event", + "Fun Spot", ) Column( @@ -157,18 +160,30 @@ fun RequestHotspotSheet( .menuAnchor() .fillMaxWidth() .height(52.dp) + .onGloballyPositioned { coordinates -> + eventTypeFieldWidth = with(density) { coordinates.size.width.toDp() } + } ) DropdownMenu( expanded = eventTypeExpanded, onDismissRequest = { eventTypeExpanded = false }, + modifier = Modifier.width(eventTypeFieldWidth), ) { eventTypeOptions.forEach { option -> DropdownMenuItem( - text = { Text(option) }, + text = { + Text( + text = option, + fontFamily = robotoFamily, + fontSize = 16.sp, + color = PrimaryText + ) + }, onClick = { eventType = option eventTypeExpanded = false }, + modifier = Modifier.fillMaxWidth(), contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding ) } diff --git a/app/src/main/res/drawable/hotspot_fun_spot_pin.xml b/app/src/main/res/drawable/hotspot_fun_spot_pin.xml new file mode 100644 index 00000000..ac975a58 --- /dev/null +++ b/app/src/main/res/drawable/hotspot_fun_spot_pin.xml @@ -0,0 +1,19 @@ + + + + + + + +