Skip to content

Add initial UI for hotspot implementation [Post-Ecosystem]#142

Open
RyanCheung555 wants to merge 5 commits intomainfrom
ryan/hotspot-page
Open

Add initial UI for hotspot implementation [Post-Ecosystem]#142
RyanCheung555 wants to merge 5 commits intomainfrom
ryan/hotspot-page

Conversation

@RyanCheung555
Copy link
Copy Markdown
Contributor

@RyanCheung555 RyanCheung555 commented Apr 23, 2026

Overview

  • Start implementing new hotspot feature UI

Changes Made

  • Import necessary map markers
  • Create hotspot page and request button
  • Link request button to hotspot form bottom sheet

Test Coverage

  • Tested on Medium Phone

Next Steps (delete if not applicable)

  • Create hotspot cards to show when clicking on map markers

Screenshots (delete if not applicable)

Hotspot UI Demo
HotspotUIDemo.webm

Summary by CodeRabbit

  • New Features
    • Users can now request new hotspots through a dedicated submission form on the home screen
    • New "Hotspots" filter category added to the home screen for discovering available hotspots
    • Hotspot location markers now displayed on the transit map

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

A new "Hotspots" feature is introduced with a request sheet for user submissions, new map markers, and a reusable PillButton component replacing the deleted AddFavoritesButton. State management is extended in the home view model, and theme colors are added for hotspot input styling.

Changes

Cohort / File(s) Summary
Component Deletion & Replacement
AddFavoritesButton.kt
Removed the AddFavoritesButton composable (63 lines) completely; functionality replaced by the new PillButton component.
New Reusable Component
PillButton.kt
Added a new PillButton composable supporting customizable icon, text, colors, typography, and full-width layout with fixed height.
Hotspot Request UI
RequestHotspotSheet.kt
Introduced a new RequestHotspotSheet composable providing form inputs (Name, NetID, Event Type dropdown, Description, Location), "Add Photos" and "Submit" pill buttons, and dismissal handling.
Bottom Sheet Integration
EcosystemBottomSheetContent.kt
Added onRequestHotspotClick callback parameter and new FilterState.HOTSPOTS branch rendering a "Request Hotspot" pill button; replaced AddFavoritesButton usage with PillButton.
Map Markers
HomeScreenMarkers.kt
Added hotspot marker rendering with FilterState.HOTSPOTS case, using hardcoded marker locations and icon resources.
Home Screen Integration
HomeScreen.kt
Added request hotspot sheet state management, ModalBottomSheet rendering, callback threading to bottom sheet content, and toast feedback on submission.
State Management
FilterState.kt, HomeViewModel.kt
Added HOTSPOTS filter state enum constant; introduced showRequestHotspotSheet state flow and toggleRequestHotspotSheet() function in view model; extended filterList to include hotspots.
Theme & Assets
Color.kt, hotspot_event_pin.xml, hotspot_fun_spot_pin.xml, hotspot_icon.xml
Standardized hex color notation (lowercase to uppercase), added three new theme colors (CardBackground, HotspotInputGray, HotspotBorderGray), and introduced three new hotspot-related vector drawable resources.

Sequence Diagram

sequenceDiagram
    actor User
    participant HomeScreen
    participant ViewModel
    participant RequestHotspotSheet
    participant System as System/Toast

    User->>HomeScreen: Tap "Request Hotspot" in ecosystem sheet
    HomeScreen->>ViewModel: Call onRequestHotspotClick()
    ViewModel->>HomeScreen: Set showRequestHotspotSheet = true
    HomeScreen->>RequestHotspotSheet: Show ModalBottomSheet
    
    User->>RequestHotspotSheet: Fill form (name, netId, type, desc, location)
    User->>RequestHotspotSheet: Tap Submit button
    RequestHotspotSheet->>HomeScreen: Invoke onSubmit callback
    HomeScreen->>ViewModel: Call toggleRequestHotspotSheet(false)
    ViewModel->>HomeScreen: Update showRequestHotspotSheet state
    HomeScreen->>RequestHotspotSheet: Hide sheet
    HomeScreen->>System: Display "Hotspot request submitted" toast
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Use new eatery backend  #137: Modifies HomeScreen and EcosystemBottomSheetContent parameters for ecosystem bottom sheet state management, overlapping integration points with hotspot callback threading.
  • Make ecosystem places searchable #134: Updates EcosystemBottomSheetContent filtered-list rendering logic and function parameters, potentially affected by the addition of the new FilterState.HOTSPOTS branch.

