From 48ace8af34003ff524481b30db8d615cd6eec2be Mon Sep 17 00:00:00 2001 From: ordinarthur <@arthurbarre.js@gmail.com> Date: Mon, 20 Apr 2026 15:31:38 +0200 Subject: [PATCH] feat(web): native share sheet for created + saved links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a "Share link" button that invokes navigator.share() so iOS home-screen PWA users (and any platform with the Web Share API) get the system share sheet — AirDrop, Messages, Mail, WhatsApp… Falls back to copy-only on platforms without support. Wired on both CloudSharePanel (just-created link) and Settings → Shared links (re-share an existing one). Co-Authored-By: Claude Opus 4.7 --- web/src/components/CloudSharePanel.tsx | 42 +++++++++++++++++++++----- web/src/pages/Settings.tsx | 24 +++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/web/src/components/CloudSharePanel.tsx b/web/src/components/CloudSharePanel.tsx index f9a43dd..6df6c75 100644 --- a/web/src/components/CloudSharePanel.tsx +++ b/web/src/components/CloudSharePanel.tsx @@ -71,6 +71,20 @@ export default function CloudSharePanel() { setTimeout(() => setCopied(false), 1500); }; + const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function"; + + const share = async (url: string, fileName: string) => { + try { + await navigator.share({ + title: `AnyDrop — ${fileName}`, + text: "File shared via AnyDrop", + url, + }); + } catch { + // User canceled the share sheet, or the browser rejected — nothing to do. + } + }; + return (
{stage.kind === "idle" && ( @@ -235,13 +249,27 @@ export default function CloudSharePanel() {
- +
+ {canShare && ( + + )} + +

{stage.shareUrl} diff --git a/web/src/pages/Settings.tsx b/web/src/pages/Settings.tsx index 5e33718..a13cc72 100644 --- a/web/src/pages/Settings.tsx +++ b/web/src/pages/Settings.tsx @@ -509,6 +509,22 @@ function SharedLinksSection() { setTimeout(() => setCopiedId((v) => (v === item.id ? null : v)), 1500); }; + const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function"; + + const onShare = async (item: LinkItem) => { + if (!item.keyFrag) return; + const url = `${window.location.origin}/r/${item.id}#k=${item.keyFrag}`; + try { + await navigator.share({ + title: `AnyDrop — ${item.filename ?? "Encrypted file"}`, + text: "File shared via AnyDrop", + url, + }); + } catch { + // User canceled — no-op. + } + }; + return (

@@ -554,6 +570,14 @@ function SharedLinksSection() {
+ {item.keyFrag && status === "active" && canShare && ( + + )} {item.keyFrag && status === "active" && (