Skip to content

Commit eb29bb1

Browse files
authored
[web]: Show the "paste" context menu item even if the permission was denied. (#2209)
Show the "paste" context menu item even if the permission was denied. Safari and Firefox ask the clipboard permission on each read action, it means on each frame during the scroll of selected text on mobile web. ## Release Notes N/A
1 parent fcad981 commit eb29bb1

File tree

4 files changed

+10
-10
lines changed

4 files changed

+10
-10
lines changed

compose/foundation/foundation/src/jsMain/kotlin/androidx/compose/foundation/internal/ClipboardUtils.js.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ private const val MIME_TYPE_PLAIN_TEXT = "text/plain"
2929

3030
internal actual suspend fun ClipEntry.readText(): String? {
3131
if (!this.hasText()) return null
32+
if (this.clipboardItems.isEmpty()) return null
3233
val blob = clipboardItems[0].getType(MIME_TYPE_PLAIN_TEXT).await<Blob>()
3334
return getTextFromBlob(blob).await<String>().toString()
3435
}
@@ -45,7 +46,10 @@ internal actual fun AnnotatedString?.toClipEntry(): ClipEntry? {
4546

4647
internal actual fun ClipEntry?.hasText(): Boolean {
4748
if (this == null) return false
48-
if (this.clipboardItems.isEmpty()) return false
49+
// Empty clipboardItems here mean that the read from web clipboard was ended with an error.
50+
// The most common reason is that the permission was denied.
51+
// In this case we want to show the "paste" menu item anyway.
52+
if (this.clipboardItems.isEmpty()) return true
4953
return doesJsArrayContainValue(this.clipboardItems[0].types, MIME_TYPE_PLAIN_TEXT)
5054
}
5155

compose/foundation/foundation/src/wasmJsMain/kotlin/androidx/compose/foundation/internal/ClipboardUtils.wasm.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ private val MIME_TYPE_PLAIN_TEXT_JS = MIME_TYPE_PLAIN_TEXT.toJsString()
3030

3131
internal actual suspend fun ClipEntry.readText(): String? {
3232
if (!this.hasText()) return null
33+
if (this.clipboardItems.length == 0) return null
3334
val blob = clipboardItems[0]!!.getType(MIME_TYPE_PLAIN_TEXT_JS).await<Blob>()
3435
return getTextFromBlob(blob).await<JsString>().toString()
3536
}
@@ -46,7 +47,10 @@ internal actual fun AnnotatedString?.toClipEntry(): ClipEntry? {
4647

4748
internal actual fun ClipEntry?.hasText(): Boolean {
4849
if (this == null) return false
49-
if (this.clipboardItems.length == 0) return false
50+
// Empty clipboardItems here mean that the read from web clipboard was ended with an error.
51+
// The most common reason is that the permission was denied.
52+
// In this case we want to show the "paste" menu item anyway.
53+
if (this.clipboardItems.length == 0) return true
5054
return doesJsArrayContainValue(this.clipboardItems.get(0)!!.types, MIME_TYPE_PLAIN_TEXT_JS)
5155
}
5256

compose/ui/ui/src/jsMain/kotlin/androidx/compose/ui/platform/PlatformClipboard.js.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ class JsPlatformClipboard : Clipboard {
3737
// The most common reason is that the permission was denied
3838
println("Failed to read from Clipboard: $it")
3939
emptyClipboardItems
40-
}.then {
41-
it
4240
}.await()
43-
44-
if (items.isEmpty()) return null
4541
return ClipEntry(items)
4642
}
4743

compose/ui/ui/src/wasmJsMain/kotlin/androidx/compose/ui/platform/PlatformClipboard.wasm.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ class WasmPlatformClipboard : Clipboard {
3737
// The most common reason is that the permission was denied
3838
println("Failed to read from Clipboard: $it")
3939
emptyClipboardItems
40-
}.then {
41-
it
4240
}.await<JsArray<ClipboardItem>>()
43-
44-
if (items.length == 0) return null
4541
return ClipEntry(items)
4642
}
4743

0 commit comments

Comments
 (0)