Suggested reviewers

  • AndrewCheung360

🐰 A hotspot request form takes flight,
With pill buttons gleaming so bright,
New markers appear on the map,
Toast notifications close the gap—
The feature's complete, what a sight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add initial UI for hotspot implementation [Post-Ecosystem]' clearly summarizes the main change—introducing initial UI components for the hotspot feature with appropriate context marker.
Description check ✅ Passed The description covers all critical sections: Overview, Changes Made, Test Coverage, Next Steps, and Screenshots. While some details could be more comprehensive, the provided information adequately documents the PR's intent and progress.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ryan/hotspot-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt (1)

27-68: Reusable API considerations for PillButton.

A few small concerns about the reusable surface:

  1. modifier.fillMaxWidth().height(40.dp) applies size modifiers after the caller's modifier, so any size set by callers is silently overridden. Consider letting callers control sizing, or at minimum apply caller's modifier last.
  2. Defaulting iconResId to R.drawable.ic_addition bakes a feature-specific asset into a generic component. Since this is now used for both "Add Favorites" and "Request Hotspot" (which presumably wants a different icon), consider making it required (no default) or defaulting to null.
  3. contentDescription defaults to text, which causes TalkBack to announce the icon label and then the text label — effectively a duplicated announcement. For decorative icons next to a label, contentDescription = null is typically preferred.
