Skip to content

feat: Add Web Share API support#24325

Open
Artur- wants to merge 5 commits into
mainfrom
feature/web-share
Open

feat: Add Web Share API support#24325
Artur- wants to merge 5 commits into
mainfrom
feature/web-share

Conversation

@Artur-

@Artur- Artur- commented May 12, 2026

Copy link
Copy Markdown
Member

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

@Artur- Artur- force-pushed the feature/web-share branch from b11fc3b to b71461f Compare May 12, 2026 13:03
@github-actions

github-actions Bot commented May 12, 2026

Copy link
Copy Markdown

Test Results

 1 442 files  + 2   1 442 suites  +2   1h 25m 53s ⏱️ + 4m 45s
10 136 tests +10  10 068 ✅ +10  68 💤 ±0  0 ❌ ±0 
10 608 runs  +10  10 539 ✅ +10  69 💤 ±0  0 ❌ ±0 

Results for commit 6c0ea96. ± Comparison against base commit 2cf1c20.

♻️ This comment has been updated with latest results.

@github-actions github-actions Bot added +0.1.0 and removed +1.0.0 labels May 13, 2026
Artur- added a commit to vaadin/use-cases that referenced this pull request May 13, 2026
Six use cases exercising Page#shareSupportSignal() and Page#share(...)
from vaadin/flow#24325: share this page, copy-link fallback, custom
message, per-item share in a list, completion feedback, and invite
link. Includes browserless tests, Fly deployment config, and an
API-GAPS.md noting the file-share / canShare / test-simulator gaps
surfaced while building the demos.
@Artur-

Artur- commented May 15, 2026

Copy link
Copy Markdown
Member Author

This should be based on trigger/action type API

Comment thread flow-server/src/main/java/com/vaadin/flow/component/page/Page.java Outdated
@Artur- Artur- force-pushed the feature/web-share branch from cc9dc4a to 7927b21 Compare May 27, 2026 10:34
Reshape Web Share around the trigger/action framework: a new ShareAction
extending PromiseAction renders the navigator.share() call inside a
trigger handler, so it inherits the user gesture the API requires (Page
.executeJs() through a server round-trip cannot). A public WebShare
facade mirrors Clipboard's onClick(button).share(content) shape, with
ShareContent as a multi-format payload builder accepting either string
literals or HasValue components for title, text, and url. A
WebShare.supportSignal() mirrors Geolocation.availabilityHintSignal()
and is seeded from a new v-ws bootstrap parameter through
ExtendedClientDetails into a UIInternals ValueSignal<WebShareSupport>.
Artur- added 4 commits June 9, 2026 11:15
# Conflicts:
#	flow-client/src/main/frontend/Flow.ts
#	flow-server/src/main/java/com/vaadin/flow/component/internal/UIInternals.java
#	flow-server/src/main/java/com/vaadin/flow/component/page/ExtendedClientDetails.java
Return a cached read-only wrapper from the WebShare support signal
accessor instead of calling asReadonly() afresh on every read, matching
the WakeLock and PageVisibility signals and the signal design guideline.
A fresh asReadonly() per call yields an unstable identity and needless
allocations.
WebShare.ts already has a named export (isShareSupported), so the
trailing 'export {}' is redundant and the client build's eslint rule
@typescript-eslint/no-useless-empty-export rejects it, failing CI.
Also pass the share-slot captures as Object[] to JsFunction.of to avoid
the non-varargs varargs-call compiler warning.
@sonarqubecloud

Copy link
Copy Markdown

@Artur- Artur- changed the title feat: Add Web Share API support via Page.share() and Page.isShareSupported() feat: Add Web Share API support Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: 🔎Iteration reviews

Development

Successfully merging this pull request may close these issues.

3 participants