diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 1585c39689e37f28082ef941e2d7adff484c087f..b6f445d7eab06610a7b6b1525f3ad253e480515a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -97,7 +97,7 @@ "expo-paste-input": "^0.1.15", "expo-quick-actions": "^6.0.2", "expo-secure-store": "~57.0.2", - "expo-sharing": "57.0.16", + "expo-sharing": "57.0.17", "expo-splash-screen": "~57.0.8", "expo-sqlite": "~57.0.2", "expo-symbols": "~57.0.2", diff --git a/patches/expo-sharing@57.0.16.patch b/patches/expo-sharing@57.0.16.patch deleted file mode 100644 index 924df6c8c7c1b26891195a4c09fc4ec2d3c320b0..0000000000000000000000000000000000000000 --- a/patches/expo-sharing@57.0.16.patch +++ /dev/null @@ -1,123 +0,0 @@ -diff --git a/android/src/main/java/expo/modules/sharing/SharingRecords.kt b/android/src/main/java/expo/modules/sharing/SharingRecords.kt -index 0b87b53f632e82f9b4c09c066d5a67e4ab2525a4..65f1c57faef3ba8caac9e137341f2b8312bb5f86 100644 ---- a/android/src/main/java/expo/modules/sharing/SharingRecords.kt -+++ b/android/src/main/java/expo/modules/sharing/SharingRecords.kt -@@ -52,7 +52,8 @@ enum class ContentType(val value: String) : Enumerable { - internal data class SharePayload( - @Field var value: String = "", - @Field var shareType: ShareType = ShareType.Text, -- @Field var mimeType: String = "text/plain" -+ @Field var mimeType: String = "text/plain", -+ @Field var originalName: String? = null - ) : Record - - @OptimizedRecord -diff --git a/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt b/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt -index 8dd50e8f04022d183884a57e4d61a7e1711b9e49..481cdb4bc64ea5ae49919071f686324f6d56d55c 100644 ---- a/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt -+++ b/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt -@@ -3,6 +3,7 @@ package expo.modules.sharing - import android.content.Context - import android.content.Intent - import android.net.Uri -+import android.provider.OpenableColumns - - internal class SimpleShareIntentDataParser { - companion object { -@@ -17,21 +18,26 @@ internal class SimpleShareIntentDataParser { - } - - private fun handleSendAction(context: Context, intent: Intent, type: String): List { -- return if (type == "text/plain") { -- val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return emptyList() -- val isUrl = android.util.Patterns.WEB_URL.matcher(text).matches() -- -- listOf( -+ val stream = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM) -+ val text = intent.getStringExtra(Intent.EXTRA_TEXT) -+ if (stream == null) { -+ if (!type.startsWith("text/") || text == null) return emptyList() -+ return listOf( - SharePayload().apply { - value = text -- shareType = if (isUrl) ShareType.Url else ShareType.Text -- mimeType = "text/plain" -+ shareType = if (android.util.Patterns.WEB_URL.matcher(text).matches()) ShareType.Url else ShareType.Text -+ mimeType = type - } - ) -- } else { -- val uri = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM) -- listOfNotNull(uri?.let { createUriPayload(context, it, type) }) - } -+ val filePayload = createUriPayload(context, stream, type) -+ if (text.isNullOrBlank()) return listOf(filePayload) -+ val textPayload = SharePayload().apply { -+ value = text -+ shareType = if (android.util.Patterns.WEB_URL.matcher(text).matches()) ShareType.Url else ShareType.Text -+ mimeType = "text/plain" -+ } -+ return listOf(textPayload, filePayload) - } - - private fun handleSendMultipleAction(context: Context, intent: Intent, type: String): List { -@@ -41,10 +47,25 @@ internal class SimpleShareIntentDataParser { - - private fun createUriPayload(context: Context, uri: Uri, defaultType: String): SharePayload { - val specificType = context.contentResolver.getType(uri) ?: defaultType -+ // A blank DISPLAY_NAME must stay null: consumers treat originalName as -+ // the attachment name, and an empty name fails contract validation. -+ val displayName = runCatching { -+ context.contentResolver.query( -+ uri, -+ arrayOf(OpenableColumns.DISPLAY_NAME), -+ null, -+ null, -+ null -+ )?.use { cursor -> -+ val column = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) -+ if (column >= 0 && cursor.moveToFirst()) cursor.getString(column) else null -+ } -+ }.getOrNull()?.takeIf { it.isNotBlank() } - return SharePayload().apply { - value = uri.toString() -- shareType = ShareType.fromMimeType(specificType) -+ shareType = if (specificType.startsWith("text/")) ShareType.File else ShareType.fromMimeType(specificType) - mimeType = specificType -+ originalName = displayName - } - } - } -diff --git a/build/Sharing.types.d.ts b/build/Sharing.types.d.ts -index e9820b061bddce91657ab7144fde98d0543a3b8e..364d63273cb3e209c6490de6dd869a01c8728fcc 100644 ---- a/build/Sharing.types.d.ts -+++ b/build/Sharing.types.d.ts -@@ -77,6 +77,11 @@ export type SharePayload = { - * @default 'text/plain' - */ - mimeType?: string; -+ /** -+ * The display name of a shared file, when the source app provided one. -+ * Populated on Android from the content resolver's `DISPLAY_NAME`. -+ */ -+ originalName?: string | null; - }; - export type BaseResolvedSharePayload = SharePayload & { - /** -diff --git a/src/Sharing.types.ts b/src/Sharing.types.ts -index d9a5ad38f5fe2856b365eb91b71abd640314a5cc..21ea45f04b88cce272e2e02de24c5e4eb8157e82 100644 ---- a/src/Sharing.types.ts -+++ b/src/Sharing.types.ts -@@ -85,6 +85,12 @@ export type SharePayload = { - * @default 'text/plain' - */ - mimeType?: string; -+ -+ /** -+ * The display name of a shared file, when the source app provided one. -+ * Populated on Android from the content resolver's `DISPLAY_NAME`. -+ */ -+ originalName?: string | null; - }; - - export type BaseResolvedSharePayload = SharePayload & { diff --git a/patches/expo-sharing@57.0.17.patch b/patches/expo-sharing@57.0.17.patch new file mode 100644 index 0000000000000000000000000000000000000000..924df6c8c7c1b26891195a4c09fc4ec2d3c320b0 --- /dev/null +++ b/patches/expo-sharing@57.0.17.patch @@ -0,0 +1,123 @@ +diff --git a/android/src/main/java/expo/modules/sharing/SharingRecords.kt b/android/src/main/java/expo/modules/sharing/SharingRecords.kt +index 0b87b53f632e82f9b4c09c066d5a67e4ab2525a4..65f1c57faef3ba8caac9e137341f2b8312bb5f86 100644 +--- a/android/src/main/java/expo/modules/sharing/SharingRecords.kt ++++ b/android/src/main/java/expo/modules/sharing/SharingRecords.kt +@@ -52,7 +52,8 @@ enum class ContentType(val value: String) : Enumerable { + internal data class SharePayload( + @Field var value: String = "", + @Field var shareType: ShareType = ShareType.Text, +- @Field var mimeType: String = "text/plain" ++ @Field var mimeType: String = "text/plain", ++ @Field var originalName: String? = null + ) : Record + + @OptimizedRecord +diff --git a/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt b/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt +index 8dd50e8f04022d183884a57e4d61a7e1711b9e49..481cdb4bc64ea5ae49919071f686324f6d56d55c 100644 +--- a/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt ++++ b/android/src/main/java/expo/modules/sharing/dataParsers/SimpleShareIntentDataParser.kt +@@ -3,6 +3,7 @@ package expo.modules.sharing + import android.content.Context + import android.content.Intent + import android.net.Uri ++import android.provider.OpenableColumns + + internal class SimpleShareIntentDataParser { + companion object { +@@ -17,21 +18,26 @@ internal class SimpleShareIntentDataParser { + } + + private fun handleSendAction(context: Context, intent: Intent, type: String): List { +- return if (type == "text/plain") { +- val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return emptyList() +- val isUrl = android.util.Patterns.WEB_URL.matcher(text).matches() +- +- listOf( ++ val stream = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM) ++ val text = intent.getStringExtra(Intent.EXTRA_TEXT) ++ if (stream == null) { ++ if (!type.startsWith("text/") || text == null) return emptyList() ++ return listOf( + SharePayload().apply { + value = text +- shareType = if (isUrl) ShareType.Url else ShareType.Text +- mimeType = "text/plain" ++ shareType = if (android.util.Patterns.WEB_URL.matcher(text).matches()) ShareType.Url else ShareType.Text ++ mimeType = type + } + ) +- } else { +- val uri = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM) +- listOfNotNull(uri?.let { createUriPayload(context, it, type) }) + } ++ val filePayload = createUriPayload(context, stream, type) ++ if (text.isNullOrBlank()) return listOf(filePayload) ++ val textPayload = SharePayload().apply { ++ value = text ++ shareType = if (android.util.Patterns.WEB_URL.matcher(text).matches()) ShareType.Url else ShareType.Text ++ mimeType = "text/plain" ++ } ++ return listOf(textPayload, filePayload) + } + + private fun handleSendMultipleAction(context: Context, intent: Intent, type: String): List { +@@ -41,10 +47,25 @@ internal class SimpleShareIntentDataParser { + + private fun createUriPayload(context: Context, uri: Uri, defaultType: String): SharePayload { + val specificType = context.contentResolver.getType(uri) ?: defaultType ++ // A blank DISPLAY_NAME must stay null: consumers treat originalName as ++ // the attachment name, and an empty name fails contract validation. ++ val displayName = runCatching { ++ context.contentResolver.query( ++ uri, ++ arrayOf(OpenableColumns.DISPLAY_NAME), ++ null, ++ null, ++ null ++ )?.use { cursor -> ++ val column = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) ++ if (column >= 0 && cursor.moveToFirst()) cursor.getString(column) else null ++ } ++ }.getOrNull()?.takeIf { it.isNotBlank() } + return SharePayload().apply { + value = uri.toString() +- shareType = ShareType.fromMimeType(specificType) ++ shareType = if (specificType.startsWith("text/")) ShareType.File else ShareType.fromMimeType(specificType) + mimeType = specificType ++ originalName = displayName + } + } + } +diff --git a/build/Sharing.types.d.ts b/build/Sharing.types.d.ts +index e9820b061bddce91657ab7144fde98d0543a3b8e..364d63273cb3e209c6490de6dd869a01c8728fcc 100644 +--- a/build/Sharing.types.d.ts ++++ b/build/Sharing.types.d.ts +@@ -77,6 +77,11 @@ export type SharePayload = { + * @default 'text/plain' + */ + mimeType?: string; ++ /** ++ * The display name of a shared file, when the source app provided one. ++ * Populated on Android from the content resolver's `DISPLAY_NAME`. ++ */ ++ originalName?: string | null; + }; + export type BaseResolvedSharePayload = SharePayload & { + /** +diff --git a/src/Sharing.types.ts b/src/Sharing.types.ts +index d9a5ad38f5fe2856b365eb91b71abd640314a5cc..21ea45f04b88cce272e2e02de24c5e4eb8157e82 100644 +--- a/src/Sharing.types.ts ++++ b/src/Sharing.types.ts +@@ -85,6 +85,12 @@ export type SharePayload = { + * @default 'text/plain' + */ + mimeType?: string; ++ ++ /** ++ * The display name of a shared file, when the source app provided one. ++ * Populated on Android from the content resolver's `DISPLAY_NAME`. ++ */ ++ originalName?: string | null; + }; + + export type BaseResolvedSharePayload = SharePayload & { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b20e4b0c33ed9405e41e13f5a0000be63bc5e5a..6789d252f9237c509a8e9d261593aa98da6fd875 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,7 +93,7 @@ patchedDependencies: '@react-navigation/native-stack@7.17.6': e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552 effect@4.0.0-beta.103: af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6 expo-audio@57.0.4: fa9a3e0442ed395d4071bb406e08c3a471c9a84700bdfa0b9ad7ff144c96041a - expo-sharing@57.0.16: 8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45 + expo-sharing@57.0.17: 8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45 react-native-gesture-handler@2.32.0: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d3 react-native-keyboard-controller@1.21.13: 6e4339347bc5bb3c9ea67d85ff5c814058b211c5750f247aba59d07869a2e787 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675 @@ -368,8 +368,8 @@ importers: specifier: ~57.0.2 version: 57.0.2(expo@57.0.18) expo-sharing: - specifier: 57.0.16 - version: 57.0.16(patch_hash=8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45)(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) + specifier: 57.0.17 + version: 57.0.17(patch_hash=8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45)(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3) expo-splash-screen: specifier: ~57.0.8 version: 57.0.8(expo@57.0.18)(typescript@6.0.3) @@ -6676,8 +6676,8 @@ packages: resolution: {integrity: sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ==} engines: {node: '>=20.16.0'} - expo-sharing@57.0.16: - resolution: {integrity: sha512-Z4ZFYLP8+EqIdUAiERYRI4/r7rtZDCRQ8Lj6jDG0xPF19B8nsFhYMgh7uyic+WulFEr+Xn7FeWgSDAuufkOyzw==} + expo-sharing@57.0.17: + resolution: {integrity: sha512-p2B4SMeNsOv6hm1NkrxRlncjwu9mmLfi0drLaEyX13Rxn4DdDFPhuaezpOK7XLogTsmWGpfR46uq6oHMvSxbfA==} peerDependencies: expo: '*' react: '*' @@ -16613,7 +16613,7 @@ snapshots: expo-server@57.0.3: {} - expo-sharing@57.0.16(patch_hash=8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45)(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): + expo-sharing@57.0.17(patch_hash=8d2e3b10eb3f52036a9a086800180ec6cebf3b75bccc5b1775117a7244d4ac45)(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)(typescript@6.0.3): dependencies: '@expo/config-plugins': 57.0.9(typescript@6.0.3) '@expo/config-types': 57.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index bbfde3775f7b9f90f57f0bf32d877d633e8504a0..ff38222efb124e38d207ad8a65073b7fc6f7a913 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -155,7 +155,7 @@ patchedDependencies: "@react-navigation/native-stack@7.17.6": patches/@react-navigation%2Fnative-stack@7.17.6.patch effect@4.0.0-beta.103: patches/effect@4.0.0-beta.103.patch expo-audio@57.0.4: patches/expo-audio@57.0.4.patch - expo-sharing@57.0.16: patches/expo-sharing@57.0.16.patch + expo-sharing@57.0.17: patches/expo-sharing@57.0.17.patch react-native-gesture-handler@2.32.0: patches/react-native-gesture-handler@2.32.0.patch react-native-keyboard-controller@1.21.13: patches/react-native-keyboard-controller@1.21.13.patch react-native-nitro-modules@0.35.9: patches/react-native-nitro-modules@0.35.9.patch