feat(web): native share sheet for created + saved links
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 39s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 39s
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 <noreply@anthropic.com>
This commit is contained in:
parent
641ec629f5
commit
48ace8af34
@ -71,6 +71,20 @@ export default function CloudSharePanel() {
|
|||||||
setTimeout(() => setCopied(false), 1500);
|
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 (
|
return (
|
||||||
<div className="paper-panel p-4 sm:p-5">
|
<div className="paper-panel p-4 sm:p-5">
|
||||||
{stage.kind === "idle" && (
|
{stage.kind === "idle" && (
|
||||||
@ -235,13 +249,27 @@ export default function CloudSharePanel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{canShare && (
|
||||||
|
<button
|
||||||
|
onClick={() => share(stage.shareUrl, stage.fileName)}
|
||||||
|
className="flex-1 py-2.5 bg-ink text-paper text-sm font-medium rounded-sm
|
||||||
|
hover:bg-signal transition-colors duration-fast ease-crisp"
|
||||||
|
>
|
||||||
|
Share link →
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => copy(stage.shareUrl)}
|
onClick={() => copy(stage.shareUrl)}
|
||||||
className="w-full py-2.5 bg-ink text-paper text-sm font-medium rounded-sm
|
className={`py-2.5 text-sm font-medium rounded-sm transition-colors duration-fast ease-crisp ${
|
||||||
hover:bg-signal transition-colors duration-fast ease-crisp"
|
canShare
|
||||||
|
? "flex-1 border border-paper-edge hover:border-ink text-ink"
|
||||||
|
: "w-full bg-ink text-paper hover:bg-signal"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{copied ? "Copied ✓" : "Copy link"}
|
{copied ? "Copied ✓" : "Copy link"}
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p className="mt-2 font-mono text-[11px] text-ink-faint break-all">
|
<p className="mt-2 font-mono text-[11px] text-ink-faint break-all">
|
||||||
{stage.shareUrl}
|
{stage.shareUrl}
|
||||||
|
|||||||
@ -509,6 +509,22 @@ function SharedLinksSection() {
|
|||||||
setTimeout(() => setCopiedId((v) => (v === item.id ? null : v)), 1500);
|
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 (
|
return (
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<div className="text-xs uppercase tracking-[0.22em] text-ink-muted">
|
<div className="text-xs uppercase tracking-[0.22em] text-ink-muted">
|
||||||
@ -554,6 +570,14 @@ function SharedLinksSection() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-end gap-1.5 shrink-0">
|
<div className="flex flex-col items-end gap-1.5 shrink-0">
|
||||||
|
{item.keyFrag && status === "active" && canShare && (
|
||||||
|
<button
|
||||||
|
onClick={() => onShare(item)}
|
||||||
|
className="text-xs text-ink hover:text-signal transition-colors"
|
||||||
|
>
|
||||||
|
Share →
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{item.keyFrag && status === "active" && (
|
{item.keyFrag && status === "active" && (
|
||||||
<button
|
<button
|
||||||
onClick={() => onCopy(item)}
|
onClick={() => onCopy(item)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user