forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbrowserShare.ts
More file actions
103 lines (91 loc) · 3.69 KB
/
Copy pathbrowserShare.ts
File metadata and controls
103 lines (91 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* browserShare.ts — Issue #488
*
* Centralised capability checks and normalised outcomes for browser
* share / clipboard / app-link flows. Replaces inline branching in
* ShareModal, QRCodeGenerator, SplitCalculator, and PaymentURIHandler.
*/
// ── Capability detection ───────────────────────────────────────────────────────
/** True when the Web Share API is available and can share URLs. */
export function canNativeShare(): boolean {
return typeof navigator !== 'undefined' && typeof navigator.share === 'function';
}
/** True when the async Clipboard API is available for writing. */
export function canWriteClipboard(): boolean {
return (
typeof navigator !== 'undefined' &&
!!navigator.clipboard &&
typeof navigator.clipboard.writeText === 'function'
);
}
// ── Outcome type ──────────────────────────────────────────────────────────────
export type ShareOutcome =
| { method: 'native-share'; success: true }
| { method: 'clipboard'; success: true }
| { method: 'clipboard'; success: false; error: Error }
| { method: 'unsupported'; success: false };
// ── Share data ─────────────────────────────────────────────────────────────────
export interface ShareData {
title?: string;
text?: string;
url: string;
}
// ── Core helper ───────────────────────────────────────────────────────────────
/**
* Attempt to share `data` using the best available browser API:
*
* 1. Native share (Web Share API) — preferred on mobile.
* 2. Async Clipboard API — desktop fallback.
* 3. Unsupported — returns `{ method: 'unsupported', success: false }`.
*
* A rejected native share (user dismissed) is treated as a clipboard fallback
* so the user still gets the URL if they cancelled the share sheet.
*/
export async function shareOrCopy(data: ShareData): Promise<ShareOutcome> {
if (canNativeShare()) {
try {
await navigator.share({ title: data.title, text: data.text, url: data.url });
return { method: 'native-share', success: true };
} catch (err) {
// User dismissed the share sheet — fall through to clipboard
const isAbort =
err instanceof DOMException && err.name === 'AbortError';
if (!isAbort) {
// Unexpected error — still try clipboard before giving up
}
}
}
if (canWriteClipboard()) {
try {
await navigator.clipboard.writeText(data.url);
return { method: 'clipboard', success: true };
} catch (err) {
return {
method: 'clipboard',
success: false,
error: err instanceof Error ? err : new Error(String(err)),
};
}
}
return { method: 'unsupported', success: false };
}
/**
* Copy text to the clipboard with a typed outcome.
* Use this when you only need clipboard (e.g. copying a raw link without sharing).
*/
export async function copyToClipboard(
text: string,
): Promise<{ success: true } | { success: false; error: Error }> {
if (!canWriteClipboard()) {
return { success: false, error: new Error('Clipboard API not supported') };
}
try {
await navigator.clipboard.writeText(text);
return { success: true };
} catch (err) {
return {
success: false,
error: err instanceof Error ? err : new Error(String(err)),
};
}
}