From 4f663a1e1d77b710f438ec53d9e5615b0d3b0f1b Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 26 Feb 2026 20:14:48 -0800 Subject: [PATCH 1/4] [tools] Fix lint error --- tools/src/promote-packages/tasks/findPackagesToPromote.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/src/promote-packages/tasks/findPackagesToPromote.ts b/tools/src/promote-packages/tasks/findPackagesToPromote.ts index 4db93caff733da..f01be7b5a21fe3 100644 --- a/tools/src/promote-packages/tasks/findPackagesToPromote.ts +++ b/tools/src/promote-packages/tasks/findPackagesToPromote.ts @@ -31,9 +31,7 @@ export const findPackagesToPromote = new Task( !!versionToReplace && (semver.prerelease(versionToReplace)?.[0] as string)?.startsWith('canary'); state.isDemoting = - !!versionToReplace && - semver.lt(pkg.packageVersion, versionToReplace) && - !replacingCanary; + !!versionToReplace && semver.lt(pkg.packageVersion, versionToReplace) && !replacingCanary; if (canPromote && (!state.isDemoting || options.list || options.demote)) { newParcels.push(parcel); From 9a4d7373e6d4cf8557069e3408e3e1badac6aab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Klocek?= Date: Fri, 27 Feb 2026 09:46:43 +0100 Subject: [PATCH 2/4] [clipboard] Remove deprecated `setString()` (#41758) # Why Similar to #41739, the `Clipboard.setString()` has been deprecated for a long time. # How Removed the function and corresponding tests. On web, left the legacy behavior fallback as a private API _(we should keep it there because of https://github.com/expo/expo/issues/17337#issuecomment-1118210857 on Safari)_. # Test Plan - Test Suite - Unit tests - NCL # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/expo-clipboard/CHANGELOG.md | 2 ++ packages/expo-clipboard/build/Clipboard.d.ts | 8 ----- .../expo-clipboard/build/Clipboard.d.ts.map | 2 +- packages/expo-clipboard/build/Clipboard.js | 17 --------- .../expo-clipboard/build/Clipboard.js.map | 2 +- .../expo-clipboard/build/ExpoClipboard.d.ts | 1 - .../build/ExpoClipboard.d.ts.map | 2 +- .../expo-clipboard/build/ExpoClipboard.js.map | 2 +- .../build/web/ClipboardModule.d.ts | 1 - .../build/web/ClipboardModule.d.ts.map | 2 +- .../build/web/ClipboardModule.js | 35 +++++++++---------- .../build/web/ClipboardModule.js.map | 2 +- packages/expo-clipboard/src/Clipboard.ts | 17 --------- packages/expo-clipboard/src/ExpoClipboard.ts | 1 - .../src/__tests__/Clipboard-test.native.ts | 8 ----- .../src/__tests__/Clipboard-test.web.ts | 6 ---- .../expo-clipboard/src/web/ClipboardModule.ts | 32 ++++++++--------- 17 files changed, 41 insertions(+), 99 deletions(-) diff --git a/packages/expo-clipboard/CHANGELOG.md b/packages/expo-clipboard/CHANGELOG.md index a2683f20d9bdaa..d02a16143a4a51 100644 --- a/packages/expo-clipboard/CHANGELOG.md +++ b/packages/expo-clipboard/CHANGELOG.md @@ -4,6 +4,8 @@ ### 🛠 Breaking changes +- Removed deprecated `setString` function. Use `setStringAsync` instead. ([#41758](https://github.com/expo/expo/pull/41758) by [@barthap](https://github.com/barthap)) + ### 🎉 New features ### 🐛 Bug fixes diff --git a/packages/expo-clipboard/build/Clipboard.d.ts b/packages/expo-clipboard/build/Clipboard.d.ts index 5b709605195537..15153c3d540350 100644 --- a/packages/expo-clipboard/build/Clipboard.d.ts +++ b/packages/expo-clipboard/build/Clipboard.d.ts @@ -22,14 +22,6 @@ export declare function getStringAsync(options?: GetStringOptions): Promise; -/** - * Sets the content of the user's clipboard. - * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead. - * - * @returns On web, this returns a boolean value indicating whether or not the string was saved to - * the user's clipboard. On iOS and Android, nothing is returned. - */ -export declare function setString(text: string): void; /** * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML). * diff --git a/packages/expo-clipboard/build/Clipboard.d.ts.map b/packages/expo-clipboard/build/Clipboard.d.ts.map index 508164a2e00155..0f2dad4788200a 100644 --- a/packages/expo-clipboard/build/Clipboard.d.ts.map +++ b/packages/expo-clipboard/build/Clipboard.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAiC,MAAM,mBAAmB,CAAC;AAE1F,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,iBAAiB,IAAI,YAAY,EAAE,CAAC;AAE7C;;;;;;;;;GASG;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;;;;;;;;GAQG;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;;;;;;;;;;;;;;;;;;;GAmBG;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,iBAAiB,CAEjG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,iBAAiB,QAEtE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,OACiC,CAAC;AAEvE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,EAAE,oBAAoB,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAiC,MAAM,mBAAmB,CAAC;AAE1F,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,iBAAiB,IAAI,YAAY,EAAE,CAAC;AAE7C;;;;;;;;;GASG;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,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED;;;;;;;;GAQG;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;;;;;;;;;;;;;;;;;;;GAmBG;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,iBAAiB,CAEjG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,iBAAiB,QAEtE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,OACiC,CAAC;AAEvE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,EAAE,oBAAoB,EAAE,CAAC"} \ No newline at end of file diff --git a/packages/expo-clipboard/build/Clipboard.js b/packages/expo-clipboard/build/Clipboard.js index 345c7c57a66385..34875686daa62a 100644 --- a/packages/expo-clipboard/build/Clipboard.js +++ b/packages/expo-clipboard/build/Clipboard.js @@ -31,23 +31,6 @@ export async function setStringAsync(text, options = {}) { } return ExpoClipboard.setStringAsync(text, options); } -/** - * Sets the content of the user's clipboard. - * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead. - * - * @returns On web, this returns a boolean value indicating whether or not the string was saved to - * the user's clipboard. On iOS and Android, nothing is returned. - */ -export function setString(text) { - if (Platform.OS === 'web') { - // on web, we need to return legacy method, - // because of different return type - return ExpoClipboard.setString(text); - } - else { - setStringAsync(text); - } -} /** * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML). * diff --git a/packages/expo-clipboard/build/Clipboard.js.map b/packages/expo-clipboard/build/Clipboard.js.map index 096ca03a10fc4d..1f67308a104690 100644 --- a/packages/expo-clipboard/build/Clipboard.js.map +++ b/packages/expo-clipboard/build/Clipboard.js.map @@ -1 +1 @@ -{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { type EventSubscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport type {\n ClipboardImage,\n ClipboardEvent,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport { ClipboardPasteButton } from './ClipboardPasteButton';\nimport ExpoClipboard, { clipboardEventName } from './ExpoClipboard';\n\nexport { EventSubscription as Subscription };\n\n/**\n * Gets the content of the user's clipboard. 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 * Note: On iOS 16+, if the user denies paste permission, this method will return an empty string.\n * Due to iOS platform limitations, there is no way to distinguish between an empty clipboard and denied permission.\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard, or an empty string if clipboard is empty or permission was denied.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise {\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 {\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 {\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 * Note: On iOS 16+, if the user denies paste permission, this method will return null.\n * Due to iOS platform limitations, there is no way to distinguish between no URL in clipboard and denied permission.\n *\n * @returns A promise that fulfills to the URL in the clipboard, or null if no URL is present or permission was denied.\n * @platform ios\n */\nexport async function getUrlAsync(): Promise {\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 {\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 {\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\n * format. Calling this method on web will prompt the user to grant your app\n * permission to \"see text and images copied to the clipboard.\"\n *\n * Note: On iOS 16+, if the user denies paste permission, this method will return null.\n * Due to iOS platform limitations, there is no way to distinguish between no image in clipboard and denied permission.\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` (this includes cases where permission was denied).\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * \n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise {\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 {\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 {\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): EventSubscription {\n return ExpoClipboard.addListener(clipboardEventName, listener);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n * @deprecated use subscription.remove() instead.\n */\nexport function removeClipboardListener(subscription: EventSubscription) {\n subscription.remove();\n}\n\n/**\n * Property that determines if the `ClipboardPasteButton` is available.\n *\n * This requires the users device to be using at least iOS 16.\n *\n * `true` if the component is available, and `false` otherwise.\n */\nexport const isPasteButtonAvailable: boolean =\n Platform.OS === 'ios' ? ExpoClipboard.isPasteButtonAvailable : false;\n\nexport * from './Clipboard.types';\nexport { ClipboardPasteButtonProps } from './ClipboardPasteButton';\n\nexport { ClipboardPasteButton };\n"]} \ No newline at end of file +{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { type EventSubscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport type {\n ClipboardImage,\n ClipboardEvent,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport { ClipboardPasteButton } from './ClipboardPasteButton';\nimport ExpoClipboard, { clipboardEventName } from './ExpoClipboard';\n\nexport { EventSubscription as Subscription };\n\n/**\n * Gets the content of the user's clipboard. 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 * Note: On iOS 16+, if the user denies paste permission, this method will return an empty string.\n * Due to iOS platform limitations, there is no way to distinguish between an empty clipboard and denied permission.\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard, or an empty string if clipboard is empty or permission was denied.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise {\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 {\n if (!ExpoClipboard.setStringAsync) {\n throw new UnavailabilityError('Clipboard', 'setStringAsync');\n }\n return ExpoClipboard.setStringAsync(text, options);\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 {\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 * Note: On iOS 16+, if the user denies paste permission, this method will return null.\n * Due to iOS platform limitations, there is no way to distinguish between no URL in clipboard and denied permission.\n *\n * @returns A promise that fulfills to the URL in the clipboard, or null if no URL is present or permission was denied.\n * @platform ios\n */\nexport async function getUrlAsync(): Promise {\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 {\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 {\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\n * format. Calling this method on web will prompt the user to grant your app\n * permission to \"see text and images copied to the clipboard.\"\n *\n * Note: On iOS 16+, if the user denies paste permission, this method will return null.\n * Due to iOS platform limitations, there is no way to distinguish between no image in clipboard and denied permission.\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` (this includes cases where permission was denied).\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * \n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise {\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 {\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 {\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): EventSubscription {\n return ExpoClipboard.addListener(clipboardEventName, listener);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n * @deprecated use subscription.remove() instead.\n */\nexport function removeClipboardListener(subscription: EventSubscription) {\n subscription.remove();\n}\n\n/**\n * Property that determines if the `ClipboardPasteButton` is available.\n *\n * This requires the users device to be using at least iOS 16.\n *\n * `true` if the component is available, and `false` otherwise.\n */\nexport const isPasteButtonAvailable: boolean =\n Platform.OS === 'ios' ? ExpoClipboard.isPasteButtonAvailable : false;\n\nexport * from './Clipboard.types';\nexport { ClipboardPasteButtonProps } from './ClipboardPasteButton';\n\nexport { ClipboardPasteButton };\n"]} \ No newline at end of file diff --git a/packages/expo-clipboard/build/ExpoClipboard.d.ts b/packages/expo-clipboard/build/ExpoClipboard.d.ts index 04859cde236a1e..a7d0977c027f13 100644 --- a/packages/expo-clipboard/build/ExpoClipboard.d.ts +++ b/packages/expo-clipboard/build/ExpoClipboard.d.ts @@ -5,7 +5,6 @@ type ExpoClipboardEvents = { [clipboardEventName]: (event: ClipboardEvent) => void; }; declare class NativeExpoClipboard extends NativeModule { - setString(text: string): void; getStringAsync(options?: GetStringOptions): Promise; setStringAsync(text: string, options?: SetStringOptions): Promise; hasStringAsync(): Promise; diff --git a/packages/expo-clipboard/build/ExpoClipboard.d.ts.map b/packages/expo-clipboard/build/ExpoClipboard.d.ts.map index 8eeb1dfd0eafb9..c3a5bd3ee8230f 100644 --- a/packages/expo-clipboard/build/ExpoClipboard.d.ts.map +++ b/packages/expo-clipboard/build/ExpoClipboard.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,KAAK,mBAAmB,GAAG;IACzB,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACvD,CAAC;AAEF,OAAO,OAAO,mBAAoB,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACzE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAC7B,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAC3D,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAC1E,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAClC,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IACvE,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjD,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAErC,sBAAsB,EAAE,OAAO,CAAC;CACjC;;AAED,wBAAyE"} \ No newline at end of file +{"version":3,"file":"ExpoClipboard.d.ts","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD,KAAK,mBAAmB,GAAG;IACzB,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACvD,CAAC;AAEF,OAAO,OAAO,mBAAoB,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACzE,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAC3D,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAC1E,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAClC,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IACvE,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACjD,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAErC,sBAAsB,EAAE,OAAO,CAAC;CACjC;;AAED,wBAAyE"} \ No newline at end of file diff --git a/packages/expo-clipboard/build/ExpoClipboard.js.map b/packages/expo-clipboard/build/ExpoClipboard.js.map index 446b622331efc0..169b84af0f8408 100644 --- a/packages/expo-clipboard/build/ExpoClipboard.js.map +++ b/packages/expo-clipboard/build/ExpoClipboard.js.map @@ -1 +1 @@ -{"version":3,"file":"ExpoClipboard.js","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAUtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAqBvD,eAAe,mBAAmB,CAAsB,eAAe,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ClipboardEvent,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\n\nexport const clipboardEventName = 'onClipboardChanged';\n\ntype ExpoClipboardEvents = {\n [clipboardEventName]: (event: ClipboardEvent) => void;\n};\n\ndeclare class NativeExpoClipboard extends NativeModule {\n setString(text: string): void;\n getStringAsync(options?: GetStringOptions): Promise;\n setStringAsync(text: string, options?: SetStringOptions): Promise;\n hasStringAsync(): Promise;\n getImageAsync(options: GetImageOptions): Promise;\n setImageAsync(base64Image: string): Promise;\n hasImageAsync(): Promise;\n getUrlAsync?: () => Promise;\n setUrlAsync?: (url: string) => Promise;\n hasUrlAsync?: () => Promise;\n\n isPasteButtonAvailable: boolean;\n}\n\nexport default requireNativeModule('ExpoClipboard');\n"]} \ No newline at end of file +{"version":3,"file":"ExpoClipboard.js","sourceRoot":"","sources":["../src/ExpoClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAUtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAoBvD,eAAe,mBAAmB,CAAsB,eAAe,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ClipboardEvent,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\n\nexport const clipboardEventName = 'onClipboardChanged';\n\ntype ExpoClipboardEvents = {\n [clipboardEventName]: (event: ClipboardEvent) => void;\n};\n\ndeclare class NativeExpoClipboard extends NativeModule {\n getStringAsync(options?: GetStringOptions): Promise;\n setStringAsync(text: string, options?: SetStringOptions): Promise;\n hasStringAsync(): Promise;\n getImageAsync(options: GetImageOptions): Promise;\n setImageAsync(base64Image: string): Promise;\n hasImageAsync(): Promise;\n getUrlAsync?: () => Promise;\n setUrlAsync?: (url: string) => Promise;\n hasUrlAsync?: () => Promise;\n\n isPasteButtonAvailable: boolean;\n}\n\nexport default requireNativeModule('ExpoClipboard');\n"]} \ No newline at end of file diff --git a/packages/expo-clipboard/build/web/ClipboardModule.d.ts b/packages/expo-clipboard/build/web/ClipboardModule.d.ts index 81393a1e4bd157..bd377f90a384b3 100644 --- a/packages/expo-clipboard/build/web/ClipboardModule.d.ts +++ b/packages/expo-clipboard/build/web/ClipboardModule.d.ts @@ -1,7 +1,6 @@ import { ClipboardImage, GetImageOptions, GetStringOptions, SetStringOptions } from '../Clipboard.types'; declare const _default: { getStringAsync(options: GetStringOptions): Promise; - setString(text: string): boolean; setStringAsync(text: string, options: SetStringOptions): Promise; hasStringAsync(): Promise; getImageAsync(_options: GetImageOptions): Promise; diff --git a/packages/expo-clipboard/build/web/ClipboardModule.d.ts.map b/packages/expo-clipboard/build/web/ClipboardModule.d.ts.map index 1bc522830712be..a85c5db4794840 100644 --- a/packages/expo-clipboard/build/web/ClipboardModule.d.ts.map +++ b/packages/expo-clipboard/build/web/ClipboardModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ClipboardModule.d.ts","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;;4BAGI,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;oBAgDhD,MAAM,GAAG,OAAO;yBAcL,MAAM,WAAW,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;sBAqCvD,OAAO,CAAC,OAAO,CAAC;4BAGV,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;+BA6B7C,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qBAkBhC,OAAO,CAAC,OAAO,CAAC;4BAGf,IAAI;+BACD,IAAI;;AA1JjC,wBA2JE"} \ No newline at end of file +{"version":3,"file":"ClipboardModule.d.ts","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;;4BAGI,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;yBA+CrC,MAAM,WAAW,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;sBAqCvD,OAAO,CAAC,OAAO,CAAC;4BAGV,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;+BA6B7C,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qBAkBhC,OAAO,CAAC,OAAO,CAAC;4BAGf,IAAI;+BACD,IAAI;;AA3IjC,wBA4IE"} \ No newline at end of file diff --git a/packages/expo-clipboard/build/web/ClipboardModule.js b/packages/expo-clipboard/build/web/ClipboardModule.js index 57faa773f129d2..72e852c5c9eb70 100644 --- a/packages/expo-clipboard/build/web/ClipboardModule.js +++ b/packages/expo-clipboard/build/web/ClipboardModule.js @@ -47,23 +47,6 @@ export default { } } }, - // TODO: (barthap) The `setString` was deprecated in SDK 45. Remove this function in a few SDK cycles. - setString(text) { - const textField = document.createElement('textarea'); - textField.textContent = text; - document.body.appendChild(textField); - textField.select(); - try { - document.execCommand('copy'); - return true; - } - catch { - return false; - } - finally { - document.body.removeChild(textField); - } - }, async setStringAsync(text, options) { switch (options.inputFormat) { case StringFormat.HTML: { @@ -95,7 +78,7 @@ export default { catch { // we can fall back to legacy behavior in any kind of failure // including navigator.clipboard unavailability - return this.setString(text); + return legacySetString(text); } } } @@ -180,4 +163,20 @@ function createHtmlClipboardItem(htmlString) { 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }), }); } +function legacySetString(text) { + const textField = document.createElement('textarea'); + textField.textContent = text; + document.body.appendChild(textField); + textField.select(); + try { + document.execCommand('copy'); + return true; + } + catch { + return false; + } + finally { + document.body.removeChild(textField); + } +} //# sourceMappingURL=ClipboardModule.js.map \ No newline at end of file diff --git a/packages/expo-clipboard/build/web/ClipboardModule.js.map b/packages/expo-clipboard/build/web/ClipboardModule.js.map index fa708024710d7a..ff1939d8d08d2e 100644 --- a/packages/expo-clipboard/build/web/ClipboardModule.js.map +++ b/packages/expo-clipboard/build/web/ClipboardModule.js.map @@ -1 +1 @@ -{"version":3,"file":"ClipboardModule.js","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\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';\nimport {\n ClipboardImage,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n StringFormat,\n} from '../Clipboard.types';\n\nexport default {\n async getStringAsync(options: GetStringOptions): Promise {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\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 {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw new CopyFailureException(error.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 {\n return await clipboardHasTypesAsync(['text/plain', 'text/html']);\n },\n async getImageAsync(_options: GetImageOptions): Promise {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw new PasteFailureException(error.message);\n }\n },\n async setImageAsync(base64image: string): Promise {\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 new ClipboardItem({\n [blob.type]: blob,\n }),\n ]);\n } catch (err: any) {\n throw new CopyFailureException(err.message);\n }\n },\n async hasImageAsync(): Promise {\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 {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw error;\n }\n}\n\nfunction createHtmlClipboardItem(htmlString: string): ClipboardItem {\n return new ClipboardItem({\n 'text/html': new Blob([htmlString], { type: 'text/html' }),\n 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }),\n });\n}\n"]} \ No newline at end of file +{"version":3,"file":"ClipboardModule.js","sourceRoot":"","sources":["../../src/web/ClipboardModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\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';\nimport {\n ClipboardImage,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n StringFormat,\n} from '../Clipboard.types';\n\nexport default {\n async getStringAsync(options: GetStringOptions): Promise {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\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 async setStringAsync(text: string, options: SetStringOptions): Promise {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw new CopyFailureException(error.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 legacySetString(text);\n }\n }\n }\n },\n async hasStringAsync(): Promise {\n return await clipboardHasTypesAsync(['text/plain', 'text/html']);\n },\n async getImageAsync(_options: GetImageOptions): Promise {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw new PasteFailureException(error.message);\n }\n },\n async setImageAsync(base64image: string): Promise {\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 new ClipboardItem({\n [blob.type]: blob,\n }),\n ]);\n } catch (err: any) {\n throw new CopyFailureException(err.message);\n }\n },\n async hasImageAsync(): Promise {\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 {\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 (error: any) {\n // it might fail, because user denied permission\n if (\n (typeof error === 'object' && error?.name === 'NotAllowedError') ||\n (await isClipboardPermissionDeniedAsync())\n ) {\n throw new NoPermissionException();\n }\n throw error;\n }\n}\n\nfunction createHtmlClipboardItem(htmlString: string): ClipboardItem {\n return new ClipboardItem({\n 'text/html': new Blob([htmlString], { type: 'text/html' }),\n 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }),\n });\n}\n\nfunction legacySetString(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"]} \ No newline at end of file diff --git a/packages/expo-clipboard/src/Clipboard.ts b/packages/expo-clipboard/src/Clipboard.ts index 6230890f111a18..b575599a6efde7 100644 --- a/packages/expo-clipboard/src/Clipboard.ts +++ b/packages/expo-clipboard/src/Clipboard.ts @@ -47,23 +47,6 @@ export async function setStringAsync( return ExpoClipboard.setStringAsync(text, options); } -/** - * Sets the content of the user's clipboard. - * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead. - * - * @returns On web, this returns a boolean value indicating whether or not the string was saved to - * the user's clipboard. On iOS and Android, nothing is returned. - */ -export function setString(text: string): void { - if (Platform.OS === 'web') { - // on web, we need to return legacy method, - // because of different return type - return ExpoClipboard.setString(text); - } else { - setStringAsync(text); - } -} - /** * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML). * diff --git a/packages/expo-clipboard/src/ExpoClipboard.ts b/packages/expo-clipboard/src/ExpoClipboard.ts index cf425128ad1f6f..43da6f1f4a0923 100644 --- a/packages/expo-clipboard/src/ExpoClipboard.ts +++ b/packages/expo-clipboard/src/ExpoClipboard.ts @@ -15,7 +15,6 @@ type ExpoClipboardEvents = { }; declare class NativeExpoClipboard extends NativeModule { - setString(text: string): void; getStringAsync(options?: GetStringOptions): Promise; setStringAsync(text: string, options?: SetStringOptions): Promise; hasStringAsync(): Promise; diff --git a/packages/expo-clipboard/src/__tests__/Clipboard-test.native.ts b/packages/expo-clipboard/src/__tests__/Clipboard-test.native.ts index a677e4643d0a1e..fc212322b2c5f5 100644 --- a/packages/expo-clipboard/src/__tests__/Clipboard-test.native.ts +++ b/packages/expo-clipboard/src/__tests__/Clipboard-test.native.ts @@ -1,20 +1,12 @@ import * as Clipboard from '../Clipboard'; -import ExpoClipboard from '../ExpoClipboard'; describe('Clipboard', () => { it('getStringAsync', () => { expect(Clipboard.getStringAsync).toBeDefined(); }); - it('setString', () => { - expect(Clipboard.setString).toBeDefined(); - }); it('setStringAsync', () => { expect(Clipboard.setStringAsync).toBeDefined(); }); - it('setString delegates to native setStringAsync', () => { - Clipboard.setString('test'); - expect(ExpoClipboard.setStringAsync).toHaveBeenCalledWith('test', {}); - }); it('addClipboardListener', () => { expect(Clipboard.addClipboardListener).toBeDefined(); }); diff --git a/packages/expo-clipboard/src/__tests__/Clipboard-test.web.ts b/packages/expo-clipboard/src/__tests__/Clipboard-test.web.ts index a7799b5ab499f6..cb99a5ab9fd401 100644 --- a/packages/expo-clipboard/src/__tests__/Clipboard-test.web.ts +++ b/packages/expo-clipboard/src/__tests__/Clipboard-test.web.ts @@ -12,10 +12,4 @@ describe('Clipboard', () => { // Browser needs to allow access to Clipboard, so the above will // be called, but it will fail. }); - - it('copies the provided string with legacy setString', () => { - document.execCommand = jest.fn().mockReturnValueOnce(true); - expect(Clipboard.setString('Dumbledore')).toBe(true); - expect(document.execCommand).toHaveBeenCalledWith('copy'); - }); }); diff --git a/packages/expo-clipboard/src/web/ClipboardModule.ts b/packages/expo-clipboard/src/web/ClipboardModule.ts index acfe34c1208ccd..863e8875e02ad5 100644 --- a/packages/expo-clipboard/src/web/ClipboardModule.ts +++ b/packages/expo-clipboard/src/web/ClipboardModule.ts @@ -69,21 +69,6 @@ export default { } } }, - // TODO: (barthap) The `setString` was deprecated in SDK 45. Remove this function in a few SDK cycles. - setString(text: string): boolean { - const textField = document.createElement('textarea'); - textField.textContent = text; - document.body.appendChild(textField); - textField.select(); - try { - document.execCommand('copy'); - return true; - } catch { - return false; - } finally { - document.body.removeChild(textField); - } - }, async setStringAsync(text: string, options: SetStringOptions): Promise { switch (options.inputFormat) { case StringFormat.HTML: { @@ -116,7 +101,7 @@ export default { } catch { // we can fall back to legacy behavior in any kind of failure // including navigator.clipboard unavailability - return this.setString(text); + return legacySetString(text); } } } @@ -209,3 +194,18 @@ function createHtmlClipboardItem(htmlString: string): ClipboardItem { 'text/plain': new Blob([htmlToPlainText(htmlString)], { type: 'text/plain' }), }); } + +function legacySetString(text: string): boolean { + const textField = document.createElement('textarea'); + textField.textContent = text; + document.body.appendChild(textField); + textField.select(); + try { + document.execCommand('copy'); + return true; + } catch { + return false; + } finally { + document.body.removeChild(textField); + } +} From 13f9d400efb25c934dd57d3e84f5b5b80082aaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Klocek?= Date: Fri, 27 Feb 2026 10:07:30 +0100 Subject: [PATCH 3/4] [core] Accept typed arrays for ArrayBuffer arguments (#43082) # Why Makes it easier to migrate functions taking `Data` / `ByteArray` as arguments into `ArrayBuffer`-like arguments without breaking changes. # How When a function expects `NativeArrayBuffer` or `JavaScriptArrayBuffer` argument, but e.g. `Uint8Array` is passed instead, access its `buffer` property instead of throwing. This also supports partial views, when a typed array is only a view over a subrange of its backing buffer - a copy is performed in such cases (parity with `Uint8Array` convertible) # Test Plan - Unit tests - Manual testing # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/expo-modules-core/CHANGELOG.md | 2 + .../jni/types/ArrayBufferConversionTest.kt | 36 +++++++++++++++++ .../src/main/cpp/NativeArrayBuffer.cpp | 15 +++++++ .../android/src/main/cpp/NativeArrayBuffer.h | 15 +++++++ .../src/main/cpp/types/FrontendConverter.cpp | 23 +++++++++-- .../common/cpp/JSI/TypedArray.cpp | 21 ++++++++++ .../common/cpp/JSI/TypedArray.h | 12 ++++++ .../DynamicTypes/DynamicArrayBufferType.swift | 14 ++++++- .../ios/JSI/EXJavaScriptTypedArray.h | 5 +++ .../ios/JSI/EXJavaScriptTypedArray.mm | 12 ++++++ .../ios/JSI/EXRawJavaScriptArrayBuffer.h | 5 +++ .../ios/Tests/ArrayBufferTests.swift | 39 +++++++++++++++++++ 12 files changed, 194 insertions(+), 5 deletions(-) diff --git a/packages/expo-modules-core/CHANGELOG.md b/packages/expo-modules-core/CHANGELOG.md index 4a95e9d6f01585..7e190bdcd4edc9 100644 --- a/packages/expo-modules-core/CHANGELOG.md +++ b/packages/expo-modules-core/CHANGELOG.md @@ -6,6 +6,8 @@ ### 🎉 New features +- `NativeArrayBuffer` and `JavaScriptArrayBuffer` arguments now also accept typed arrays. ([#43082](https://github.com/expo/expo/pull/43082) by [@barthap](https://github.com/barthap)) + ### 🐛 Bug fixes - [iOS] Fix memory leak due to retain cycle in SwiftUI views. ([#43468](https://github.com/expo/expo/pull/43468) by [@nishan](https://github.com/intergalacticspacehighway)) diff --git a/packages/expo-modules-core/android/src/androidTest/java/expo/modules/kotlin/jni/types/ArrayBufferConversionTest.kt b/packages/expo-modules-core/android/src/androidTest/java/expo/modules/kotlin/jni/types/ArrayBufferConversionTest.kt index 2366d776762c4e..5266d6571a7793 100644 --- a/packages/expo-modules-core/android/src/androidTest/java/expo/modules/kotlin/jni/types/ArrayBufferConversionTest.kt +++ b/packages/expo-modules-core/android/src/androidTest/java/expo/modules/kotlin/jni/types/ArrayBufferConversionTest.kt @@ -24,6 +24,42 @@ class ArrayBufferConversionTest { jsAssertion = {} ) + @Test + fun js_array_buffer_accepts_full_typed_array() = conversionTest( + jsValue = "new Uint8Array([0x00, 0xff])", + nativeAssertion = { arrayBuffer -> + Truth.assertThat(arrayBuffer.size()).isEqualTo(2) + Truth.assertThat(arrayBuffer.readByte(0)).isEqualTo(0x00.toByte()) + Truth.assertThat(arrayBuffer.readByte(1)).isEqualTo(0xff.toByte()) + }, + map = {}, + jsAssertion = {} + ) + + @Test + fun js_array_buffer_accepts_partial_typed_array_view() = conversionTest( + jsValue = "new Uint8Array(new Uint8Array([1,2,3,4,5]).buffer, 1, 2)", + nativeAssertion = { arrayBuffer -> + Truth.assertThat(arrayBuffer.size()).isEqualTo(2) + Truth.assertThat(arrayBuffer.readByte(0)).isEqualTo(2.toByte()) + Truth.assertThat(arrayBuffer.readByte(1)).isEqualTo(3.toByte()) + }, + map = {}, + jsAssertion = {} + ) + + @Test + fun native_array_buffer_accepts_partial_typed_array_view() = conversionTest( + jsValue = "new Uint8Array(new Uint8Array([1,2,3,4,5]).buffer, 1, 2)", + nativeAssertion = { arrayBuffer -> + Truth.assertThat(arrayBuffer.size()).isEqualTo(2) + Truth.assertThat(arrayBuffer.readByte(0)).isEqualTo(2.toByte()) + Truth.assertThat(arrayBuffer.readByte(1)).isEqualTo(3.toByte()) + }, + map = {}, + jsAssertion = {} + ) + @Test fun js_array_buffer_should_be_returned() = conversionTest( jsValue = "new Uint8Array([0x00, 0xff]).buffer", diff --git a/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.cpp b/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.cpp index 0d689205d00f79..653ca3f192bb9f 100644 --- a/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.cpp +++ b/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.cpp @@ -59,6 +59,21 @@ NativeArrayBuffer::newInstance(JSIContext *jsiContext, jsi::Runtime &runtime, return value; } +jni::local_ref +NativeArrayBuffer::newInstance(JSIContext *jsiContext, jsi::Runtime &runtime, + expo::TypedArray& typedArray) { + + size_t size = typedArray.byteLength(runtime); + + auto byteBuffer = jni::JByteBuffer::allocateDirect(static_cast(size)); + byteBuffer->order(jni::JByteOrder::nativeOrder()); + memcpy(byteBuffer->getDirectAddress(), typedArray.getRawPointer(runtime), size); + + auto value = NativeArrayBuffer::newObjectCxxArgs(byteBuffer); + jsiContext->jniDeallocator->addReference(value); + return value; +} + NativeArrayBuffer::NativeArrayBuffer(const jni::alias_ref &byteBuffer) : buffer(std::make_shared(byteBuffer)) { } diff --git a/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.h b/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.h index 73f7b019aad461..d2239831decb8c 100644 --- a/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.h +++ b/packages/expo-modules-core/android/src/main/cpp/NativeArrayBuffer.h @@ -7,6 +7,8 @@ #include #include +#include "TypedArray.h" + #include namespace expo { @@ -49,12 +51,25 @@ class NativeArrayBuffer : public jni::HybridClass byteBuffer ); + /** + * Allocates a new NativeArrayBuffer by copying the contents of the given ArrayBuffer. + */ static jni::local_ref newInstance( JSIContext *jsiContext, jsi::Runtime& runtime, jsi::ArrayBuffer& arrayBuffer ); + /** + * Allocates a new NativeArrayBuffer by copying only the bytes within the typed array's + * view range — not the entire backing buffer. + */ + static jni::local_ref newInstance( + JSIContext *jsiContext, + jsi::Runtime& runtime, + expo::TypedArray& typedArray + ); + explicit NativeArrayBuffer(const jni::alias_ref& byteBuffer); [[nodiscard]] int size(); diff --git a/packages/expo-modules-core/android/src/main/cpp/types/FrontendConverter.cpp b/packages/expo-modules-core/android/src/main/cpp/types/FrontendConverter.cpp index fdaad62cc2b823..df13962fce77e7 100644 --- a/packages/expo-modules-core/android/src/main/cpp/types/FrontendConverter.cpp +++ b/packages/expo-modules-core/android/src/main/cpp/types/FrontendConverter.cpp @@ -207,7 +207,14 @@ jobject NativeArrayBufferFrontendConverter::convert( const jsi::Value &value ) const { JSIContext *jsiContext = getJSIContext(rt); - auto arrayBuffer = value.asObject(rt).getArrayBuffer(rt); + auto object = value.asObject(rt); + + if (isTypedArray(rt, object)) { + auto typedArray = TypedArray(rt, object); + return NativeArrayBuffer::newInstance(jsiContext, rt, typedArray).release(); + } + + auto arrayBuffer = object.getArrayBuffer(rt); return NativeArrayBuffer::newInstance( jsiContext, rt, @@ -221,7 +228,7 @@ bool NativeArrayBufferFrontendConverter::canConvert( ) const { if (value.isObject()) { auto object = value.getObject(rt); - return object.isArrayBuffer(rt); + return object.isArrayBuffer(rt) || isTypedArray(rt, object); } return false; } @@ -270,10 +277,14 @@ jobject JavaScriptArrayBufferFrontendConverter::convert( const jsi::Value &value ) const { JSIContext *jsiContext = getJSIContext(rt); + auto object = value.asObject(rt); + auto arrayBuffer = isTypedArray(rt, object) ? + TypedArray(rt, object).getViewedBufferSlice(rt) : + object.getArrayBuffer(rt); return JavaScriptArrayBuffer::newInstance( jsiContext, jsiContext->runtimeHolder->weak_from_this(), - std::make_shared(value.asObject(rt).getArrayBuffer(rt)) + std::make_shared(std::move(arrayBuffer)) ).release(); } @@ -281,7 +292,11 @@ bool JavaScriptArrayBufferFrontendConverter::canConvert( jsi::Runtime &rt, const jsi::Value &value ) const { - return value.isObject() && value.getObject(rt).isArrayBuffer(rt); + if (value.isObject()) { + auto object = value.getObject(rt); + return object.isArrayBuffer(rt) || isTypedArray(rt, object); + } + return false; } jobject JavaScriptFunctionFrontendConverter::convert( diff --git a/packages/expo-modules-core/common/cpp/JSI/TypedArray.cpp b/packages/expo-modules-core/common/cpp/JSI/TypedArray.cpp index 34b94e62d8c3ff..58952ca6cd3fff 100644 --- a/packages/expo-modules-core/common/cpp/JSI/TypedArray.cpp +++ b/packages/expo-modules-core/common/cpp/JSI/TypedArray.cpp @@ -51,6 +51,27 @@ jsi::ArrayBuffer TypedArray::getBuffer(jsi::Runtime &runtime) const { } } +jsi::ArrayBuffer TypedArray::getViewedBufferSlice(jsi::Runtime &runtime) const { + auto buffer = getBuffer(runtime); + + auto offset = byteOffset(runtime); + auto length = byteLength(runtime); + + if (offset == 0 && length == buffer.size(runtime)) { + return buffer; + } + + jsi::Function sliceFunc = buffer.getPropertyAsFunction(runtime, "slice"); + + // ArrayBuffer.prototype.slice(begin, end) + // Note: end is byteOffset + byteLength + auto slicedBuffer = sliceFunc.callWithThis(runtime, buffer, { + jsi::Value((double)offset), + jsi::Value((double)offset + length) + }); + return slicedBuffer.asObject(runtime).getArrayBuffer(runtime); +} + void* TypedArray::getRawPointer(jsi::Runtime &runtime) const { return reinterpret_cast(getBuffer(runtime).data(runtime) + byteOffset(runtime)); } diff --git a/packages/expo-modules-core/common/cpp/JSI/TypedArray.h b/packages/expo-modules-core/common/cpp/JSI/TypedArray.h index 558842b3a10dd4..c45bed67113595 100644 --- a/packages/expo-modules-core/common/cpp/JSI/TypedArray.h +++ b/packages/expo-modules-core/common/cpp/JSI/TypedArray.h @@ -38,8 +38,20 @@ class TypedArray : public jsi::Object { size_t byteLength(jsi::Runtime &runtime) const; + /** + * Returns the typed array's backing ArrayBuffer. + * Always returns the full buffer, even when the typed array covers only a subset of it. + */ jsi::ArrayBuffer getBuffer(jsi::Runtime &runtime) const; + /** + * Returns only the portion of the backing buffer spanned by this typed array's view. + * If the view covers the entire buffer, returns the buffer directly (zero-copy). + * If the view is a subset, allocates a new ArrayBuffer containing only that slice + * via ArrayBuffer.prototype.slice() — this involves a data copy. + */ + jsi::ArrayBuffer getViewedBufferSlice(jsi::Runtime &runtime) const; + void* getRawPointer(jsi::Runtime &runtime) const; }; diff --git a/packages/expo-modules-core/ios/Core/DynamicTypes/DynamicArrayBufferType.swift b/packages/expo-modules-core/ios/Core/DynamicTypes/DynamicArrayBufferType.swift index 7824b57d4e8633..7f5fdffb69c325 100644 --- a/packages/expo-modules-core/ios/Core/DynamicTypes/DynamicArrayBufferType.swift +++ b/packages/expo-modules-core/ios/Core/DynamicTypes/DynamicArrayBufferType.swift @@ -18,11 +18,23 @@ internal struct DynamicArrayBufferType: AnyDynamicType { Converts JS array buffer to its native representation. */ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any { + if let jsTypedArray = jsValue.getTypedArray() { + switch innerType { + case is JavaScriptArrayBuffer.Type: + return JavaScriptArrayBuffer(jsTypedArray.getViewedBufferSlice()) + case is NativeArrayBuffer.Type: + let typedArray = TypedArray(jsTypedArray) + return NativeArrayBuffer.copy(of: typedArray.rawPointer, count: typedArray.byteLength) + default: + throw ArrayBufferArgumentTypeException(innerType) + } + } + guard let rawArrayBuffer = jsValue.getArrayBuffer() else { throw NotArrayBufferException(innerType) } - let jsArrayBuffer = JavaScriptArrayBuffer(rawArrayBuffer) + let jsArrayBuffer = JavaScriptArrayBuffer(rawArrayBuffer) return switch innerType { case is JavaScriptArrayBuffer.Type: jsArrayBuffer case is NativeArrayBuffer.Type: jsArrayBuffer.copy() diff --git a/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.h b/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.h index d74d7d47599873..1fc0fe10306e67 100644 --- a/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.h +++ b/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.h @@ -3,6 +3,7 @@ #import #import #import +#import // We need to redefine the C++ enum (see TypedArray.h) in an Objective-C way to expose it to Swift. // Please keep them in-sync! @@ -27,4 +28,8 @@ NS_SWIFT_NAME(JavaScriptTypedArray) - (nonnull void *)getUnsafeMutableRawPointer; +- (nonnull EXJavaScriptArrayBuffer *)getBuffer; + +- (nonnull EXJavaScriptArrayBuffer *)getViewedBufferSlice; + @end diff --git a/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.mm b/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.mm index 1bfad3d3a2f153..74d2b4d6fbe534 100644 --- a/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.mm +++ b/packages/expo-modules-core/ios/JSI/EXJavaScriptTypedArray.mm @@ -26,4 +26,16 @@ - (nonnull void *)getUnsafeMutableRawPointer return _typedArrayPtr->getRawPointer(*[_runtime get]); } +- (nonnull EXJavaScriptArrayBuffer *)getBuffer +{ + auto bufferObject = std::make_shared(_typedArrayPtr->getBuffer(*[_runtime get])); + return [[EXJavaScriptArrayBuffer alloc] initWith:bufferObject runtime:_runtime]; +} + +- (nonnull EXJavaScriptArrayBuffer *)getViewedBufferSlice +{ + auto bufferObject = std::make_shared(_typedArrayPtr->getViewedBufferSlice(*[_runtime get])); + return [[EXJavaScriptArrayBuffer alloc] initWith:bufferObject runtime:_runtime]; +} + @end diff --git a/packages/expo-modules-core/ios/JSI/EXRawJavaScriptArrayBuffer.h b/packages/expo-modules-core/ios/JSI/EXRawJavaScriptArrayBuffer.h index 5a6f938a9e9095..8949790eb2c844 100644 --- a/packages/expo-modules-core/ios/JSI/EXRawJavaScriptArrayBuffer.h +++ b/packages/expo-modules-core/ios/JSI/EXRawJavaScriptArrayBuffer.h @@ -11,5 +11,10 @@ NS_SWIFT_NAME(RawJavaScriptArrayBuffer) @interface EXJavaScriptArrayBuffer : EXJavaScriptObject +#ifdef __cplusplus +- (nonnull instancetype)initWith:(std::shared_ptr)jsObjectPtr + runtime:(EXJavaScriptRuntime *)runtime; +#endif + @end diff --git a/packages/expo-modules-core/ios/Tests/ArrayBufferTests.swift b/packages/expo-modules-core/ios/Tests/ArrayBufferTests.swift index 593116649af386..3c050c3fbd8846 100644 --- a/packages/expo-modules-core/ios/Tests/ArrayBufferTests.swift +++ b/packages/expo-modules-core/ios/Tests/ArrayBufferTests.swift @@ -198,6 +198,40 @@ struct ArrayBufferTests { #expect(arrayBuffer.byteLength == 16) } + @Test + func `ArrayBuffer argument accepts full typed arrays`() throws { + let result = try runtime.eval([ + "typedArray = new Uint8Array([42, 84])", + "expo.modules.ArrayBufferTests.readBytesAsArray(typedArray, 2)" + ]).asArray() + + #expect(try result[0]?.asInt() == 42) + #expect(try result[1]?.asInt() == 84) + } + + @Test + func `JavaScriptArrayBuffer accepts partial typed array view`() throws { + let result = try runtime.eval([ + "arrayBuffer = new Uint8Array([1,2,3,4,5]).buffer", + "view = new Uint8Array(arrayBuffer, 1, 2)", + "expo.modules.ArrayBufferTests.readBytesAsArray(view, 2)" + ]).asArray() + + #expect(try result[0]?.asInt() == 2) + #expect(try result[1]?.asInt() == 3) + } + + @Test + func `NativeArrayBuffer accepts partial typed array view`() throws { + let result = try runtime.eval([ + "view = new Uint8Array(new Uint8Array([1,2,3,4,5]).buffer, 1, 2)", + "expo.modules.ArrayBufferTests.readNativeBufferBytesAsArray(view, 2)" + ]).asArray() + + #expect(try result[0]?.asInt() == 2) + #expect(try result[1]?.asInt() == 3) + } + @Test func `returns ArrayBuffer to JavaScript`() throws { let buffer = try runtime.eval("expo.modules.ArrayBufferTests.createNative(32)").asArrayBuffer() @@ -363,6 +397,11 @@ private final class ArrayBufferTestModule: Module { let data = Data(bytes: buffer.rawPointer, count: min(count, buffer.byteLength)) return Array(data) } + + Function("readNativeBufferBytesAsArray") { (buffer: NativeArrayBuffer, count: Int) -> [UInt8] in + let data = Data(bytes: buffer.rawPointer, count: min(count, buffer.byteLength)) + return Array(data) + } Function("fillWithPattern") { (buffer: JavaScriptArrayBuffer, pattern: UInt8) in memset(buffer.rawPointer, Int32(pattern), buffer.byteLength) From edde0c4b43c0a3b2878312a53c5f1d9c71b1c83d Mon Sep 17 00:00:00 2001 From: Muhammad Awais <69242396+awaisanjumx2@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:22:22 +0500 Subject: [PATCH 4/4] [iOS] [MediaLibrary] Add support to add or remove assets to Favorites smart album (#43459) # Why iOS gives us the ability to add or remove assets from the system Favorites smart album through [PHAssetChangeRequest](https://developer.apple.com/documentation/photos/phassetchangerequest/isfavorite), but `expo-media-library` does not implement this. We patched the library for our own use. Opening this PR in case someone else needs this feature too. # How `setAssetFavoriteAsync` takes an asset and a boolean flag. When `true`, it adds the asset to the system "Favorites" smart album; when `false`, it removes it. It returns a boolean indicating whether the operation succeeded. # Test Plan Tested on iOS using the Media Library screen in the `bare-expo` example app. Attaching a screen recording of the feature. https://github.com/user-attachments/assets/aec8515e-f87b-4b9b-a4a7-d6d8164b5315 # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- .../MediaLibrary/MediaDetailsScreen.tsx | 42 +++++++++++++++++-- apps/test-suite/tests/MediaLibrary.js | 11 +++++ packages/expo-media-library/CHANGELOG.md | 2 + .../build/MediaLibrary.d.ts | 8 ++++ .../build/MediaLibrary.d.ts.map | 2 +- .../expo-media-library/build/MediaLibrary.js | 18 ++++++++ .../build/MediaLibrary.js.map | 2 +- .../ios/MediaLibraryExceptions.swift | 6 +++ .../ios/MediaLibraryModule.swift | 22 ++++++++++ .../expo-media-library/src/MediaLibrary.ts | 26 ++++++++++++ 10 files changed, 134 insertions(+), 5 deletions(-) diff --git a/apps/native-component-list/src/screens/MediaLibrary/MediaDetailsScreen.tsx b/apps/native-component-list/src/screens/MediaLibrary/MediaDetailsScreen.tsx index 28dda6e9449425..93efee17993b2f 100644 --- a/apps/native-component-list/src/screens/MediaLibrary/MediaDetailsScreen.tsx +++ b/apps/native-component-list/src/screens/MediaLibrary/MediaDetailsScreen.tsx @@ -2,7 +2,7 @@ import { StackScreenProps } from '@react-navigation/stack'; import { Image } from 'expo-image'; import * as MediaLibrary from 'expo-media-library'; import React from 'react'; -import { ScrollView, StyleSheet, View, Alert } from 'react-native'; +import { ScrollView, StyleSheet, View, Alert, Platform } from 'react-native'; import Button from '../../components/Button'; import HeadingText from '../../components/HeadingText'; @@ -21,12 +21,19 @@ export default class MediaDetailsScreen extends React.Component { title: 'MediaLibrary Asset', }; - state = { + state: { + details: MediaLibrary.AssetInfo | null; + detailsWithoutDownloadingFromNetwork: MediaLibrary.AssetInfo | null; + } = { details: null, detailsWithoutDownloadingFromNetwork: null, }; componentDidMount() { + this.getAssetDetails(); + } + + getAssetDetails = () => { const { asset } = this.props.route.params; MediaLibrary.getAssetInfoAsync(asset, { shouldDownloadFromNetwork: false }).then((details) => { this.setState({ detailsWithoutDownloadingFromNetwork: details }); @@ -34,7 +41,7 @@ export default class MediaDetailsScreen extends React.Component { MediaLibrary.getAssetInfoAsync(asset).then((details) => { this.setState({ details }); }); - } + }; goBack() { const { navigation, route } = this.props; @@ -81,6 +88,26 @@ export default class MediaDetailsScreen extends React.Component { } }; + addToFavorites = async () => { + const { asset } = this.props.route.params!; + + const success = await MediaLibrary.setAssetFavoriteAsync(asset, true); + if (success) { + alert('Asset marked as favorite!'); + this.getAssetDetails(); + } + }; + + removeFromFavorites = async () => { + const { asset } = this.props.route.params!; + + const success = await MediaLibrary.setAssetFavoriteAsync(asset, false); + if (success) { + alert('Asset removed from favorites!'); + this.getAssetDetails(); + } + }; + renderAsset(asset: MediaLibrary.Asset) { const aspectRatio = asset.height ? asset.width / asset.height : 1; @@ -127,6 +154,15 @@ export default class MediaDetailsScreen extends React.Component { )} + {!album && details && Platform.OS === 'ios' && ( +