authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-07-30 03:01:56-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-30 03:01:56-07:00
log2652fee49bdb0a7d286124cd2bd318155d4a7931
tree59312eb6d8402f836e2b471dd65f0e76da96388d
parent2f466ffc9ca21b2d536082dd821a1f57e0f354b1
signature Signed by PGP key B5690EEEBB952194

fix(mobile): support dragged images in the composer (#4953)


1 files changed, 38 insertions(+), 2 deletions(-)

apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorView.swift+38-2
......@@ -141,7 +141,7 @@ private final class ComposerTextView: UITextView {
141141 replace(textRange, withText: "")
142142 }
143143
144 private func loadImages(from providers: [NSItemProvider]) {
144 func loadImages(from providers: [NSItemProvider]) {
145145 let group = DispatchGroup()
146146 let lock = NSLock()
147147 var images = [UIImage?](repeating: nil, count: providers.count)
......@@ -282,7 +282,7 @@ private final class ComposerTextView: UITextView {
282282 }
283283}
284284
285public final class T3ComposerEditorView: ExpoView, UITextViewDelegate {
285public final class T3ComposerEditorView: ExpoView, UITextViewDelegate, UITextDropDelegate {
286286 private let textView = ComposerTextView()
287287 private let placeholderLabel = UILabel()
288288 private var value = ""
......@@ -326,6 +326,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate {
326326
327327 clipsToBounds = false
328328 textView.delegate = self
329 textView.textDropDelegate = self
329330 textView.backgroundColor = .clear
330331 textView.textContainerInset = .zero
331332 textView.textContainer.lineFragmentPadding = 0
......@@ -500,6 +501,41 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate {
500501 return true
501502 }
502503
504 public func textDroppableView(
505 _ textDroppableView: UIView & UITextDroppable,
506 proposalForDrop drop: UITextDropRequest
507 ) -> UITextDropProposal {
508 guard droppedImageProviders(in: drop) != nil else {
509 return drop.suggestedProposal
510 }
511
512 // The composer owns image drops so UIKit does not insert NSTextAttachments
513 // that the controlled plain-text value cannot represent.
514 let proposal = UITextDropProposal(operation: .copy)
515 proposal.dropAction = .insert
516 proposal.dropPerformer = .delegate
517 return proposal
518 }
519
520 public func textDroppableView(
521 _ textDroppableView: UIView & UITextDroppable,
522 willPerformDrop drop: UITextDropRequest
523 ) {
524 guard let imageProviders = droppedImageProviders(in: drop) else {
525 return
526 }
527 textView.loadImages(from: imageProviders)
528 }
529
530 private func droppedImageProviders(in drop: UITextDropRequest) -> [NSItemProvider]? {
531 let providers = drop.dropSession.items.map(\.itemProvider)
532 guard !providers.isEmpty,
533 providers.allSatisfy({ $0.canLoadObject(ofClass: UIImage.self) }) else {
534 return nil
535 }
536 return providers
537 }
538
503539 public func textViewDidBeginEditing(_ textView: UITextView) {
504540 onComposerFocus()
505541 }