♻️ Proposed refactor
 fun PillButton(
     onClick: () -> Unit,
     text: String,
     modifier: Modifier = Modifier,
-    `@DrawableRes` iconResId: Int? = R.drawable.ic_addition,
-    contentDescription: String = text,
+    `@DrawableRes` iconResId: Int? = null,
+    contentDescription: String? = null,
     colors: ButtonColors = ButtonDefaults.buttonColors(
         containerColor = Color.White,
         contentColor = Color.Black
     ),
     ...
 ) {
     Button(
         onClick = onClick,
         colors = colors,
-        modifier = modifier
-            .fillMaxWidth()
-            .height(40.dp),
+        modifier = Modifier
+            .fillMaxWidth()
+            .height(40.dp)
+            .then(modifier),
     ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt`
around lines 27 - 68, PillButton currently forces sizing and a non-null default
icon and duplicates accessibility text: move the size constraints off the
caller's Modifier so the passed-in modifier is applied last (ensure
modifier.then(Modifier.fillMaxWidth().height(40.dp)) or better, remove forced
sizing and document sizing behavior) so callers can override size; change
iconResId default from R.drawable.ic_addition to null (or make iconResId
required) to avoid baking a feature-specific asset into the reusable component;
and set the icon's contentDescription default to null (or use a separate
iconContentDescription param) so decorative icons don't duplicate the Text
announcement; update the Icon usage in PillButton to only supply
contentDescription when non-null.
app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt (1)

29-32: Move placeholder list into the HOTSPOTS branch (or a top-level val).

placeholderHotspotMarkers is allocated on every recomposition of HomeScreenMarkers, even when the current filterState is not HOTSPOTS. Since its contents are constant, hoist it to a top-level private val or at least scope it inside the FilterState.HOTSPOTS branch to avoid unnecessary allocation.

Also — per the PR's "Next steps", remember to replace these hardcoded coordinates with real data before shipping. Adding a TODO comment here would make that easier to track.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt`
around lines 29 - 32, placeholderHotspotMarkers is being reallocated on every
recomposition of HomeScreenMarkers; move its constant list into the
FilterState.HOTSPOTS branch or hoist it as a top-level private val (e.g.,
private val PLACEHOLDER_HOTSPOT_MARKERS) so it’s not recreated each
recomposition, and add a TODO noting these hardcoded coordinates must be
replaced with real data before shipping; update references in HomeScreenMarkers
to use the hoisted/branch-scoped symbol instead of the local variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt`:
- Around line 55-64: The sheet currently captures form fields (name, netId,
eventType, description, location) but calls onSubmit with no data; update the
RequestHotspotSheet API to accept a request payload (e.g., a HotspotRequest or
similar) and change the onSubmit parameter to onSubmit: (HotspotRequest) ->
Unit; before invoking onSubmit, validate required fields (at minimum name and
netId and eventType) and only call onSubmit with a constructed HotspotRequest
when validation passes (otherwise show an error UI or disable the submit
action); apply the same change where the sheet is reused (the other
RequestHotspotSheet usage around the block referenced 222-231) so callers
receive the filled model.
- Around line 210-219: The PillButton for "Add Photos" is a tappable no-op;
change it so it is not interactive until the picker is implemented by either
removing the active onClick handler or disabling/hiding the control: update the
PillButton usage (the PillButton call with text "Add Photos" and current onClick
= {}) to either (a) set the button non-interactive (use the PillButton's
enabled/disabled API or equivalent) and optionally change the label to "Add
Photos (coming soon)", or (b) conditionally render nothing until the photo
picker is wired; ensure you only modify the PillButton invocation (the onClick
parameter and/or enabled/visibility) so no empty callback remains.

In `@app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt`:
- Around line 514-521: The onSubmit handler currently closes the sheet
(homeViewModel.toggleRequestHotspotSheet(false)) and shows a success Toast even
though backend submission is TODO; stop asserting success until submission is
implemented by removing or changing those actions: do not call
homeViewModel.toggleRequestHotspotSheet(false) nor show the "Hotspot request
submitted" Toast inside the onSubmit lambda; instead keep the sheet open (or
show a neutral message like "Submission not implemented" via Toast/Snackbar) and
only call toggleRequestHotspotSheet(false) and show a success Toast after the
actual network request completes successfully in the future submission flow.

---

Nitpick comments:
In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt`:
- Around line 29-32: placeholderHotspotMarkers is being reallocated on every
recomposition of HomeScreenMarkers; move its constant list into the
FilterState.HOTSPOTS branch or hoist it as a top-level private val (e.g.,
private val PLACEHOLDER_HOTSPOT_MARKERS) so it’s not recreated each
recomposition, and add a TODO noting these hardcoded coordinates must be
replaced with real data before shipping; update references in HomeScreenMarkers
to use the hoisted/branch-scoped symbol instead of the local variable.

In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt`:
- Around line 27-68: PillButton currently forces sizing and a non-null default
icon and duplicates accessibility text: move the size constraints off the
caller's Modifier so the passed-in modifier is applied last (ensure
modifier.then(Modifier.fillMaxWidth().height(40.dp)) or better, remove forced
sizing and document sizing behavior) so callers can override size; change
iconResId default from R.drawable.ic_addition to null (or make iconResId
required) to avoid baking a feature-specific asset into the reusable component;
and set the icon's contentDescription default to null (or use a separate
iconContentDescription param) so decorative icons don't duplicate the Text
announcement; update the Icon usage in PillButton to only supply
contentDescription when non-null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9a6b9e6-a65c-4b6b-8eca-71c7853e66d1

📥 Commits

Reviewing files that changed from the base of the PR and between 11de6d2 and 2f866d3.

📒 Files selected for processing (12)
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/EcosystemBottomSheetContent.kt
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/HomeScreenMarkers.kt
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/PillButton.kt
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt
  • app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt
  • app/src/main/java/com/cornellappdev/transit/ui/theme/Color.kt
  • app/src/main/java/com/cornellappdev/transit/ui/viewmodels/FilterState.kt
  • app/src/main/java/com/cornellappdev/transit/ui/viewmodels/HomeViewModel.kt
  • app/src/main/res/drawable/hotspot_event_pin.xml
  • app/src/main/res/drawable/hotspot_fun_spot_pin.xml
  • app/src/main/res/drawable/hotspot_icon.xml
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/cornellappdev/transit/ui/components/home/AddFavoritesButton.kt

Comment on lines +55 to +64
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("") }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Submit should pass the entered request data.

The sheet owns all form fields, but onSubmit has no payload, so callers cannot persist what the user entered. Pass a request model and gate obvious empty required fields before invoking submit.

🐛 Proposed submit API shape
+data class HotspotRequestInput(
+    val name: String,
+    val netId: String,
+    val eventType: String,
+    val description: String,
+    val location: String,
+)
+
 `@OptIn`(ExperimentalMaterial3Api::class)
 `@Composable`
 fun RequestHotspotSheet(
     onDismiss: () -> Unit,
-    onSubmit: () -> Unit,
+    onSubmit: (HotspotRequestInput) -> 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("") }
+    val canSubmit = name.isNotBlank() &&
+        netId.isNotBlank() &&
+        eventType.isNotBlank() &&
+        location.isNotBlank()
         PillButton(
-            onClick = onSubmit,
+            onClick = {
+                if (!canSubmit) return@PillButton
+
+                onSubmit(
+                    HotspotRequestInput(
+                        name = name.trim(),
+                        netId = netId.trim(),
+                        eventType = eventType,
+                        description = description.trim(),
+                        location = location.trim(),
+                    )
+                )
+            },
             text = "Submit",
             iconResId = null,

Also applies to: 222-231

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt`
around lines 55 - 64, The sheet currently captures form fields (name, netId,
eventType, description, location) but calls onSubmit with no data; update the
RequestHotspotSheet API to accept a request payload (e.g., a HotspotRequest or
similar) and change the onSubmit parameter to onSubmit: (HotspotRequest) ->
Unit; before invoking onSubmit, validate required fields (at minimum name and
netId and eventType) and only call onSubmit with a constructed HotspotRequest
when validation passes (otherwise show an error UI or disable the submit
action); apply the same change where the sheet is reused (the other
RequestHotspotSheet usage around the block referenced 222-231) so callers
receive the filled model.

Comment on lines +210 to +219
PillButton(
onClick = {},
text = "Add Photos",
colors = ButtonDefaults.buttonColors(
containerColor = DividerGray,
contentColor = SecondaryText
),
textColor = SecondaryText,
iconTint = SecondaryText
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid shipping a tappable no-op.

The “Add Photos” button currently accepts taps but does nothing. Hide it until the picker is wired, or add a real callback.

🧹 Proposed minimal fix until photo upload is implemented
-            PillButton(
-                onClick = {},
-                text = "Add Photos",
-                colors = ButtonDefaults.buttonColors(
-                    containerColor = DividerGray,
-                    contentColor = SecondaryText
-                ),
-                textColor = SecondaryText,
-                iconTint = SecondaryText
-            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PillButton(
onClick = {},
text = "Add Photos",
colors = ButtonDefaults.buttonColors(
containerColor = DividerGray,
contentColor = SecondaryText
),
textColor = SecondaryText,
iconTint = SecondaryText
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/transit/ui/components/home/RequestHotspotSheet.kt`
around lines 210 - 219, The PillButton for "Add Photos" is a tappable no-op;
change it so it is not interactive until the picker is implemented by either
removing the active onClick handler or disabling/hiding the control: update the
PillButton usage (the PillButton call with text "Add Photos" and current onClick
= {}) to either (a) set the button non-interactive (use the PillButton's
enabled/disabled API or equivalent) and optionally change the label to "Add
Photos (coming soon)", or (b) conditionally render nothing until the photo
picker is wired; ensure you only modify the PillButton invocation (the onClick
parameter and/or enabled/visibility) so no empty callback remains.

Comment on lines +514 to +521
onSubmit = {
homeViewModel.toggleRequestHotspotSheet(false)
// TODO: Connect to backend
Toast.makeText(
context,
"Hotspot request submitted",
Toast.LENGTH_SHORT
).show()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t show success for a request that is not submitted.

Line 516 still has the backend TODO, but the UI closes the sheet and tells users “Hotspot request submitted.” That drops the request while confirming success.

🐛 Proposed minimal guard until submission is implemented
                 onSubmit = {
-                    homeViewModel.toggleRequestHotspotSheet(false)
-                    // TODO: Connect to backend
+                    // TODO: Connect to backend and only show success after persistence succeeds.
                     Toast.makeText(
                         context,
-                        "Hotspot request submitted",
+                        "Hotspot request submission is not available yet",
                         Toast.LENGTH_SHORT
                     ).show()
                 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onSubmit = {
homeViewModel.toggleRequestHotspotSheet(false)
// TODO: Connect to backend
Toast.makeText(
context,
"Hotspot request submitted",
Toast.LENGTH_SHORT
).show()
onSubmit = {
// TODO: Connect to backend and only show success after persistence succeeds.
Toast.makeText(
context,
"Hotspot request submission is not available yet",
Toast.LENGTH_SHORT
).show()
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/cornellappdev/transit/ui/screens/HomeScreen.kt` around
lines 514 - 521, The onSubmit handler currently closes the sheet
(homeViewModel.toggleRequestHotspotSheet(false)) and shows a success Toast even
though backend submission is TODO; stop asserting success until submission is
implemented by removing or changing those actions: do not call
homeViewModel.toggleRequestHotspotSheet(false) nor show the "Hotspot request
submitted" Toast inside the onSubmit lambda; instead keep the sheet open (or
show a neutral message like "Submission not implemented" via Toast/Snackbar) and
only call toggleRequestHotspotSheet(false) and show a success Toast after the
actual network request completes successfully in the future submission flow.

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.

1 participant