refactor!: redesign Geolocation as static utility with watcher handle#24279
Merged
Conversation
Artur-
added a commit
to vaadin/docs
that referenced
this pull request
May 6, 2026
Reflects vaadin/flow#24279: the per-UI Geolocation facade is replaced with static entry points, GeolocationTracker becomes GeolocationWatcher with both listener and signal consumption styles, and availability is exposed via Geolocation.availabilityHintSignal(). Also covers the explicit-UI overloads for background threads.
Artur-
added a commit
to vaadin/use-cases
that referenced
this pull request
May 8, 2026
Track Flow's geolocation redesign (vaadin/flow#24279). Point flow.version at 25.2.geo-api-update-SNAPSHOT (vaadin.version stays on 25.2-SNAPSHOT because the snapshot is only published for flow-bom) and migrate every use case off the per-UI facade: - get(callback) -> Geolocation.getPosition(onSuccess, onError) with separate success/error consumers; the GeolocationOutcome switch goes away. - get(opts, callback) -> Geolocation.getPosition(onSuccess, onError, opts) with options as the trailing argument. - track(...) -> Geolocation.watchPosition(...); GeolocationTracker -> GeolocationWatcher; valueSignal() -> positionSignal(). UC2's local tracker/ensureTracker renamed accordingly. - availabilitySignal() -> Geolocation.availabilityHintSignal(ui), used in UC3 and UC4.
Replace the per-UI Geolocation facade with a static utility class. The one-shot getPosition and watchPosition entry points are now static; UI is implicit (UI.getCurrent()) with explicit-UI overloads for background threads. Per-UI state (the GeolocationClient and the availability signal) lives in UIInternals; static methods resolve the client there or install a default one through the GeolocationClientFactory Lookup SPI. Also formalises the test seam: GeolocationClient and WatchHandle become public; GeolocationOutcome becomes package-private since the public API no longer exposes it. The watcher's stop()/resume() and the bridge- failure error path are unchanged.
The browser-supplied message is unstandardised and not safe to show to end users. The new name signals that intent — applications that show something to the user should branch on errorCode() instead, and use debugInfo() only for log lines and bug reports. The wire format is preserved via @JsonProperty("message") so the client-side error payload is unchanged.
1adae2f to
c78973b
Compare
mcollovati
requested changes
May 8, 2026
Geolocation.watchPosition no longer requires the owner to be attached. If it isn't, the watcher registers a one-shot attach listener and activates the underlying browser watch on first attach. This makes the method safe to call from a view constructor.
Wrap each onSuccess/onError invocation in getPosition() and each listener invocation in GeolocationWatcher.dispatch() with a try/catch that forwards RuntimeException to VaadinSession.getErrorHandler(). Previously, exceptions thrown by getPosition consumers were silently swallowed by CompletableFuture.whenComplete; an exception from a watcher listener aborted the dispatch loop and prevented later listeners from receiving the same reading.
mcollovati
reviewed
May 8, 2026
| * Flow listener APIs. Rethrows when no error handler is reachable (e.g. | ||
| * during session teardown) so the exception is not silently lost. | ||
| */ | ||
| static void deliverSafely(UI ui, Runnable callback) { |
Collaborator
There was a problem hiding this comment.
Definitely not for this PR, but I wonder if we could extract this (and similar cases) as a general utility.
The options parameter on getPosition() and watchPosition() is no longer nullable. Callers who want browser defaults use the overloads that take no options argument; an empty GeolocationOptions (GeolocationOptions.builder().build()) is the explicit way to express the same thing. The record fields stay independently optional. This removes the null-vs-empty ambiguity raised in review and aligns the SPI (GeolocationClient) with the public API.
mcollovati
previously approved these changes
May 8, 2026
|
mcollovati
approved these changes
May 8, 2026
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.



Redesigns the Geolocation API as a static utility class with a watcher handle, replacing the previous per-UI facade.
API surface
Geolocation.getPosition() and Geolocation.watchPosition() are static. UI is implicit (UI.getCurrent()), with explicit-UI overloads for background threads. watchPosition() returns a GeolocationWatcher handle that exposes both a callback API (addPositionListener) and a reactive Signal.
The owner component drives the watcher's lifecycle: watchPosition() accepts an unattached owner and starts the underlying browser watch on first attach (so it's safe from a view constructor); the watch auto-stops on detach; stop() and resume() work explicitly. Per-UI state (the GeolocationClient and the availability signal) lives in UIInternals; static methods resolve the client there or install a default one through the GeolocationClientFactory Lookup SPI.
options is now a non-null parameter — callers who want browser defaults use the no-options overloads, or pass an empty GeolocationOptions explicitly. The fields inside GeolocationOptions remain independently optional.
Error model
Callback exceptions thrown from onSuccess/onError in getPosition and from watcher position listeners are routed to VaadinSession.getErrorHandler() instead of being silently swallowed by CompletableFuture.whenComplete or aborting the listener loop. GeolocationError.message() is renamed to debugInfo() to make it clear the value is a free-form, non-localised string suitable for logs and bug reports — not for UI display.
Test seam
GeolocationClient and WatchHandle become public so external test drivers and native bridges can replace the production client through Lookup. GeolocationOutcome becomes package-private since the public API no longer exposes it. The watcher's bridge-failure error path is unchanged.