forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 💫
1diff --git a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
2index 2164aec4ec1d..d216db6d2927 100644
3--- a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
4+++ b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
5@@ -511,14 +511,17 @@ class ExpoPasteInputView: ExpoView {
6 var attachmentRanges: [NSRange] = []
7 var mediaPayloads: [MediaPayload] = []
8
9+ // Only track ranges for attachments we successfully extract a real payload
10+ // from. Attachments without a payload (e.g. iOS dictation placeholders)
11+ // are left alone — sanitizing them would delete characters the system
12+ // manages itself, and emitting "unsupported" would raise a spurious error.
13 attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, _ in
14 guard let attachment = value as? NSTextAttachment else {
15 return
16 }
17
18- attachmentRanges.append(range)
19-
20 if let payload = self.extractMediaPayload(from: attachment, textView: textView, range: range) {
21+ attachmentRanges.append(range)
22 mediaPayloads.append(payload)
23 }
24 }
25@@ -529,9 +532,8 @@ class ExpoPasteInputView: ExpoView {
26 return
27 }
28
29- attachmentRanges.append(range)
30-
31 if let payload = self.extractMediaPayload(from: adaptiveGlyph) {
32+ attachmentRanges.append(range)
33 mediaPayloads.append(payload)
34 }
35 }
36@@ -539,17 +541,12 @@ class ExpoPasteInputView: ExpoView {
37
38 attachmentRanges = uniqueRanges(attachmentRanges)
39
40- guard !attachmentRanges.isEmpty else {
41- return
42- }
43-
44- sanitizeAttachments(in: textView, ranges: attachmentRanges)
45-
46 guard !mediaPayloads.isEmpty else {
47- handleUnsupportedPaste()
48 return
49 }
50
51+ sanitizeAttachments(in: textView, ranges: attachmentRanges)
52+
53 emitImagesAsync(for: mediaPayloads)
54 }
55
56@@ -651,6 +648,11 @@ class ExpoPasteInputView: ExpoView {
57 }
58
59 private func extractMediaPayload(from attachment: NSTextAttachment, textView: UITextView, range: NSRange) -> MediaPayload? {
60+ // Only accept attachments that carry real image payloads. We intentionally
61+ // do not fall back to `image(forBounds:)` or rendering the text view's
62+ // hierarchy, because system-inserted attachments (e.g. the iOS dictation
63+ // placeholder) draw themselves via those paths and would cause us to
64+ // emit a screenshot of the composer as a "pasted image".
65 if let fileWrapperData = attachment.fileWrapper?.regularFileContents,
66 let payload = extractMediaPayload(fromData: fileWrapperData) {
67 return payload
68@@ -667,20 +669,6 @@ class ExpoPasteInputView: ExpoView {
69 return .image(image)
70 }
71
72- let attachmentBounds = attachment.bounds.size.width > 0 && attachment.bounds.size.height > 0
73- ? attachment.bounds
74- : CGRect(origin: .zero, size: CGSize(width: 128, height: 128))
75-
76- if let image = attachment.image(forBounds: attachmentBounds, textContainer: textView.textContainer, characterIndex: range.location),
77- image.size.width > 0,
78- image.size.height > 0 {
79- return .image(image)
80- }
81-
82- if let renderedImage = renderTextAttachment(in: textView, range: range) {
83- return .image(renderedImage)
84- }
85-
86 return nil
87 }
88
89@@ -701,47 +689,6 @@ class ExpoPasteInputView: ExpoView {
90 return .imageData(data)
91 }
92
93- private func renderTextAttachment(in textView: UITextView, range: NSRange) -> UIImage? {
94- let glyphRange = textView.layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
95- var rect = textView.layoutManager.boundingRect(forGlyphRange: glyphRange, in: textView.textContainer)
96-
97- rect.origin.x += textView.textContainerInset.left - textView.contentOffset.x
98- rect.origin.y += textView.textContainerInset.top - textView.contentOffset.y
99- rect = rect.integral
100-
101- guard rect.width > 1, rect.height > 1 else {
102- return nil
103- }
104-
105- let format = UIGraphicsImageRendererFormat.default()
106- format.scale = textView.window?.screen.scale ?? UIScreen.main.scale
107- format.opaque = false
108-
109- let image = UIGraphicsImageRenderer(size: rect.size, format: format).image { _ in
110- let drawRect = CGRect(
111- origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y),
112- size: textView.bounds.size
113- )
114-
115- if textView.window != nil {
116- textView.drawHierarchy(in: drawRect, afterScreenUpdates: false)
117- } else {
118- guard let context = UIGraphicsGetCurrentContext() else {
119- return
120- }
121-
122- context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
123- textView.layer.render(in: context)
124- }
125- }
126-
127- guard image.size.width > 0, image.size.height > 0 else {
128- return nil
129- }
130-
131- return image
132- }
133-
134 @available(iOS 18.0, *)
135 private func handleAdaptiveImageGlyphInsertion(_ adaptiveGlyph: NSAdaptiveImageGlyph) -> Bool {
136 guard let payload = extractMediaPayload(from: adaptiveGlyph) else {