Skip to content

Improve mobile map UI and add controls onboarding tour#3680

Merged
Baalmart merged 2 commits into
stagingfrom
fix/map-ui
Jun 25, 2026
Merged

Improve mobile map UI and add controls onboarding tour#3680
Baalmart merged 2 commits into
stagingfrom
fix/map-ui

Conversation

@2phonebabykeem

@2phonebabykeem 2phonebabykeem commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Refreshes map cards, overlay controls, and search sheet styling for dark mode consistency with dashboard cards (chevrons, muted location text, modal close icon colors).
  • Reworks map controls into unified pills (layers, locate, zoom) with symmetric edge spacing and improved tap targets.
  • Adds a first-time spotlight tour (matching the exposure place-card pattern) that explains map style, your location, and zoom controls; the location step is shown only when GPS is available.

Test plan

  • Open Map tab on a fresh install / after clearing map_controls_tour_seen in SharedPreferences and confirm the 2–3 step tour appears
  • Verify tour skips the location step when location permission is denied
  • Tap through all tour steps and confirm it does not reappear on next visit
  • Check map control alignment: legend (left) and layers/locate/zoom (right) have matching visual inset from screen edges
  • Confirm layers picker, locate (when available), and zoom in/out all work after the tour
  • Smoke test map air quality card, forecast flow, and search sheet in light and dark mode
  • Spot-check dashboard analytics/nearby cards for chevron placement and location subtitle color in dark mode

Summary by CodeRabbit

  • New Features

    • Added a guided tour for map controls to help users discover key map actions.
    • Introduced a clearer forecast view entry point on air quality cards.
  • Bug Fixes

    • Improved location text handling across dashboard and map cards for more reliable display.
    • Updated map and search text colors for better consistency with the app theme.
  • Style

    • Refined card and map control layouts, spacing, and icon placement for a cleaner interface.

Align map controls with dashboard card styling, fix asymmetric edge spacing, and introduce a first-time spotlight tour for map layer, location, and zoom actions.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@2phonebabykeem, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 36 minutes and 50 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d76dc79e-eaf9-45eb-b2bf-40b4e189e25f

📥 Commits

Reviewing files that changed from the base of the PR and between 17b6754 and 09e8204.

📒 Files selected for processing (4)
  • src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart
  • src/mobile/lib/src/app/map/widgets/map_controls.dart
  • src/mobile/lib/src/app/map/widgets/map_controls_tour.dart
  • src/mobile/lib/src/app/map/widgets/map_overlay_controls.dart
📝 Walkthrough

Walkthrough

Dashboard cards, map controls, and map air-quality/search surfaces were restyled. MapScreen now tracks a persisted controls tour, anchors it to keyed controls, and sequences the forecast modal with explicit async checks.

Changes

Mobile map and dashboard updates

Layer / File(s) Summary
Shared map control primitives
src/mobile/lib/src/app/map/widgets/map_controls.dart, src/mobile/lib/src/app/map/widgets/map_overlay_controls.dart
MapIconButton, MapZoomGroup, and MapAqLegend use shared sizing and decoration helpers, and overlay controls now accept keys for anchored controls.
Controls tour and page wiring
src/mobile/lib/src/app/map/widgets/map_controls_tour.dart, src/mobile/lib/src/app/map/pages/map_page.dart
MapControlsTour renders the spotlight overlay, while MapScreen reads the persisted tour flag, builds the tour steps, and shows the overlay with keyed controls and the side-slot legend.
Forecast modal sequencing
src/mobile/lib/src/app/map/pages/map_page.dart
_showForecastModal now hides the text input overlay around an awaited ForecastOverviewPage.showForMeasurement(...) call and checks mounted between steps.
Map air quality card and search text
src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart, src/mobile/lib/src/app/map/widgets/map_search_sheet.dart
MapAirQualityCard changes its location/AQI display, forecast button, and dismiss styling, and search result text now uses AppTextColors.headline(context).
Dashboard card header styling
src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart, src/mobile/lib/src/app/dashboard/widgets/analytics_card.dart, src/mobile/lib/src/app/dashboard/widgets/nearby_measurement_card.dart, src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart
Dashboard cards now use chevron SVG helpers, muted location tinting, adjusted header alignment, and NearbyView drops the commented distance argument.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MapScreen
  participant ForecastOverviewPage

  User->>MapScreen: tap forecast view
  MapScreen->>MapScreen: hide text input overlay
  MapScreen->>MapScreen: await frame delay and mounted checks
  MapScreen->>ForecastOverviewPage: showForMeasurement(...)
  ForecastOverviewPage-->>MapScreen: close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

