Show the reader's current location on the map and in the add-place form #198
Loading…
Reference in a new issue
No description provided.
Delete branch "current-location"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Task 254 (#78). ADR-0167.
One shared
CurrentLocationButtonis the only thing that ever asks for the Geolocation permission, and only on a press —Homefloats it top-right of the map ("Show my location", plots a non-interactive dot outside the cluster layer and takes the map there via the existingMapZoomRequest),PlaceFormputs it beside the coordinate fields ("Use current location", routes throughHandlePickso a located coordinate is indistinguishable from a typed one downstream).geolocation.jsfeature-detectsnavigator.geolocationand never rejects;window.isSecureContextdistinguishesinsecurefromunsupported, the ADR-0142 posture applied to a second secure-context API.30 new tests; WebUI suite 885 passing, 4 skipped. Every new test was watched failing against a deliberate break of the behaviour it guards — the press-only import, the coordinate-completeness guard, each distinct failure message, the message reset, the
JSDisconnectedExceptioncatch,role="alert", the redundant-call guard, the first-render plot, Home'sCurrentLocation/zoom/inert wiring, and the form's recentre and full-precision handling.Two judgements a reviewer may want to overturn: pressing "Show my location" moves the map to it at zoom 16, and a refused permission shows "Location permission was refused. Allow it in your site settings, then try again." rather than anything more specific about which browser.
Verdict: changes needed
Two blockers and three smaller items; inline.
MapZoomRequestdoes not survive alongside PR #197 as both branches stand. #197 deletes the type,LeafletMap.ZoomRequest,ExplicitZoomLevel,_renderedZoomRequestId,Home._zoomRequestandHandleZoomToPlace; this PR gives all of them a new caller. Whichever merges second fails to compile. See the inline note onHome.razor.csfor the resolution and for what has to change in #197 and ADR-0166.The secure-context detect is wrong for this particular API, and wrong precisely on the deployment it was written for — inline on
geolocation.js.navigator.geolocationis not[SecureContext]-gated the waynavigator.shareandnavigator.clipboardare.Zoom 16 on press zooms a close-in reader out, and the jump is then persisted over their saved viewport — inline on
Home.razor.cs.The
inerttest does not discriminate — inline onHomeTests.cs.Coverage as reported (90.86%) is below the
PlaceMark.WebUIfloor of 91.5 incoverage-baseline.json, so the ratchet step fails on that number. Reconcile against CI's own measurement for run #808 before merging rather than a locally measured figure.CurrentLocationButton_ModuleUnreachable_ShowsTheGenericMessageRatherThanThrowingthrows fromcurrentPositionAsync, not from theimport; its comment claims the module failing to load is covered, and it is not. Cover it or narrow the comment.@ -234,0 +246,4 @@/// and the same exception ADR-0101 already carves out for a reader who asked directly. Plotting the/// dot without moving the view would answer "where am I" with a marker somewhere off-screen./// </summary>private void HandleCurrentLocation(MapCoordinates coordinates)MapZoomRequestmoves to a fixedExplicitZoomLevelof 16, so a reader who had zoomed to 17-19 is zoomed out by "Show my location" — andHandleViewportChangedthen persists the new centre and zoom throughViewportStore, so the view they had is gone for good. There is no way back.Centre on the dot at the map's current zoom, or at
max(current, 16)if you want a floor for a reader who was looking at the whole world. That needs the zoom to become part of the request (ornullmeaning "keep what the map has") rather than a shared constant, since ADR-0132's 16 was chosen for reading a single place, not for answering "where am I".@ -234,0 +249,4 @@private void HandleCurrentLocation(MapCoordinates coordinates){_currentLocation = coordinates;_zoomRequest = new MapZoomRequest(coordinates, Guid.NewGuid());Cross-branch break. PR #197 (
place-detail-panel-controls, task 251, ADR-0166) deletesMapZoomRequest.cs,LeafletMap.ZoomRequest,ExplicitZoomLevel,_renderedZoomRequestIdandHome._zoomRequest/HandleZoomToPlace. There is no textual conflict worth trusting here, but whichever branch merges second stops compiling.Resolution: the type survives — it is no longer dead, because this is a second legitimate consumer of "centre and zoom here, again if asked again". #197 should be reduced to removing the button,
PlaceDetailPanel.OnZoomToPlaceandHome.HandleZoomToPlace, keeping the mechanism; ADR-0166 is overclaiming where it says the chain went wholly, andMapZoomRequest's andLeafletMap.ZoomRequest's doc comments both need re-pointing from "PlaceDetailPanel's own zoom button" to task 254. Then rebase whichever branch merges second onto the other and re-run — a green run on a base predating the other PR proves nothing about this.If you would rather drop
MapZoomRequestfrom this PR instead, whatever replaces it still needs a per-press identity:CurrentLocationon its own is deduped by value inOnParametersSetAsync, so a second press after the reader has panned away would reachmap.jswith nothing and appear to do nothing.@ -0,0 +46,4 @@// Optional chaining on `navigator.geolocation` itself, not merely a `typeof navigator.geolocation`// check: the property is absent entirely in an insecure context, and reading `.getCurrentPosition`// off it directly would throw rather than degrade.if (typeof navigator.geolocation?.getCurrentPosition !== "function") {navigator.geolocationis not removed on insecure origins. Unlikenavigator.shareandnavigator.clipboard, it is not[SecureContext]in its IDL — Chrome and Firefox keep the property and fail the call withPERMISSION_DENIED(Chrome additionally logs its own deprecation warning). So onhttp://192.168.1.2:5017this detect passes,getCurrentPositionruns, and the reader is told "Location permission was refused. Allow it in your site settings, then try again." — a false claim they will act on and find nothing, which is the exact failure ADR-0142 exists to prevent.Test
window.isSecureContextfirst and returninsecurebefore touching the API at all; keep the existing check afterwards for the genuinely-unsupported case. That also avoids Chrome's own console warning, since the call never happens.ADR-0167's "it is not merely restricted over plain HTTP — it is absent.
navigator.geolocationisundefined, not a callable that fails" needs correcting with it, as does the comment block at the top of this file.@ -1076,0 +1133,4 @@/// panel that has just made everything behind it background content (ADR-0086)./// </summary>[Fact]public void Home_ModalTaskPanelOpen_MarksTheCurrentLocationControlAsBackgroundContent()This passes unchanged if the binding were hardcoded
inert="true"— it only ever observes the open case, so it does not discriminate the thing it guards. Add the closed case asserting the attribute is absent while no modal-task panel is open; without it, a control permanently unreachable to the keyboard would ship green.d8e69b42ea3938a5f29eActioned in
3938a5f, rebased ontob3e1239.geolocation.jsreadswindow.isSecureContextfirst and returnsinsecurebefore the API is touched; the feature detect follows and now only answers "this browser has none". Compared againstfalseexplicitly, so a browser too old to implement the property is not refused. ADR-0167's Context, Decision and the file's own comment block are rewritten — the false claim that the property is absent is replaced with why it is not, and why that makes the ordering load-bearing. NewGeolocationSecureContextFirstTestsholds the ordering from source text (best-effort, states what it cannot see); watched red against both the swap and the deletion.MapZoomRequestkept. Its doc comment andLeafletMap.ZoomRequest's now name the mechanism and both consumers rather than the button.MapZoomRequestgainsint? MinimumZoom:nullkeeps ADR-0132's fixed level exactly, a value routes to a newmap.jscentreAtLeast, which isMath.max(map.getZoom(), minimumZoom).Homenames its own floor of 16 with its own reasoning rather than sharingExplicitZoomLevel. Centring at the current zoom with no floor was rejected: the whole-world default (zoom 2) would put the dot in an ocean. TheMath.maxitself is not provable from this stack — named in the ADR alongside the other JS gaps.Closed case added for
inert; red withinert="true"hardcoded. The interop-failure theory is now 4 cases — both exception types against both theimportand the call — with the import failing through a substitutedIJSRuntime, since bUnit refusesSetup<IJSObjectReference>andSetupModulecannot be made to fail. Dropping theJSExceptioncatch reddens the import case, so the load path is genuinely covered rather than claimed.My baseline reading was taken in Debug; CI builds and tests in Release. Debug emits ~950 extra coverable lines in this assembly (3737 vs 2786), which is the whole discrepancy. Re-measured in Release:
main91.565%, this branch 91.6433%, floor 91.5. Floor unchanged.Full suite on the rebased branch: WebUI 895/4 skipped, Architecture 135, Domain 39, Infrastructure 360, Api 622.
Verdict: changes needed
Nothing in the runtime code. All five findings from the previous round are correctly actioned and I checked each rather than taking the account: the secure check is the first statement of the only entry point with no module-level access ahead of it;
=== falseis the right test andundefinedfalls through as documented;centreAtLeast'sMath.maxandsetView(center, zoom)argument order are right andMinimumZoom = nullemits the identicalsetViewcall ADR-0132's consumer had;ThrowingJSRuntimedoes fail theimportitself; the closedinertcase is there. Rebase ontob3e1239is clean and the two commit subjects are fine.Two things left.
ADR-0167 asserts something false, and leans on it. "This project has no JavaScript test runner" —
tests/PlaceMark.E2E.Testsis a 46-test Playwright suite that runs in thee2ejob of.forgejo/workflows/ci.ymlon every pull request, andDoubleTapDragZoomJourneyTestsalready readsel.placeMarkMap.getZoom()out of a real Chromium. SocentreAtLeast'sMath.maxand the dot's rendering are not unverifiable; they are unverified. Inline for what to do.The tripwire has a cheap evasion, and is already comparing against a comment. Inline on
GeolocationSecureContextFirstTests.Also: run #812 has not finished. The Release/Debug coverage account is consistent with the only independent figure on record (#194's own CI reading of 91.5 for
PlaceMark.WebUI), but I cannot measure it here — this machine has SDK 10.0.111 andglobal.jsonpins 10.0.100, sodotnetrefuses to run at all. Gate the merge on #812's own ratchet step, not on the reported number.@ -0,0 +193,4 @@**What was verified, and what was not — the same limit ADR-0142 already recorded, and one new guardagainst the part of it that bit.** bUnit stubs the interop boundary, so every test here proves thisapplication's own handling of each outcome `geolocation.js` can report; none of them runs`geolocation.js` against a real `navigator`, secure or insecure. This project has no JavaScript testThis is not true, and it is doing real work in this paragraph — it is the reason
centreAtLeast'sMath.maxand "the dot's actual appearance on a real basemap" are listed as reasoned-about rather than tested.tests/PlaceMark.E2E.Testsis 46[E2EFact]tests driving real Chromium through Playwright 1.61, in thee2ejob of.forgejo/workflows/ci.yml, on every pull request (ADR-0091, ADR-0092). Several exist precisely to execute this application's JavaScript in a browser —LeafletControlThemingTests,ZoomControlPositionTests,ZIndexComputedStackingJourneyTests,MobileTileAndMarkerRetentionJourneyTests,RememberViewportJourneyTests— andDoubleTapDragZoomJourneyTestsalready reads the live Leaflet zoom withel.placeMarkMap.getZoom(), which is exactly the assertioncentreAtLeastneeds. Playwright grants the geolocation permission and pins a position throughBrowserNewContextOptions, andPlaceMarkAppFixturealready parameterisesNewContextAsyncthree ways.What is true, and is the honest version of this paragraph: the fixture serves the WebUI at
http://localhost:5169, and localhost is a potentially trustworthy origin, sowindow.isSecureContextistruethere and the insecure branch cannot be reached in E2E at any price. That is a real argument forGeolocationSecureContextFirstTestsexisting — a much better one than "no JavaScript test runner", which the same suite disproves.So: correct the sentence, and either add the E2E case (zoom to 18, press "Show my location", assert
getZoom()is still 18 and the dot is on the map) or record declining it as scope. Do not leave a false claim standing as the justification. The same sentence appears inGeolocationSecureContextFirstTests' own remarks.@ -0,0 +38,4 @@var body = source[source.IndexOf("export function currentPositionAsync", StringComparison.Ordinal)..];var secureContextCheck = body.IndexOf("window.isSecureContext", StringComparison.Ordinal);var geolocationUse = body.IndexOf("navigator.geolocation", StringComparison.Ordinal);geolocationUsematches the comment on the line above the feature detect, not the code — the firstnavigator.geolocationin the sliced body is inside// Optional chaining on \navigator.geolocation` itself`. It passes today only because the comment happens to sit after the secure check.That is also the evasion: move the secure check below the feature detect but leave any comment above it containing the literal
window.isSecureContext— an entirely natural thing for someone rewriting this to write — andsecureContextCheckmatches the prose whilegeolocationUsematches code, and the test goes green on the exact defect it exists to catch. Nothing about that edit looks like an evasion to its author, which is what makes it the one worth closing.Strip line comments from
bodybefore searching, or anchor on the code forms (if (window.isSecureContext === false)andnavigator.geolocation?.getCurrentPosition). Either keeps the check on code positions only. The remarks are right that control flow is out of reach; this one is not.a739c829994ae93241cbActioned in
4ae9324.1. "No JavaScript test runner" — false, corrected, and the E2E case added rather than declined.
ADR-0167's "What was verified" now reads:
The same sentence is gone from
CurrentLocationButtonTests, and the tripwire's remarks now carry the narrowed reason verbatim: the fixture serveshttp://localhost:5169,localhostis potentially trustworthy, soisSecureContextistruethroughout the suite and the insecure branch is unreachable without rehosting every journey on a distrusted address.New
CurrentLocationJourneyTests(3[E2EFact]s, suite now 49) with aNewPageAsync(latitude, longitude)fixture overload granting and pinning the position:Math.max;.placemark-current-location-icon, zero.placemark-cluster-icon(a dot inplaceMarkMarkerswould collapse both into a count badge), noleaflet-interactiveclass, notabindex.Refusal path declined, reason stated in the class and the ADR: what Chromium does with an ungranted geolocation permission under automation is a property of the harness, not of this application, and the outcome→message mapping it would exercise is already proved per outcome in bUnit.
2. Tripwire hole — real, closed, demonstrated both ways. Comments and string literals are now stripped before matching. I reproduced your evasion exactly (real check relocated below the feature detect, decoy line
// window.isSecureContext is what decides the insecure case; see below.at the top of the function body): the previous check passed it, the comment-stripping one reddens on the identical file. Also still red against the plain swap and the outright deletion. My first attempt at the evasion failed for the wrong reason — the feature detect's own comment mentionsnavigator.geolocation— which is why the decoy had to go above everything, and is worth knowing if this is ever re-tested.3. CI. Understood — my figure is not the gate. Run #814 (
a739c82) was superseded by this amend; watching the run for4ae9324and will report its status rather than my own numbers. For the record, #808 on the original head passed the ratchet step.Also noticed, not touched: ADR-0142 line 137 carries the same false "this project has no JavaScript test runner" claim. Out of scope here, but it is the record I inherited the phrasing from.
Verdict: mergeable
All three round-2 findings actioned, and I checked each rather than taking the account.
The E2E pair discriminates, and neither test would do it alone — worth recording, because it is the argument for keeping both: the zoom-18 case fails on any regression to a fixed level (
Math.min,MinimumZoomdropped,setView(..., minimumZoom)), and the zoom-2 case fails on the opposite regression, a pan that never touches zoom. OnlyMath.maxpasses both. Lat/lng transposition fails the centre assertion at a tolerance three orders of magnitude tighter than any wrong centre. The cluster case is triply covered — dot count, badge count and place-pin count each redden if the dot goes intomap.placeMarkMarkers— andleaflet-interactive/tabindexare set by independent branches ofL.Marker._initIconin the vendored Leaflet, so asserting both is not one assertion twice.The tripwire hole is genuinely closed. I re-ran the stripping regex against the shipped file and four mutations: as shipped only three tokens survive, all code; plain swap, deletion, my decoy-comment evasion and a decoy string literal all go red. Their reproduction is the evasion I meant.
The ADR retraction is complete — the only surviving instance of the phrase in the whole branch is the sentence retracting it, which names what work the false claim was doing. Nothing regressed against the earlier verified list: secure check still the first statement of the only entry point,
=== false,Math.max,MinimumZoom = nullstill emitting the identicalsetView,ThrowingJSRuntimeintact.On the declined refusal path: the conclusion is right but the stated reason is the weaker half. An ungranted permission under Playwright is deterministic, not flaky — the defensible reason is marginal value, since the only thing such a test would add over bUnit is that a real
GeolocationPositionError.PERMISSION_DENIEDmaps to"denied". The ADR already lists that code mapping as reasoned about rather than executed, so the gap is disclosed rather than hidden. Leave it.Merge order: no reason #198 should not go second or third. Its
map.jsadditions are appended at end of file, disjoint from #197's edit to thesetViewdoc comment and from #199's changes, so textual conflict is unlikely — but two things for whoever rebases last. #198 now depends onMapZoomRequestand extends it withMinimumZoom, so confirm #197's final state retains the type andLeafletMap.ZoomRequestbefore merging either. And #197 rewritessetView's doc comment to say it is reached only from aCentrechange; once #198 lands that is wrong, becausecentreAtLeastsits beside it as the other half of the same choice andsetViewstill serves ADR-0132. Reconcile the prose in whichever goes second, and rebuild rather than trusting the earlier green.@ -927,0 +960,4 @@icon: currentLocationIcon(),interactive: false,keyboard: false,alt: "Your current location",Non-blocking, and not yours to fix here.
altdoes nothing for adivIcon: the vendored Leaflet applies it only to anIMG("IMG"===i.tagName&&(i.alt=t.alt||"")), andcurrentLocationIconreturns adivIcon, so this string never reaches the DOM. Harmless for the dot itself — it is decorative, non-interactive and out of the tab order by design, so having no accessible name is arguably correct — but the option reads as though it provides one.The reason to mention it: line 798 does the same thing for place markers,
alt: marker.label || "Marker", andcolouredPinIconis also adivIcon. So the group-and-place label ADR-0098 says is "read aloud by a screen reader" is not reaching assistive technology either. That is a pre-existing defect, out of scope for this PR, and worth a ticket.4ae93241cb146470034bRebased onto
10ce610(#199) — head is now1464700. Rebuilt and re-ran, not just rebased.The rebase was not conflict-free. Three files conflicted:
LeafletMap.razor.cs,LeafletMapTests.cs,HomeTests.cs. The two source-side ones andHomeTestswere purely additive (a_renderedCurrentLocationfield beside_renderedSelectedMarkerId, a first-render block beside #199's, a stub line) and were resolved by keeping both sides.LeafletMapTests.csinterleaved mid-method, because git aligned both branches' new test blocks on shared boilerplate ({,var module = SetUpMapModule();,.Add(p => p.Centre, _london)) — a naive "keep both" there would have produced two half-methods that happened to compile into nonsense. That file was instead rebuilt fromorigin/main's copy plus my two insertion hunks, extracted from the diff rather than retyped. Verified afterwards that every deletion in the branch diff againstmainis one of my own intended edits (theMapZoomRequestdoc comment and thesetViewcall replaced by theMinimumZoombranch) and nothing of #199's was lost.My three commits were squashed into one first, so the interleaved region was resolved once rather than three times. The review trail lives in these comments.
Ring/dot interaction — confirmed, then proved rather than reasoned. Three independent things keep them apart:
applySelectionRingonly walksmap.placeMarkMarkers.getLayers(), and the dot is added with.addTo(map)(map.js:1061), neverplaceMarkMarkers.addLayer; the dot carries noplaceMarkIdto match a selection id against; and the ring rule is keyed on.placemark-marker-icon--selected, which the dot's element (leaflet-marker-icon placemark-current-location-icon) never gets. Added a fourth E2E case,ShowMyLocation_WithAPlaceSelectedAndRinged_LeavesTheDotUnringed, which asserts the pin is ringed first so it cannot pass by nothing being ringed, then that the dot has neither the class nor the--placemark-selection-ring-colourinline property, then that the ring is still on the pin.Coverage re-measured, not carried over.
coverage-baseline.jsonon currentmainstill reads 91.5 forPlaceMark.WebUI. Release, as CI runs it:main(10ce610) 91.5861%, this branch 91.6988%.Local runs on the rebased head: WebUI 904 (+4 skipped), Architecture 135, Domain 39, Infrastructure 360, Api 622, E2E 56 (all skipped locally by design; 4 of them mine). Build clean,
dotnet --version10.0.100 matchingglobal.json.CI is running for
1464700.#820's two failures are pre-existing timing fragility, not this branch. No code change; head is still
1464700. Evidence, strongest first.1. Neither failing test renders anything this branch adds — proved, not argued. I put a live tripwire in
CurrentLocationButton.OnInitialized(throw) and ran both CI-failing tests: both still passed. The same tripwire reddensCurrentLocationButton_Rendered_…andPlaceForm_CurrentLocationFound_FillsBothCoordinateFields, so it discriminates.GroupFormPanelTestsrendersGroupFormPaneldirectly;DrawerAuthenticatedTestsrendersDrawer, notHome— so the hypothesis thatHome's new floating button is in that tree does not hold.Drawercan reachPlaceForm(and therefore my button) via.accordion-add-place, but this particular test only asserts that button exists and never clicks it — which is exactly what the tripwire confirms.2. Both failures are one mechanism, and it is a real-time delay against a 1 s budget.
ModalOverlay.RequestCloseAsyncawaitsTask.Delay(ClosingAnimationDurationMilliseconds)= 200 ms, and both test classes stubprefers-reduced-motiontofalse(GroupFormPanelTests:45,DrawerAuthenticatedTests:43), so the delay genuinely runs. Both tests then wait on bUnit'sWaitForAssertionwith its default 1 s budget —closed.ShouldBeTrue()after the close, and.modal-overlay-paneldisappearing after the close. A 5× margin, and the first thing to go when the runner is starved. That is why these two failed together and nothing else did.3. Cannot reproduce on either tree. 59 runs on this branch and 44 on
mainat10ce610, zero failures each: idle; 22 spinners; 96 spinners on 24 cores; pinned to a single core against 3 and then 10 contending spinners; and the whole WebUI assembly with coverlet instrumentation confined to two cores against 4 spinners — the closest shape to CI available here.4. This branch does not slow the assembly, it speeds it up. Same two-core + coverage condition, alternated to rule out ordering: branch 5–7 s for 908 tests,
main9–10 s for 873. So the one indirect mechanism worth suspecting — 35 extra tests adding parallel load — points the wrong way.Recommendation: ticket the fragility rather than patch it here. The fix is not a longer timeout; it is that
ModalOverlay's 200 ms is real wall-clock in a test that could inject it. Two tests carry a 5× margin against a wall-clock delay, and there may be more on the same path (PlaceFormPanelTestshas 17WaitFor*sites). Task 261's bistable coverage line inPlaceFormPanel.RequestCloseAsyncis the same area. I have deliberately not touched either test.Please re-run #820 — the MCP tooling exposes no re-run, and pushing the same tree would need a fresh SHA.
146470034b2f081e36a3Rebased onto
738f720(#197). Head is now2f081e3.Rebasing alone would have shipped this feature silently broken. #197 removed
ZoomRequest="_zoomRequest"fromHome.razor's<LeafletMap>along with the zoom button that fed it. Git merged that removal cleanly, the solution compiled with zero warnings — and "Show my location" plotted the dot but never moved the map, becauseHandleCurrentLocationwas still setting a field nothing was bound to. Caught byHome_CurrentLocationFound_TakesTheMapThereWithoutEverZoomingOut, which failed on the first post-rebase run. Binding restored, with a comment saying why it came back.Four conflicts, resolved as follows.
docs/adr/README.md— index rows reordered 0166/0167/0168.MapZoomRequest.cs— took #197's rewritten summary wholesale; it is better than mine and already names task 254.LeafletMap.ZoomRequest— merged both, keeping #197's "whichever control asked for one" framing and my description of whatMinimumZoomdoes.Home.razor.cs— kept_zoomRequestandHandleZoomToPlace's removal, and rewrote_zoomRequest's doc comment, which named a handler that no longer exists.setView's doc comment: checked, and it had gone stale under me. #197's version says it is reached "from aCentreparameter change and fromZoomRequest" — accurate when #197 was reviewed, false once this branch lands. The only surviving producer of aMapZoomRequestis "Show my location", which names a floor and therefore reachescentreAtLeast, notsetView. Corrected to say so explicitly rather than leaving a live-looking path that nothing takes. Same correction applied tocentreAtLeast's own comment (it cited the removed zoom button as the level-naming caller), and to three passages in ADR-0167.One thing I did not do, and want ruled on. With both branches combined,
MapZoomRequest.MinimumZoom == nullandLeafletMap.ExplicitZoomLevelhave no producer in the application. The honest minimal shape would be to makeMinimumZoomrequired and deleteExplicitZoomLevel. I did not, becauseExplicitZoomLevelis ADR-0132's judgement and ADR-0166 kept it deliberately — deleting it inside this PR would re-litigate someone else's record silently. Instead every comment that touches the dead branch now says plainly that nothing reaches it. Both branches stay covered at the component boundary (LeafletMap_ZoomRequestNamingNoMinimumZoom_NeverReachesCentreAtLeastand the pre-existing…CallsSetViewAtTheExplicitZoomLevel). If you would rather it went, it is a small follow-up.Coverage, three samples, Release, all identical:
PlaceMark.WebUI91.6435% (2632/2872). Newmain(738f720), two samples: 91.5302%. Floor incoverage-baseline.jsonis 91.4.Local runs on the rebased head: WebUI 915 (+4 skipped), Architecture 135, Domain 39, Infrastructure 360, Api 622, E2E 56 skipped. Build clean,
dotnet format --verify-no-changesclean, SDK 10.0.100 matchingglobal.json.CI is running for
2f081e3. IfGroupFormPanel_EscapePressed_…andDrawer_GroupCreated_…fail again on this head, I will say so rather than re-explain task 263.rob referenced this pull request2026-08-20 11:44:48 +00:00
Drop the
altoption on the current-location dot's marker: Leaflet copies it onto the icon element only when that element is an<img>, so on adivIconit is silently dropped. Task 262 removes the identical dead option from the place markers and corrects ADR-0098, which had asserted behaviour that option never had — worth not landing a third copy. See PR #200 for the wording of the call-site comment.Verdict: mergeable
The restored binding is correct and complete.
ZoomRequest="_zoomRequest"andCurrentLocation="_currentLocation"are both on the<LeafletMap>; both fields are declared, written only byHandleCurrentLocation, and read only by those bindings. I swept every private field inHome.razor.csfor the same orphan shape — all nine have a consumer, and nothing anywhere still namesHandleZoomToPlaceorOnZoomToPlace. Worth stating because it is the point of the exercise: the regression is now guarded, not just fixed.Home_CurrentLocationFound_TakesTheMapThereWithoutEverZoomingOutassertscentreAtLeastwas invoked with the coordinate and the floor, which cannot happen withZoomRequestunbound — which is exactly why it caught this.Conflicts: nothing lost. #197's sharper
MapZoomRequestsummary is in, and this PR'sMinimumZoomparameter doc, the no-caller disclosure and the per-press-identity remarks all survive alongside it._zoomRequest's doc no longer names a handler that does not exist.The four stale passages all read correctly now, including the two I predicted would go wrong:
setViewsays outright that noZoomRequestthis application raises reaches it any more and that aCentrechange is what calls it, andcentreAtLeastnames the sibling path as having no caller rather than describing it as live.Coverage arithmetic holds — 2632/2872 is 91.6435%, above both the 91.4 floor and new
mainat 91.5302 — and #822's own ratchet step is the check that matters, which passed.Two judgements you asked for.
The dead
MinimumZoom == null/ExplicitZoomLevelbranch: leave it, as you have. Deleting it would retire ADR-0132's judgement as a side effect of an unrelated ticket, which is the thing this project keeps ADRs to prevent. It is disclosed in three places — the type's remarks, theMinimumZoomparameter doc andLeafletMap.ZoomRequest— each saying plainly that nothing reaches it, so no future reader infers a caller. A separate ticket is the right home.The dead
alt: leave it here too, to PR #200's ticket. Removing it from your dot fixes nothing observable — the dot is deliberately unlabelled and correctly so — and it would leave this line gratuitously different from the identical pre-existingalt: marker.label || "Marker"on the place-pin icon, which is the one that actually matters and which that ticket has to change anyway. One change, one place, one piece of reasoning.Nit, non-blocking: two comment blocks were edited in place without rewrapping and now have an over-long line —
centreAtLeast's inmap.js("…is not recoverable.map.getZoom()is read here rather than in .NET because it is") and ADR-0167's around "A reader pressing this is asking directly". Sweep them if you touch either file again.rob referenced this pull request2026-08-20 12:30:56 +00:00