diff --git a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java index f9f9de71cb4..bd852174c9c 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/Geolocation.java @@ -16,6 +16,7 @@ package com.vaadin.flow.component.geolocation; import java.io.Serializable; +import java.util.Objects; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -41,11 +42,15 @@ *
* Two usage modes: *
* Button locate = new Button("Use my location");
* locate.addClickListener(
- * e -> UI.getCurrent().getGeolocation().get(outcome -> {
- * switch (outcome) {
- * case GeolocationPosition pos -> showNearest(
- * pos.coords().latitude(), pos.coords().longitude());
- * case GeolocationError err -> showManualEntry();
- * }
- * }));
+ * e -> e.getUI().getGeolocation()
+ * .get(pos -> showNearest(pos.coords().latitude(),
+ * pos.coords().longitude()),
+ * err -> showManualEntry()));
*
*
* @@ -176,21 +178,29 @@ private static GeolocationClient resolveClient(UI ui) { } /** - * Requests the user's current position once. The callback receives a - * {@link GeolocationOutcome} — either a {@link GeolocationPosition} or a - * {@link GeolocationError}. Use {@code switch} pattern matching on the - * outcome; no dead "pending" arm is needed because one-shot requests never - * produce that value. + * Requests the user's current position once. On a successful reading + * {@code onSuccess} is invoked with the {@link GeolocationPosition}; if the + * browser reports an error instead {@code onError} is invoked with the + * {@link GeolocationError}. The pair mirrors the W3C + * {@code getCurrentPosition(success, error)} signature and matches + * {@link GeolocationTracker#addPositionListener + * GeolocationTracker.addPositionListener}, so callers can share the same + * handler shape between one-shot and watch APIs. *
* The call returns immediately. The browser may show a permission dialog on
- * the first call; after the user responds, the callback is invoked on the
- * UI thread.
+ * the first call; after the user responds, exactly one of the callbacks is
+ * invoked on the UI thread.
*
- * @param callback
- * invoked with the outcome once the browser reports it
+ * @param onSuccess
+ * invoked with the position on a successful reading; not
+ * {@code null}
+ * @param onError
+ * invoked with the error if the browser reports one; not
+ * {@code null}
*/
- public void get(SerializableConsumer
* The call returns immediately. The browser may show a permission dialog on
- * the first call; after the user responds, the callback is invoked on the
- * UI thread.
+ * the first call; after the user responds, exactly one of the callbacks is
+ * invoked on the UI thread.
*
+ * @param onSuccess
+ * invoked with the position on a successful reading; not
+ * {@code null}
+ * @param onError
+ * invoked with the error if the browser reports one; not
+ * {@code null}
* @param options
* accuracy / timeout / cache-age tuning, or {@code null} to use
* the browser defaults
- * @param callback
- * invoked with the outcome once the browser reports it
*/
- public void get(@Nullable GeolocationOptions options,
- SerializableConsumer
* This is one of the three possible values of a
- * {@link GeolocationTracker#valueSignal()} signal, and one of the two values a
- * {@link Geolocation#get} callback can receive. Typical application code
- * switches on {@link #errorCode()} to react to the specific reason:
+ * {@link GeolocationTracker#valueSignal()} signal, and the value passed to the
+ * error callback of {@link Geolocation#get Geolocation.get}. Typical
+ * application code switches on {@link #errorCode()} to react to the specific
+ * reason:
*
*
- * Returned to the callback of {@link Geolocation#get}. Use this instead of
- * {@link GeolocationResult} when you only need to handle the Position / Error
- * branches and want the {@code switch} to stay exhaustive without a dead
- * Pending arm.
- *
- *
* This is one of the three possible values of a
- * {@link GeolocationTracker#valueSignal()} signal, and one of the two values a
- * {@link Geolocation#get} callback can receive.
+ * {@link GeolocationTracker#valueSignal()} signal, and the value passed to the
+ * success callback of {@link Geolocation#get Geolocation.get}.
*
* @param coords
* the latitude/longitude and related fields; see
diff --git a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationResult.java b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationResult.java
index ec766db4985..deacba3a6e8 100644
--- a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationResult.java
+++ b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationResult.java
@@ -29,9 +29,9 @@
*
- * ui.getGeolocation().get(result -> {
- * if (result instanceof GeolocationError err) {
- * switch (err.errorCode()) {
- * case PERMISSION_DENIED ->
- * showExplanation("Location is blocked for this site.");
- * case POSITION_UNAVAILABLE ->
- * showRetry("Could not determine your location.");
- * case TIMEOUT -> showRetry("Location request took too long.");
- * case UNKNOWN -> showGenericError(err.message());
- * }
+ * ui.getGeolocation().get(pos -> showNearest(pos), err -> {
+ * switch (err.errorCode()) {
+ * case PERMISSION_DENIED ->
+ * showExplanation("Location is blocked for this site.");
+ * case POSITION_UNAVAILABLE ->
+ * showRetry("Could not determine your location.");
+ * case TIMEOUT -> showRetry("Location request took too long.");
+ * case UNKNOWN -> showGenericError("Could not read your location.");
* }
* });
*
diff --git a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationOutcome.java b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationOutcome.java
index 5a646be8679..8620b15d715 100644
--- a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationOutcome.java
+++ b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationOutcome.java
@@ -18,22 +18,14 @@
/**
* The actual answer to a geolocation request — either a successful reading or
* an error. Narrower than {@link GeolocationResult}: the "waiting for first
- * reading" {@link GeolocationPending} state is excluded because one-shot
- * {@link Geolocation#get} never produces it.
+ * reading" {@link GeolocationPending} state is excluded because a one-shot
+ * request never produces it.
*
- * ui.getGeolocation().get(outcome -> {
- * switch (outcome) {
- * case GeolocationPosition pos -> showNearest(pos);
- * case GeolocationError err -> showManualEntry();
- * }
- * });
- *
+ * Used as the result type of the internal {@link GeolocationClient#get} future,
+ * where the sum-type encoding keeps Pending out of the contract. Application
+ * code rarely references this type directly: {@link Geolocation#get
+ * Geolocation.get} delivers the position or the error through separate
+ * callbacks.
*/
public sealed interface GeolocationOutcome extends GeolocationResult
permits GeolocationPosition, GeolocationError {
diff --git a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPending.java b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPending.java
index 3e618fbbb33..94596841170 100644
--- a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPending.java
+++ b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPending.java
@@ -18,8 +18,8 @@
/**
* The initial state of a newly started tracking session, held by
* {@link GeolocationTracker#valueSignal()} until the browser reports its first
- * position or error. One-shot {@link Geolocation#get} callbacks never receive
- * this value.
+ * position or error. One-shot {@link Geolocation#get} requests never produce
+ * this value — they deliver a position or an error through separate callbacks.
*/
public record GeolocationPending() implements GeolocationResult {
}
diff --git a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPosition.java b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPosition.java
index be50d447b0e..8f44321cb5a 100644
--- a/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPosition.java
+++ b/flow-server/src/main/java/com/vaadin/flow/component/geolocation/GeolocationPosition.java
@@ -22,8 +22,8 @@
* moment in time they were taken.
*
* The sealed hierarchy is designed for exhaustive pattern matching. A
* {@code switch} covering the three permitted variants is guaranteed complete
diff --git a/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationClientSeamTest.java b/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationClientSeamTest.java
index 61b5a0359ac..6318914ca65 100644
--- a/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationClientSeamTest.java
+++ b/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationClientSeamTest.java
@@ -36,7 +36,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -71,7 +70,8 @@ void lookupFactory_resolvedAtConstruction_clientReceivesGetCalls() {
.thenReturn(unused -> fake);
UI freshUi = new MockUI();
- freshUi.getGeolocation().get(outcome -> {
+ freshUi.getGeolocation().get(pos -> {
+ }, err -> {
});
assertEquals(1, fake.getCalls.size(),
@@ -83,7 +83,8 @@ void setClient_routesGetThroughInstalledClient() {
FakeClient fake = new FakeClient();
ui.getGeolocation().setClient(fake);
- ui.getGeolocation().get(outcome -> {
+ ui.getGeolocation().get(pos -> {
+ }, err -> {
});
assertEquals(1, fake.getCalls.size(),
@@ -131,21 +132,21 @@ void track_handleIsNullAfterStop() {
}
@Test
- void get_callbackReceivesUnknownErrorWhenClientFutureFailsExceptionally() {
+ void get_onErrorReceivesUnknownErrorWhenClientFutureFailsExceptionally() {
FakeClient fake = new FakeClient();
fake.nextGetResult = CompletableFuture
.failedFuture(new RuntimeException(
"Client-side geolocation.get failed: boom"));
ui.getGeolocation().setClient(fake);
- AtomicReference<@Nullable GeolocationOutcome> received = new AtomicReference<>();
- ui.getGeolocation().get(received::set);
+ AtomicReference<@Nullable GeolocationPosition> position = new AtomicReference<>();
+ AtomicReference<@Nullable GeolocationError> error = new AtomicReference<>();
+ ui.getGeolocation().get(position::set, error::set);
- GeolocationOutcome outcome = received.get();
- assertNotNull(outcome,
- "callback must fire even when the JS bridge fails");
- GeolocationError err = assertInstanceOf(GeolocationError.class, outcome,
- "infra failure should surface as a GeolocationError");
+ GeolocationError err = error.get();
+ assertNotNull(err, "onError must fire even when the JS bridge fails");
+ assertNull(position.get(),
+ "onSuccess must stay silent when the bridge fails");
assertEquals(GeolocationErrorCode.UNKNOWN, err.errorCode(),
"error code should be UNKNOWN for client-bridge failures");
assertFalse(err.message().contains("boom"),
diff --git a/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationTest.java b/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationTest.java
index 49765ca7ba3..8de2b73d68b 100644
--- a/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationTest.java
+++ b/flow-server/src/test/java/com/vaadin/flow/component/geolocation/GeolocationTest.java
@@ -156,7 +156,8 @@ void get_executesPromiseJs() {
TestComponent component = new TestComponent();
ui.add(component);
- ui.getGeolocation().get(result -> {
+ ui.getGeolocation().get(pos -> {
+ }, err -> {
});
List