Skip to content

ERA-13861: Prompt for location permission on 'Use my location' and surface denied state - #1706

Open
jeslefcourt wants to merge 8 commits into
developfrom
ERA-13861
Open

ERA-13861: Prompt for location permission on 'Use my location' and surface denied state#1706
jeslefcourt wants to merge 8 commits into
developfrom
ERA-13861

Conversation

@jeslefcourt

@jeslefcourt jeslefcourt commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements ERA-13861 (from support request ERCS-7991): on community input forms, clicking Use my location without a granted browser permission silently did nothing, because a page-load geolocation request had already consumed the browser's permission prompt.

  • GeoLocationWatcher is no longer mounted on the public community route, so the browser's permission prompt fires at click time. All other routes keep their existing load-time behavior.
  • When permission is denied, the location picker shows "Location sharing blocked by browser" under the coordinate example text and ghosts the button (aria-disabled + click guard, keeping the popover focus trap intact; aria-describedby announces the reason). Updates live via the Permissions API change event if the user denies mid-session.
  • The denied-state UI applies on all surfaces; GetUserLocationButton's error toast is suppressed for PERMISSION_DENIED only (timeout / position-unavailable still toast).
  • Browsers without Permissions API geolocation support (legacy Safari) fall back to a memoized one-shot getCurrentPosition probe, kicked at community page load.
  • New permissionBlockedMessage string translated in all six locales; I18N_FILES_VERSION bumped to 1.52.
  • Also carries the community-form container-fill change from the closed feature-environment PR ERA-13788 PR for Feature Environment (close when done) #1695 (former branch ERA-13788).

Test plan

  • New coverage: probe utility unit tests; MenuPopover denied/granted/prompt, live change event, Safari fallback path, ghosting, and focus-trap cycling; GetUserLocationButton toast suppression and click guard; CommunityPage probe gating.
  • jest across LocationPicker, GetUserLocationButton, CommunityPage, utils, ReportManager, GeoLocationWatcher and all other consumers: all suites pass.
  • eslint / stylelint on touched files: no new problems (pre-existing issues untouched).

🤖 Generated with Claude Code

jeslefcourt and others added 4 commits August 10, 2026 14:49
The report detail view is 46rem wide because that is the sidebar panel
width. The community page renders it standalone in a centered full-viewport
container, leaving the panel marooned with white space on either side.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rface denied state

The public community page no longer requests geolocation at load, so the
browser permission prompt fires when the reporter clicks 'Use my location'.
When permission is denied, the location picker shows 'Location sharing
blocked by browser' and ghosts the button (aria-disabled, click guard) on
all surfaces; the permission-denied error toast is removed. Browsers
without Permissions API geolocation support fall back to a memoized
one-shot getCurrentPosition probe, kicked at community page load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeslefcourt jeslefcourt self-assigned this Aug 18, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@jeslefcourt
jeslefcourt requested a balanced review from Copilot August 18, 2026 18:34
@jeslefcourt

Copy link
Copy Markdown
Contributor Author

@claude review

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 community event forms to handle geolocation permission denial and expands their layout.

Changes:

  • Adds permission detection, blocked-state UI, accessibility behavior, and tests.
  • Avoids mounting the global geolocation watcher on community routes.
  • Adds translations and wider community-form styling.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/location/permission-probe.js Adds memoized permission probing.
src/utils/location/permission-probe.test.js Tests probe behavior.
src/ReportManager/ReportDetailView/index.js Applies community-specific styling.
src/ReportManager/ReportDetailView/styles.module.scss Widens community forms.
src/LocationPicker/MenuPopover/index.js Adds denied-state handling.
src/LocationPicker/MenuPopover/index.test.js Tests permission and focus behavior.
src/LocationPicker/MenuPopover/styles.module.scss Styles blocked messaging and hover behavior.
src/GetUserLocationButton/index.js Adds ghosting, guards, and toast suppression.
src/GetUserLocationButton/index.test.js Tests disabled and error behavior.
src/GetUserLocationButton/styles.module.scss Styles the ghosted button.
src/CommunityPage/index.js Adds legacy-browser probing.
src/CommunityPage/index.test.js Tests probe gating.
src/index.js Conditionally mounts the geolocation watcher.
src/i18n.js Bumps translation cache version.
public/locales/en-US/components.json Adds English blocked message.
public/locales/es/components.json Adds Spanish blocked message.
public/locales/fr/components.json Adds French blocked message.
public/locales/ne-NP/components.json Adds Nepali blocked message.
public/locales/pt/components.json Adds Portuguese blocked message.
public/locales/sw/components.json Adds Swahili blocked message.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/index.js Outdated
Comment thread src/CommunityPage/index.js
Comment thread src/LocationPicker/MenuPopover/index.js Outdated
…k on rejected permission query

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeslefcourt

