expo-clipboard 3.0.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/clipboard/ClipboardModule.kt +26 -48
- package/build/Clipboard.d.ts +8 -4
- package/build/Clipboard.d.ts.map +1 -1
- package/build/Clipboard.js +8 -4
- package/build/Clipboard.js.map +1 -1
- package/build/Clipboard.types.d.ts +2 -2
- package/build/Clipboard.types.js.map +1 -1
- package/build/ExpoClipboard.d.ts +1 -1
- package/build/ExpoClipboard.d.ts.map +1 -1
- package/build/ExpoClipboard.js +2 -2
- package/build/ExpoClipboard.js.map +1 -1
- package/build/web/ClipboardModule.d.ts.map +1 -1
- package/build/web/ClipboardModule.js +14 -2
- package/build/web/ClipboardModule.js.map +1 -1
- package/ios/ClipboardModule.swift +13 -13
- package/ios/ExpoClipboard.podspec +8 -1
- package/ios/Tests/ClipboardModuleSpec.swift +318 -0
- package/ios/Tests/MockUIPasteboard.swift +90 -0
- package/package.json +3 -3
- package/src/Clipboard.ts +8 -4
- package/src/Clipboard.types.ts +2 -2
- package/src/ExpoClipboard.ts +2 -2
- package/src/web/ClipboardModule.ts +13 -2
- package/tsconfig.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 4.0.0 — 2022-10-25
|
|
14
|
+
|
|
15
|
+
### 🛠 Breaking changes
|
|
16
|
+
|
|
17
|
+
- Bumped iOS deployment target to 13.0 and deprecated support for iOS 12. ([#18873](https://github.com/expo/expo/pull/18873) by [@tsapeta](https://github.com/tsapeta))
|
|
18
|
+
|
|
19
|
+
## 3.1.0 — 2022-07-07
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- Fixed clipboard listener returning invalid `contentTypes` value for images on Android. ([#17644](https://github.com/expo/expo/pull/17644) by [@barthap](https://github.com/barthap))
|
|
24
|
+
- Fixed `setStringAsync` causing bouncing in Safari. ([#18010](https://github.com/expo/expo/pull/18010) by [@barthap](https://github.com/barthap))
|
|
25
|
+
|
|
26
|
+
### 💡 Others
|
|
27
|
+
|
|
28
|
+
- Migrated Expo modules definitions to the new naming convention. ([#17193](https://github.com/expo/expo/pull/17193) by [@tsapeta](https://github.com/tsapeta))
|
|
29
|
+
- Migrated Android module to use Sweet API coroutines. ([#18036](https://github.com/expo/expo/pull/18036) by [@barthap](https://github.com/barthap))
|
|
30
|
+
|
|
13
31
|
## 3.0.1 — 2022-04-20
|
|
14
32
|
|
|
15
33
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '
|
|
6
|
+
version = '4.0.0'
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
9
9
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
@@ -74,7 +74,7 @@ android {
|
|
|
74
74
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
75
75
|
targetSdkVersion safeExtGet("targetSdkVersion", 31)
|
|
76
76
|
versionCode 3
|
|
77
|
-
versionName '
|
|
77
|
+
versionName '4.0.0'
|
|
78
78
|
}
|
|
79
79
|
lintOptions {
|
|
80
80
|
abortOnError false
|
|
@@ -11,19 +11,12 @@ import android.text.Spanned
|
|
|
11
11
|
import android.text.TextUtils
|
|
12
12
|
import android.util.Log
|
|
13
13
|
import androidx.core.os.bundleOf
|
|
14
|
-
import expo.modules.core.errors.ModuleDestroyedException
|
|
15
14
|
import expo.modules.core.utilities.ifNull
|
|
16
|
-
import expo.modules.kotlin.Promise
|
|
17
15
|
import expo.modules.kotlin.exception.CodedException
|
|
16
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
18
17
|
|
|
19
18
|
import expo.modules.kotlin.modules.Module
|
|
20
19
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
21
|
-
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
22
|
-
import kotlinx.coroutines.CoroutineScope
|
|
23
|
-
import kotlinx.coroutines.Dispatchers
|
|
24
|
-
import kotlinx.coroutines.SupervisorJob
|
|
25
|
-
import kotlinx.coroutines.cancel
|
|
26
|
-
import kotlinx.coroutines.launch
|
|
27
20
|
import java.io.File
|
|
28
21
|
|
|
29
22
|
private const val moduleName = "ExpoClipboard"
|
|
@@ -41,10 +34,10 @@ private enum class ContentType(val jsName: String) {
|
|
|
41
34
|
|
|
42
35
|
class ClipboardModule : Module() {
|
|
43
36
|
override fun definition() = ModuleDefinition {
|
|
44
|
-
|
|
37
|
+
Name(moduleName)
|
|
45
38
|
|
|
46
39
|
// region Strings
|
|
47
|
-
|
|
40
|
+
AsyncFunction("getStringAsync") { options: GetStringOptions ->
|
|
48
41
|
val item = clipboardManager.firstItem
|
|
49
42
|
when (options.preferredFormat) {
|
|
50
43
|
StringFormat.PLAIN -> item?.coerceToPlainText(context)
|
|
@@ -52,7 +45,7 @@ class ClipboardModule : Module() {
|
|
|
52
45
|
} ?: ""
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
AsyncFunction("setStringAsync") { content: String, options: SetStringOptions ->
|
|
56
49
|
val clip = when (options.inputFormat) {
|
|
57
50
|
StringFormat.PLAIN -> ClipData.newPlainText(null, content)
|
|
58
51
|
StringFormat.HTML -> {
|
|
@@ -62,10 +55,10 @@ class ClipboardModule : Module() {
|
|
|
62
55
|
}
|
|
63
56
|
}
|
|
64
57
|
clipboardManager.setPrimaryClip(clip)
|
|
65
|
-
return@
|
|
58
|
+
return@AsyncFunction true
|
|
66
59
|
}
|
|
67
60
|
|
|
68
|
-
|
|
61
|
+
AsyncFunction("hasStringAsync") {
|
|
69
62
|
clipboardManager
|
|
70
63
|
.primaryClipDescription
|
|
71
64
|
?.hasTextContent
|
|
@@ -74,76 +67,63 @@ class ClipboardModule : Module() {
|
|
|
74
67
|
// endregion
|
|
75
68
|
|
|
76
69
|
// region Images
|
|
77
|
-
|
|
70
|
+
AsyncFunction("getImageAsync") Coroutine { options: GetImageOptions ->
|
|
78
71
|
val imageUri = clipboardManager
|
|
79
72
|
.takeIf { clipboardHasItemWithType("image/*") }
|
|
80
73
|
?.firstItem
|
|
81
74
|
?.uri
|
|
82
75
|
.ifNull {
|
|
83
|
-
|
|
84
|
-
return@function
|
|
76
|
+
return@Coroutine null
|
|
85
77
|
}
|
|
86
78
|
|
|
87
|
-
|
|
79
|
+
try {
|
|
80
|
+
val imageResult = imageFromContentUri(context, imageUri, options)
|
|
81
|
+
return@Coroutine imageResult.toBundle()
|
|
82
|
+
} catch (err: Throwable) {
|
|
88
83
|
err.printStackTrace()
|
|
89
|
-
|
|
84
|
+
throw when (err) {
|
|
90
85
|
is CodedException -> err
|
|
91
86
|
is SecurityException -> NoPermissionException(err)
|
|
92
87
|
else -> PasteFailureException(err, kind = "image")
|
|
93
88
|
}
|
|
94
|
-
promise.reject(rejectionCause)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
moduleCoroutineScope.launch(exceptionHandler) {
|
|
98
|
-
val imageResult = imageFromContentUri(context, imageUri, options)
|
|
99
|
-
promise.resolve(imageResult.toBundle())
|
|
100
89
|
}
|
|
101
90
|
}
|
|
102
91
|
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
AsyncFunction("setImageAsync") Coroutine { imageData: String ->
|
|
93
|
+
try {
|
|
94
|
+
val clip = clipDataFromBase64Image(context, imageData, clipboardCacheDir)
|
|
95
|
+
clipboardManager.setPrimaryClip(clip)
|
|
96
|
+
} catch (err: Throwable) {
|
|
105
97
|
err.printStackTrace()
|
|
106
|
-
|
|
98
|
+
throw when (err) {
|
|
107
99
|
is CodedException -> err
|
|
108
100
|
else -> CopyFailureException(err, kind = "image")
|
|
109
101
|
}
|
|
110
|
-
promise.reject(rejectionCause)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
moduleCoroutineScope.launch(exceptionHandler) {
|
|
114
|
-
val clip = clipDataFromBase64Image(context, imageData, clipboardCacheDir)
|
|
115
|
-
clipboardManager.setPrimaryClip(clip)
|
|
116
|
-
promise.resolve(null)
|
|
117
102
|
}
|
|
118
103
|
}
|
|
119
104
|
|
|
120
|
-
|
|
105
|
+
AsyncFunction("hasImageAsync") {
|
|
121
106
|
clipboardManager.primaryClipDescription?.hasMimeType("image/*") == true
|
|
122
107
|
}
|
|
123
108
|
//endregion
|
|
124
109
|
|
|
125
110
|
// region Events
|
|
126
|
-
|
|
111
|
+
Events(CLIPBOARD_CHANGED_EVENT_NAME)
|
|
127
112
|
|
|
128
|
-
|
|
113
|
+
OnCreate {
|
|
129
114
|
clipboardEventEmitter = ClipboardEventEmitter()
|
|
130
115
|
clipboardEventEmitter.attachListener()
|
|
131
116
|
}
|
|
132
117
|
|
|
133
|
-
|
|
118
|
+
OnDestroy {
|
|
134
119
|
clipboardEventEmitter.detachListener()
|
|
135
|
-
try {
|
|
136
|
-
moduleCoroutineScope.cancel(ModuleDestroyedException())
|
|
137
|
-
} catch (e: IllegalStateException) {
|
|
138
|
-
// Ignore: The coroutine scope has no job in it
|
|
139
|
-
}
|
|
140
120
|
}
|
|
141
121
|
|
|
142
|
-
|
|
122
|
+
OnActivityEntersBackground {
|
|
143
123
|
clipboardEventEmitter.pauseListening()
|
|
144
124
|
}
|
|
145
125
|
|
|
146
|
-
|
|
126
|
+
OnActivityEntersForeground {
|
|
147
127
|
clipboardEventEmitter.resumeListening()
|
|
148
128
|
}
|
|
149
129
|
// endregion
|
|
@@ -158,8 +138,6 @@ class ClipboardModule : Module() {
|
|
|
158
138
|
get() = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
|
|
159
139
|
?: throw ClipboardUnavailableException()
|
|
160
140
|
|
|
161
|
-
private val moduleCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
162
|
-
|
|
163
141
|
private val clipboardCacheDir: File by lazy {
|
|
164
142
|
File(context.cacheDir, CLIPBOARD_DIRECTORY_NAME).also { it.mkdirs() }
|
|
165
143
|
}
|
|
@@ -188,7 +166,7 @@ class ClipboardModule : Module() {
|
|
|
188
166
|
"contentTypes" to listOfNotNull(
|
|
189
167
|
ContentType.PLAIN_TEXT.takeIf { clip.hasTextContent },
|
|
190
168
|
ContentType.HTML.takeIf { clip.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML) },
|
|
191
|
-
ContentType.
|
|
169
|
+
ContentType.IMAGE.takeIf { clip.hasMimeType("image/*") }
|
|
192
170
|
).map { it.jsName }
|
|
193
171
|
)
|
|
194
172
|
)
|
package/build/Clipboard.d.ts
CHANGED
|
@@ -48,21 +48,25 @@ export declare function hasStringAsync(): Promise<boolean>;
|
|
|
48
48
|
* Gets the URL from the user's clipboard.
|
|
49
49
|
*
|
|
50
50
|
* @returns A promise that fulfills to the URL in the clipboard.
|
|
51
|
-
* @platform
|
|
51
|
+
* @platform ios
|
|
52
52
|
*/
|
|
53
53
|
export declare function getUrlAsync(): Promise<string | null>;
|
|
54
54
|
/**
|
|
55
55
|
* Sets a URL in the user's clipboard.
|
|
56
56
|
*
|
|
57
|
+
* This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that
|
|
58
|
+
* it sets the clipboard content type to be a URL. It lets your app or other apps know that the
|
|
59
|
+
* clipboard contains a URL and behave accordingly.
|
|
60
|
+
*
|
|
57
61
|
* @param url The URL to save to the clipboard.
|
|
58
|
-
* @platform
|
|
62
|
+
* @platform ios
|
|
59
63
|
*/
|
|
60
64
|
export declare function setUrlAsync(url: string): Promise<void>;
|
|
61
65
|
/**
|
|
62
66
|
* Returns whether the clipboard has a URL content.
|
|
63
67
|
*
|
|
64
68
|
* @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.
|
|
65
|
-
* @platform
|
|
69
|
+
* @platform ios
|
|
66
70
|
*/
|
|
67
71
|
export declare function hasUrlAsync(): Promise<boolean>;
|
|
68
72
|
/**
|
|
@@ -98,7 +102,7 @@ export declare function getImageAsync(options: GetImageOptions): Promise<Clipboa
|
|
|
98
102
|
*/
|
|
99
103
|
export declare function setImageAsync(base64Image: string): Promise<void>;
|
|
100
104
|
/**
|
|
101
|
-
* Returns whether the clipboard has
|
|
105
|
+
* Returns whether the clipboard has an image content.
|
|
102
106
|
*
|
|
103
107
|
* On web, this requires the user to grant your app permission to _"see text and images copied to the clipboard"_.
|
|
104
108
|
*
|
package/build/Clipboard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAiC,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAO3B,aAAK,cAAc,GAAG;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAExC;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK1D;AAED
|
|
1
|
+
{"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAiC,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAO3B,aAAK,cAAc,GAAG;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAExC;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAKpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAK5F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtE;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAKtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,YAAY,CAe5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,YAAY,QAEjE;AAED,cAAc,mBAAmB,CAAC"}
|
package/build/Clipboard.js
CHANGED
|
@@ -63,7 +63,7 @@ export function hasStringAsync() {
|
|
|
63
63
|
* Gets the URL from the user's clipboard.
|
|
64
64
|
*
|
|
65
65
|
* @returns A promise that fulfills to the URL in the clipboard.
|
|
66
|
-
* @platform
|
|
66
|
+
* @platform ios
|
|
67
67
|
*/
|
|
68
68
|
export async function getUrlAsync() {
|
|
69
69
|
if (!ExpoClipboard.getUrlAsync) {
|
|
@@ -74,8 +74,12 @@ export async function getUrlAsync() {
|
|
|
74
74
|
/**
|
|
75
75
|
* Sets a URL in the user's clipboard.
|
|
76
76
|
*
|
|
77
|
+
* This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that
|
|
78
|
+
* it sets the clipboard content type to be a URL. It lets your app or other apps know that the
|
|
79
|
+
* clipboard contains a URL and behave accordingly.
|
|
80
|
+
*
|
|
77
81
|
* @param url The URL to save to the clipboard.
|
|
78
|
-
* @platform
|
|
82
|
+
* @platform ios
|
|
79
83
|
*/
|
|
80
84
|
export async function setUrlAsync(url) {
|
|
81
85
|
if (!ExpoClipboard.setUrlAsync) {
|
|
@@ -87,7 +91,7 @@ export async function setUrlAsync(url) {
|
|
|
87
91
|
* Returns whether the clipboard has a URL content.
|
|
88
92
|
*
|
|
89
93
|
* @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.
|
|
90
|
-
* @platform
|
|
94
|
+
* @platform ios
|
|
91
95
|
*/
|
|
92
96
|
export async function hasUrlAsync() {
|
|
93
97
|
if (!ExpoClipboard.hasUrlAsync) {
|
|
@@ -138,7 +142,7 @@ export async function setImageAsync(base64Image) {
|
|
|
138
142
|
return ExpoClipboard.setImageAsync(base64Image);
|
|
139
143
|
}
|
|
140
144
|
/**
|
|
141
|
-
* Returns whether the clipboard has
|
|
145
|
+
* Returns whether the clipboard has an image content.
|
|
142
146
|
*
|
|
143
147
|
* On web, this requires the user to grant your app permission to _"see text and images copied to the clipboard"_.
|
|
144
148
|
*
|
package/build/Clipboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ContentType,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport ExpoClipboard from './ExpoClipboard';\n\nconst emitter = new EventEmitter(ExpoClipboard);\n\nconst onClipboardEventName = 'onClipboardChanged';\n\ntype ClipboardEvent = {\n /**\n * @deprecated Returns empty string. Use [`getStringAsync()`](#getstringasyncoptions) instead to retrieve clipboard content.\n */\n content: string;\n /**\n * An array of content types that are available on the clipboard.\n */\n contentTypes: ContentType[];\n};\n\nexport { Subscription, ClipboardEvent };\n\n/**\n * Gets the content of the user's clipboard. Please note that calling this method on web will prompt\n * the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise<string> {\n if (!ExpoClipboard.getStringAsync) {\n throw new UnavailabilityError('Clipboard', 'getStringAsync');\n }\n return await ExpoClipboard.getStringAsync(options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n *\n * @param text The string to save to the clipboard.\n * @param options Options for the clipboard content to be set.\n * @returns On web, this returns a promise that fulfills to a boolean value indicating whether or not\n * the string was saved to the user's clipboard. On iOS and Android, the promise always resolves to `true`.\n */\nexport async function setStringAsync(\n text: string,\n options: SetStringOptions = {}\n): Promise<boolean> {\n if (!ExpoClipboard.setStringAsync) {\n throw new UnavailabilityError('Clipboard', 'setStringAsync');\n }\n return ExpoClipboard.setStringAsync(text, options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead.\n *\n * @returns On web, this returns a boolean value indicating whether or not the string was saved to\n * the user's clipboard. On iOS and Android, nothing is returned.\n */\nexport function setString(text: string): void {\n if (Platform.OS === 'web') {\n // on web, we need to return legacy method,\n // because of different return type\n return ExpoClipboard.setString(text);\n } else {\n setStringAsync(text);\n }\n}\n\n/**\n * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML).\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has text content, resolves to `false` otherwise.\n */\nexport function hasStringAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasStringAsync) {\n throw new UnavailabilityError('Clipboard', 'hasStringAsync');\n }\n return ExpoClipboard.hasStringAsync();\n}\n\n/**\n * Gets the URL from the user's clipboard.\n *\n * @returns A promise that fulfills to the URL in the clipboard.\n * @platform iOS\n */\nexport async function getUrlAsync(): Promise<string | null> {\n if (!ExpoClipboard.getUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'getUrlAsync');\n }\n return await ExpoClipboard.getUrlAsync();\n}\n\n/**\n * Sets a URL in the user's clipboard.\n *\n * @param url The URL to save to the clipboard.\n * @platform iOS\n */\nexport async function setUrlAsync(url: string): Promise<void> {\n if (!ExpoClipboard.setUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'setUrlAsync');\n }\n return ExpoClipboard.setUrlAsync(url);\n}\n\n/**\n * Returns whether the clipboard has a URL content.\n *\n * @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.\n * @platform iOS\n */\nexport async function hasUrlAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'hasUrlAsync');\n }\n return await ExpoClipboard.hasUrlAsync();\n}\n\n/**\n * Gets the image from the user's clipboard and returns it in the specified format. Please note that calling\n * this method on web will prompt the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options A `GetImageOptions` object to specify the desired format of the image.\n * @returns If there was an image in the clipboard, the promise resolves to\n * a [`ClipboardImage`](#clipboardimage) object containing the base64 string and metadata of the image.\n * Otherwise, it resolves to `null`.\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * <Image source={{ uri: img?.data }} style={{ width: 200, height: 200 }} />\n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!ExpoClipboard.getImageAsync) {\n throw new UnavailabilityError('Clipboard', 'getImageAsync');\n }\n return await ExpoClipboard.getImageAsync(options);\n}\n\n/**\n * Sets an image in the user's clipboard.\n *\n * @param base64Image Image encoded as a base64 string, without MIME type.\n *\n * @example\n * ```tsx\n * const result = await ImagePicker.launchImageLibraryAsync({\n * mediaTypes: ImagePicker.MediaTypeOptions.Images,\n * base64: true,\n * });\n * await Clipboard.setImageAsync(result.base64);\n * ```\n */\nexport async function setImageAsync(base64Image: string): Promise<void> {\n if (!ExpoClipboard.setImageAsync) {\n throw new UnavailabilityError('Clipboard', 'setImageAsync');\n }\n return ExpoClipboard.setImageAsync(base64Image);\n}\n\n/**\n * Returns whether the clipboard has a image content.\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has image content, resolves to `false` otherwise.\n */\nexport async function hasImageAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasImageAsync) {\n throw new UnavailabilityError('Clipboard', 'hasImageAsync');\n }\n return ExpoClipboard.hasImageAsync();\n}\n\n/**\n * Adds a listener that will fire whenever the content of the user's clipboard changes. This method\n * is a no-op on Web.\n *\n * @param listener Callback to execute when listener is triggered. The callback is provided a\n * single argument that is an object containing information about clipboard contents.\n *\n * @example\n * ```typescript\n * Clipboard.addClipboardListener(({ contentTypes }: ClipboardEvent) => {\n * if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {\n * Clipboard.getStringAsync().then(content => {\n * alert('Copy pasta! Here\\'s the string that was copied: ' + content)\n * });\n * } else if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {\n * alert('Yay! Clipboard contains an image');\n * }\n * });\n * ```\n */\nexport function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {\n // TODO: Get rid of this wrapper once we remove deprecated `content` property (not before SDK47)\n const listenerWrapper = (event: ClipboardEvent) => {\n const wrappedEvent: ClipboardEvent = {\n ...event,\n get content(): string {\n console.warn(\n \"The 'content' property of the clipboard event is deprecated. Use 'getStringAsync()' instead to get clipboard content\"\n );\n return '';\n },\n };\n listener(wrappedEvent);\n };\n return emitter.addListener<ClipboardEvent>(onClipboardEventName, listenerWrapper);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n *\n * @param subscription The subscription to remove (created by addClipboardListener).\n *\n * @example\n * ```typescript\n * const subscription = addClipboardListener(() => {\n * alert('Copy pasta!');\n * });\n * removeClipboardListener(subscription);\n * ```\n */\nexport function removeClipboardListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\nexport * from './Clipboard.types';\n"]}
|
|
1
|
+
{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ContentType,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport ExpoClipboard from './ExpoClipboard';\n\nconst emitter = new EventEmitter(ExpoClipboard);\n\nconst onClipboardEventName = 'onClipboardChanged';\n\ntype ClipboardEvent = {\n /**\n * @deprecated Returns empty string. Use [`getStringAsync()`](#getstringasyncoptions) instead to retrieve clipboard content.\n */\n content: string;\n /**\n * An array of content types that are available on the clipboard.\n */\n contentTypes: ContentType[];\n};\n\nexport { Subscription, ClipboardEvent };\n\n/**\n * Gets the content of the user's clipboard. Please note that calling this method on web will prompt\n * the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise<string> {\n if (!ExpoClipboard.getStringAsync) {\n throw new UnavailabilityError('Clipboard', 'getStringAsync');\n }\n return await ExpoClipboard.getStringAsync(options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n *\n * @param text The string to save to the clipboard.\n * @param options Options for the clipboard content to be set.\n * @returns On web, this returns a promise that fulfills to a boolean value indicating whether or not\n * the string was saved to the user's clipboard. On iOS and Android, the promise always resolves to `true`.\n */\nexport async function setStringAsync(\n text: string,\n options: SetStringOptions = {}\n): Promise<boolean> {\n if (!ExpoClipboard.setStringAsync) {\n throw new UnavailabilityError('Clipboard', 'setStringAsync');\n }\n return ExpoClipboard.setStringAsync(text, options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead.\n *\n * @returns On web, this returns a boolean value indicating whether or not the string was saved to\n * the user's clipboard. On iOS and Android, nothing is returned.\n */\nexport function setString(text: string): void {\n if (Platform.OS === 'web') {\n // on web, we need to return legacy method,\n // because of different return type\n return ExpoClipboard.setString(text);\n } else {\n setStringAsync(text);\n }\n}\n\n/**\n * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML).\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has text content, resolves to `false` otherwise.\n */\nexport function hasStringAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasStringAsync) {\n throw new UnavailabilityError('Clipboard', 'hasStringAsync');\n }\n return ExpoClipboard.hasStringAsync();\n}\n\n/**\n * Gets the URL from the user's clipboard.\n *\n * @returns A promise that fulfills to the URL in the clipboard.\n * @platform ios\n */\nexport async function getUrlAsync(): Promise<string | null> {\n if (!ExpoClipboard.getUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'getUrlAsync');\n }\n return await ExpoClipboard.getUrlAsync();\n}\n\n/**\n * Sets a URL in the user's clipboard.\n *\n * This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that\n * it sets the clipboard content type to be a URL. It lets your app or other apps know that the\n * clipboard contains a URL and behave accordingly.\n *\n * @param url The URL to save to the clipboard.\n * @platform ios\n */\nexport async function setUrlAsync(url: string): Promise<void> {\n if (!ExpoClipboard.setUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'setUrlAsync');\n }\n return ExpoClipboard.setUrlAsync(url);\n}\n\n/**\n * Returns whether the clipboard has a URL content.\n *\n * @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.\n * @platform ios\n */\nexport async function hasUrlAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'hasUrlAsync');\n }\n return await ExpoClipboard.hasUrlAsync();\n}\n\n/**\n * Gets the image from the user's clipboard and returns it in the specified format. Please note that calling\n * this method on web will prompt the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options A `GetImageOptions` object to specify the desired format of the image.\n * @returns If there was an image in the clipboard, the promise resolves to\n * a [`ClipboardImage`](#clipboardimage) object containing the base64 string and metadata of the image.\n * Otherwise, it resolves to `null`.\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * <Image source={{ uri: img?.data }} style={{ width: 200, height: 200 }} />\n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!ExpoClipboard.getImageAsync) {\n throw new UnavailabilityError('Clipboard', 'getImageAsync');\n }\n return await ExpoClipboard.getImageAsync(options);\n}\n\n/**\n * Sets an image in the user's clipboard.\n *\n * @param base64Image Image encoded as a base64 string, without MIME type.\n *\n * @example\n * ```tsx\n * const result = await ImagePicker.launchImageLibraryAsync({\n * mediaTypes: ImagePicker.MediaTypeOptions.Images,\n * base64: true,\n * });\n * await Clipboard.setImageAsync(result.base64);\n * ```\n */\nexport async function setImageAsync(base64Image: string): Promise<void> {\n if (!ExpoClipboard.setImageAsync) {\n throw new UnavailabilityError('Clipboard', 'setImageAsync');\n }\n return ExpoClipboard.setImageAsync(base64Image);\n}\n\n/**\n * Returns whether the clipboard has an image content.\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has image content, resolves to `false` otherwise.\n */\nexport async function hasImageAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasImageAsync) {\n throw new UnavailabilityError('Clipboard', 'hasImageAsync');\n }\n return ExpoClipboard.hasImageAsync();\n}\n\n/**\n * Adds a listener that will fire whenever the content of the user's clipboard changes. This method\n * is a no-op on Web.\n *\n * @param listener Callback to execute when listener is triggered. The callback is provided a\n * single argument that is an object containing information about clipboard contents.\n *\n * @example\n * ```typescript\n * Clipboard.addClipboardListener(({ contentTypes }: ClipboardEvent) => {\n * if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {\n * Clipboard.getStringAsync().then(content => {\n * alert('Copy pasta! Here\\'s the string that was copied: ' + content)\n * });\n * } else if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {\n * alert('Yay! Clipboard contains an image');\n * }\n * });\n * ```\n */\nexport function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {\n // TODO: Get rid of this wrapper once we remove deprecated `content` property (not before SDK47)\n const listenerWrapper = (event: ClipboardEvent) => {\n const wrappedEvent: ClipboardEvent = {\n ...event,\n get content(): string {\n console.warn(\n \"The 'content' property of the clipboard event is deprecated. Use 'getStringAsync()' instead to get clipboard content\"\n );\n return '';\n },\n };\n listener(wrappedEvent);\n };\n return emitter.addListener<ClipboardEvent>(onClipboardEventName, listenerWrapper);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n *\n * @param subscription The subscription to remove (created by addClipboardListener).\n *\n * @example\n * ```typescript\n * const subscription = addClipboardListener(() => {\n * alert('Copy pasta!');\n * });\n * removeClipboardListener(subscription);\n * ```\n */\nexport function removeClipboardListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\nexport * from './Clipboard.types';\n"]}
|
|
@@ -58,7 +58,7 @@ export interface GetStringOptions {
|
|
|
58
58
|
/**
|
|
59
59
|
* The target format of the clipboard string to be converted to, if possible.
|
|
60
60
|
*
|
|
61
|
-
* @default
|
|
61
|
+
* @default StringFormat.PLAIN_TEXT
|
|
62
62
|
*/
|
|
63
63
|
preferredFormat?: StringFormat;
|
|
64
64
|
}
|
|
@@ -67,7 +67,7 @@ export interface SetStringOptions {
|
|
|
67
67
|
* The input format of the provided string.
|
|
68
68
|
* Adjusting this option can help other applications interpret copied string properly.
|
|
69
69
|
*
|
|
70
|
-
* @default
|
|
70
|
+
* @default StringFormat.PLAIN_TEXT
|
|
71
71
|
*/
|
|
72
72
|
inputFormat?: StringFormat;
|
|
73
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Clipboard.types.js","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AAyCA;;GAEG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf;;OAEG;IACH,0BAAW,CAAA;AACb,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,wCAAwB,CAAA;IACxB,6BAAa,CAAA;AACf,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB","sourcesContent":["// @needsAudit\nexport interface GetImageOptions {\n /**\n * The format of the clipboard image to be converted to.\n */\n format: 'png' | 'jpeg';\n /**\n * Specify the quality of the returned image, between `0` and `1`. Defaults to `1` (highest quality).\n * Applicable only when `format` is set to `jpeg`, ignored otherwise.\n * @default 1\n */\n jpegQuality?: number;\n}\n\n// @needsAudit\nexport interface ClipboardImage {\n /**\n * A Base64-encoded string of the image data.\n * Its format is dependent on the `format` option.\n *\n * > **NOTE:** The string is already prepended with `data:image/png;base64,` or `data:image/jpeg;base64,` prefix.\n *\n * You can use it directly as the source of an `Image` element.\n * @example\n * ```ts\n * <Image\n * source={{ uri: clipboardImage.data }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n data: string;\n /**\n * Dimensions (`width` and `height`) of the image pasted from clipboard.\n */\n size: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Type used to define what type of data is stored in the clipboard.\n */\nexport enum ContentType {\n PLAIN_TEXT = 'plain-text',\n HTML = 'html',\n IMAGE = 'image',\n /**\n * @platform iOS\n */\n URL = 'url',\n}\n\n/**\n * Type used to determine string format stored in the clipboard.\n */\nexport enum StringFormat {\n PLAIN_TEXT = 'plainText',\n HTML = 'html',\n}\n\nexport interface GetStringOptions {\n /**\n * The target format of the clipboard string to be converted to, if possible.\n *\n * @default
|
|
1
|
+
{"version":3,"file":"Clipboard.types.js","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AAyCA;;GAEG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf;;OAEG;IACH,0BAAW,CAAA;AACb,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,wCAAwB,CAAA;IACxB,6BAAa,CAAA;AACf,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB","sourcesContent":["// @needsAudit\nexport interface GetImageOptions {\n /**\n * The format of the clipboard image to be converted to.\n */\n format: 'png' | 'jpeg';\n /**\n * Specify the quality of the returned image, between `0` and `1`. Defaults to `1` (highest quality).\n * Applicable only when `format` is set to `jpeg`, ignored otherwise.\n * @default 1\n */\n jpegQuality?: number;\n}\n\n// @needsAudit\nexport interface ClipboardImage {\n /**\n * A Base64-encoded string of the image data.\n * Its format is dependent on the `format` option.\n *\n * > **NOTE:** The string is already prepended with `data:image/png;base64,` or `data:image/jpeg;base64,` prefix.\n *\n * You can use it directly as the source of an `Image` element.\n * @example\n * ```ts\n * <Image\n * source={{ uri: clipboardImage.data }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n data: string;\n /**\n * Dimensions (`width` and `height`) of the image pasted from clipboard.\n */\n size: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Type used to define what type of data is stored in the clipboard.\n */\nexport enum ContentType {\n PLAIN_TEXT = 'plain-text',\n HTML = 'html',\n IMAGE = 'image',\n /**\n * @platform iOS\n */\n URL = 'url',\n}\n\n/**\n * Type used to determine string format stored in the clipboard.\n */\nexport enum StringFormat {\n PLAIN_TEXT = 'plainText',\n HTML = 'html',\n}\n\nexport interface GetStringOptions {\n /**\n * The target format of the clipboard string to be converted to, if possible.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n preferredFormat?: StringFormat;\n}\n\nexport interface SetStringOptions {\n /**\n * The input format of the provided string.\n * Adjusting this option can help other applications interpret copied string properly.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n inputFormat?: StringFormat;\n}\n"]}
|
package/build/ExpoClipboard.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":";AAEA,
|
|
1
|
+
{"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":";AAEA,wBAAoD"}
|
package/build/ExpoClipboard.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
export default requireNativeModule('ExpoClipboard');
|
|
3
3
|
//# sourceMappingURL=ExpoClipboard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoClipboard.js","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ExpoClipboard.js","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAe,mBAAmB,CAAC,eAAe,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nexport default requireNativeModule('ExpoClipboard');\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipboardModule.d.ts","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;;;4BAqBI,gBAAgB,GAAG,QAAQ,MAAM,CAAC;oBA6ChD,MAAM,GAAG,OAAO;yBAcL,MAAM,WAAW,gBAAgB,GAAG,QAAQ,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ClipboardModule.d.ts","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;;;4BAqBI,gBAAgB,GAAG,QAAQ,MAAM,CAAC;oBA6ChD,MAAM,GAAG,OAAO;yBAcL,MAAM,WAAW,gBAAgB,GAAG,QAAQ,OAAO,CAAC;sBAkCvD,QAAQ,OAAO,CAAC;4BAGV,eAAe,GAAG,QAAQ,cAAc,GAAG,IAAI,CAAC;+BA0B7C,MAAM,GAAG,QAAQ,IAAI,CAAC;qBAuBhC,QAAQ,OAAO,CAAC;4BAGf,IAAI;+BACD,IAAI;;AAzJjC,wBA0JE"}
|
|
@@ -85,8 +85,20 @@ export default {
|
|
|
85
85
|
throw new CopyFailureException(e.message);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
default:
|
|
89
|
-
|
|
88
|
+
default: {
|
|
89
|
+
try {
|
|
90
|
+
if (!navigator.clipboard) {
|
|
91
|
+
throw new Error();
|
|
92
|
+
}
|
|
93
|
+
await navigator.clipboard.writeText(text);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// we can fall back to legacy behavior in any kind of failure
|
|
98
|
+
// including navigator.clipboard unavailability
|
|
99
|
+
return this.setString(text);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
90
102
|
}
|
|
91
103
|
},
|
|
92
104
|
async hasStringAsync() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipboardModule.js","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,gCAAgC,GACjC,MAAM,SAAS,CAAC;AAEjB,eAAe;IACb,IAAI,IAAI;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,OAAyB;QAC5C,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;YACxB,MAAM,IAAI,6BAA6B,EAAE,CAAC;SAC3C;QAED,IAAI;YACF,QAAQ,OAAO,CAAC,eAAe,EAAE;gBAC/B,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;oBACtB,yBAAyB;oBACzB,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;oBACxD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC,cAAc,CAAC,CAAC;oBAC5D,IAAI,CAAC,IAAI,EAAE;wBACT,0BAA0B;wBAC1B,OAAO,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;qBAC7C;oBACD,OAAO,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;iBACxC;gBACD,OAAO,CAAC,CAAC;oBACP,IAAI,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;oBAChD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,EAAE,EAAE;wBACxB,oDAAoD;wBACpD,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;wBACxD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC,cAAc,CAAC,CAAC;wBAC5D,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,IAAI,EAAE,CAAC;wBACpC,IAAI,GAAG,eAAe,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;qBACxC;oBACD,OAAO,IAAI,CAAC;iBACb;aACF;SACF;QAAC,OAAO,CAAC,EAAE;YACV,gDAAgD;YAChD,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,MAAM,gCAAgC,EAAE,CAAC,EAAE;gBAC9E,MAAM,IAAI,qBAAqB,EAAE,CAAC;aACnC;YAED,IAAI;gBACF,oBAAoB;gBACpB,aAAa;gBACb,OAAO,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;aAC7C;YAAC,MAAM;gBACN,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC,CAAC;aAC5E;SACF;IACH,CAAC;IACD,sGAAsG;IACtG,SAAS,CAAC,IAAY;QACpB,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACrD,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,SAAS,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI;YACF,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;SACb;QAAC,MAAM;YACN,OAAO,KAAK,CAAC;SACd;gBAAS;YACR,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;SACtC;IACH,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,OAAyB;QAC1D,QAAQ,OAAO,CAAC,WAAW,EAAE;YAC3B,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;gBACtB,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;oBACxB,MAAM,IAAI,6BAA6B,EAAE,CAAC;iBAC3C;gBAED,IAAI;oBACF,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;oBACzD,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;oBACtD,OAAO,IAAI,CAAC;iBACb;gBAAC,OAAO,CAAC,EAAE;oBACV,gDAAgD;oBAChD,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,MAAM,gCAAgC,EAAE,CAAC,EAAE;wBAC9E,MAAM,IAAI,qBAAqB,EAAE,CAAC;qBACnC;oBACD,MAAM,IAAI,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;iBAC3C;aACF;YACD;gBACE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;SAC/B;IACH,CAAC;IACD,KAAK,CAAC,cAAc;QAClB,OAAO,MAAM,sBAAsB,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,QAAyB;QAC3C,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;YACxB,MAAM,IAAI,6BAA6B,EAAE,CAAC;SAC3C;QAED,IAAI;YACF,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,cAAc,CAAC,CAAC;YAC7D,IAAI,CAAC,IAAI,EAAE;gBACT,OAAO,IAAI,CAAC;aACb;YAED,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACrC,iBAAiB,CAAC,IAAI,CAAC;gBACvB,yBAAyB,CAAC,IAAI,CAAC;aAChC,CAAC,CAAC;YAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SACvB;QAAC,OAAO,CAAC,EAAE;YACV,gDAAgD;YAChD,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,MAAM,gCAAgC,EAAE,CAAC,EAAE;gBAC9E,MAAM,IAAI,qBAAqB,EAAE,CAAC;aACnC;YACD,MAAM,IAAI,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC5C;IACH,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,WAAmB;QACrC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;YACxB,MAAM,IAAI,6BAA6B,EAAE,CAAC;SAC3C;QAED,IAAI;YACF,0FAA0F;YAC1F,oDAAoD;YACpD,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YACpD,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;gBAC9B,wFAAwF;gBACxF,0BAA0B;gBAC1B,kBAAkB;gBAClB,kGAAkG;gBAClG,gDAAgD;gBAChD,IAAI,aAAa,CAAC;oBAChB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI;iBACuB,CAAC;aAC5C,CAAC,CAAC;SACJ;QAAC,OAAO,GAAQ,EAAE;YACjB,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;SAC7C;IACH,CAAC;IACD,KAAK,CAAC,aAAa;QACjB,OAAO,MAAM,sBAAsB,CAAC,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,oBAAoB,KAAU,CAAC;IAC/B,uBAAuB,KAAU,CAAC;CACnC,CAAC;AAEF;;;;GAIG;AACH,KAAK,UAAU,sBAAsB,CAAC,KAAe;IACnD,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;QACxB,MAAM,IAAI,6BAA6B,EAAE,CAAC;KAC3C;IAED,IAAI;QACF,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACxD,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;KAC1F;IAAC,OAAO,CAAC,EAAE;QACV,gDAAgD;QAChD,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,MAAM,gCAAgC,EAAE,CAAC,EAAE;YAC9E,MAAM,IAAI,qBAAqB,EAAE,CAAC;SACnC;QACD,MAAM,CAAC,CAAC;KACT;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,UAAkB;IACjD,OAAO,IAAI,aAAa,CAAC;QACvB,sFAAsF;QACtF,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QAC1D,sFAAsF;QACtF,YAAY,EAAE,IAAI,IAAI,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;KAC9E,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n ClipboardImage,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n StringFormat,\n} from '../Clipboard.types';\nimport {\n ClipboardUnavailableException,\n CopyFailureException,\n NoPermissionException,\n PasteFailureException,\n} from './Exceptions';\nimport {\n base64toBlob,\n blobToBase64Async,\n findHtmlInClipboardAsync,\n findImageInClipboardAsync,\n getImageSizeFromBlobAsync,\n htmlToPlainText,\n isClipboardPermissionDeniedAsync,\n} from './Utils';\n\nexport default {\n get name(): string {\n return 'ExpoClipboard';\n },\n async getStringAsync(options: GetStringOptions): Promise<string> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n switch (options.preferredFormat) {\n case StringFormat.HTML: {\n // Try reading HTML first\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findHtmlInClipboardAsync(clipboardItems);\n if (!blob) {\n // Fall back to plain text\n return await navigator.clipboard.readText();\n }\n return await new Response(blob).text();\n }\n default: {\n let text = await navigator.clipboard.readText();\n if (!text || text === '') {\n // If there's no direct plain text, try reading HTML\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findHtmlInClipboardAsync(clipboardItems);\n const blobText = await blob?.text();\n text = htmlToPlainText(blobText ?? '');\n }\n return text;\n }\n }\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n\n try {\n // Internet Explorer\n // @ts-ignore\n return window.clipboardData.getData('Text');\n } catch {\n return Promise.reject(new Error('Unable to retrieve item from clipboard'));\n }\n }\n },\n // TODO: (barthap) The `setString` was deprecated in SDK 45. Remove this function in a few SDK cycles.\n setString(text: string): boolean {\n const textField = document.createElement('textarea');\n textField.textContent = text;\n document.body.appendChild(textField);\n textField.select();\n try {\n document.execCommand('copy');\n return true;\n } catch {\n return false;\n } finally {\n document.body.removeChild(textField);\n }\n },\n async setStringAsync(text: string, options: SetStringOptions): Promise<boolean> {\n switch (options.inputFormat) {\n case StringFormat.HTML: {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItemInput = createHtmlClipboardItem(text);\n await navigator.clipboard.write([clipboardItemInput]);\n return true;\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw new CopyFailureException(e.message);\n }\n }\n default:\n return this.setString(text);\n }\n },\n async hasStringAsync(): Promise<boolean> {\n return await clipboardHasTypesAsync(['text/plain', 'text/html']);\n },\n async getImageAsync(_options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findImageInClipboardAsync(clipboardItems);\n if (!blob) {\n return null;\n }\n\n const [data, size] = await Promise.all([\n blobToBase64Async(blob),\n getImageSizeFromBlobAsync(blob),\n ]);\n\n return { data, size };\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw new PasteFailureException(e.message);\n }\n },\n async setImageAsync(base64image: string): Promise<void> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n // we set it always to `image/png` because it's the only format supported by the clipboard\n // but it seems to work even when provided jpeg data\n const blob = base64toBlob(base64image, 'image/png');\n await navigator.clipboard.write([\n // I cannot use `@ts-expect-error` here because some environments consider this correct:\n // expo-module build - OK,\n // et gdad - error\n // Fixed in TS >4.4.3: https://github.com/microsoft/TypeScript/issues/46116#issuecomment-932443415\n // @ts-ignore Some tools seem to use TS <= 4.4.3\n new ClipboardItem({\n [blob.type]: blob,\n } as Record<string, ClipboardItemDataType>),\n ]);\n } catch (err: any) {\n throw new CopyFailureException(err.message);\n }\n },\n async hasImageAsync(): Promise<boolean> {\n return await clipboardHasTypesAsync(['image/png', 'image/jpeg']);\n },\n addClipboardListener(): void {},\n removeClipboardListener(): void {},\n};\n\n/**\n * Resolves to true if clipboard has one of provided {@link types}.\n * @throws `ClipboardUnavailableException` if AsyncClipboard API is not available\n * @throws `NoPermissionException` if user denied permission\n */\nasync function clipboardHasTypesAsync(types: string[]): Promise<boolean> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItems = await navigator.clipboard.read();\n return clipboardItems.flatMap((item) => item.types).some((type) => types.includes(type));\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw e;\n }\n}\n\nfunction createHtmlClipboardItem(htmlString: string): ClipboardItem {\n return new ClipboardItem({\n // @ts-ignore `Blob` from `lib.dom.d.ts` and the one from `@types/react-native` differ\n 'text/html': new Blob([htmlString], { type: 'text/html' }),\n // @ts-ignore `Blob` from `lib.dom.d.ts` and the one from `@types/react-native` differ\n 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ClipboardModule.js","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ClipboardImage,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n StringFormat,\n} from '../Clipboard.types';\nimport {\n ClipboardUnavailableException,\n CopyFailureException,\n NoPermissionException,\n PasteFailureException,\n} from './Exceptions';\nimport {\n base64toBlob,\n blobToBase64Async,\n findHtmlInClipboardAsync,\n findImageInClipboardAsync,\n getImageSizeFromBlobAsync,\n htmlToPlainText,\n isClipboardPermissionDeniedAsync,\n} from './Utils';\n\nexport default {\n get name(): string {\n return 'ExpoClipboard';\n },\n async getStringAsync(options: GetStringOptions): Promise<string> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n switch (options.preferredFormat) {\n case StringFormat.HTML: {\n // Try reading HTML first\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findHtmlInClipboardAsync(clipboardItems);\n if (!blob) {\n // Fall back to plain text\n return await navigator.clipboard.readText();\n }\n return await new Response(blob).text();\n }\n default: {\n let text = await navigator.clipboard.readText();\n if (!text || text === '') {\n // If there's no direct plain text, try reading HTML\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findHtmlInClipboardAsync(clipboardItems);\n const blobText = await blob?.text();\n text = htmlToPlainText(blobText ?? '');\n }\n return text;\n }\n }\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n\n try {\n // Internet Explorer\n // @ts-ignore\n return window.clipboardData.getData('Text');\n } catch {\n return Promise.reject(new Error('Unable to retrieve item from clipboard'));\n }\n }\n },\n // TODO: (barthap) The `setString` was deprecated in SDK 45. Remove this function in a few SDK cycles.\n setString(text: string): boolean {\n const textField = document.createElement('textarea');\n textField.textContent = text;\n document.body.appendChild(textField);\n textField.select();\n try {\n document.execCommand('copy');\n return true;\n } catch {\n return false;\n } finally {\n document.body.removeChild(textField);\n }\n },\n async setStringAsync(text: string, options: SetStringOptions): Promise<boolean> {\n switch (options.inputFormat) {\n case StringFormat.HTML: {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItemInput = createHtmlClipboardItem(text);\n await navigator.clipboard.write([clipboardItemInput]);\n return true;\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw new CopyFailureException(e.message);\n }\n }\n default: {\n try {\n if (!navigator.clipboard) {\n throw new Error();\n }\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // we can fall back to legacy behavior in any kind of failure\n // including navigator.clipboard unavailability\n return this.setString(text);\n }\n }\n }\n },\n async hasStringAsync(): Promise<boolean> {\n return await clipboardHasTypesAsync(['text/plain', 'text/html']);\n },\n async getImageAsync(_options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItems = await navigator.clipboard.read();\n const blob = await findImageInClipboardAsync(clipboardItems);\n if (!blob) {\n return null;\n }\n\n const [data, size] = await Promise.all([\n blobToBase64Async(blob),\n getImageSizeFromBlobAsync(blob),\n ]);\n\n return { data, size };\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw new PasteFailureException(e.message);\n }\n },\n async setImageAsync(base64image: string): Promise<void> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n // we set it always to `image/png` because it's the only format supported by the clipboard\n // but it seems to work even when provided jpeg data\n const blob = base64toBlob(base64image, 'image/png');\n await navigator.clipboard.write([\n // I cannot use `@ts-expect-error` here because some environments consider this correct:\n // expo-module build - OK,\n // et gdad - error\n // Fixed in TS >4.4.3: https://github.com/microsoft/TypeScript/issues/46116#issuecomment-932443415\n // @ts-ignore Some tools seem to use TS <= 4.4.3\n new ClipboardItem({\n [blob.type]: blob,\n } as Record<string, ClipboardItemDataType>),\n ]);\n } catch (err: any) {\n throw new CopyFailureException(err.message);\n }\n },\n async hasImageAsync(): Promise<boolean> {\n return await clipboardHasTypesAsync(['image/png', 'image/jpeg']);\n },\n addClipboardListener(): void {},\n removeClipboardListener(): void {},\n};\n\n/**\n * Resolves to true if clipboard has one of provided {@link types}.\n * @throws `ClipboardUnavailableException` if AsyncClipboard API is not available\n * @throws `NoPermissionException` if user denied permission\n */\nasync function clipboardHasTypesAsync(types: string[]): Promise<boolean> {\n if (!navigator.clipboard) {\n throw new ClipboardUnavailableException();\n }\n\n try {\n const clipboardItems = await navigator.clipboard.read();\n return clipboardItems.flatMap((item) => item.types).some((type) => types.includes(type));\n } catch (e) {\n // it might fail, because user denied permission\n if (e.name === 'NotAllowedError' || (await isClipboardPermissionDeniedAsync())) {\n throw new NoPermissionException();\n }\n throw e;\n }\n}\n\nfunction createHtmlClipboardItem(htmlString: string): ClipboardItem {\n return new ClipboardItem({\n // @ts-ignore `Blob` from `lib.dom.d.ts` and the one from `@types/react-native` differ\n 'text/html': new Blob([htmlString], { type: 'text/html' }),\n // @ts-ignore `Blob` from `lib.dom.d.ts` and the one from `@types/react-native` differ\n 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }),\n });\n}\n"]}
|
|
@@ -7,11 +7,11 @@ let onClipboardChanged = "onClipboardChanged"
|
|
|
7
7
|
|
|
8
8
|
public class ClipboardModule: Module {
|
|
9
9
|
public func definition() -> ModuleDefinition {
|
|
10
|
-
|
|
10
|
+
Name("ExpoClipboard")
|
|
11
11
|
|
|
12
12
|
// MARK: Strings
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
AsyncFunction("getStringAsync") { (options: GetStringOptions) -> String in
|
|
15
15
|
switch options.preferredFormat {
|
|
16
16
|
case .plainText:
|
|
17
17
|
return UIPasteboard.general.string ?? ""
|
|
@@ -20,7 +20,7 @@ public class ClipboardModule: Module {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
AsyncFunction("setStringAsync") { (content: String?, options: SetStringOptions) -> Bool in
|
|
24
24
|
switch options.inputFormat {
|
|
25
25
|
case .plainText:
|
|
26
26
|
UIPasteboard.general.string = content
|
|
@@ -31,27 +31,27 @@ public class ClipboardModule: Module {
|
|
|
31
31
|
return true
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
AsyncFunction("hasStringAsync") { () -> Bool in
|
|
35
35
|
return UIPasteboard.general.hasStrings || UIPasteboard.general.hasHTML
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// MARK: URLs
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
AsyncFunction("getUrlAsync") { () -> String? in
|
|
41
41
|
return UIPasteboard.general.url?.absoluteString
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
AsyncFunction("setUrlAsync") { (url: URL) in
|
|
45
45
|
UIPasteboard.general.url = url
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
AsyncFunction("hasUrlAsync") { () -> Bool in
|
|
49
49
|
return UIPasteboard.general.hasURLs
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// MARK: Images
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
AsyncFunction("setImageAsync") { (content: String) in
|
|
55
55
|
guard let data = Data(base64Encoded: content),
|
|
56
56
|
let image = UIImage(data: data) else {
|
|
57
57
|
throw InvalidImageException(content)
|
|
@@ -59,11 +59,11 @@ public class ClipboardModule: Module {
|
|
|
59
59
|
UIPasteboard.general.image = image
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
AsyncFunction("hasImageAsync") { () -> Bool in
|
|
63
63
|
return UIPasteboard.general.hasImages
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
AsyncFunction("getImageAsync") { (options: GetImageOptions) -> [String: Any]? in
|
|
67
67
|
guard let image = UIPasteboard.general.image else {
|
|
68
68
|
return nil
|
|
69
69
|
}
|
|
@@ -84,9 +84,9 @@ public class ClipboardModule: Module {
|
|
|
84
84
|
|
|
85
85
|
// MARK: Events
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Events(onClipboardChanged)
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
OnStartObserving {
|
|
90
90
|
NotificationCenter.default.removeObserver(self, name: UIPasteboard.changedNotification, object: nil)
|
|
91
91
|
NotificationCenter.default.addObserver(
|
|
92
92
|
self,
|
|
@@ -96,7 +96,7 @@ public class ClipboardModule: Module {
|
|
|
96
96
|
)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
OnStopObserving {
|
|
100
100
|
NotificationCenter.default.removeObserver(self, name: UIPasteboard.changedNotification, object: nil)
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package['license']
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
|
-
s.platform = :ios, '
|
|
13
|
+
s.platform = :ios, '13.0'
|
|
14
14
|
s.swift_version = '5.4'
|
|
15
15
|
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
16
16
|
s.static_framework = true
|
|
@@ -29,4 +29,11 @@ Pod::Spec.new do |s|
|
|
|
29
29
|
else
|
|
30
30
|
s.source_files = "**/*.{h,m,swift}"
|
|
31
31
|
end
|
|
32
|
+
|
|
33
|
+
s.exclude_files = 'Tests/'
|
|
34
|
+
s.test_spec 'Tests' do |test_spec|
|
|
35
|
+
test_spec.dependency 'ExpoModulesTestCore'
|
|
36
|
+
|
|
37
|
+
test_spec.source_files = 'Tests/**/*.{m,swift}'
|
|
38
|
+
end
|
|
32
39
|
end
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import ExpoModulesTestCore
|
|
2
|
+
|
|
3
|
+
@testable import ExpoModulesCore
|
|
4
|
+
@testable import ExpoClipboard
|
|
5
|
+
import UIKit
|
|
6
|
+
import MobileCoreServices
|
|
7
|
+
|
|
8
|
+
class ClipboardModuleSpec: ExpoSpec {
|
|
9
|
+
override func spec() {
|
|
10
|
+
let appContext = AppContext()
|
|
11
|
+
let holder = ModuleHolder(appContext: appContext, module: ClipboardModule(appContext: appContext))
|
|
12
|
+
|
|
13
|
+
func testModuleFunction<T>(_ functionName: String, args: [Any], _ block: @escaping (T?) -> Void) {
|
|
14
|
+
waitUntil { done in
|
|
15
|
+
holder.call(function: functionName, args: args) { result in
|
|
16
|
+
let value = try! result.get()
|
|
17
|
+
expect(value).to(beAKindOf(T?.self))
|
|
18
|
+
block(value as? T)
|
|
19
|
+
done()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func expectModuleFunctionThrows<T>(_ functionName: String, args: [Any], exception: T.Type) where T: Exception {
|
|
25
|
+
waitUntil { done in
|
|
26
|
+
holder.call(function: functionName, args: args) { result in
|
|
27
|
+
expect(result).to(beFailure(exception: exception))
|
|
28
|
+
done()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
beforeSuite {
|
|
34
|
+
swizzleGeneralPasteboard()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// MARK: - Strings
|
|
38
|
+
|
|
39
|
+
describe("getStringAsync") {
|
|
40
|
+
let function = "getStringAsync"
|
|
41
|
+
|
|
42
|
+
it("returns plain text from the clipboard") {
|
|
43
|
+
let expectedString = "hello"
|
|
44
|
+
UIPasteboard.general.string = expectedString
|
|
45
|
+
|
|
46
|
+
// let options = GetStringOptions(preferredFormat: .plainText)
|
|
47
|
+
let options = [
|
|
48
|
+
"preferredFormat": "plainText"
|
|
49
|
+
]
|
|
50
|
+
testModuleFunction(function, args: [options]) { (result: String?) in
|
|
51
|
+
expect(result) == expectedString
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
it("returns html from the clipboard") {
|
|
56
|
+
let expectedHtml = "<p>hello</p>"
|
|
57
|
+
UIPasteboard.general.items = [[
|
|
58
|
+
kUTTypeHTML as String: expectedHtml
|
|
59
|
+
]]
|
|
60
|
+
|
|
61
|
+
// let options = GetStringOptions(preferredFormat: .html)
|
|
62
|
+
let options = [
|
|
63
|
+
"preferredFormat": "html"
|
|
64
|
+
]
|
|
65
|
+
testModuleFunction(function, args: [options]) { (result: String?) in
|
|
66
|
+
expect(result) == expectedHtml
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
it("returns empty string if no text in clipboard") {
|
|
71
|
+
UIPasteboard.general.items = []
|
|
72
|
+
|
|
73
|
+
let options = [
|
|
74
|
+
"preferredFormat": "plainText"
|
|
75
|
+
]
|
|
76
|
+
testModuleFunction(function, args: [options]) { (result: String?) in
|
|
77
|
+
expect(result) == ""
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("setStringAsync") {
|
|
83
|
+
let function = "setStringAsync"
|
|
84
|
+
|
|
85
|
+
it("copies string to clipboard") {
|
|
86
|
+
let expectedString = "hello"
|
|
87
|
+
let options = [
|
|
88
|
+
"inputFormat": "plainText"
|
|
89
|
+
]
|
|
90
|
+
testModuleFunction(function, args: [expectedString, options]) { (result: Bool?) in
|
|
91
|
+
expect(result) == true
|
|
92
|
+
expect(UIPasteboard.general.hasStrings) == true
|
|
93
|
+
expect(UIPasteboard.general.string) == expectedString
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
it("copies HTML to clipboard") {
|
|
98
|
+
let expectedHtml = "<p>hello</p>"
|
|
99
|
+
let options = [
|
|
100
|
+
"inputFormat": "html"
|
|
101
|
+
]
|
|
102
|
+
testModuleFunction(function, args: [expectedHtml, options]) { (result: Bool?) in
|
|
103
|
+
let mockPasteboard = UIPasteboard.StaticVars.mockPastebaord
|
|
104
|
+
expect(result) == true
|
|
105
|
+
expect(mockPasteboard._items.count) == 3
|
|
106
|
+
expect(mockPasteboard._items[kUTTypeRTF as String]).notTo(beNil())
|
|
107
|
+
expect(mockPasteboard._items[kUTTypeHTML as String] as? String).to(contain("hello"))
|
|
108
|
+
expect(mockPasteboard._items[kUTTypeUTF8PlainText as String] as? String).to(contain("hello"))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
describe("hasStringAsync") {
|
|
114
|
+
let function = "hasStringAsync"
|
|
115
|
+
|
|
116
|
+
it("returns true when clipboard contains a string") {
|
|
117
|
+
UIPasteboard.general.string = "hello world"
|
|
118
|
+
|
|
119
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
120
|
+
expect(result) == true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
it("returns false when there are no no items") {
|
|
125
|
+
UIPasteboard.general.items = []
|
|
126
|
+
|
|
127
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
128
|
+
expect(result) == false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
it("returns false when items are not strings") {
|
|
133
|
+
UIPasteboard.general.image = UIImage()
|
|
134
|
+
|
|
135
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
136
|
+
expect(result) == false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// MARK: - Images
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Base64-encoded PNG data - it's a 1x1 image (a black pixel), 8 bit depth
|
|
145
|
+
* the data has length of 68 bytes after decoding
|
|
146
|
+
*/
|
|
147
|
+
let testImageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
|
|
148
|
+
let testImage = UIImage(data: Data(base64Encoded: testImageBase64)!)!
|
|
149
|
+
|
|
150
|
+
describe("setImageAsync") {
|
|
151
|
+
it("copies image to clipboard") {
|
|
152
|
+
testModuleFunction("setImageAsync", args: [testImageBase64]) { (_: Any?) in
|
|
153
|
+
let pasteboardImgData = UIPasteboard.general.image?.pngData()?.base64EncodedString()
|
|
154
|
+
expect(UIPasteboard.general.hasImages) == true
|
|
155
|
+
// compare first 10 characters only as UIImage can optimize the data so it differs
|
|
156
|
+
expect(pasteboardImgData?.prefix(10)) == testImageBase64.prefix(10)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
it("throws when given invalid base64") {
|
|
161
|
+
let base64 = "invalid"
|
|
162
|
+
expectModuleFunctionThrows("setImageAsync", args: [base64], exception: InvalidImageException.self)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
describe("getImageAsync") {
|
|
167
|
+
let function = "getImageAsync"
|
|
168
|
+
|
|
169
|
+
it("returns PNG image from the clipboard") {
|
|
170
|
+
let expectedImgData = testImage.pngData()!.base64EncodedString()
|
|
171
|
+
UIPasteboard.general.image = testImage
|
|
172
|
+
|
|
173
|
+
let options = [
|
|
174
|
+
"format": "png"
|
|
175
|
+
]
|
|
176
|
+
testModuleFunction(function, args: [options]) { (result: [String: Any?]?) in
|
|
177
|
+
let imgData = result!["data"]! as? String?
|
|
178
|
+
let imgSize = result!["size"]! as? [String: Any]
|
|
179
|
+
expect(imgSize!["width"] as? CGFloat) == CGFloat(1)
|
|
180
|
+
expect(imgSize!["height"] as? CGFloat) == CGFloat(1)
|
|
181
|
+
expect(imgData!).to(beginWith("data:image/png;base64,\(expectedImgData)"))
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
it("returns JPEG image from the clipboard") {
|
|
186
|
+
let expectedImgData = testImage.jpegData(compressionQuality: 1.0)!.base64EncodedString()
|
|
187
|
+
UIPasteboard.general.image = testImage
|
|
188
|
+
|
|
189
|
+
let options = [
|
|
190
|
+
"format": "jpeg"
|
|
191
|
+
]
|
|
192
|
+
testModuleFunction(function, args: [options]) { (result: [String: Any?]?) in
|
|
193
|
+
let imgData = result!["data"]! as? String?
|
|
194
|
+
let imgSize = result!["size"]! as? [String: Any]
|
|
195
|
+
expect(imgSize!["width"] as? CGFloat) == CGFloat(1)
|
|
196
|
+
expect(imgSize!["height"] as? CGFloat) == CGFloat(1)
|
|
197
|
+
expect(imgData!).to(beginWith("data:image/jpeg;base64,\(expectedImgData)"))
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
it("returns nil if no image in the clipboard") {
|
|
202
|
+
UIPasteboard.general.items = []
|
|
203
|
+
|
|
204
|
+
let options = [
|
|
205
|
+
"imageFormat": "png"
|
|
206
|
+
]
|
|
207
|
+
testModuleFunction(function, args: [options]) { (result: UIImage?) in
|
|
208
|
+
expect(result).to(beNil())
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
describe("hasImageAsync") {
|
|
214
|
+
let function = "hasImageAsync"
|
|
215
|
+
|
|
216
|
+
it("returns true when there is image") {
|
|
217
|
+
UIPasteboard.general.image = UIImage()
|
|
218
|
+
|
|
219
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
220
|
+
expect(result) == true
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
it("returns false when there are no no items") {
|
|
225
|
+
UIPasteboard.general.items = []
|
|
226
|
+
|
|
227
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
228
|
+
expect(result) == false
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
it("returns false when items are not images") {
|
|
233
|
+
UIPasteboard.general.items = []
|
|
234
|
+
UIPasteboard.general.string = "not an image"
|
|
235
|
+
|
|
236
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
237
|
+
expect(result) == false
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// MARK: - URLs
|
|
243
|
+
|
|
244
|
+
describe("setUrlAsync") {
|
|
245
|
+
it("copies URL to the clipboard") {
|
|
246
|
+
let urlString = "https://expo.dev"
|
|
247
|
+
|
|
248
|
+
testModuleFunction("setUrlAsync", args: [urlString]) { (_: Any?) in
|
|
249
|
+
expect(UIPasteboard.general.hasURLs) == true
|
|
250
|
+
expect(UIPasteboard.general.url?.absoluteString) == urlString
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
describe("getUrlAsync") {
|
|
256
|
+
let function = "getUrlAsync"
|
|
257
|
+
|
|
258
|
+
it("returns URL from the clipboard") {
|
|
259
|
+
let expectedUrl = URL(string: "http://expo.dev")
|
|
260
|
+
UIPasteboard.general.url = expectedUrl
|
|
261
|
+
|
|
262
|
+
testModuleFunction(function, args: []) { (result: String?) in
|
|
263
|
+
expect(result) == expectedUrl?.absoluteString
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
it("returns nil if no URL in the clipboard") {
|
|
268
|
+
UIPasteboard.general.items = []
|
|
269
|
+
|
|
270
|
+
testModuleFunction(function, args: []) { (result: String?) in
|
|
271
|
+
expect(result).to(beNil())
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
describe("hasUrlAsync") {
|
|
277
|
+
let function = "hasUrlAsync"
|
|
278
|
+
|
|
279
|
+
it("returns true when there is a URL") {
|
|
280
|
+
UIPasteboard.general.url = URL(string: "https://expo.dev")
|
|
281
|
+
|
|
282
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
283
|
+
expect(result) == true
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
it("returns false when there are no no items") {
|
|
288
|
+
UIPasteboard.general.items = []
|
|
289
|
+
|
|
290
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
291
|
+
expect(result) == false
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
it("returns false when items are not URLs") {
|
|
296
|
+
UIPasteboard.general.items = []
|
|
297
|
+
UIPasteboard.general.string = "not an url"
|
|
298
|
+
|
|
299
|
+
testModuleFunction(function, args: []) { (result: Bool?) in
|
|
300
|
+
expect(result) == false
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// TODO: (barthap) Replace this with built-in beFailure() when upgraded to Nimble 10.0
|
|
308
|
+
func beFailure<Success, Failure, T: Exception>(exception: T.Type) -> Predicate<Result<Success, Failure>> {
|
|
309
|
+
return Predicate.simple("be \(exception)") { actualExpression in
|
|
310
|
+
guard let actual = try actualExpression.evaluate(),
|
|
311
|
+
case let .failure(error) = actual,
|
|
312
|
+
(error as? Exception)?.rootCause is T
|
|
313
|
+
else {
|
|
314
|
+
return .doesNotMatch
|
|
315
|
+
}
|
|
316
|
+
return .matches
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
class MockUIPasteboard: UIPasteboard {
|
|
4
|
+
var _items: [String: Any] = [:]
|
|
5
|
+
|
|
6
|
+
override var items: [[String: Any]] {
|
|
7
|
+
get {
|
|
8
|
+
return [_items]
|
|
9
|
+
}
|
|
10
|
+
set {
|
|
11
|
+
_items = newValue.count > 0 ? newValue[0] : [:]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
override var string: String? {
|
|
16
|
+
get {
|
|
17
|
+
return _items["string"] as? String
|
|
18
|
+
}
|
|
19
|
+
set {
|
|
20
|
+
_items = ["string": newValue as Any]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override var url: URL? {
|
|
25
|
+
get {
|
|
26
|
+
return _items["url"] as? URL
|
|
27
|
+
}
|
|
28
|
+
set {
|
|
29
|
+
_items = ["url": newValue as Any]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override var image: UIImage? {
|
|
34
|
+
get {
|
|
35
|
+
return _items["image"] as? UIImage
|
|
36
|
+
}
|
|
37
|
+
set {
|
|
38
|
+
_items = ["image": newValue as Any]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override var hasStrings: Bool {
|
|
43
|
+
return _items["string"] != nil
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override var hasImages: Bool {
|
|
47
|
+
return _items["image"] != nil
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override var hasURLs: Bool {
|
|
51
|
+
return _items["url"] != nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override func value(forPasteboardType pasteboardType: String) -> Any? {
|
|
55
|
+
return _items[pasteboardType]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override func data(forPasteboardType pasteboardType: String) -> Data? {
|
|
59
|
+
return _items[pasteboardType] as? Data
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override func setItems(_ items: [[String: Any]], options: [UIPasteboard.OptionsKey: Any] = [:]) {
|
|
63
|
+
self.items = items
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override func contains(pasteboardTypes: [String]) -> Bool {
|
|
67
|
+
return _items.contains(where: { key, _ in
|
|
68
|
+
pasteboardTypes.contains(key)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
extension UIPasteboard {
|
|
74
|
+
struct StaticVars {
|
|
75
|
+
static var mockPastebaord = MockUIPasteboard()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@objc dynamic class var swizzledGeneralPasteboard: UIPasteboard {
|
|
79
|
+
return StaticVars.mockPastebaord
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func swizzleGeneralPasteboard() {
|
|
84
|
+
// replace UIPasteboard.general getter with MockUIPasteboard instance
|
|
85
|
+
let originSelector = #selector(getter:UIPasteboard.general)
|
|
86
|
+
let swizzleSelector = #selector(getter:UIPasteboard.swizzledGeneralPasteboard)
|
|
87
|
+
let originMethod = class_getClassMethod(UIPasteboard.self, originSelector)
|
|
88
|
+
let swizzleMethod = class_getClassMethod(UIPasteboard.self, swizzleSelector)
|
|
89
|
+
method_exchangeImplementations(originMethod!, swizzleMethod!)
|
|
90
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-clipboard",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "ExpoClipboard standalone module",
|
|
5
5
|
"main": "build/Clipboard.js",
|
|
6
6
|
"types": "build/Clipboard.d.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"homepage": "https://docs.expo.dev/versions/latest/sdk/clipboard",
|
|
32
32
|
"dependencies": {},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"expo-module-scripts": "^
|
|
34
|
+
"expo-module-scripts": "^3.0.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"expo": "*"
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"jest": {
|
|
40
40
|
"preset": "expo-module-scripts/universal"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "eab2b09c735fb0fc2bf734a3f29a6593adba3838"
|
|
43
43
|
}
|
package/src/Clipboard.ts
CHANGED
|
@@ -93,7 +93,7 @@ export function hasStringAsync(): Promise<boolean> {
|
|
|
93
93
|
* Gets the URL from the user's clipboard.
|
|
94
94
|
*
|
|
95
95
|
* @returns A promise that fulfills to the URL in the clipboard.
|
|
96
|
-
* @platform
|
|
96
|
+
* @platform ios
|
|
97
97
|
*/
|
|
98
98
|
export async function getUrlAsync(): Promise<string | null> {
|
|
99
99
|
if (!ExpoClipboard.getUrlAsync) {
|
|
@@ -105,8 +105,12 @@ export async function getUrlAsync(): Promise<string | null> {
|
|
|
105
105
|
/**
|
|
106
106
|
* Sets a URL in the user's clipboard.
|
|
107
107
|
*
|
|
108
|
+
* This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that
|
|
109
|
+
* it sets the clipboard content type to be a URL. It lets your app or other apps know that the
|
|
110
|
+
* clipboard contains a URL and behave accordingly.
|
|
111
|
+
*
|
|
108
112
|
* @param url The URL to save to the clipboard.
|
|
109
|
-
* @platform
|
|
113
|
+
* @platform ios
|
|
110
114
|
*/
|
|
111
115
|
export async function setUrlAsync(url: string): Promise<void> {
|
|
112
116
|
if (!ExpoClipboard.setUrlAsync) {
|
|
@@ -119,7 +123,7 @@ export async function setUrlAsync(url: string): Promise<void> {
|
|
|
119
123
|
* Returns whether the clipboard has a URL content.
|
|
120
124
|
*
|
|
121
125
|
* @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.
|
|
122
|
-
* @platform
|
|
126
|
+
* @platform ios
|
|
123
127
|
*/
|
|
124
128
|
export async function hasUrlAsync(): Promise<boolean> {
|
|
125
129
|
if (!ExpoClipboard.hasUrlAsync) {
|
|
@@ -173,7 +177,7 @@ export async function setImageAsync(base64Image: string): Promise<void> {
|
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
/**
|
|
176
|
-
* Returns whether the clipboard has
|
|
180
|
+
* Returns whether the clipboard has an image content.
|
|
177
181
|
*
|
|
178
182
|
* On web, this requires the user to grant your app permission to _"see text and images copied to the clipboard"_.
|
|
179
183
|
*
|
package/src/Clipboard.types.ts
CHANGED
|
@@ -64,7 +64,7 @@ export interface GetStringOptions {
|
|
|
64
64
|
/**
|
|
65
65
|
* The target format of the clipboard string to be converted to, if possible.
|
|
66
66
|
*
|
|
67
|
-
* @default
|
|
67
|
+
* @default StringFormat.PLAIN_TEXT
|
|
68
68
|
*/
|
|
69
69
|
preferredFormat?: StringFormat;
|
|
70
70
|
}
|
|
@@ -74,7 +74,7 @@ export interface SetStringOptions {
|
|
|
74
74
|
* The input format of the provided string.
|
|
75
75
|
* Adjusting this option can help other applications interpret copied string properly.
|
|
76
76
|
*
|
|
77
|
-
* @default
|
|
77
|
+
* @default StringFormat.PLAIN_TEXT
|
|
78
78
|
*/
|
|
79
79
|
inputFormat?: StringFormat;
|
|
80
80
|
}
|
package/src/ExpoClipboard.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
2
|
|
|
3
|
-
export default
|
|
3
|
+
export default requireNativeModule('ExpoClipboard');
|
|
@@ -103,8 +103,19 @@ export default {
|
|
|
103
103
|
throw new CopyFailureException(e.message);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
default:
|
|
107
|
-
|
|
106
|
+
default: {
|
|
107
|
+
try {
|
|
108
|
+
if (!navigator.clipboard) {
|
|
109
|
+
throw new Error();
|
|
110
|
+
}
|
|
111
|
+
await navigator.clipboard.writeText(text);
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
// we can fall back to legacy behavior in any kind of failure
|
|
115
|
+
// including navigator.clipboard unavailability
|
|
116
|
+
return this.setString(text);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
108
119
|
}
|
|
109
120
|
},
|
|
110
121
|
async hasStringAsync(): Promise<boolean> {
|
package/tsconfig.json
CHANGED