Skip to content

Mobile-web parity, share embeds, locate on grant, no mini-map (#129, #135, #136, #137) - #33

Merged
theobong merged 109 commits into
mainfrom
feat/mobile-web-parity-share-embeds
Sep 23, 2026
Merged

theobong merged 109 commits into
mainfrom
feat/mobile-web-parity-share-embeds

Conversation

@theobong

@theobong theobong commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Resolves civfix/issue-tracker#129
Resolves civfix/issue-tracker#135
Resolves civfix/issue-tracker#136
Resolves civfix/issue-tracker#137

What changed

  • #129: On a phone browser, reports, events, posts, profiles, settings and organizations now open as full pages that slide in over the tab, like the mobile app, and the page respects the notch and the app-download banner. Web only.
  • #135: When you allow location for the first time, the map now moves to where you are instead of staying on the last place you looked. Web and mobile.
  • #136: Links to reports, events, posts, organizations and profiles now show a rich card (title, details, picture) when pasted into iMessage, Slack or WhatsApp. Shared from web and mobile.
  • #137: Event pages no longer show a small map at the top, and report and event pages get a "Show on map" link under the address that takes you to the pin on the main map. Web and mobile.

Before you start

Verify

#129 — full pages on a phone browser — [Web] phone width

Use a phone, or the browser's phone emulation.

  1. Open civfix.dev — Expect: the bottom tab bar shows "Home", "Map", "Messages" and "Report".
  2. Tap a post in the "Home" feed — Expect: the post slides in from the right as a full page and the bottom tab bar hides.
  3. Tap the author's name on the post — Expect: their profile slides in over the post.
  4. Tap the back arrow at the top left ("Back") — Expect: the profile slides off to the right and the post is there again.
  5. Tap "Back" again — Expect: the "Home" feed is back at the same scroll position and the tab bar shows again.
  6. Tap "Map", then tap a report pin — Expect: a full "Report" page slides in, with no pull-up sheet and no tab bar.
  7. Tap "Back" — Expect: you are on the map with the tab bar showing.
  8. Signed in as the citizen, tap your avatar at the top ("Open profile") — Expect: the "You" page slides in as a full page.
  9. Tap the gear at the top right ("Settings") — Expect: "Settings" slides in over "You".
  10. Tap "Back" twice — Expect: first "You", then the tab you started on, with the tab bar showing.
  11. Open civfix.dev/orgs/ followed by the organization's handle — Expect: the organization page opens as a full page with no tab bar.
  12. On a real phone, tap the search box ("Events, reports, streets…") — Expect: the page does not zoom in.
  13. On an iPhone in Safari, with the "Faster reporting in the app" banner showing, open any report — Expect: the page header starts below the banner; nothing is hidden behind it.
  14. Tap "Dismiss" on the banner — Expect: the banner goes away and the page header moves up, clear of the notch.
  15. Turn a notched iPhone to landscape — Expect: the content stays clear of the notch on both sides and the tab bar stays clear of the home bar.

#129 — the desktop layout is unchanged — [Web] desktop

  1. Open civfix.dev in a wide window and click a report pin — Expect: the report opens in the side panel next to the map, with no slide-in page.

#135 — the map moves to you when you allow location — [Web] desktop and phone

Set civfix.dev's location permission in the browser to "Ask" before each step. Open the map once beforehand and pan to another city so a remembered view exists.

  1. Reload civfix.dev — Expect: the map shows the other city and the browser asks for your location.
  2. Choose Allow — Expect: the map flies to your location at street level and the "Your location" dot appears.
  3. Reset the permission to "Ask", reload, drag the map before answering, then choose Allow — Expect: the map stays where you dragged it.
  4. With permission now allowed, pan to another city and reload — Expect: the map opens on that city and does not fly to you.
  5. On a phone browser, reset the permission to "Ask", open civfix.dev, tap "Map" and choose Allow — Expect: the map flies to your location.

#135 — the map moves to you when you allow location — [Mobile]

Fresh install, so location has never been asked.

  1. Finish onboarding with "Look around first" — Expect: the "See what's happening near you?" sheet appears over the map.
  2. Tap "Use my location" — Expect: the iOS location dialog appears.
  3. Choose Allow While Using App — Expect: the map moves to your location at street level.
  4. Reinstall. In the simulator set Features > Location to None, tap "Use my location", allow, then set a Custom Location within 15 seconds — Expect: the map first shows your rough area, then flies to the custom location when it arrives.
  5. Repeat step 4, but drag the map before setting the custom location — Expect: the map stays where you dragged it when the location arrives.
  6. Reinstall and tap "Continue with approximate location" on the sheet — Expect: the sheet closes, no iOS dialog appears and the map shows your rough area.
  7. Tap the arrow button on the map ("Recenter on my location") — Expect: the iOS location dialog appears, and after Allow the map moves to your location.

#136 — report link cards — [Web] and [Mobile]

  1. As the citizen, open your photo report and tap the options button next to the title ("Report options") — Expect: a menu opens.
  2. Tap "Share" — Expect: web: the share sheet opens, or the toast "Link copied" shows; mobile: the iOS share sheet opens. The link ends in the report's reference code in capitals.
  3. Paste the link into iMessage or Slack — Expect: a large card titled like "Graffiti in Los Angeles on civfix", with the status, title and description underneath and the report's first photo.
  4. Open "Report options", tap "Hide from map", wait five minutes, then paste the same link again — Expect: a plain civfix card: title "civfix" with "Report neighborhood issues, browse the map, and join local cleanups. civfix connects residents and city services."
  5. Open "Report options" and tap "Show on map again" to undo.

#136 — event link cards — [Web] and [Mobile]

Signed in as the host.

  1. Open the public event and tap the share button next to the title ("Share this event") — Expect: the share sheet opens, or the toast "Link copied" shows.
  2. Paste the link into iMessage or Slack — Expect: a large card titled " on civfix", with the date and time, the host name and the description, and the cover image.
  3. Share and paste the "Unlisted" event — Expect: a card titled " on civfix" with only the date and "A volunteer event on civfix", and the civfix logo image instead of any cover.
  4. Share and paste the "Private" event — Expect: the plain civfix card (title "civfix").

#136 — post link cards — [Web] and [Mobile]

Needs the backend PR deployed to staging.

  1. As the citizen, in the "Home" feed tap "Share" under your photo post — Expect: the "Share post" sheet opens.
  2. Web: tap "Share another way". Mobile: tap "Copy link" — Expect: the share sheet opens, or the toast "Link copied" shows.
  3. Paste the link into iMessage or Slack — Expect: a large card titled " (@) on civfix", with the post text and the post's photo.
  4. On the text-only post, tap "More options for this post", then "Copy link" — Expect: the toast "Link copied".
  5. Paste it — Expect: a card with your name and handle, the post text, and the civfix logo image.
  6. Delete the text-only post with "Delete post" and "Yes, delete post", wait five minutes, then paste its link again — Expect: the plain civfix card.

#136 — organization and profile link cards — [Web] and [Mobile]

  1. Open the organization page and tap the share button in its header ("Share a link to this organization") — Expect: the share sheet opens, or the toast "Link copied" shows.
  2. Paste the link — Expect: a small square card with the logo, titled " (@) on civfix", with its verified label if it has one, its event count and its description.
  3. On web, open another citizen's profile, copy the address from the address bar and paste it — Expect: a card titled " (@) on civfix" with their bio, and their photo as a small square if they have one.

#137 — event pages without the small map — [Web] and [Mobile]

  1. Open an event without a cover — Expect: the page starts with the title; there is no small map and no empty picture area at the top, including while it loads.
  2. Open the public event with a cover — Expect: the cover image fills the top of the page.

#137 — "Show on map" — [Web] desktop

  1. Open a report in the side panel — Expect: under the address are the copy button ("Copy address"), the maps button ("Open in a maps app") and a "Show on map" link.
  2. Click the address text — Expect: nothing happens.
  3. Click "Show on map" — Expect: the side panel stays open and the map glides to the pin, clear of the panel, with the pin highlighted.
  4. Repeat on an event — Expect: the map glides to the event and it is highlighted.

#137 — "Show on map" — [Web] phone width

  1. From the "Home" feed, open a report — Expect: it opens as a full page with "Show on map" under the address.
  2. Tap "Show on map" — Expect: you land on the "Map" tab with the tab bar showing, the map glides to the pin and the pin is highlighted.
  3. Drag the map — Expect: the highlight goes away and the map does not fly again.
  4. Tap "Show on map" on a report far outside the current map area — Expect: the pin appears highlighted after the map arrives.
  5. Tap "Show on map" again, then tap "Home" and come back to "Map" — Expect: no pin is still highlighted.

#137 — "Show on map" — [Mobile]

  1. From "Home", open a report — Expect: "Show on map" sits under the address.
  2. Tap "Show on map" — Expect: the app switches to the "Map" tab, flies to the pin and highlights it.
  3. Pan the map with a finger — Expect: the highlight goes away.
  4. Repeat on an event — Expect: the map flies to the event and highlights it.
  5. Send yourself a report link in Messages, tap it, then tap "Show on map" — Expect: you land on the app's main map at the pin, not back on the previous screen.

Regression

Moving around full pages on a phone browser — [Web] phone width

  1. Tap the search box ("Events, reports, streets…"), search for a street, open a result, then tap "Back" — Expect: the result opened as a full page, and you return to the search with your text still there.
  2. Tap "Messages", open a conversation, tap a member's avatar, then "Back" — Expect: the profile slides in over the conversation and back returns to it.
  3. Open "Settings", then "Language" — Expect: "Language" slides in over "Settings".
  4. Open three pages in a row, then use the browser's back and forward buttons — Expect: each page comes back without sliding and the address bar matches the page shown.
  5. Reload while on a page — Expect: the same page reappears.
  6. On "Map", long-press the map and tap "Report an issue here" — Expect: "Add here" opens as a bottom sheet over the map, not a full page.

Space around the notch and home bar — [Web] iPhone Safari

  1. Signed in, open a post from the feed and look at the reply box ("Post your reply") at the bottom — Expect: one gap above the home bar, not two.
  2. Copy a link from any "Share" — Expect: the "Link copied" toast sits just above the tab bar or reply box, not floating high.
  3. On a report, tap "Open in a maps app" — Expect: the sheet with "Open in Apple Maps" and "Open in Google Maps" clears the home bar once.
  4. As the host, open civfix.dev/manage, in portrait and landscape — Expect: the bottom tabs clear the home bar and no text is under the notch.
  5. Open the host's public event page at civfix.dev/e/ plus its page name, and civfix.dev/legal/terms, in portrait and landscape — Expect: the text starts below the notch and is not hidden in landscape.

"Show on map" and the report pin tools — [Web] [Mobile]

  1. Open "Map layers" and tap "Clear all", then open a report and tap "Show on map" — Expect: the map still shows that one report, highlighted.
  2. Tap the highlighted pin — Expect: the report opens and the highlight goes away.
  3. On web, after "Show on map", press the browser back button — Expect: the report page comes back and nothing stays highlighted on the map.
  4. On web, long-press the map, tap "Report an issue here", then "Cancel" — Expect: the dropped pin goes away and the map is as before.

Post addresses on web — [Web]

  1. Signed out, open a post link — Expect: "Sign in to see this conversation" and "Replies are only visible to signed-in neighbors."
  2. Sign in from that screen — Expect: you return to the same post with its replies and the "Post your reply" box.
  3. Signed in, open a post from notifications — Expect: the post with its replies and the "Post your reply" box, not a single post with a "View conversation" button.
  4. Reload — Expect: the same screen comes back.
  5. Open a post from a post link inside a chat, then reload — Expect: the same replies screen before and after the reload.
  6. Open a report link, an event link, a profile link and an organization link, then reload each — Expect: each opens its own page and stays on it after reload.

Link cards for every other link — [Web]

  1. Paste a report link that uses the report's long id instead of its reference code — Expect: the same report card.
  2. As the host, open the public event, tap "Host dashboard", then "Share event", and paste the link — Expect: the same event card.
  3. Paste a profile link with capital letters in the handle — Expect: that person's card.
  4. Paste a report link with the reference code written in lowercase letters — Expect: the plain civfix card.

When the web map moves to you — [Web]

  1. With location already allowed, tap "Recenter on my location" — Expect: the map flies to you.
  2. Block location for the site, then tap "Recenter on my location" — Expect: the map goes to your rough area.
  3. Reset the permission to "Ask", open a report link directly in a new tab and choose Allow — Expect: the map stays on the report's pin.
  4. Reset the permission to "Ask", open a report, tap "Show on map", then choose Allow — Expect: the map stays on the highlighted pin.

Location on mobile — [Mobile]

  1. With location allowed, tap "Recenter on my location" — Expect: the map moves to you straight away.
  2. With location denied in iOS Settings, tap "Recenter on my location" — Expect: the map goes to your rough area.
  3. Tap "Report" and take a photo with "Take photo" — Expect: the report's location is filled in as before.
  4. With the app closed and location allowed, tap a report link in Messages, then tap "Show on map" straight away — Expect: the map stays on the highlighted pin and does not jump to your location.

The address row — [Web] [Mobile]

  1. Tap the address text, then long-press it — Expect: nothing happens either way.
  2. Tap "Copy address" — Expect: the toast "Address copied".
  3. On iPhone, tap "Open in a maps app" — Expect: a sheet with "Open in Apple Maps" and "Open in Google Maps".
  4. Register for an event and open "Your ticket" — Expect: the address shows copy and maps buttons, and no "Show on map".
  5. In "Settings", "Language", pick "Deutsch" and open a report — Expect: the link reads "Auf der Karte anzeigen" and fits without being cut off. Switch back to English.

The app-download banner — [Web] phone browser

  1. With the banner showing, open "Home", the search box, "Messages" and a report — Expect: each starts just below the banner with no extra gap.
  2. Tap "Dismiss" — Expect: the content moves up to the top.

Leaving a linked screen on mobile — [Mobile]

  1. Open a report from a link in Messages, then tap "Back" — Expect: you go back to where you were, as before.
  2. Signed in, tap your avatar to open "You", tap the gear ("Settings"), then "Sign out" — Expect: you land on the app's main screen, signed out.

Event pages — [Web] [Mobile]

  1. As the host, edit the public event and look under "Cover image" — Expect: the hint reads "Shown at the top of your event page."

Dark mode, reduced motion and home-screen app — [Web] phone width

  1. In "Settings", "Appearance", choose "Dark", then open a report — Expect: the page slides in with a dark background and no white flash.
  2. Turn on Reduce Motion in the phone's settings, then open and close a report — Expect: the page appears and disappears without sliding.
  3. Add civfix.dev to the home screen and open it from the icon — Expect: it opens without the browser's address bar, the icon name is "civfix" and there is no download banner.

When the server can't be reached — [Web] local

Run the web app locally with the API stopped.

  1. Open a post link and a report link — Expect: the app loads, and each page shows its error or sign-in message instead of a blank screen.

Not covered

  • Real iPhone Safari (notch, home-screen app, search-box zoom) is checked only if the tester has an iPhone; desktop phone emulation does not reproduce it.
  • Android phones and Android Chrome are not in this plan.
  • Firefox, and Safari's "Allow Once", may not report a new location permission, so the #135 web move may not happen there.
  • "Show on map" for a report hidden inside a numbered cluster.
  • Profiles have no share button in the app, so profile cards are checked only by pasting the address.
  • Post cards show the plain civfix card until the backend PR is on staging.
  • Long-pressing the separate highlighted pin on mobile.

theobong and others added 30 commits September 15, 2026 23:14
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The announcement is event content, not a private message: targeting decides who is
NOTIFIED, never who may read, so the event-detail section and the detail page are
public and carry no audience chip or delivery counts. Those host-only fields are
absent from the public projection, and that absence IS the gate the card reads.

Nav kinds `announcements` (/cleanups/:id/announcements) and `announcement`
(/cleanups/:id/announcements/:announcementId) are registered across the union,
ALL_DETAIL_KINDS, entryFromPath/pathForEntry, DETAIL_BODY, BODY_LAYOUT and
BodyRouter, plus mobile route wrappers and SHELL_HOST_ROUTE_NAMES. The detail path
is exactly the link the API puts on an announcement notification, so a push tap
deep-links straight into it; notificationNav.test.ts now enumerates both.

ENTRY_IDENTITY_FIELDS gains `announcementId`, which lengthens the entry
discriminator - the four pinned layer-key literals move with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Base automatically changed from feat/feed-and-polish-batch to main September 23, 2026 01:57
@theobong
theobong merged commit 3dac8a9 into main Sep 23, 2026
3 checks passed
@theobong
theobong deleted the feat/mobile-web-parity-share-embeds branch September 23, 2026 02:02
@greptile-apps

greptile-apps Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

Not safe to merge until location grants reliably recenter the map and the required token-based styling is applied.

Fix All in Claude CodeFindings

  1. P1 Location grant is ignored ▶
  2. P2 Use semantic style tokens ▶
Fix with agent prompt
### Issue 1
apps/community-web/src/lib/locate.ts:139-141
If a browser returns a successful geolocation fix but its Permissions API is unavailable, rejects, or remains `"prompt"`, this code reports `prompted: false`. The remembered-camera guard then keeps the map at the prior city instead of flying to the newly granted user location. Treat a successful initial location callback as grant evidence when permission-state queries cannot reliably establish the transition.

### Issue 2
apps/community-web/src/styles/design.css:240-252
These safe-area fallbacks and the input-size floor introduce raw `px` values, including `0px` and `16px`, instead of consuming semantic CSS variables or theme tokens. This violates the repository’s frontend styling directive; define and use semantic tokens for these values. The repository requirement must be satisfied before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

Comment on lines +139 to +141
if (before !== "prompt") return { precise, prompted: false }
const after = await geolocationPermissionState()
return { precise, prompted: after === "granted" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Location grant is ignored

If a browser returns a successful geolocation fix but its Permissions API is unavailable, rejects, or remains "prompt", this code reports prompted: false. The remembered-camera guard then keeps the map at the prior city instead of flying to the newly granted user location. Treat a successful initial location callback as grant evidence when permission-state queries cannot reliably establish the transition.

Artifacts

Evidence from the check

  • The executed Vitest source verbatim stubs browser APIs, triggers a successful position callback, and checks the composed camera decision; it demonstrates the affected path.

Command output from the check

  • The control command completed successfully and shows a detectable grant releases the remembered camera and permits the precise-location fly.

Command output from the check

  • The reproduction command completed successfully and shows all three API variants retain the remembered camera despite receiving precise coordinates.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/community-web/src/lib/locate.ts
Line: 139-141

Comment:
**Location grant is ignored**

If a browser returns a successful geolocation fix but its Permissions API is unavailable, rejects, or remains `"prompt"`, this code reports `prompted: false`. The remembered-camera guard then keeps the map at the prior city instead of flying to the newly granted user location. Treat a successful initial location callback as grant evidence when permission-state queries cannot reliably establish the transition.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment on lines +240 to +252
left: env(safe-area-inset-left, 0px);
right: env(safe-area-inset-right, 0px);
background: var(--paper);
overflow: hidden;
touch-action: manipulation;
--ios-focus-zoom-floor: 16px;
}

body:not(:has(.cf-shell)) {
height: auto;
min-height: 100%;
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Use semantic style tokens

These safe-area fallbacks and the input-size floor introduce raw px values, including 0px and 16px, instead of consuming semantic CSS variables or theme tokens. This violates the repository’s frontend styling directive; define and use semantic tokens for these values. The repository requirement must be satisfied before merging.

Rule Used: # civfix review rules civfix is a live civic-tech platform that will hold government contracts. Review every PR for correctness, security and performance. Flag real defects with evidence; skip style nits that lint already covers. ## Repos - **... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/community-web/src/styles/design.css
Line: 240-252

Comment:
**Use semantic style tokens**

These safe-area fallbacks and the input-size floor introduce raw `px` values, including `0px` and `16px`, instead of consuming semantic CSS variables or theme tokens. This violates the repository’s frontend styling directive; define and use semantic tokens for these values. The repository requirement must be satisfied before merging.

**Rule Used:** # civfix review rules  civfix is a live civic-tech platform that will hold government contracts. Review every PR for **correctness, security and performance**. Flag real defects with evidence; skip style nits that lint already covers.  ## Repos  - **... ([source](https://app.greptile.com/civfix/-/custom-context?memory=39a53925-3d93-4c82-980e-27b67393717d))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

@greptile-apps

greptile-apps Bot commented Sep 23, 2026

Copy link
Copy Markdown

Comments Outside Diff

These findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.

  • P2 Successful initial geolocation grant can be blocked by a remembered map camera ▶

    • Bug
      • When navigator.geolocation succeeds but permission-state detection is unavailable, rejects, or remains prompt, the resolver returns prompted: false. The map's remembered-camera guard therefore prevents adopting/flying to the new precise user location.
    • Cause
      • resolvePreciseCenterAfterPrompt() only marks the callback as prompted when the pre-query is prompt and the post-query reports granted; it has no success-callback fallback for unreliable or absent Permissions API state.
    • Fix
      • Treat a successful initial geolocation callback as a prompt grant when the pre-state/post-state cannot reliably disprove it, or otherwise pass explicit initial-grant evidence to the remembered-camera adoption guard.

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