Skip to content

feat: Add Web Share API support (#24325) (CP: 25.2)#24621

Open
vaadin-bot wants to merge 1 commit into
25.2from
cherry-pick-24325-to-25.2-1781274044014
Open

feat: Add Web Share API support (#24325) (CP: 25.2)#24621
vaadin-bot wants to merge 1 commit into
25.2from
cherry-pick-24325-to-25.2-1781274044014

Conversation

@vaadin-bot

Copy link
Copy Markdown
Collaborator

This PR cherry-picks changes from the original PR #24325 to branch 25.2.

Original PR description

Summary

  • Adds a WebShare facade for the browser's Web Share API: WebShare.onClick(button).share(ShareContent.create().title("...").text("...").url("...")) opens the native share sheet when the user clicks, optionally reporting the outcome back to the server through onShared/onError callbacks.
  • The share call runs through the trigger/action framework (a new internal ShareAction extending PromiseAction), so navigator.share() executes inside the client-side click handler and inherits the transient user gesture the API requires — a server round-trip via Page.executeJs() would lose the activation and the browser would reject the call.
  • WebShare.supportSignal() exposes feature detection as a read-only signal, seeded during the bootstrap handshake via a new v-ws parameter (Flow.tsExtendedClientDetails → a ValueSignal<WebShareSupport> on UIInternals), so views can decide whether to show a share affordance before any view code runs.

Details

  • ShareContent is a builder with title/text/url slots; each slot accepts either a string literal or a HasValue component, in which case the component's value property is read on the client at gesture time. At least one slot must be set — the Web Share API rejects an empty payload, so this is validated server-side.
  • onError deliberately fires for the AbortError the browser reports when the user dismisses the share sheet without picking a target, not only for true failures; this matches browser semantics and is documented on the callback.
  • The support signal mirrors the existing geolocation/wake-lock pattern: UNKNOWN only before the first handshake, then a stable SUPPORTED/UNSUPPORTED for the rest of the session. The read-only wrapper is cached on UIInternals for stable identity.
  • Unit tests cover the rendered navigator.share({...}) payload composition (literal and property-backed slots, partial payloads, promise observation wrapping) and the signal seeding.

API Changes: feature/web-share vs origin/main

6 classes affected — 5 new public types, 2 methods added to an existing class.

com.vaadin.flow.component.internal.UIInternals

// Added
public Signal<WebShareSupport> getWebShareSupportSignalReadOnly()
public void setWebShareSupport(WebShareSupport support) // framework use only

com.vaadin.flow.component.trigger.internal.ShareAction

// Added
public class ShareAction extends PromiseAction<Void> // internal trigger-framework action
public ShareAction(Action.@Nullable Input<String> titleInput, Action.@Nullable Input<String> textInput, Action.@Nullable Input<String> urlInput)
public ShareAction(Action.@Nullable Input<String> titleInput, Action.@Nullable Input<String> textInput, Action.@Nullable Input<String> urlInput, SerializableRunnable onShared, SerializableConsumer<Error> onError)
protected JsFunction toPromiseJs(Trigger trigger)

com.vaadin.flow.component.webshare.ShareContent

// Added
public final class ShareContent implements Serializable
public static ShareContent create()
public ShareContent title(String literal)
public <C extends Component & HasValue<?, String>> ShareContent title(C source)
public ShareContent text(String literal)
public <C extends Component & HasValue<?, String>> ShareContent text(C source)
public ShareContent url(String literal)
public <C extends Component & HasValue<?, String>> ShareContent url(C source)

com.vaadin.flow.component.webshare.WebShare

// Added
public final class WebShare implements Serializable
public static <T extends Component & ClickNotifier<?>> WebShareBinding onClick(T component)
public static Signal<WebShareSupport> supportSignal()
public static Signal<WebShareSupport> supportSignal(UI ui)

com.vaadin.flow.component.webshare.WebShareBinding

// Added
public final class WebShareBinding implements Serializable
public void share(ShareContent content)
public void share(ShareContent content, SerializableRunnable onShared, SerializableConsumer<Error> onError)

com.vaadin.flow.component.webshare.WebShareSupport

// Added
public enum WebShareSupport
UNKNOWN // initial value before the first bootstrap handshake
SUPPORTED
UNSUPPORTED

## Summary

- Adds a `WebShare` facade for the browser's [Web Share
API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API):
`WebShare.onClick(button).share(ShareContent.create().title("...").text("...").url("..."))`
opens the native share sheet when the user clicks, optionally reporting
the outcome back to the server through `onShared`/`onError` callbacks.
- The share call runs through the trigger/action framework (a new
internal `ShareAction` extending `PromiseAction`), so
`navigator.share()` executes inside the client-side click handler and
inherits the transient user gesture the API requires — a server
round-trip via `Page.executeJs()` would lose the activation and the
browser would reject the call.
- `WebShare.supportSignal()` exposes feature detection as a read-only
signal, seeded during the bootstrap handshake via a new `v-ws` parameter
(`Flow.ts` → `ExtendedClientDetails` → a `ValueSignal<WebShareSupport>`
on `UIInternals`), so views can decide whether to show a share
affordance before any view code runs.

## Details

- `ShareContent` is a builder with `title`/`text`/`url` slots; each slot
accepts either a string literal or a `HasValue` component, in which case
the component's `value` property is read on the client at gesture time.
At least one slot must be set — the Web Share API rejects an empty
payload, so this is validated server-side.
- `onError` deliberately fires for the `AbortError` the browser reports
when the user dismisses the share sheet without picking a target, not
only for true failures; this matches browser semantics and is documented
on the callback.
- The support signal mirrors the existing geolocation/wake-lock pattern:
`UNKNOWN` only before the first handshake, then a stable
`SUPPORTED`/`UNSUPPORTED` for the rest of the session. The read-only
wrapper is cached on `UIInternals` for stable identity.
- Unit tests cover the rendered `navigator.share({...})` payload
composition (literal and property-backed slots, partial payloads,
promise observation wrapping) and the signal seeding.

## API Changes: feature/web-share vs origin/main

6 classes affected — 5 new public types, 2 methods added to an existing
class.

### com.vaadin.flow.component.internal.UIInternals

```java
// Added
public Signal<WebShareSupport> getWebShareSupportSignalReadOnly()
public void setWebShareSupport(WebShareSupport support) // framework use only
```

### com.vaadin.flow.component.trigger.internal.ShareAction

```java
// Added
public class ShareAction extends PromiseAction<Void> // internal trigger-framework action
public ShareAction(Action.@nullable Input<String> titleInput, Action.@nullable Input<String> textInput, Action.@nullable Input<String> urlInput)
public ShareAction(Action.@nullable Input<String> titleInput, Action.@nullable Input<String> textInput, Action.@nullable Input<String> urlInput, SerializableRunnable onShared, SerializableConsumer<Error> onError)
protected JsFunction toPromiseJs(Trigger trigger)
```

### com.vaadin.flow.component.webshare.ShareContent

```java
// Added
public final class ShareContent implements Serializable
public static ShareContent create()
public ShareContent title(String literal)
public <C extends Component & HasValue<?, String>> ShareContent title(C source)
public ShareContent text(String literal)
public <C extends Component & HasValue<?, String>> ShareContent text(C source)
public ShareContent url(String literal)
public <C extends Component & HasValue<?, String>> ShareContent url(C source)
```

### com.vaadin.flow.component.webshare.WebShare

```java
// Added
public final class WebShare implements Serializable
public static <T extends Component & ClickNotifier<?>> WebShareBinding onClick(T component)
public static Signal<WebShareSupport> supportSignal()
public static Signal<WebShareSupport> supportSignal(UI ui)
```

### com.vaadin.flow.component.webshare.WebShareBinding

```java
// Added
public final class WebShareBinding implements Serializable
public void share(ShareContent content)
public void share(ShareContent content, SerializableRunnable onShared, SerializableConsumer<Error> onError)
```

### com.vaadin.flow.component.webshare.WebShareSupport

```java
// Added
public enum WebShareSupport
UNKNOWN // initial value before the first bootstrap handshake
SUPPORTED
UNSUPPORTED
```
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Test Results

 1 449 files  + 2   1 449 suites  +2   1h 25m 37s ⏱️ + 1m 14s
10 229 tests +10  10 161 ✅ +11  68 💤 ±0  0 ❌ ±0 
10 701 runs  +10  10 632 ✅ +11  69 💤 ±0  0 ❌ ±0 

Results for commit 3685317. ± Comparison against base commit e8f1605.

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