Copy link
Copy Markdown
Contributor Author

Addressed Copilot's review in 2381481:

  • src/index.js basename mismatch — fixed. The gate now matches prefixedRoutePattern(REACT_APP_ROUTE_PREFIX, APP_ROUTES.COMMUNITY) (new tested helper in utils/navigation.js), so a non-root route prefix no longer defeats it.
  • MenuPopover permissions.query rejection — fixed. A rejected query now falls through to the getCurrentPosition probe instead of reporting "not denied", so the blocked message and ghosted button still appear.
  • CommunityPage load-time probe — intentionally kept. On browsers without Permissions API geolocation support there is no way to detect a previously denied permission without a probe; deferring it to the click can't surface the blocked state before interaction. This tradeoff only affects legacy Safari; Permissions-API browsers never request geolocation at community page load.

@jeslefcourt

Copy link
Copy Markdown
Contributor Author

@claude review

…sion probe

A denied click now flips the inline blocked state directly, covering
one-time prompt dismissals that leave the permission at 'prompt' with no
change event. The probe memoizes only granted results, uses cheap
low-accuracy options, and the denied-error predicate requires the
PERMISSION_DENIED constant. The status live region is always mounted so
screen readers announce the denial, and prefixedRoutePattern normalizes
a missing leading slash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeslefcourt

Copy link
Copy Markdown
Contributor Author

Since the @claude review GitHub app never responded, a local Claude multi-angle review (8 finders + verification) ran instead. Verified findings addressed in 6fb028c:

  • Silent denied click — dismissing/one-time-blocking the native prompt leaves the permission at prompt (no change event), so the click produced no toast and no inline message. A denied click now flips the inline blocked state directly (onPermissionDenied), covering every detection path.
  • Probe staleness — only granted results are memoized now, so a user who re-enables location isn't ghosted until reload; probe uses cheap low-accuracy/cached-fix options instead of a 10s GPS spin.
  • Denied-error predicate — now requires the PERMISSION_DENIED constant, so non-geolocation errors with code: 1 toast instead of being silently swallowed.
  • Live region — the role="status" message container is always mounted (text toggles), so screen readers actually announce the denial.
  • Promise-shape hardening in the permission effect, denied string dedup, leading-slash normalization in prefixedRoutePattern, and comment cleanups.

Reviewed-but-not-changed (deliberate): the load-time Safari probe stays (product decision — only way to detect a prior denial without the Permissions API); no watcher/redux refactors (non-community behavior is frozen for this PR); the stored-location fast path can serve a stale fix on the community page if a reporter moves between sites — flagged separately as a possible follow-up.

…mmunity info loads

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeslefcourt

Copy link
Copy Markdown
Contributor Author

Two approved follow-ups in 1d0137d:

  • "Use my location" trusts the stored position only while fresh (< 60s by its timestamp); anything older does a real device read. This fixes the community page silently reusing the first fix when a reporter moves between sites. QA note: in the authenticated app this means the button now frequently shows the brief loading overlay and re-reads the device instead of returning instantly — the stored fix's timestamp is often older than a minute on a stationary device even with the watcher running. That's intentional (current position beats instant-but-stale); if the overlay proves annoying, a cheap follow-up is dispatching watch updates to the store as they arrive.
  • The community page's Safari probe now waits for the community info to load successfully — invalid community URLs and the error screen never trigger a geolocation prompt.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

🚀 PR Environment Deployed

App Sync Health Image
pr-web-era-13861 ✅ Synced ✅ Healthy 3967a4a351c902f9b5a977d79139f76660712024

Access: https://era-13861.dev.pamdas.org

View in ArgoCD

…o the stored position on transient errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeslefcourt

Copy link
Copy Markdown
Contributor Author

Regression fix in $(git rev-parse --short HEAD): the freshness check made every stale-store click do a fresh high-accuracy read with maximumAge: 0, which desktops often can't satisfy — clicking "Use my location" toasted "Could not read your current location" while the map jump (store-backed) worked. The read now accepts browser-cached fixes up to 60s old, and a transient failure (timeout / position unavailable) falls back to the stored position instead of toasting. Denied errors still surface the blocked state, and the toast remains for the no-position-at-all case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants