Skip to content

feat(region-filter): honor ?region= in the URL on every page (#2255) - #2256

Merged
efiten merged 2 commits into
Kpa-clawbot:masterfrom
anieto:feat/region-filter-url-param
Oct 22, 2026
Merged

efiten merged 2 commits into
Kpa-clawbot:masterfrom
anieto:feat/region-filter-url-param

Conversation

@anieto

@anieto anieto commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Fixes #2255.

Only the packets page read ?region= from the hash, with its own one-off code. Live, nodes, observers, channels, analytics and scope audit ignored it, so a link like this opened with whatever region the visitor last picked:

https://<instance>/#/live?region=SJC

We link into our instance from a community directory (one link per region), and wanted those links to land on the live map or node list already filtered, not only on packets.

What changes

public/region-filter.js

  • ?region= (comma-separated, trimmed) overrides the saved selection and is saved like a click in the control, the same as packets did through setSelected().
  • Fresh load: it's applied when the script runs, before any page reads the selection. This matters for live, which fetches nodes before it calls RegionFilter.init().
  • In-app navigation: init() applies it before its await, so a page that loads after init() uses it. If it changed the selection, listeners are told once regions load, so a page that already fetched (live) reloads instead of showing the old region's data under the new region's label.
  • Unknown codes are dropped by the existing cleanup in fetchRegions(). No param, or an empty one, leaves the saved selection alone.

public/packets.js

  • Drops its own _pendingUrlRegion handling; RegionFilter covers it.
  • Removes its region and area listeners in destroy(). They were registered without a handle and outlived the page: after visiting packets, a region change on any other page reloaded packets and replaceState'd the URL to #/packets?... while you were on another page. That already happened with the dropdown on master; the in-app notify above would make it happen on every region link.

Tests

tests/unit/test-region-filter-url-param.js, 14 assertions:

  • URL region overrides the saved selection; comma lists, whitespace, unknown codes, empty param, no param, no hash
  • fresh load: selection is set before init() is called, and init() doesn't notify
  • in-app navigation: a changed region notifies listeners once, the same region doesn't
  • selection is saved to localStorage
  • packets keeps handles for both listeners and removes them in destroy()

With region-filter.js from master, 5 of the first 8 assertions fail.

How I checked:

  • test-all.sh suites all pass except test-issue-1956-release-routing.js and test-preflight-xss-gate.js. Both also fail on unmodified master on macOS: they shell out to bash-4 features (mapfile, and a syntax error in check-xss-sinks.sh under bash 3.2).
  • eslint@8.57.1 on the two files: 0 errors, 3 warnings, the same as master.
  • Browser: served public/ locally with /api proxied to a live instance and checked the requests each page made:
    • fresh load of #/nodes?region=AUS, #/live?region=DFW, #/packets?region=HOU: the first request carries the URL's region and the dropdown shows it
    • packets → #/live?region=DFW: live fetches with the old region, then reloads with DFW; the dropdown shows DFW
    • packets → #/nodes?region=AUS: no packets requests, and the URL stays on #/nodes

I couldn't run the Playwright E2E suite locally (no Go toolchain on this machine).

Only the packets page read ?region= from the hash, with its own one-off
code. Live, nodes, observers, channels, analytics and scope audit ignored
it, so a link like #/live?region=SJC opened with whatever region the
visitor last picked.

RegionFilter now applies the param itself. It overrides the saved
selection and is saved like a click in the control, as packets did. On a
fresh load it's applied when region-filter.js runs, before any page reads
the selection (live fetches nodes before calling init). On in-app
navigation init() applies it and, if that changed the selection, notifies
listeners once regions load, so a page that already fetched reloads.
Unknown codes are dropped by the existing cleanup in fetchRegions().

packets.js drops its own ?region= handling, and now removes its region
and area listeners in destroy(). They outlived the page, so after
visiting packets a region change on any other page reloaded packets and
replaceState'd the URL to #/packets.

@efiten efiten left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this fixes #2255 the way the issue describes. The packets cleanup also stops packets reloading and rewriting the URL from other pages.

Checked on 477dc531:

  • test-region-filter-url-param.js (14/14), test-region-filter-setselected.js (4/4) and test-issue-2012-clear-filters-selection.js pass.
  • CI is green.
  • The RegionFilter.onChange listeners on the other pages (analytics, channels, live, nodes, observers, scope audit) already keep a handle and remove it in destroy(), so packets was the only one left over.

One change before merge:

1. Unknown codes are only dropped on the first init() of a page load. fetchRegions() returns early once _loaded is set (region-filter.js:48), so on in-app navigation applyUrlRegion() keeps codes that are not configured. I ran region-filter.js from this branch in a vm context with regions SJC and SFO:

  • init() on #/packets, then hash #/nodes?region=XXX and init() again: getSelected() is ['XXX'], regionQueryString() is &region=XXX, and ["XXX"] is saved to localStorage.
  • The same with region=SJC,XXX: regionQueryString() is &region=SJC%2CXXX.

On a fresh load the existing cleanup does drop them, which is what the unknown-codes test covers. Could applyUrlRegion() keep only the codes in _regions when _loaded is true, with a test for the in-app case? The PR body line "Unknown codes are dropped by the existing cleanup" would then hold for both paths.

One question, not blocking:

2. Two loads on in-app navigation to a page that fetches after init(). From reading the code (not measured in a browser): nodes calls loadNodes() in its init (nodes.js:584), and analytics calls loadAnalytics() (analytics.js:307). Both already use the new selection, because applyUrlRegion() runs before the await. The notify after fetchRegions() then runs their listener, which loads again. For nodes that is a second full paginated /api/nodes fetch, because the listener clears _allNodes. Only live needs the notify, because it fetches before init(). This only happens when a link inside the app changes the region, not on the directory links from #2255. Which do you prefer:

  • (a) leave it as is,
  • (b) move live's RegionFilter.init() before its first loadNodes() and drop the notify, or
  • (c) something else you had in mind?

fetchRegions() only cleans the selection on its first call, so after regions
had loaded, applyUrlRegion() kept codes that aren't configured. Filter them
against the loaded regions; with none left, that's all regions, the same as
the cleanup on a fresh load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anieto

anieto commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for checking this so carefully.

1. Fixed in 9c2ea45. Once regions have loaded, applyUrlRegion() keeps only codes in _regions, and falls back to all regions if none are left, the same as the fresh-load cleanup. Added two tests for in-app navigation: SJC,XXX → ['SJC'], and XXX alone → all regions, with nothing saved.

2. I'd prefer (a) for this PR. Only in-app links that change the region trigger the second load. Doing (b) means moving live's RegionFilter.init() ahead of its awaited loadNodes() in init(), which is a bigger reordering than I'd like to bundle here. Happy to open a follow-up issue for it if you'd like.

@efiten efiten left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Checked 9c2ea45a: applyUrlRegion() now drops unknown codes once regions have loaded. The url-param tests pass 16/16, and with the filter line reverted both new in-app tests fail. (a) for point 2 is fine, a follow-up issue for it is welcome. Thanks.

@efiten
efiten merged commit dbcdca9 into Kpa-clawbot:master Oct 22, 2026
12 checks passed
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.

?region= in the URL only works on the packets page

2 participants