Skip to content

Commit 4452fad

Browse files
committed
clipboard: Add useClipboardHasURL hook
1 parent 92c79ff commit 4452fad

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Helpers for @react-native-clipboard/clipboard
3+
*
4+
* @flow strict-local
5+
*/
6+
7+
import { useState, useCallback, useEffect } from 'react';
8+
import { AppState, Platform } from 'react-native';
9+
import Clipboard from '@react-native-clipboard/clipboard';
10+
11+
import * as logging from '../utils/logging';
12+
import { tryParseUrl } from '../utils/url';
13+
14+
/**
15+
* A Hook for the current value of Clipboard.hasURL, when known.
16+
*
17+
* https://github.com/react-native-clipboard/clipboard#hasurl
18+
*
19+
* With a hack to simulate Clipboard.hasURL on iOS <14, and on Android.
20+
*
21+
* Returns the payload of the most recently settled Clipboard.hasURL()
22+
* Promise; otherwise `null` if that Promise rejected, or if no such Promise
23+
* has settled.
24+
*
25+
* Re-queries when clipboard value changes, and when app state changes to
26+
* "active".
27+
*
28+
* Subject to subtle races. Don't use for anything critical, and do a
29+
* sanity-check in clipboard reads informed by the result of this (e.g.,
30+
* check the retrieved string with `tryParseUrl`).
31+
*/
32+
export function useClipboardHasURL(): boolean | null {
33+
const [result, setResult] = useState<boolean | null>(null);
34+
35+
const getAndSetResult = useCallback(async () => {
36+
try {
37+
// TODO(ios-14): Simplify conditional and jsdoc.
38+
if (Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 14) {
39+
setResult(await Clipboard.hasURL());
40+
} else {
41+
// Hack: Simulate Clipboard.hasURL
42+
setResult(!!tryParseUrl(await Clipboard.getString()));
43+
}
44+
} catch (e) {
45+
logging.error(e);
46+
setResult(null);
47+
}
48+
}, []);
49+
50+
useEffect(() => {
51+
getAndSetResult();
52+
53+
const clipboardListener = Clipboard.addListener(() => {
54+
getAndSetResult();
55+
});
56+
57+
const appStateChangeListener = AppState.addEventListener('change', s => {
58+
if (s === 'active') {
59+
getAndSetResult();
60+
}
61+
});
62+
63+
return () => {
64+
clipboardListener.remove();
65+
AppState.removeEventListener('change', appStateChangeListener);
66+
};
67+
}, [getAndSetResult]);
68+
69+
return result;
70+
}
71+
72+
// We probably don't want a useClipboardHasWebURL. The implementation of
73+
// Clipboard.hasWebURL on iOS is such that it matches when the copied string
74+
// *has* a URL, not when it *is* a URL.

0 commit comments

Comments
 (0)