Skip to content

refactor!: redesign Geolocation as static utility with watcher handle#24279

Merged
Artur- merged 7 commits into
mainfrom
feature/geo-api-update
May 8, 2026
Merged

refactor!: redesign Geolocation as static utility with watcher handle#24279
Artur- merged 7 commits into
mainfrom
feature/geo-api-update

Conversation

@Artur-
Copy link
Copy Markdown
Member

@Artur- Artur- commented May 6, 2026

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.

@github-actions github-actions Bot added the +1.0.0 label May 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Test Results

 1 404 files  ±0   1 404 suites  ±0   1h 20m 40s ⏱️ - 2m 2s
10 128 tests ±0  10 058 ✅ ±0  70 💤 ±0  0 ❌ ±0 
10 603 runs  ±0  10 524 ✅ ±0  79 💤 ±0  0 ❌ ±0 

Results for commit e0748e4. ± Comparison against base commit 09ccde4.

♻️ This comment has been updated with latest results.

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.
Artur- added 2 commits May 8, 2026 09:44
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.
@Artur- Artur- force-pushed the feature/geo-api-update branch from 1adae2f to c78973b Compare May 8, 2026 09:47
@Artur- Artur- marked this pull request as ready for review May 8, 2026 09:47
@Artur- Artur- requested a review from mcollovati May 8, 2026 09:47
Comment thread flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java Outdated
Comment thread flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java Outdated
Artur- added 3 commits May 8, 2026 11:10
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.
Bring back the @param and @return tags on the framework-internal port
methods that were dropped in the static-utility refactor.
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.
* 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) {
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.

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
mcollovati previously approved these changes May 8, 2026
@Artur- Artur- enabled auto-merge May 8, 2026 11:46
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 8, 2026

@Artur- Artur- added this pull request to the merge queue May 8, 2026
Merged via the queue into main with commit eca39c2 May 8, 2026
30 of 31 checks passed
@Artur- Artur- deleted the feature/geo-api-update branch May 8, 2026 12:18
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.

2 participants