Skip to content

Commit 72a66b9

Browse files
authored
Merge pull request #754 from anh-ngquang/allow-pasting-images-into-composer
Add support for pasting images into composer text input
2 parents b12468a + d2bc203 commit 72a66b9

File tree

7 files changed

+41
-5
lines changed

7 files changed

+41
-5
lines changed

Sources/StreamChatSwiftUI/ChatChannel/Composer/ComposerTextInputView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct ComposerTextInputView: UIViewRepresentable {
1818
var editable: Bool
1919
var maxMessageLength: Int?
2020
var currentHeight: CGFloat
21+
var onImagePasted: ((UIImage) -> Void)?
2122

2223
func makeUIView(context: Context) -> InputTextView {
2324
let inputTextView: InputTextView
@@ -33,6 +34,7 @@ struct ComposerTextInputView: UIViewRepresentable {
3334
inputTextView.placeholderLabel.text = placeholder
3435
inputTextView.contentInsetAdjustmentBehavior = .never
3536
inputTextView.setContentCompressionResistancePriority(.streamLow, for: .horizontal)
37+
inputTextView.onImagePasted = onImagePasted
3638

3739
if utils.messageListConfig.becomesFirstResponderOnOpen {
3840
inputTextView.becomeFirstResponder()

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ public struct ComposerInputView<Factory: ViewFactory>: View, KeyboardReadable {
372372
placeholder: isInCooldown ? L10n.Composer.Placeholder.slowMode : L10n.Composer.Placeholder.message,
373373
editable: !isInCooldown,
374374
maxMessageLength: maxMessageLength,
375-
currentHeight: textFieldHeight
375+
currentHeight: textFieldHeight,
376+
onImagePasted: viewModel.imagePasted(_:)
376377
)
377378
.accessibilityIdentifier("ComposerTextInputView")
378379
.accessibilityElement(children: .contain)

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,20 @@ open class MessageComposerViewModel: ObservableObject {
510510
addedAssets = images
511511
}
512512

513+
public func imagePasted(_ image: UIImage) {
514+
guard let imageURL = try? image.temporaryLocalFileUrl() else {
515+
log.error("Failed to write image to local temporary file")
516+
return
517+
}
518+
let addedImage = AddedAsset(
519+
image: image,
520+
id: UUID().uuidString,
521+
url: imageURL,
522+
type: .image
523+
)
524+
addedAssets.append(addedImage)
525+
}
526+
513527
public func removeAttachment(with id: String) {
514528
if id.isURL, let url = URL(string: id) {
515529
var urls = [URL]()

Sources/StreamChatSwiftUI/DefaultViewFactory.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,8 @@ extension ViewFactory {
632632
placeholder: String,
633633
editable: Bool,
634634
maxMessageLength: Int?,
635-
currentHeight: CGFloat
635+
currentHeight: CGFloat,
636+
onImagePasted: ((UIImage) -> Void)?
636637
) -> some View {
637638
ComposerTextInputView(
638639
text: text,
@@ -641,7 +642,8 @@ extension ViewFactory {
641642
placeholder: placeholder,
642643
editable: editable,
643644
maxMessageLength: maxMessageLength,
644-
currentHeight: currentHeight
645+
currentHeight: currentHeight,
646+
onImagePasted: onImagePasted
645647
)
646648
}
647649

Sources/StreamChatSwiftUI/Utils/Common/InputTextView.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class InputTextView: UITextView, AccessibilityView {
5252
}
5353
}
5454
}
55+
56+
var onImagePasted: ((UIImage) -> Void)?
5557

5658
override open func didMoveToSuperview() {
5759
super.didMoveToSuperview()
@@ -143,9 +145,22 @@ class InputTextView: UITextView, AccessibilityView {
143145

144146
override open func paste(_ sender: Any?) {
145147
super.paste(sender)
148+
if let pastedImage = UIPasteboard.general.image,
149+
let onImagePasted {
150+
onImagePasted(pastedImage)
151+
return
152+
}
146153
handleTextChange()
147154
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
148155
self?.scrollToBottom()
149156
}
150157
}
158+
159+
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
160+
if action == #selector(paste(_:)) && onImagePasted != nil && UIPasteboard.general.image != nil {
161+
return true
162+
} else {
163+
return super.canPerformAction(action, withSender: sender)
164+
}
165+
}
151166
}

Sources/StreamChatSwiftUI/ViewFactory.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,8 @@ public protocol ViewFactory: AnyObject {
651651
placeholder: String,
652652
editable: Bool,
653653
maxMessageLength: Int?,
654-
currentHeight: CGFloat
654+
currentHeight: CGFloat,
655+
onImagePasted: ((UIImage) -> Void)?
655656
) -> ComposerTextInputViewType
656657

657658
associatedtype TrailingComposerViewType: View

StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,8 @@ class ViewFactory_Tests: StreamChatTestCase {
832832
placeholder: "Send a message",
833833
editable: true,
834834
maxMessageLength: nil,
835-
currentHeight: 40
835+
currentHeight: 40,
836+
onImagePasted: nil
836837
)
837838

838839
// Then

0 commit comments

Comments
 (0)