expo-clipboard 1.0.1 → 2.0.1

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
@@ -8,6 +8,44 @@
8
8
 
9
9
  ### 🐛 Bug fixes
10
10
 
11
+ ### 💡 Others
12
+
13
+ ## 2.0.1 — 2021-10-01
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 2.0.0 — 2021-09-28
18
+
19
+ ### 🛠 Breaking changes
20
+
21
+ - Dropped support for iOS 11.0 ([#14383](https://github.com/expo/expo/pull/14383) by [@cruzach](https://github.com/cruzach))
22
+
23
+ ### 🐛 Bug fixes
24
+
25
+ - Fix building errors from use_frameworks! in Podfile. ([#14523](https://github.com/expo/expo/pull/14523) by [@kudo](https://github.com/kudo))
26
+
27
+ ### 💡 Others
28
+
29
+ - Clean up Android code. ([#13517](https://github.com/expo/expo/pull/13517) by [@mstach60161](https://github.com/mstach60161))
30
+ - Migrated from `@unimodules/core` to `expo-modules-core`. ([#13757](https://github.com/expo/expo/pull/13757) by [@tsapeta](https://github.com/tsapeta))
31
+
32
+ ## 1.1.0 — 2021-06-16
33
+
34
+ ### 🎉 New features
35
+
36
+ - Added Clipboard event listener ([#13050](https://github.com/expo/expo/pull/13050) by [@cruzach](https://github.com/cruzach))
37
+
38
+ ### 🐛 Bug fixes
39
+
40
+ - Fixed `getStringAsync` causing crashes on Web when an exception is thrown. ([#12494](https://github.com/expo/expo/pull/12494) by [@robertherber](https://github.com/robertherber))
41
+ - Fixed newlines not being copied on web. ([#12951](https://github.com/expo/expo/pull/12951) by [@cruzach](https://github.com/cruzach))
42
+
43
+ ## 1.0.2 — 2021-03-23
44
+
45
+ ### 🐛 Bug fixes
46
+
47
+ - Remove peerDependencies and unimodulePeerDependencies from Expo modules. ([#11980](https://github.com/expo/expo/pull/11980) by [@brentvatne](https://github.com/brentvatne))
48
+
11
49
  ## 1.0.1 — 2020-12-08
12
50
 
13
51
  _This version does not introduce any user-facing changes._
@@ -0,0 +1,79 @@
1
+ apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+ apply plugin: 'maven'
4
+
5
+ group = 'host.exp.exponent'
6
+ version = '2.0.1'
7
+
8
+ buildscript {
9
+ // Simple helper that allows the root project to override versions declared by this library.
10
+ ext.safeExtGet = { prop, fallback ->
11
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
12
+ }
13
+
14
+ repositories {
15
+ mavenCentral()
16
+ }
17
+
18
+ dependencies {
19
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.4.21')}")
20
+ }
21
+ }
22
+
23
+ // Upload android library to maven with javadoc and android sources
24
+ configurations {
25
+ deployerJars
26
+ }
27
+
28
+ // Creating sources with comments
29
+ task androidSourcesJar(type: Jar) {
30
+ classifier = 'sources'
31
+ from android.sourceSets.main.java.srcDirs
32
+ }
33
+
34
+ // Put the androidSources and javadoc to the artifacts
35
+ artifacts {
36
+ archives androidSourcesJar
37
+ }
38
+
39
+ uploadArchives {
40
+ repositories {
41
+ mavenDeployer {
42
+ configuration = configurations.deployerJars
43
+ repository(url: mavenLocal().url)
44
+ }
45
+ }
46
+ }
47
+
48
+ android {
49
+ compileSdkVersion rootProject.ext.compileSdkVersion
50
+
51
+ compileOptions {
52
+ sourceCompatibility JavaVersion.VERSION_1_8
53
+ targetCompatibility JavaVersion.VERSION_1_8
54
+ }
55
+
56
+ defaultConfig {
57
+ minSdkVersion rootProject.ext.minSdkVersion
58
+ targetSdkVersion rootProject.ext.targetSdkVersion
59
+ versionCode 3
60
+ versionName '2.0.1'
61
+ }
62
+ lintOptions {
63
+ abortOnError false
64
+ }
65
+ }
66
+
67
+ repositories {
68
+ mavenCentral()
69
+ }
70
+
71
+ dependencies {
72
+ implementation project(':expo-modules-core')
73
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.4.21')}"
74
+
75
+ if (project.findProject(':unimodules-test-core')) {
76
+ testImplementation project(':unimodules-test-core')
77
+ }
78
+ testImplementation "org.robolectric:robolectric:4.3.1"
79
+ }
@@ -0,0 +1,2 @@
1
+ <manifest package="expo.modules.clipboard">
2
+ </manifest>
@@ -0,0 +1,49 @@
1
+ package expo.modules.clipboard
2
+
3
+ import android.content.Context
4
+ import android.content.ClipboardManager
5
+ import android.os.Bundle
6
+ import android.util.Log
7
+
8
+ import expo.modules.core.ModuleRegistry
9
+ import expo.modules.core.interfaces.LifecycleEventListener
10
+ import expo.modules.core.interfaces.services.EventEmitter
11
+ import expo.modules.core.interfaces.services.UIManager
12
+
13
+ class ClipboardEventEmitter(context: Context, moduleRegistry: ModuleRegistry) : LifecycleEventListener {
14
+ private val onClipboardEventName = "onClipboardChanged"
15
+ private var isListening = true
16
+ private var eventEmitter = moduleRegistry.getModule(EventEmitter::class.java)
17
+
18
+ init {
19
+ moduleRegistry.getModule(UIManager::class.java).registerLifecycleEventListener(this)
20
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
21
+ if (clipboard == null) {
22
+ Log.e("Clipboard", "CLIPBOARD_SERVICE unavailable. Events wont be received")
23
+ } else {
24
+ clipboard.addPrimaryClipChangedListener {
25
+ if (isListening) {
26
+ val clip = clipboard.primaryClip
27
+ if (clip != null && clip.itemCount >= 1) {
28
+ eventEmitter.emit(
29
+ onClipboardEventName,
30
+ Bundle().apply {
31
+ putString("content", clip.getItemAt(0).text.toString())
32
+ }
33
+ )
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ override fun onHostResume() {
41
+ isListening = true
42
+ }
43
+
44
+ override fun onHostPause() {
45
+ isListening = false
46
+ }
47
+
48
+ override fun onHostDestroy() = Unit
49
+ }
@@ -0,0 +1,41 @@
1
+ package expo.modules.clipboard
2
+
3
+ import android.content.Context
4
+ import android.content.ClipData
5
+ import android.content.ClipboardManager
6
+
7
+ import expo.modules.core.ExportedModule
8
+ import expo.modules.core.ModuleRegistry
9
+ import expo.modules.core.Promise
10
+ import expo.modules.core.interfaces.ExpoMethod
11
+
12
+ class ClipboardModule(context: Context) : ExportedModule(context) {
13
+ private val NAME = "ExpoClipboard"
14
+
15
+ override fun getName(): String {
16
+ return NAME
17
+ }
18
+
19
+ override fun onCreate(moduleRegistry: ModuleRegistry) {
20
+ ClipboardEventEmitter(context, moduleRegistry)
21
+ }
22
+
23
+ @ExpoMethod
24
+ fun getStringAsync(promise: Promise) {
25
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
26
+ val clip = clipboard.primaryClip
27
+ if (clip != null && clip.itemCount >= 1) {
28
+ promise.resolve(clip.getItemAt(0).text)
29
+ } else {
30
+ promise.resolve("")
31
+ }
32
+ }
33
+
34
+ @ExpoMethod
35
+ fun setString(content: String, promise: Promise) {
36
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
37
+ val clip = ClipData.newPlainText(null, content)
38
+ clipboard.setPrimaryClip(clip)
39
+ promise.resolve(null)
40
+ }
41
+ }
@@ -0,0 +1,12 @@
1
+ package expo.modules.clipboard
2
+
3
+ import android.content.Context
4
+
5
+ import expo.modules.core.BasePackage
6
+ import expo.modules.core.ExportedModule
7
+
8
+ class ClipboardPackage : BasePackage() {
9
+ override fun createExportedModules(context: Context): List<ExportedModule> {
10
+ return listOf(ClipboardModule(context) as ExportedModule)
11
+ }
12
+ }
@@ -1,2 +1,53 @@
1
- import ExpoClipboard from './ExpoClipboard';
2
- export default ExpoClipboard;
1
+ import { EventEmitter, Subscription } from 'expo-modules-core';
2
+ declare type ClipboardEvent = {
3
+ /**
4
+ * The new content of the user's clipboard.
5
+ */
6
+ content: string;
7
+ };
8
+ export { Subscription, EventEmitter, ClipboardEvent };
9
+ /**
10
+ * Gets the content of the user's clipboard. Please note that calling this method on web will prompt
11
+ * the user to grant your app permission to "see text and images copied to the clipboard."
12
+ *
13
+ * @returns A promise that resolves to the content of the clipboard.
14
+ */
15
+ export declare function getStringAsync(): Promise<string>;
16
+ /**
17
+ * Sets the content of the user's clipboard.
18
+ *
19
+ * @param text The string to save to the clipboard.
20
+ *
21
+ * @returns On web, this returns a boolean value indicating whether or not the string was saved to
22
+ * the user's clipboard. On iOS and Android, nothing is returned.
23
+ */
24
+ export declare function setString(text: string): void;
25
+ /**
26
+ * Adds a listener that will fire whenever the content of the user's clipboard changes. This method
27
+ * is a no-op on Web.
28
+ *
29
+ * @param listener Callback to execute when listener is triggered. The callback is provided a
30
+ * single argument that is an object with a `content` key.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * addClipboardListener(({ content }: ClipboardEvent) => {
35
+ * alert('Copy pasta! Here's the string that was copied: ' + content);
36
+ * });
37
+ * ```
38
+ */
39
+ export declare function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription;
40
+ /**
41
+ * Removes the listener added by addClipboardListener. This method is a no-op on Web.
42
+ *
43
+ * @param subscription The subscription to remove (created by addClipboardListener).
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const subscription = addClipboardListener(() => {
48
+ * alert('Copy pasta!');
49
+ * });
50
+ * removeClipboardListener(subscription);
51
+ * ```
52
+ */
53
+ export declare function removeClipboardListener(subscription: Subscription): void;
@@ -1,3 +1,65 @@
1
+ import { EventEmitter, UnavailabilityError } from 'expo-modules-core';
1
2
  import ExpoClipboard from './ExpoClipboard';
2
- export default ExpoClipboard;
3
+ const emitter = new EventEmitter(ExpoClipboard);
4
+ const onClipboardEventName = 'onClipboardChanged';
5
+ export { EventEmitter };
6
+ /**
7
+ * Gets the content of the user's clipboard. Please note that calling this method on web will prompt
8
+ * the user to grant your app permission to "see text and images copied to the clipboard."
9
+ *
10
+ * @returns A promise that resolves to the content of the clipboard.
11
+ */
12
+ export async function getStringAsync() {
13
+ if (!ExpoClipboard.getStringAsync) {
14
+ throw new UnavailabilityError('Clipboard', 'getStringAsync');
15
+ }
16
+ return await ExpoClipboard.getStringAsync();
17
+ }
18
+ /**
19
+ * Sets the content of the user's clipboard.
20
+ *
21
+ * @param text The string to save to the clipboard.
22
+ *
23
+ * @returns On web, this returns a boolean value indicating whether or not the string was saved to
24
+ * the user's clipboard. On iOS and Android, nothing is returned.
25
+ */
26
+ export function setString(text) {
27
+ if (!ExpoClipboard.setString) {
28
+ throw new UnavailabilityError('Clipboard', 'setString');
29
+ }
30
+ return ExpoClipboard.setString(text);
31
+ }
32
+ /**
33
+ * Adds a listener that will fire whenever the content of the user's clipboard changes. This method
34
+ * is a no-op on Web.
35
+ *
36
+ * @param listener Callback to execute when listener is triggered. The callback is provided a
37
+ * single argument that is an object with a `content` key.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * addClipboardListener(({ content }: ClipboardEvent) => {
42
+ * alert('Copy pasta! Here's the string that was copied: ' + content);
43
+ * });
44
+ * ```
45
+ */
46
+ export function addClipboardListener(listener) {
47
+ return emitter.addListener(onClipboardEventName, listener);
48
+ }
49
+ /**
50
+ * Removes the listener added by addClipboardListener. This method is a no-op on Web.
51
+ *
52
+ * @param subscription The subscription to remove (created by addClipboardListener).
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const subscription = addClipboardListener(() => {
57
+ * alert('Copy pasta!');
58
+ * });
59
+ * removeClipboardListener(subscription);
60
+ * ```
61
+ */
62
+ export function removeClipboardListener(subscription) {
63
+ emitter.removeSubscription(subscription);
64
+ }
3
65
  //# sourceMappingURL=Clipboard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,eAAe,aAAa,CAAC","sourcesContent":["import ExpoClipboard from './ExpoClipboard';\nexport default ExpoClipboard;\n"]}
1
+ {"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEpF,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,aAAa,CAAC,CAAC;AAEhD,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AASlD,OAAO,EAAgB,YAAY,EAAkB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;KAC9D;IACD,OAAO,MAAM,aAAa,CAAC,cAAc,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE;QAC5B,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;KACzD;IACD,OAAO,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAyC;IAC5E,OAAO,OAAO,CAAC,WAAW,CAAiB,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CAAC,YAA0B;IAChE,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';\n\nimport ExpoClipboard from './ExpoClipboard';\n\nconst emitter = new EventEmitter(ExpoClipboard);\n\nconst onClipboardEventName = 'onClipboardChanged';\n\ntype ClipboardEvent = {\n /**\n * The new content of the user's clipboard.\n */\n content: string;\n};\n\nexport { Subscription, EventEmitter, 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 * @returns A promise that resolves to the content of the clipboard.\n */\nexport async function getStringAsync(): Promise<string> {\n if (!ExpoClipboard.getStringAsync) {\n throw new UnavailabilityError('Clipboard', 'getStringAsync');\n }\n return await ExpoClipboard.getStringAsync();\n}\n\n/**\n * Sets the content of the user's clipboard.\n *\n * @param text The string to save to the clipboard.\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 (!ExpoClipboard.setString) {\n throw new UnavailabilityError('Clipboard', 'setString');\n }\n return ExpoClipboard.setString(text);\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 with a `content` key.\n *\n * @example\n * ```typescript\n * addClipboardListener(({ content }: ClipboardEvent) => {\n * alert('Copy pasta! Here's the string that was copied: ' + content);\n * });\n * ```\n */\nexport function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {\n return emitter.addListener<ClipboardEvent>(onClipboardEventName, listener);\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"]}
@@ -1,5 +1,2 @@
1
- declare const _default: {
2
- getStringAsync(): Promise<string>;
3
- setString(text: string): void;
4
- };
1
+ declare const _default: import("expo-modules-core").ProxyNativeModule;
5
2
  export default _default;
@@ -1,11 +1,3 @@
1
- // Temporary for SDK 40 until we make our own native implementation
2
- import Clipboard from 'react-native/Libraries/Components/Clipboard/Clipboard';
3
- export default {
4
- async getStringAsync() {
5
- return await Clipboard.getString();
6
- },
7
- setString(text) {
8
- Clipboard.setString(text);
9
- },
10
- };
1
+ import { NativeModulesProxy } from 'expo-modules-core';
2
+ export default NativeModulesProxy.ExpoClipboard;
11
3
  //# sourceMappingURL=ExpoClipboard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoClipboard.js","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,SAAS,MAAM,uDAAuD,CAAC;AAE9E,eAAe;IACb,KAAK,CAAC,cAAc;QAClB,OAAO,MAAM,SAAS,CAAC,SAAS,EAAE,CAAC;IACrC,CAAC;IACD,SAAS,CAAC,IAAY;QACpB,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC","sourcesContent":["// Temporary for SDK 40 until we make our own native implementation\nimport Clipboard from 'react-native/Libraries/Components/Clipboard/Clipboard';\n\nexport default {\n async getStringAsync(): Promise<string> {\n return await Clipboard.getString();\n },\n setString(text: string): void {\n Clipboard.setString(text);\n },\n};\n"]}
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"]}
@@ -2,5 +2,7 @@ declare const _default: {
2
2
  readonly name: string;
3
3
  getStringAsync(): Promise<string>;
4
4
  setString(text: string): boolean;
5
+ addClipboardListener(): void;
6
+ removeClipboardListener(): void;
5
7
  };
6
8
  export default _default;
@@ -14,7 +14,7 @@ export default {
14
14
  text = window.clipboardData.getData('Text');
15
15
  }
16
16
  catch (e) {
17
- Promise.reject(new Error('Unable to retrieve item from clipboard.'));
17
+ return Promise.reject(new Error('Unable to retrieve item from clipboard.'));
18
18
  }
19
19
  }
20
20
  return text;
@@ -22,7 +22,7 @@ export default {
22
22
  setString(text) {
23
23
  let success = false;
24
24
  const textField = document.createElement('textarea');
25
- textField.innerText = text;
25
+ textField.textContent = text;
26
26
  document.body.appendChild(textField);
27
27
  textField.select();
28
28
  try {
@@ -33,5 +33,7 @@ export default {
33
33
  document.body.removeChild(textField);
34
34
  return success;
35
35
  },
36
+ addClipboardListener() { },
37
+ removeClipboardListener() { },
36
38
  };
37
39
  //# sourceMappingURL=ExpoClipboard.web.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoClipboard.web.js","sourceRoot":"","sources":["../src/ExpoClipboard.web.ts"],"names":[],"mappings":"AAAA,eAAe;IACb,IAAI,IAAI;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI;YACF,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;SAC7C;QAAC,OAAO,CAAC,EAAE;YACV,IAAI;gBACF,oBAAoB;gBACpB,aAAa;gBACb,IAAI,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;aAC7C;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;aACtE;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,SAAS,CAAC,IAAY;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACrD,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC;QAC3B,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,GAAG,IAAI,CAAC;SAChB;QAAC,OAAO,CAAC,EAAE,GAAE;QACd,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAC","sourcesContent":["export default {\n get name(): string {\n return 'ExpoClipboard';\n },\n async getStringAsync(): Promise<string> {\n let text = '';\n try {\n text = await navigator.clipboard.readText();\n } catch (e) {\n try {\n // Internet Explorer\n // @ts-ignore\n text = window.clipboardData.getData('Text');\n } catch (e) {\n Promise.reject(new Error('Unable to retrieve item from clipboard.'));\n }\n }\n return text;\n },\n setString(text: string): boolean {\n let success = false;\n const textField = document.createElement('textarea');\n textField.innerText = text;\n document.body.appendChild(textField);\n textField.select();\n try {\n document.execCommand('copy');\n success = true;\n } catch (e) {}\n document.body.removeChild(textField);\n return success;\n },\n};\n"]}
1
+ {"version":3,"file":"ExpoClipboard.web.js","sourceRoot":"","sources":["../src/ExpoClipboard.web.ts"],"names":[],"mappings":"AAAA,eAAe;IACb,IAAI,IAAI;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI;YACF,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;SAC7C;QAAC,OAAO,CAAC,EAAE;YACV,IAAI;gBACF,oBAAoB;gBACpB,aAAa;gBACb,IAAI,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;aAC7C;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;aAC7E;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,SAAS,CAAC,IAAY;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;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,GAAG,IAAI,CAAC;SAChB;QAAC,OAAO,CAAC,EAAE,GAAE;QACd,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,oBAAoB,KAAU,CAAC;IAC/B,uBAAuB,KAAU,CAAC;CACnC,CAAC","sourcesContent":["export default {\n get name(): string {\n return 'ExpoClipboard';\n },\n async getStringAsync(): Promise<string> {\n let text = '';\n try {\n text = await navigator.clipboard.readText();\n } catch (e) {\n try {\n // Internet Explorer\n // @ts-ignore\n text = window.clipboardData.getData('Text');\n } catch (e) {\n return Promise.reject(new Error('Unable to retrieve item from clipboard.'));\n }\n }\n return text;\n },\n setString(text: string): boolean {\n let success = false;\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 success = true;\n } catch (e) {}\n document.body.removeChild(textField);\n return success;\n },\n addClipboardListener(): void {},\n removeClipboardListener(): void {},\n};\n"]}
@@ -0,0 +1,8 @@
1
+ // Copyright © 2021 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/EXExportedModule.h>
4
+ #import <ExpoModulesCore/EXModuleRegistryConsumer.h>
5
+ #import <ExpoModulesCore/EXEventEmitter.h>
6
+
7
+ @interface EXClipboardModule : EXExportedModule <EXModuleRegistryConsumer, EXEventEmitter>
8
+ @end
@@ -0,0 +1,88 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ #import <EXClipboard/EXClipboardModule.h>
4
+
5
+ #import <ExpoModulesCore/EXEventEmitterService.h>
6
+
7
+ static NSString * const onClipboardEventName = @"onClipboardChanged";
8
+
9
+ @interface EXClipboardModule ()
10
+
11
+ @property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
12
+ @property (nonatomic, assign) BOOL isListening;
13
+ @property (nonatomic, assign) BOOL isBeingObserved;
14
+ @property (nonatomic, weak) id<EXEventEmitterService> eventEmitter;
15
+
16
+ @end
17
+
18
+ @implementation EXClipboardModule
19
+
20
+ EX_EXPORT_MODULE(ExpoClipboard);
21
+
22
+ # pragma mark - EXModuleRegistryConsumer
23
+
24
+ - (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
25
+ {
26
+ _moduleRegistry = moduleRegistry;
27
+ _eventEmitter = [moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)];
28
+ }
29
+
30
+ # pragma mark - Exported methods
31
+
32
+ EX_EXPORT_METHOD_AS(getStringAsync,
33
+ getStringAsyncWithResolver:(EXPromiseResolveBlock)resolve
34
+ reject:(EXPromiseRejectBlock)reject)
35
+ {
36
+ UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
37
+ resolve((clipboard.string ?: @""));
38
+ }
39
+
40
+ EX_EXPORT_METHOD_AS(setString,
41
+ setStringWithContent:(NSString *)content
42
+ resolver:(EXPromiseResolveBlock)resolve
43
+ rejecter:(EXPromiseRejectBlock)reject)
44
+ {
45
+ UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
46
+ clipboard.string = (content ? : @"");
47
+ }
48
+
49
+ # pragma mark - EXEventEmitter
50
+
51
+ - (NSArray<NSString *> *)supportedEvents
52
+ {
53
+ return @[onClipboardEventName];
54
+ }
55
+
56
+ - (void)startObserving
57
+ {
58
+ [self setIsBeingObserved:YES];
59
+ }
60
+
61
+ - (void)stopObserving
62
+ {
63
+ [self setIsBeingObserved:NO];
64
+ }
65
+
66
+ - (void)setIsBeingObserved:(BOOL)isBeingObserved
67
+ {
68
+ _isBeingObserved = isBeingObserved;
69
+ BOOL shouldListen = _isBeingObserved;
70
+ if (shouldListen && !_isListening) {
71
+ // Avoid setting duplicate observers
72
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIPasteboardChangedNotification object:nil];
73
+ [[NSNotificationCenter defaultCenter] addObserver:self
74
+ selector:@selector(listenToClipboard) name:UIPasteboardChangedNotification object:nil];
75
+ _isListening = YES;
76
+ } else if (!shouldListen && _isListening) {
77
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIPasteboardChangedNotification object:nil];
78
+ _isListening = NO;
79
+ }
80
+ }
81
+
82
+ - (void)listenToClipboard
83
+ {
84
+ UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
85
+ [_eventEmitter sendEventWithName:onClipboardEventName body:@{ @"content" : clipboard.string ?: @"" }];
86
+ }
87
+
88
+ @end
@@ -0,0 +1,28 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'EXClipboard'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platform = :ios, '12.0'
14
+ s.source = { git: 'https://github.com/expo/expo.git' }
15
+ s.static_framework = true
16
+ s.source_files = 'EXClipboard/**/*.{h,m}'
17
+ s.preserve_paths = 'EXClipboard/**/*.{h,m}'
18
+ s.requires_arc = true
19
+
20
+ s.dependency 'ExpoModulesCore'
21
+
22
+ if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
23
+ s.source_files = "#{s.name}/**/*.h"
24
+ s.vendored_frameworks = "#{s.name}.xcframework"
25
+ else
26
+ s.source_files = "#{s.name}/**/*.{h,m}"
27
+ end
28
+ end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-clipboard",
3
- "version": "1.0.1",
3
+ "version": "2.0.1",
4
4
  "description": "ExpoClipboard standalone module",
5
5
  "main": "build/Clipboard.js",
6
6
  "types": "build/Clipboard.d.ts",
@@ -28,16 +28,15 @@
28
28
  },
29
29
  "author": "650 Industries, Inc.",
30
30
  "license": "MIT",
31
- "homepage": "https://docs.expo.io/versions/latest/sdk/clipboard",
32
- "peerDependencies": {
33
- "react": "*",
34
- "react-native": "*"
31
+ "homepage": "https://docs.expo.dev/versions/latest/sdk/clipboard",
32
+ "dependencies": {
33
+ "expo-modules-core": "~0.4.2"
35
34
  },
36
35
  "devDependencies": {
37
- "expo-module-scripts": "^1.2.0"
36
+ "expo-module-scripts": "^2.0.0"
38
37
  },
39
38
  "jest": {
40
39
  "preset": "expo-module-scripts/universal"
41
40
  },
42
- "gitHead": "1ff5f429a90f9933d9ed447534c87c8928b45cfc"
41
+ "gitHead": "2718b696f4a6919905b0f47ebb24ff65b42d8ff9"
43
42
  }
package/src/Clipboard.ts CHANGED
@@ -1,2 +1,79 @@
1
+ import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';
2
+
1
3
  import ExpoClipboard from './ExpoClipboard';
2
- export default ExpoClipboard;
4
+
5
+ const emitter = new EventEmitter(ExpoClipboard);
6
+
7
+ const onClipboardEventName = 'onClipboardChanged';
8
+
9
+ type ClipboardEvent = {
10
+ /**
11
+ * The new content of the user's clipboard.
12
+ */
13
+ content: string;
14
+ };
15
+
16
+ export { Subscription, EventEmitter, ClipboardEvent };
17
+
18
+ /**
19
+ * Gets the content of the user's clipboard. Please note that calling this method on web will prompt
20
+ * the user to grant your app permission to "see text and images copied to the clipboard."
21
+ *
22
+ * @returns A promise that resolves to the content of the clipboard.
23
+ */
24
+ export async function getStringAsync(): Promise<string> {
25
+ if (!ExpoClipboard.getStringAsync) {
26
+ throw new UnavailabilityError('Clipboard', 'getStringAsync');
27
+ }
28
+ return await ExpoClipboard.getStringAsync();
29
+ }
30
+
31
+ /**
32
+ * Sets the content of the user's clipboard.
33
+ *
34
+ * @param text The string to save to the clipboard.
35
+ *
36
+ * @returns On web, this returns a boolean value indicating whether or not the string was saved to
37
+ * the user's clipboard. On iOS and Android, nothing is returned.
38
+ */
39
+ export function setString(text: string): void {
40
+ if (!ExpoClipboard.setString) {
41
+ throw new UnavailabilityError('Clipboard', 'setString');
42
+ }
43
+ return ExpoClipboard.setString(text);
44
+ }
45
+
46
+ /**
47
+ * Adds a listener that will fire whenever the content of the user's clipboard changes. This method
48
+ * is a no-op on Web.
49
+ *
50
+ * @param listener Callback to execute when listener is triggered. The callback is provided a
51
+ * single argument that is an object with a `content` key.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * addClipboardListener(({ content }: ClipboardEvent) => {
56
+ * alert('Copy pasta! Here's the string that was copied: ' + content);
57
+ * });
58
+ * ```
59
+ */
60
+ export function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {
61
+ return emitter.addListener<ClipboardEvent>(onClipboardEventName, listener);
62
+ }
63
+
64
+ /**
65
+ * Removes the listener added by addClipboardListener. This method is a no-op on Web.
66
+ *
67
+ * @param subscription The subscription to remove (created by addClipboardListener).
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const subscription = addClipboardListener(() => {
72
+ * alert('Copy pasta!');
73
+ * });
74
+ * removeClipboardListener(subscription);
75
+ * ```
76
+ */
77
+ export function removeClipboardListener(subscription: Subscription) {
78
+ emitter.removeSubscription(subscription);
79
+ }
@@ -1,11 +1,3 @@
1
- // Temporary for SDK 40 until we make our own native implementation
2
- import Clipboard from 'react-native/Libraries/Components/Clipboard/Clipboard';
1
+ import { NativeModulesProxy } from 'expo-modules-core';
3
2
 
4
- export default {
5
- async getStringAsync(): Promise<string> {
6
- return await Clipboard.getString();
7
- },
8
- setString(text: string): void {
9
- Clipboard.setString(text);
10
- },
11
- };
3
+ export default NativeModulesProxy.ExpoClipboard;
@@ -12,7 +12,7 @@ export default {
12
12
  // @ts-ignore
13
13
  text = window.clipboardData.getData('Text');
14
14
  } catch (e) {
15
- Promise.reject(new Error('Unable to retrieve item from clipboard.'));
15
+ return Promise.reject(new Error('Unable to retrieve item from clipboard.'));
16
16
  }
17
17
  }
18
18
  return text;
@@ -20,7 +20,7 @@ export default {
20
20
  setString(text: string): boolean {
21
21
  let success = false;
22
22
  const textField = document.createElement('textarea');
23
- textField.innerText = text;
23
+ textField.textContent = text;
24
24
  document.body.appendChild(textField);
25
25
  textField.select();
26
26
  try {
@@ -30,4 +30,6 @@ export default {
30
30
  document.body.removeChild(textField);
31
31
  return success;
32
32
  },
33
+ addClipboardListener(): void {},
34
+ removeClipboardListener(): void {},
33
35
  };
package/unimodule.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "expo-clipboard",
3
+ "platforms": ["ios", "android", "web"]
4
+ }