137 lines
5.0 KiB
Diff
137 lines
5.0 KiB
Diff
diff --git a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
|
|
index 2164aec4ec1d..d216db6d2927 100644
|
|
--- a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
|
|
+++ b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
|
|
@@ -511,14 +511,17 @@ class ExpoPasteInputView: ExpoView {
|
|
var attachmentRanges: [NSRange] = []
|
|
var mediaPayloads: [MediaPayload] = []
|
|
|
|
+ // Only track ranges for attachments we successfully extract a real payload
|
|
+ // from. Attachments without a payload (e.g. iOS dictation placeholders)
|
|
+ // are left alone — sanitizing them would delete characters the system
|
|
+ // manages itself, and emitting "unsupported" would raise a spurious error.
|
|
attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, _ in
|
|
guard let attachment = value as? NSTextAttachment else {
|
|
return
|
|
}
|
|
|
|
- attachmentRanges.append(range)
|
|
-
|
|
if let payload = self.extractMediaPayload(from: attachment, textView: textView, range: range) {
|
|
+ attachmentRanges.append(range)
|
|
mediaPayloads.append(payload)
|
|
}
|
|
}
|
|
@@ -529,9 +532,8 @@ class ExpoPasteInputView: ExpoView {
|
|
return
|
|
}
|
|
|
|
- attachmentRanges.append(range)
|
|
-
|
|
if let payload = self.extractMediaPayload(from: adaptiveGlyph) {
|
|
+ attachmentRanges.append(range)
|
|
mediaPayloads.append(payload)
|
|
}
|
|
}
|
|
@@ -539,17 +541,12 @@ class ExpoPasteInputView: ExpoView {
|
|
|
|
attachmentRanges = uniqueRanges(attachmentRanges)
|
|
|
|
- guard !attachmentRanges.isEmpty else {
|
|
- return
|
|
- }
|
|
-
|
|
- sanitizeAttachments(in: textView, ranges: attachmentRanges)
|
|
-
|
|
guard !mediaPayloads.isEmpty else {
|
|
- handleUnsupportedPaste()
|
|
return
|
|
}
|
|
|
|
+ sanitizeAttachments(in: textView, ranges: attachmentRanges)
|
|
+
|
|
emitImagesAsync(for: mediaPayloads)
|
|
}
|
|
|
|
@@ -651,6 +648,11 @@ class ExpoPasteInputView: ExpoView {
|
|
}
|
|
|
|
private func extractMediaPayload(from attachment: NSTextAttachment, textView: UITextView, range: NSRange) -> MediaPayload? {
|
|
+ // Only accept attachments that carry real image payloads. We intentionally
|
|
+ // do not fall back to `image(forBounds:)` or rendering the text view's
|
|
+ // hierarchy, because system-inserted attachments (e.g. the iOS dictation
|
|
+ // placeholder) draw themselves via those paths and would cause us to
|
|
+ // emit a screenshot of the composer as a "pasted image".
|
|
if let fileWrapperData = attachment.fileWrapper?.regularFileContents,
|
|
let payload = extractMediaPayload(fromData: fileWrapperData) {
|
|
return payload
|
|
@@ -667,20 +669,6 @@ class ExpoPasteInputView: ExpoView {
|
|
return .image(image)
|
|
}
|
|
|
|
- let attachmentBounds = attachment.bounds.size.width > 0 && attachment.bounds.size.height > 0
|
|
- ? attachment.bounds
|
|
- : CGRect(origin: .zero, size: CGSize(width: 128, height: 128))
|
|
-
|
|
- if let image = attachment.image(forBounds: attachmentBounds, textContainer: textView.textContainer, characterIndex: range.location),
|
|
- image.size.width > 0,
|
|
- image.size.height > 0 {
|
|
- return .image(image)
|
|
- }
|
|
-
|
|
- if let renderedImage = renderTextAttachment(in: textView, range: range) {
|
|
- return .image(renderedImage)
|
|
- }
|
|
-
|
|
return nil
|
|
}
|
|
|
|
@@ -701,47 +689,6 @@ class ExpoPasteInputView: ExpoView {
|
|
return .imageData(data)
|
|
}
|
|
|
|
- private func renderTextAttachment(in textView: UITextView, range: NSRange) -> UIImage? {
|
|
- let glyphRange = textView.layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
|
|
- var rect = textView.layoutManager.boundingRect(forGlyphRange: glyphRange, in: textView.textContainer)
|
|
-
|
|
- rect.origin.x += textView.textContainerInset.left - textView.contentOffset.x
|
|
- rect.origin.y += textView.textContainerInset.top - textView.contentOffset.y
|
|
- rect = rect.integral
|
|
-
|
|
- guard rect.width > 1, rect.height > 1 else {
|
|
- return nil
|
|
- }
|
|
-
|
|
- let format = UIGraphicsImageRendererFormat.default()
|
|
- format.scale = textView.window?.screen.scale ?? UIScreen.main.scale
|
|
- format.opaque = false
|
|
-
|
|
- let image = UIGraphicsImageRenderer(size: rect.size, format: format).image { _ in
|
|
- let drawRect = CGRect(
|
|
- origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y),
|
|
- size: textView.bounds.size
|
|
- )
|
|
-
|
|
- if textView.window != nil {
|
|
- textView.drawHierarchy(in: drawRect, afterScreenUpdates: false)
|
|
- } else {
|
|
- guard let context = UIGraphicsGetCurrentContext() else {
|
|
- return
|
|
- }
|
|
-
|
|
- context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
|
|
- textView.layer.render(in: context)
|
|
- }
|
|
- }
|
|
-
|
|
- guard image.size.width > 0, image.size.height > 0 else {
|
|
- return nil
|
|
- }
|
|
-
|
|
- return image
|
|
- }
|
|
-
|
|
@available(iOS 18.0, *)
|
|
private func handleAdaptiveImageGlyphInsertion(_ adaptiveGlyph: NSAdaptiveImageGlyph) -> Bool {
|
|
guard let payload = extractMediaPayload(from: adaptiveGlyph) else {
|