Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

release_v_2.22.0 #112

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public protocol WysiwygItemProviderHelper {
func isPasteSupported(for itemProvider: NSItemProvider) -> Bool
}

/// Handler for key commands.
public typealias KeyCommandHandler = (WysiwygKeyCommand) -> Bool
/// Handler for paste events.
public typealias PasteHandler = (NSItemProvider) -> Void

Expand All @@ -43,7 +41,7 @@ public struct WysiwygComposerView: View {
private let placeholderColor: Color
private let viewModel: WysiwygComposerViewModelProtocol
private let itemProviderHelper: WysiwygItemProviderHelper?
private let keyCommandHandler: KeyCommandHandler?
private let keyCommands: [WysiwygKeyCommand]?
private let pasteHandler: PasteHandler?

// MARK: - Public
Expand All @@ -57,28 +55,27 @@ public struct WysiwygComposerView: View {
/// See `WysiwygComposerViewModel.swift` for triggerable actions.
/// - itemProviderHelper: A helper to determine if an item can be pasted into the hosting application.
/// If omitted, most non-text paste events will be ignored.
/// - keyCommandHandler: A handler for key commands.
/// If omitted, default behaviour will be applied. See `WysiwygKeyCommand.swift`.
/// - keyCommands: The supported key commands that can be provided with their associated action
/// - pasteHandler: A handler for paste events.
/// If omitted, the composer will try to paste content as raw text.
public init(placeholder: String,
placeholderColor: Color = .init(UIColor.placeholderText),
viewModel: WysiwygComposerViewModelProtocol,
itemProviderHelper: WysiwygItemProviderHelper?,
keyCommandHandler: KeyCommandHandler?,
keyCommands: [WysiwygKeyCommand]?,
pasteHandler: PasteHandler?) {
self.placeholder = placeholder
self.placeholderColor = placeholderColor
self.viewModel = viewModel
self.itemProviderHelper = itemProviderHelper
self.keyCommandHandler = keyCommandHandler
self.keyCommands = keyCommands
self.pasteHandler = pasteHandler
}

public var body: some View {
UITextViewWrapper(viewModel: viewModel,
itemProviderHelper: itemProviderHelper,
keyCommandHandler: keyCommandHandler,
keyCommands: keyCommands,
pasteHandler: pasteHandler)
.accessibilityLabel(placeholder)
.background(placeholderView, alignment: .topLeading)
Expand All @@ -105,7 +102,7 @@ struct UITextViewWrapper: UIViewRepresentable {
/// If omitted, most non-text paste events will be ignored.
private let itemProviderHelper: WysiwygItemProviderHelper?
/// A handler for key commands. If omitted, default behaviour will be applied. See `WysiwygKeyCommand.swift`.
private let keyCommandHandler: KeyCommandHandler?
private let keyCommands: [WysiwygKeyCommand]?
/// A handler for paste events. If omitted, the composer will try to paste content as raw text.
private let pasteHandler: PasteHandler?

Expand All @@ -115,10 +112,10 @@ struct UITextViewWrapper: UIViewRepresentable {

init(viewModel: WysiwygComposerViewModelProtocol,
itemProviderHelper: WysiwygItemProviderHelper?,
keyCommandHandler: KeyCommandHandler?,
keyCommands: [WysiwygKeyCommand]?,
pasteHandler: PasteHandler?) {
self.itemProviderHelper = itemProviderHelper
self.keyCommandHandler = keyCommandHandler
self.keyCommands = keyCommands
self.pasteHandler = pasteHandler
self.viewModel = viewModel
}
Expand Down Expand Up @@ -151,9 +148,8 @@ struct UITextViewWrapper: UIViewRepresentable {
Coordinator(viewModel.replaceText,
viewModel.select,
viewModel.didUpdateText,
viewModel.enter,
itemProviderHelper: itemProviderHelper,
keyCommandHandler: keyCommandHandler,
keyCommands: keyCommands,
pasteHandler: pasteHandler)
}

Expand All @@ -167,25 +163,22 @@ struct UITextViewWrapper: UIViewRepresentable {
var replaceText: (NSRange, String) -> Bool
var select: (NSRange) -> Void
var didUpdateText: () -> Void
var enter: () -> Void
let keyCommands: [WysiwygKeyCommand]?

private let itemProviderHelper: WysiwygItemProviderHelper?
private let keyCommandHandler: KeyCommandHandler?
private let pasteHandler: PasteHandler?

init(_ replaceText: @escaping (NSRange, String) -> Bool,
_ select: @escaping (NSRange) -> Void,
_ didUpdateText: @escaping () -> Void,
_ enter: @escaping () -> Void,
itemProviderHelper: WysiwygItemProviderHelper?,
keyCommandHandler: KeyCommandHandler?,
keyCommands: [WysiwygKeyCommand]?,
pasteHandler: PasteHandler?) {
self.replaceText = replaceText
self.select = select
self.didUpdateText = didUpdateText
self.enter = enter
self.itemProviderHelper = itemProviderHelper
self.keyCommandHandler = keyCommandHandler
self.keyCommands = keyCommands
self.pasteHandler = pasteHandler
}

Expand Down Expand Up @@ -231,32 +224,13 @@ struct UITextViewWrapper: UIViewRepresentable {
guard let itemProviderHelper else {
return false
}

return itemProviderHelper.isPasteSupported(for: itemProvider)
}

func textViewDidReceiveKeyCommand(_ textView: UITextView, keyCommand: WysiwygKeyCommand) {
if !handleKeyCommand(keyCommand) {
processDefault(for: keyCommand)
}
}

func textView(_ textView: UITextView, didReceivePasteWith provider: NSItemProvider) {
pasteHandler?(provider)
}

private func handleKeyCommand(_ keyCommand: WysiwygKeyCommand) -> Bool {
guard let keyCommandHandler else { return false }

return keyCommandHandler(keyCommand)
}

private func processDefault(for keyCommand: WysiwygKeyCommand) {
switch keyCommand {
case .enter, .shiftEnter:
enter()
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,16 @@ protocol WysiwygTextViewDelegate: AnyObject {
/// - Parameter itemProvider: The item provider.
/// - Returns: True if it can be pasted, false otherwise.
func isPasteSupported(for itemProvider: NSItemProvider) -> Bool

/// Notify the delegate that a key command has been received by the text view.
///
/// - Parameters:
/// - textView: Composer text view.
/// - keyCommand: Key command received.
func textViewDidReceiveKeyCommand(_ textView: UITextView, keyCommand: WysiwygKeyCommand)


/// Notify the delegate that a paste event has beeb received by the text view.
///
/// - Parameters:
/// - textView: Composer text view.
/// - provider: Item provider for the paste event.
func textView(_ textView: UITextView, didReceivePasteWith provider: NSItemProvider)

/// The supported key commands for the text view.
var keyCommands: [WysiwygKeyCommand]? { get }
}

/// A markdown protocol used to provide additional context to the text view when displaying mentions through the text attachment provider
Expand Down Expand Up @@ -161,15 +157,14 @@ public class WysiwygTextView: UITextView {
// Enter Key commands support

override public var keyCommands: [UIKeyCommand]? {
WysiwygKeyCommand.allCases.map { UIKeyCommand(input: $0.input,
modifierFlags: $0.modifierFlags,
action: #selector(keyCommandAction)) }
wysiwygDelegate?.keyCommands?.map { UIKeyCommand(input: $0.input,
modifierFlags: $0.modifierFlags,
action: #selector(keyCommandAction)) }
}

@objc func keyCommandAction(sender: UIKeyCommand) {
guard let command = WysiwygKeyCommand.from(sender) else { return }

wysiwygDelegate?.textViewDidReceiveKeyCommand(self, keyCommand: command)

// This needs to be handled here, if the selector was directly added to the WysiwygKeyCommand it would not work properly.
@objc private func keyCommandAction(sender: UIKeyCommand) {
wysiwygDelegate?.keyCommands?.first(where: { $0.input == sender.input && $0.modifierFlags == sender.modifierFlags })?.action()
}

// Paste support
Expand Down
37 changes: 9 additions & 28 deletions Sources/WysiwygComposer/Components/WysiwygKeyCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,14 @@

import UIKit

/// An enum describing key commands that can be handled by the hosting application.
/// This can be done by providing a `KeyCommandHandler` to the `WysiwygComposerView`.
/// If handler is nil, or if the handler returns false, a default behaviour will be applied (see cases description).
public enum WysiwygKeyCommand: CaseIterable {
/// User pressed `enter`. Default behaviour: a line feed is created.
/// Note: in the context of a messaging app, this is usually used to send a message.
case enter
/// User pressed `shift` + `enter`. Default behaviour: a line feed is created.
case shiftEnter

var input: String {
switch self {
case .enter, .shiftEnter:
return "\r"
}
}

var modifierFlags: UIKeyModifierFlags {
switch self {
case .enter:
return []
case .shiftEnter:
return .shift
}
}

static func from(_ keyCommand: UIKeyCommand) -> WysiwygKeyCommand? {
WysiwygKeyCommand.allCases.first(where: { $0.input == keyCommand.input && $0.modifierFlags == keyCommand.modifierFlags })
/// An class that describes key commands that can be handled by the hosting application wth their associated action
public struct WysiwygKeyCommand {
/// A default initialiser for the enter command which is most commonly used
public static func enter(action: @escaping () -> Void) -> WysiwygKeyCommand {
WysiwygKeyCommand(input: "\r", modifierFlags: [], action: action)
}

let input: String
let modifierFlags: UIKeyModifierFlags
let action: () -> Void
}
Loading