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 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
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '3.0.1'
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 '3.0.1'
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
- name(moduleName)
37
+ Name(moduleName)
45
38
 
46
39
  // region Strings
47
- function("getStringAsync") { options: GetStringOptions ->
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
- function("setStringAsync") { content: String, options: SetStringOptions ->
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@function true
58
+ return@AsyncFunction true
66
59
  }
67
60
 
68
- function("hasStringAsync") {
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
- function("getImageAsync") { options: GetImageOptions, promise: Promise ->
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
- promise.resolve(null)
84
- return@function
76
+ return@Coroutine null
85
77
  }
86
78
 
87
- val exceptionHandler = CoroutineExceptionHandler { _, err ->
79
+ try {
80
+ val imageResult = imageFromContentUri(context, imageUri, options)
81
+ return@Coroutine imageResult.toBundle()
82
+ } catch (err: Throwable) {
88
83
  err.printStackTrace()
89
- val rejectionCause = when (err) {
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
- function("setImageAsync") { imageData: String, promise: Promise ->
104
- val exceptionHandler = CoroutineExceptionHandler { _, err ->
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
- val rejectionCause = when (err) {
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
- function("hasImageAsync") {
105
+ AsyncFunction("hasImageAsync") {
121
106
  clipboardManager.primaryClipDescription?.hasMimeType("image/*") == true
122
107
  }
123
108
  //endregion
124
109
 
125
110
  // region Events
126
- events(CLIPBOARD_CHANGED_EVENT_NAME)
111
+ Events(CLIPBOARD_CHANGED_EVENT_NAME)
127
112
 
128
- onCreate {
113
+ OnCreate {
129
114
  clipboardEventEmitter = ClipboardEventEmitter()
130
115
  clipboardEventEmitter.attachListener()
131
116
  }
132
117
 
133
- onDestroy {
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
- onActivityEntersBackground {
122
+ OnActivityEntersBackground {
143
123
  clipboardEventEmitter.pauseListening()
144
124
  }
145
125
 
146
- onActivityEntersForeground {
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.HTML.takeIf { clip.hasMimeType("image/*") }
169
+ ContentType.IMAGE.takeIf { clip.hasMimeType("image/*") }
192
170
  ).map { it.jsName }
193
171
  )
194
172
  )
@@ -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 iOS
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 iOS
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 iOS
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 a image content.
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
  *
@@ -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;;;;;GAKG;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"}
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"}
@@ -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 iOS
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 iOS
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 iOS
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 a image content.
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
  *
@@ -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 `StringFormat.PLAIN_TEXT`
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 `StringFormat.PLAIN_TEXT`
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 `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"]}
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"]}
@@ -1,3 +1,3 @@
1
- declare const _default: import("expo-modules-core").ProxyNativeModule;
1
+ declare const _default: any;
2
2
  export default _default;
3
3
  //# sourceMappingURL=ExpoClipboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":";AAEA,wBAAgD"}
1
+ {"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":";AAEA,wBAAoD"}
@@ -1,3 +1,3 @@
1
- import { NativeModulesProxy } from 'expo-modules-core';
2
- export default NativeModulesProxy.ExpoClipboard;
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,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,eAAe,kBAAkB,CAAC,aAAa,CAAC","sourcesContent":["import { NativeModulesProxy } from 'expo-modules-core';\n\nexport default NativeModulesProxy.ExpoClipboard;\n"]}
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;sBAuBvD,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;;AA9IjC,wBA+IE"}
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
- return this.setString(text);
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":"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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 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
- name("ExpoClipboard")
10
+ Name("ExpoClipboard")
11
11
 
12
12
  // MARK: Strings
13
13
 
14
- function("getStringAsync") { (options: GetStringOptions) -> String in
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
- function("setStringAsync") { (content: String?, options: SetStringOptions) -> Bool in
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
- function("hasStringAsync") { () -> Bool in
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
- function("getUrlAsync") { () -> String? in
40
+ AsyncFunction("getUrlAsync") { () -> String? in
41
41
  return UIPasteboard.general.url?.absoluteString
42
42
  }
43
43
 
44
- function("setUrlAsync") { (url: URL) in
44
+ AsyncFunction("setUrlAsync") { (url: URL) in
45
45
  UIPasteboard.general.url = url
46
46
  }
47
47
 
48
- function("hasUrlAsync") { () -> Bool in
48
+ AsyncFunction("hasUrlAsync") { () -> Bool in
49
49
  return UIPasteboard.general.hasURLs
50
50
  }
51
51
 
52
52
  // MARK: Images
53
53
 
54
- function("setImageAsync") { (content: String) in
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
- function("hasImageAsync") { () -> Bool in
62
+ AsyncFunction("hasImageAsync") { () -> Bool in
63
63
  return UIPasteboard.general.hasImages
64
64
  }
65
65
 
66
- function("getImageAsync") { (options: GetImageOptions) -> [String: Any]? in
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
- events(onClipboardChanged)
87
+ Events(onClipboardChanged)
88
88
 
89
- onStartObserving {
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
- onStopObserving {
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, '12.0'
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.0.1",
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": "^2.0.0"
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": "2b35d9008fef42cb53473331c37693ca12e9bf12"
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 iOS
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 iOS
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 iOS
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 a image content.
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
  *
@@ -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 `StringFormat.PLAIN_TEXT`
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 `StringFormat.PLAIN_TEXT`
77
+ * @default StringFormat.PLAIN_TEXT
78
78
  */
79
79
  inputFormat?: StringFormat;
80
80
  }
@@ -1,3 +1,3 @@
1
- import { NativeModulesProxy } from 'expo-modules-core';
1
+ import { requireNativeModule } from 'expo-modules-core';
2
2
 
3
- export default NativeModulesProxy.ExpoClipboard;
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
- return this.setString(text);
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
@@ -5,5 +5,5 @@
5
5
  "outDir": "./build"
6
6
  },
7
7
  "include": ["./src"],
8
- "exclude": ["**/__mocks__/*", "**/__tests__/*"]
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__stories__/*"]
9
9
  }