mobile-app

Suggested reviewers

  • Baalmart

Poem

A chevron winked, the map took flight,
Soft controls glowed in measured light,
Tour steps danced from screen to screen,
Forecast panes slid in between,
And cards wore colors crisp and bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: mobile map UI refinements and a new controls onboarding tour.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/map-ui

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mobile/lib/src/app/map/widgets/map_controls.dart (1)

76-93: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

The 56px tap wrapper isn't actually tappable — effective hit area is 44px.

The GestureDetector wraps the inner 44px Container (default deferToChild hit testing), so the surrounding 12px inside the _mapControlTapSize (56px) SizedBox is inert. The net tap target is 44px, which is below the 48px Material minimum and doesn't match the intent encoded in the sizing constants and the inset comment on Line 15.

Move the gesture to the outer wrapper with an opaque hit test so the full 56px responds:

♿ Proposed fix to honor the 56px tap target
-    return SizedBox(
-      width: _mapControlTapSize,
-      height: _mapControlTapSize,
-      child: Center(
-        child: GestureDetector(
-          onTap: onTap,
-          child: Container(
+    return SizedBox(
+      width: _mapControlTapSize,
+      height: _mapControlTapSize,
+      child: GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onTap: onTap,
+        child: Center(
+          child: Container(
             width: _mapControlVisualSize,
             height: _mapControlVisualSize,
             decoration: _mapControlDecoration(
               background: bg,
               borderRadius: BorderRadius.circular(10),
             ),
             child: Icon(icon, size: _mapControlIconSize, color: iconColor),
           ),
         ),
       ),
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/map/widgets/map_controls.dart` around lines 76 - 93,
The tap target in the map control builder is too small because GestureDetector
is attached to the inner 44px Container instead of the outer 56px SizedBox.
Update the tap handling in the map control widget so the full _mapControlTapSize
area responds, using the outer wrapper with an opaque hit test behavior rather
than the inner visual container. Keep the visual Container and icon sizing
unchanged, and ensure the hit area matches the sizing constants and inset intent
in the map controls helper.
🧹 Nitpick comments (1)
src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart (1)

212-223: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the chevron widget into a shared dashboard component.

Line 212 introduces the same chevron rendering logic that now also appears in analytics_card.dart (Line 58) and nearby_measurement_card.dart (Line 61). Centralizing this avoids style drift (size/tint/asset path) across card headers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart`
around lines 212 - 223, The chevron rendering logic is duplicated in _chevron
within swipeable_analytics_card.dart and the same pattern also exists in
analytics_card and nearby_measurement_card, so extract it into a shared
dashboard component. Create a single reusable chevron widget in the dashboard
shared UI layer, then replace the local implementations in the affected card
header widgets with that shared component so the asset path, size, and tint stay
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart`:
- Around line 58-59: The aqLabel fallback in map_air_quality_card.dart is too
weak because MapAirQualityCard can render an empty AQI chip when pmValue exists
but measurement.aqiCategory is null or empty. Update the aqLabel logic in the
widget that builds the AQI badge so it uses a non-empty fallback string whenever
a reading exists but the category is missing, and keep the existing “No data”
behavior only for null pmValue. Use the MapAirQualityCard build logic and the
aqLabel assignment as the place to apply the fix.

---

Outside diff comments:
In `@src/mobile/lib/src/app/map/widgets/map_controls.dart`:
- Around line 76-93: The tap target in the map control builder is too small
because GestureDetector is attached to the inner 44px Container instead of the
outer 56px SizedBox. Update the tap handling in the map control widget so the
full _mapControlTapSize area responds, using the outer wrapper with an opaque
hit test behavior rather than the inner visual container. Keep the visual
Container and icon sizing unchanged, and ensure the hit area matches the sizing
constants and inset intent in the map controls helper.

---

Nitpick comments:
In
`@src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart`:
- Around line 212-223: The chevron rendering logic is duplicated in _chevron
within swipeable_analytics_card.dart and the same pattern also exists in
analytics_card and nearby_measurement_card, so extract it into a shared
dashboard component. Create a single reusable chevron widget in the dashboard
shared UI layer, then replace the local implementations in the affected card
header widgets with that shared component so the asset path, size, and tint stay
consistent.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 35d9b199-1e26-4256-8904-aed51d636876

📥 Commits

Reviewing files that changed from the base of the PR and between a84056e and 17b6754.

📒 Files selected for processing (10)
  • src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart
  • src/mobile/lib/src/app/dashboard/widgets/analytics_card.dart
  • src/mobile/lib/src/app/dashboard/widgets/nearby_measurement_card.dart
  • src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart
  • src/mobile/lib/src/app/map/pages/map_page.dart
  • src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart
  • src/mobile/lib/src/app/map/widgets/map_controls.dart
  • src/mobile/lib/src/app/map/widgets/map_controls_tour.dart
  • src/mobile/lib/src/app/map/widgets/map_overlay_controls.dart
  • src/mobile/lib/src/app/map/widgets/map_search_sheet.dart
💤 Files with no reviewable changes (1)
  • src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart

Comment thread src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the mobile Map UI to improve dark-mode consistency and control ergonomics, and introduces a first-time onboarding tour that spotlights map controls.

Changes:

  • Refines map card, search sheet, and dashboard card styling (text/icon colors, chevrons, muted location text).
  • Reworks map overlay controls into unified “pill” controls with aligned insets and larger intended tap areas.
  • Adds a guided spotlight tour for map controls, persisted via SharedPreferences, with an optional “Your location” step.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/mobile/lib/src/app/map/widgets/map_search_sheet.dart Uses AppTextColors.headline for better theme consistency in search results.
src/mobile/lib/src/app/map/widgets/map_overlay_controls.dart Adds keys for controls and aligns right-side inset using shared constants.
src/mobile/lib/src/app/map/widgets/map_controls.dart Introduces new control sizing constants and refreshed control visuals.
src/mobile/lib/src/app/map/widgets/map_controls_tour.dart New guided spotlight tour overlay implementation for map controls.
src/mobile/lib/src/app/map/widgets/map_air_quality_card.dart Refreshes map AQ card layout and forecast CTA styling.
src/mobile/lib/src/app/map/pages/map_page.dart Wires up the map controls tour and aligns legend with controls inset.
src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart Removes unused/commented distance argument.
src/mobile/lib/src/app/dashboard/widgets/nearby_measurement_card.dart Updates muted location styling and adds chevron affordance.
src/mobile/lib/src/app/dashboard/widgets/analytics_card.dart Updates muted location styling and adds chevron affordance.
src/mobile/lib/src/app/dashboard/pages/location_selection/components/swipeable_analytics_card.dart Updates muted location styling and adds chevron affordance.

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

Comment on lines +207 to +213
if (pmValue != null)
SvgPicture.asset(
getAirQualityIcon(measurement, pmValue),
width: 36,
height: 36,
)
else
Comment on lines 76 to 80
return SizedBox(
width: 48,
height: 48,
width: _mapControlTapSize,
height: _mapControlTapSize,
child: Center(
child: GestureDetector(
Comment on lines +117 to +123
return SizedBox(
width: _mapControlTapSize,
height: _mapZoomTapHeight,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapUp: (details) {
Comment on lines +203 to +216
final screen = MediaQuery.of(context).size;
final bubbleBg = isDark ? AppColors.darkHighlight : Colors.white;
final textColor = isDark ? Colors.white : const Color(0xFF1A1D23);
final subColor =
isDark ? AppColors.boldHeadlineColor2 : AppColors.boldHeadlineColor3;

final bubbleWidth =
(screen.width - _hPad * 2 - _arrowW - 12).clamp(220.0, _maxBubbleWidth);
final left = (targetRect.left - bubbleWidth - _arrowW - 12)
.clamp(_hPad, screen.width - bubbleWidth - _hPad);

final bubbleTop = (targetRect.center.dy - 72)
.clamp(_hPad + 8, screen.height * 0.55);

Comment on lines +12 to +24
this.layersKey,
this.locateKey,
this.zoomKey,
});

final bool isDark;
final VoidCallback onLayersTap;
final VoidCallback? onLocateTap;
final VoidCallback onZoomIn;
final VoidCallback onZoomOut;
final Key? layersKey;
final Key? locateKey;
final Key? zoomKey;
Use Unknown/unavailable fallbacks when category or icon is missing, expand map control hit areas to the full tap wrapper, respect safe-area padding in the controls tour, and type tour keys as GlobalKey.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Baalmart
Baalmart merged commit 04e2154 into staging Jun 25, 2026
25 of 27 checks passed
@Baalmart
Baalmart deleted the fix/map-ui branch June 25, 2026 02:30
@Baalmart Baalmart mentioned this pull request Jun 25, 2026
3 tasks
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.

4 participants