Skip to content

Commit ab6869c

Browse files
committed
Add support for pasting images into composer text input
1 parent 5a7e399 commit ab6869c

File tree

8 files changed

+44
-5
lines changed

8 files changed

+44
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
33

44
# Upcoming
55

6+
### ✅ Added
7+
- Add support for pasting images into composer text input
8+
69
### 🐞 Fixed
710
- Fix visibility of tabbar when reactions are shown [#750](https://github.com/GetStream/stream-chat-swiftui/pull/750)
811
### 🔄 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
@@ -364,7 +364,8 @@ public struct ComposerInputView<Factory: ViewFactory>: View, KeyboardReadable {
364364
placeholder: isInCooldown ? L10n.Composer.Placeholder.slowMode : L10n.Composer.Placeholder.message,
365365
editable: !isInCooldown,
366366
maxMessageLength: maxMessageLength,
367-
currentHeight: textFieldHeight
367+
currentHeight: textFieldHeight,
368+
onImagePasted: viewModel.imagePasted(_:)
368369
)
369370
.accessibilityIdentifier("ComposerTextInputView")
370371
.accessibilityElement(children: .contain)

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,20 @@ open class MessageComposerViewModel: ObservableObject {
392392
addedAssets = images
393393
}
394394

395+
public func imagePasted(_ image: UIImage) {
396+
guard let imageURL = try? image.temporaryLocalFileUrl() else {
397+
log.error("Failed to write image to local temporary file")
398+
return
399+
}
400+
let addedImage = AddedAsset(
401+
image: image,
402+
id: UUID().uuidString,
403+
url: imageURL,
404+
type: .image
405+
)
406+
addedAssets.append(addedImage)
407+
}
408+
395409
public func removeAttachment(with id: String) {
396410
if id.isURL, let url = URL(string: id) {
397411
var urls = [URL]()

Sources/StreamChatSwiftUI/DefaultViewFactory.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,8 @@ extension ViewFactory {
624624
placeholder: String,
625625
editable: Bool,
626626
maxMessageLength: Int?,
627-
currentHeight: CGFloat
627+
currentHeight: CGFloat,
628+
onImagePasted: ((UIImage) -> Void)?
628629
) -> some View {
629630
ComposerTextInputView(
630631
text: text,
@@ -633,7 +634,8 @@ extension ViewFactory {
633634
placeholder: placeholder,
634635
editable: editable,
635636
maxMessageLength: maxMessageLength,
636-
currentHeight: currentHeight
637+
currentHeight: currentHeight,
638+
onImagePasted: onImagePasted
637639
)
638640
}
639641

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
@@ -637,7 +637,8 @@ public protocol ViewFactory: AnyObject {
637637
placeholder: String,
638638
editable: Bool,
639639
maxMessageLength: Int?,
640-
currentHeight: CGFloat
640+
currentHeight: CGFloat,
641+
onImagePasted: ((UIImage) -> Void)?
641642
) -> ComposerTextInputViewType
642643

643644
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)