Carleton Map (Beta): native iOS map view#7576
Draft
hawkrives wants to merge 25 commits into
Draft
Conversation
Captures the brainstormed plan for porting carls-app/carls' map-carls view into AAO as a native iOS map screen, rendering Carleton data and style as an interim before St. Olaf data and a non-Mapbox SDK land. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
18-task TDD plan covering: carletonClient peer in @frogpond/api, JS deps, iOS native (Podfile/Info.plist/PrivacyInfo/CI script), Mapbox token init, pure utility ports with tests, React Query hook, selection context, four UI components with tests, MapScreen itself, navigation registration with native form-sheet detents, home-screen tile, jest mock, and final verification. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Adds the Podfile flag, location usage description, precise-location privacy manifest entry, and a Mapbox-downloads ~/.netrc block to ci_post_clone.sh gated on $MAPBOX_DOWNLOADS_TOKEN. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
The original Task 7 commit slipped past pre-commit because mise's pretty task formats in place but the file was already staged before formatting, so the changes never landed in the index. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Implement BuildingList with FlatList rendering building rows using ListRow from @frogpond/lists. Each row displays building name and nickname (if present). Tapping a row calls onSelect(buildingId). Empty state shows "No buildings to show." message. All 3 tests pass: - renders one row per building - invokes onSelect with building id when pressed - renders empty state when given no buildings https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Add moduleNameMapper to jest config to mock font files (.ttf, .otf) and image files imported by @react-native-vector-icons and other packages. Create jest/file-mock.js to handle these imports in test environment. This fixes test suite parsing errors when components use icon fonts. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
The two issues — `require-unicode-regexp` on the empty-state matcher and `react/jsx-sort-props` on the FlatList — came from the implementation plan's code blocks; the implementer pasted them verbatim. mise's pretty task only formats; it doesn't lint, so they slipped past pre-commit. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Also fixes TS overload resolution in home/index.tsx (navigation.navigate call with large union now exceeds TypeScript's overload-matching limit; cast to `never` to safely bypass it). https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Required jest.config.ts change to add @rnmapbox/maps to esmPackages so that the setup-jest helper (which uses ES import syntax) can be properly loaded during Jest initialization. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
- MapScreen.handlePress uses navigation.replace to match BuildingPicker. Without this, a map-tap left a stale BuildingPicker beneath the info card; closing info revealed the picker from before the tap rather than returning to the map cleanly. - BuildingInfo close button padding bumped from 8x4 to 20x12 (≥44x44pt total tap area) per CLAUDE.md mobile guidelines. - BuildingInfo ScrollView gains contentInsetAdjustmentBehavior="automatic" matching the convention used in transportation/bus/detail.tsx for sheets inside native stacks. - LinkSection renders plain Text (not TouchableOpacity with accessibilityRole="link") when the parsed href is empty, so VoiceOver no longer announces a link that does nothing. - Building.photos type changed from one-element tuple [string] to Array<string> to match the runtime payload shape. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
The @rnmapbox/maps Podfile flag added in the Carleton Map work makes pod install download Mapbox-iOS-SDK from api.mapbox.com, which requires netrc credentials. ci_post_clone.sh handled this for Xcode Cloud already; both check.yml (iOS build) and cocoapods.yml (lockfile-update bot triggered by ios/Podfile changes) need the same handling. The step writes ~/.netrc only when MAPBOX_DOWNLOADS_TOKEN is set in repository secrets. When unset it emits a GitHub Actions warning and lets pod install fail downstream with its own clearer error, mirroring the ci_post_clone.sh pattern. REQUIRED before merge: provision MAPBOX_DOWNLOADS_TOKEN as a repository secret in Settings → Secrets and variables → Actions. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
These were the brainstorming spec and the implementation plan for the Carleton Map work. Their content is captured in the PR description and the diff itself; keeping them committed would just be noise in the repo going forward. https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
drewvolz
reviewed
Apr 27, 2026
hawkrives
commented
Apr 27, 2026
|
|
||
| function AddressLink({address}: {address: string}) { | ||
| let onPress = () => { | ||
| let url = `http://maps.apple.com/?q=${encodeURIComponent(address)}` |
Member
Author
There was a problem hiding this comment.
Should probably be https
Comment on lines
+19
to
+20
| // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment | ||
| let navigation = useNavigation<any>() |
Member
Author
There was a problem hiding this comment.
Let's figure out the right type here instead of allowing any
| staleTime: 1000 * 60 * 60, // 1 hour — building data changes rarely | ||
| }) | ||
|
|
||
| export function useMapData(): UseQueryResult< |
Member
Author
There was a problem hiding this comment.
Probably can just inline this?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the carls-app/carls map view to AAO as a native iOS screen. Interim: renders Carleton's building data and Mapbox style. St. Olaf-specific data and a non-Mapbox SDK are tracked as follow-ups below.
source/views/map/view withMapScreen,BuildingPicker,BuildingInfo, all built on@rnmapbox/mapsand React Navigation 7's native form-sheet detents (no custom drag overlay).carletonClientpeer in@frogpond/apifor callingcarleton.api.frogpond.tech/v1directly.NSLocationWhenInUseUsageDescription+ a precise-location entry inPrivacyInfo.xcprivacy.Required before merge
MAPBOX_PUBLIC_TOKENplaceholder ('pk.TODO_REPLACE_BEFORE_MERGE') insource/lib/mapbox.tswith the realpk.…Mapbox public access token.MAPBOX_DOWNLOADS_TOKENas a GitHub Actions repository secret (Settings → Secrets and variables → Actions). The workflow steps to consume it are already added tocheck.ymlandcocoapods.yml; they degrade with a::warning::if the secret is unset and letpod installfail downstream with its own clearer error. The same secret needs to land in Xcode Cloud's environment as well —ci_post_clone.shalready reads it from there.ios/Podfile.lockon a Mac with the netrc credentials set, then commit. OnceMAPBOX_DOWNLOADS_TOKENis provisioned, the cocoapods.yml bot can do this automatically on the next push.Architecture notes
Map,MapBuildingPicker, andMapBuildingInfoare wrapped in a singleStack.GroupwhosescreenLayoutinjectsMapSelectionProvider, so all three share selection state viaMapSelectionContext.[0.5, 1.0](medium / large) withsheetLargestUndimmedDetentIndex: 'last'so the map stays interactive at the largest detent.MapView.onPress→lookupBuildingByCoordinates([lng, lat], features)(point-in-polygon via@turf/boolean-point-in-polygon) →selectBuilding(id)→navigation.replace('MapBuildingInfo', …).MapScreenusesuseFocusEffect(notuseEffect) to navigate toMapBuildingPickerwhenever the map regains focus — so closingBuildingInforeturns the user to the picker rather than a bare map.fuzzyfindagainst${name} ${nickname}(lowercased). When the search box is non-empty, the category picker hides.accessibilityLabel/accessibilityRoleon every interactive element that supports them.ListRowfrom@frogpond/listsdoesn't acceptaccessibilityLabeldirectly — VoiceOver auto-derives from<Title>text children, consistent with all existing callers.Code changes
style/fixfollow-ups during execution and twofix(map):/ci:commits responding to final code review.parseLinkString,lookupBuildingByCoordinates,BuildingList,CategoryPicker,BuildingPicker). Total suite: 41 suites, 213 passed, 3 skipped.TODO(map):markers (Mapbox public token + St. Olaf style URL) — both intentional, both grep-able.Cross-cutting infrastructure (small additions, all justified)
jest/file-mock.js+moduleNameMapperinjest.config.ts— needed because@frogpond/liststransitively requiresreact-native-vector-iconsfont files.@rnmapbox/mapsadded toesmPackagesinjest.config.ts— itssetup-jesthelper ships ESM.view.view as nevercast insource/views/home/index.tsx— adding three names toRootViewsParamListexceeded TypeScript's overload-resolution limit onnavigation.navigate.~/.netrcwrite step added tocheck.ymlandcocoapods.yml(matches the existingci_post_clone.shpattern for Xcode Cloud).Manual test plan (iOS)
Follow-up issues to file
carleton.api.frogpond.tech) to St. Olaf, once a St. Olaf GeoJSON endpoint and Mapbox style exist.MapReporterView(the "report a building data issue" flow — deliberately skipped from this PR's scope).https://claude.ai/code/session_01PMi93zZdHERYyHUZrv7sGz
Generated by Claude Code