feat(mobile): show a full-screen map on the Map tab #148

Merged
ullrich merged 1 commit from mobile-full-screen-map into main 2026-09-12 07:34:31 +00:00
Owner

The Map tab was a placeholder rendering the word "Map" centred on a blank screen. It now renders a MapLibre map over OpenStreetMap raster tiles.

What changed

  • Full-bleed layoutStyleSheet.absoluteFill, so the map runs under the status bar and tab bar. Only the attribution is inset out of their way, via safe-area insets.
  • Camera opens on central Europe at zoom 6 — the same neutral framing RouteMap falls back to when it has no geometry to fit.
  • Shared style — the OSM style moves out of RouteMap into lib/map/osm-style.ts so the app's two maps cannot drift apart. It carries the attribution string the OSM tile usage policy requires.
  • Fallback — keeps RouteMap's !Map guard, which renders a placeholder when the native module isn't linked (e.g. under Expo Go).

Deliberately not included

  • No location permission. Centring on the user would be more useful, and expo-location is already a dependency, but it's a new data-collection surface needing a privacy manifest entry. Worth a follow-up.
  • No i18n. The mobile app has no useTranslation usage today, so this matches the surrounding code rather than wiring up react-i18next here. Two strings are involved (fallback text, attribution).

Tests

app/__tests__/map-screen.test.tsx — 4 tests covering the OSM style, the full-bleed layout, the default camera, the attribution, and the unlinked-module fallback.

pnpm typecheck 14/14, pnpm test 12/12, pnpm lint 14/14 — verified standalone on main, so this is independent of #147.

Note on running it

The app cannot actually boot until #147 lands (react/react-native-renderer mismatch). This PR's own checks pass regardless — jest never reaches the native renderer check — but to see the map on a device, merge #147 first.

Verified on an Android emulator: tiles render edge to edge with the attribution clear of the tab bar.

🤖 Generated with Claude Code

The Map tab was a placeholder rendering the word "Map" centred on a blank screen. It now renders a MapLibre map over OpenStreetMap raster tiles. ### What changed - **Full-bleed layout** — `StyleSheet.absoluteFill`, so the map runs under the status bar and tab bar. Only the attribution is inset out of their way, via safe-area insets. - **Camera** opens on central Europe at zoom 6 — the same neutral framing `RouteMap` falls back to when it has no geometry to fit. - **Shared style** — the OSM style moves out of `RouteMap` into `lib/map/osm-style.ts` so the app's two maps cannot drift apart. It carries the attribution string the [OSM tile usage policy](https://operations.osmfoundation.org/policies/tiles/) requires. - **Fallback** — keeps `RouteMap`'s `!Map` guard, which renders a placeholder when the native module isn't linked (e.g. under Expo Go). ### Deliberately not included - **No location permission.** Centring on the user would be more useful, and `expo-location` is already a dependency, but it's a new data-collection surface needing a privacy manifest entry. Worth a follow-up. - **No i18n.** The mobile app has no `useTranslation` usage today, so this matches the surrounding code rather than wiring up react-i18next here. Two strings are involved (fallback text, attribution). ### Tests `app/__tests__/map-screen.test.tsx` — 4 tests covering the OSM style, the full-bleed layout, the default camera, the attribution, and the unlinked-module fallback. `pnpm typecheck` 14/14, `pnpm test` 12/12, `pnpm lint` 14/14 — verified standalone on `main`, so this is independent of #147. ### Note on running it The app cannot actually boot until #147 lands (react/react-native-renderer mismatch). This PR's own checks pass regardless — jest never reaches the native renderer check — but to see the map on a device, merge #147 first. Verified on an Android emulator: tiles render edge to edge with the attribution clear of the tab bar. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(mobile): show a full-screen map on the Map tab
All checks were successful
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 47s
CI / Dockerfile Package Check (pull_request) Successful in 15s
CI / Checks (pull_request) Successful in 4m45s
CI / Visual Tests (pull_request) Successful in 2m45s
CI / Journal Image Smoke Test (pull_request) Successful in 9m4s
CI / E2E Tests (pull_request) Successful in 7m5s
Cancel superseded CI / Cancel in-flight CI (pull_request) Successful in 13s
CD Staging / Tear Down PR Preview (pull_request) Successful in 27s
6056055fd6
The Map tab was a placeholder rendering the word "Map" centred on a
blank screen. It now renders a MapLibre map over OpenStreetMap raster
tiles.

The map is full-bleed: it fills the screen with StyleSheet.absoluteFill
and runs under the status bar and tab bar, so only the attribution is
inset out of their way via safe-area insets. The camera opens on central
Europe at zoom 6 — the same neutral framing RouteMap falls back to when
it has no geometry to fit.

The OSM style moves out of RouteMap into lib/map/osm-style.ts so the two
maps in the app cannot drift apart, and carries the attribution string
the OSM tile usage policy requires. The screen keeps RouteMap's `!Map`
guard, which renders a placeholder when the native module isn't linked
(for example under Expo Go).

No location permission is requested: centring on the user would need a
new data-collection surface and a privacy manifest entry.

Verified on an Android emulator: tiles render edge to edge with the
attribution clear of the tab bar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
trails-cool/trails!148
No description provided.