Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom menu presentation #584

Merged
merged 4 commits into from
Jul 4, 2019
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 @@ -153,6 +153,7 @@ open class BaseMessageCollectionViewCell<BubbleViewType>: UICollectionViewCell,

public private (set) lazy var longPressGestureRecognizer: UILongPressGestureRecognizer = {
let longpressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(BaseMessageCollectionViewCell.bubbleLongPressed(_:)))
longpressGestureRecognizer.cancelsTouchesInView = true
longpressGestureRecognizer.delegate = self
return longpressGestureRecognizer
}()
Expand Down Expand Up @@ -189,6 +190,10 @@ open class BaseMessageCollectionViewCell<BubbleViewType>: UICollectionViewCell,
return gestureRecognizer === self.longPressGestureRecognizer
}

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return gestureRecognizer === self.longPressGestureRecognizer && otherGestureRecognizer is UILongPressGestureRecognizer
}

open override func prepareForReuse() {
super.prepareForReuse()
self.removeAccessoryView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,40 @@

import Chatto

public final class TextMessageMenuItemPresenter: ChatItemMenuPresenterProtocol {
public protocol TextMessageMenuItemPresenterProtocol {
func shouldShowMenu(for text: String, item: MessageModelProtocol) -> Bool
func canPerformMenuControllerAction(_ action: Selector, for text: String, item: MessageModelProtocol) -> Bool
func performMenuControllerAction(_ action: Selector, for text: String, item: MessageModelProtocol)
}

public final class TextMessageMenuItemPresenter: TextMessageMenuItemPresenterProtocol {

// MARK: - Private properties

private let pasteboard: UIPasteboard
private let textProvider: () -> String

// MARK: - Instantiation

public init(pasteboard: UIPasteboard = .general, textProvider: @escaping () -> String) {
public init(pasteboard: UIPasteboard = .general) {
self.pasteboard = pasteboard
self.textProvider = textProvider
}

// MARK: - ChatItemMenuPresenterProtocol
// MARK: - TextMessageMenuItemPresenterProtocol

public func shouldShowMenu() -> Bool {
public func shouldShowMenu(for text: String, item: MessageModelProtocol) -> Bool {
return true
}

public func canPerformMenuControllerAction(_ action: Selector) -> Bool {
public func canPerformMenuControllerAction(_ action: Selector, for text: String, item: MessageModelProtocol) -> Bool {
return action == .copy
}

public func performMenuControllerAction(_ action: Selector) {
public func performMenuControllerAction(_ action: Selector, for text: String, item: MessageModelProtocol) {
guard action == .copy else {
assertionFailure("Unexpected action")
return
}
self.pasteboard.string = self.textProvider()
self.pasteboard.string = text
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,27 @@ open class TextMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
public typealias ModelT = ViewModelBuilderT.ModelT
public typealias ViewModelT = ViewModelBuilderT.ViewModelT

public init (
messageModel: ModelT,
viewModelBuilder: ViewModelBuilderT,
interactionHandler: InteractionHandlerT?,
sizingCell: TextMessageCollectionViewCell,
baseCellStyle: BaseMessageCollectionViewCellStyleProtocol,
textCellStyle: TextMessageCollectionViewCellStyleProtocol,
layoutCache: NSCache<AnyObject, AnyObject>) {
self.layoutCache = layoutCache
self.textCellStyle = textCellStyle
super.init(
messageModel: messageModel,
viewModelBuilder: viewModelBuilder,
interactionHandler: interactionHandler,
sizingCell: sizingCell,
cellStyle: baseCellStyle
)
self.menuPresenter = TextMessageMenuItemPresenter { [weak self] in self?.messageViewModel.text ?? "" }
public init (messageModel: ModelT,
viewModelBuilder: ViewModelBuilderT,
interactionHandler: InteractionHandlerT?,
sizingCell: TextMessageCollectionViewCell,
baseCellStyle: BaseMessageCollectionViewCellStyleProtocol,
textCellStyle: TextMessageCollectionViewCellStyleProtocol,
layoutCache: NSCache<AnyObject, AnyObject>,
menuPresenter: TextMessageMenuItemPresenterProtocol?) {
self.layoutCache = layoutCache
self.textCellStyle = textCellStyle
self.menuPresenter = menuPresenter
super.init(
messageModel: messageModel,
viewModelBuilder: viewModelBuilder,
interactionHandler: interactionHandler,
sizingCell: sizingCell,
cellStyle: baseCellStyle
)
}

private var menuPresenter: ChatItemMenuPresenterProtocol?
private let menuPresenter: TextMessageMenuItemPresenterProtocol?
let layoutCache: NSCache<AnyObject, AnyObject>
let textCellStyle: TextMessageCollectionViewCellStyleProtocol

Expand Down Expand Up @@ -109,14 +109,14 @@ open class TextMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
}

open override func canShowMenu() -> Bool {
return self.menuPresenter?.shouldShowMenu() ?? false
return self.menuPresenter?.shouldShowMenu(for: self.messageViewModel.text, item: self.messageModel) ?? false
}

open override func canPerformMenuControllerAction(_ action: Selector) -> Bool {
return self.menuPresenter?.canPerformMenuControllerAction(action) ?? false
return self.menuPresenter?.canPerformMenuControllerAction(action, for: self.messageViewModel.text, item: self.messageModel) ?? false
}

open override func performMenuControllerAction(_ action: Selector) {
self.menuPresenter?.performMenuControllerAction(action)
self.menuPresenter?.performMenuControllerAction(action, for: self.messageViewModel.text, item: self.messageModel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,29 @@
import Foundation
import Chatto

open class TextMessagePresenterBuilder<ViewModelBuilderT, InteractionHandlerT>
: ChatItemPresenterBuilderProtocol where
open class TextMessagePresenterBuilder<ViewModelBuilderT, InteractionHandlerT>: ChatItemPresenterBuilderProtocol where
ViewModelBuilderT: ViewModelBuilderProtocol,
ViewModelBuilderT.ViewModelT: TextMessageViewModelProtocol,
InteractionHandlerT: BaseMessageInteractionHandlerProtocol,
InteractionHandlerT.ViewModelT == ViewModelBuilderT.ViewModelT {

typealias ViewModelT = ViewModelBuilderT.ViewModelT
typealias ModelT = ViewModelBuilderT.ModelT

public init(
viewModelBuilder: ViewModelBuilderT,
interactionHandler: InteractionHandlerT? = nil) {
self.viewModelBuilder = viewModelBuilder
self.interactionHandler = interactionHandler
public init(viewModelBuilder: ViewModelBuilderT,
interactionHandler: InteractionHandlerT? = nil,
menuPresenter: TextMessageMenuItemPresenterProtocol? = TextMessageMenuItemPresenter()) {
self.viewModelBuilder = viewModelBuilder
self.interactionHandler = interactionHandler
self.menuPresenter = menuPresenter
}

let viewModelBuilder: ViewModelBuilderT
let interactionHandler: InteractionHandlerT?
let layoutCache = NSCache<AnyObject, AnyObject>()
private let viewModelBuilder: ViewModelBuilderT
private let interactionHandler: InteractionHandlerT?
private let menuPresenter: TextMessageMenuItemPresenterProtocol?
private let layoutCache = NSCache<AnyObject, AnyObject>()

lazy var sizingCell: TextMessageCollectionViewCell = {
private lazy var sizingCell: TextMessageCollectionViewCell = {
var cell: TextMessageCollectionViewCell?
if Thread.isMainThread {
cell = TextMessageCollectionViewCell.sizingCell()
Expand All @@ -66,13 +68,16 @@ open class TextMessagePresenterBuilder<ViewModelBuilderT, InteractionHandlerT>
}

open func createPresenterWithChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol {
return self.createPresenter(withChatItem: chatItem,
viewModelBuilder: self.viewModelBuilder,
interactionHandler: self.interactionHandler,
sizingCell: self.sizingCell,
baseCellStyle: self.baseMessageStyle,
textCellStyle: self.textCellStyle,
layoutCache: self.layoutCache)
return self.createPresenter(
withChatItem: chatItem,
viewModelBuilder: self.viewModelBuilder,
interactionHandler: self.interactionHandler,
sizingCell: self.sizingCell,
baseCellStyle: self.baseMessageStyle,
textCellStyle: self.textCellStyle,
layoutCache: self.layoutCache,
menuPresenter: self.menuPresenter
)
}

open func createPresenter(withChatItem chatItem: ChatItemProtocol,
Expand All @@ -81,7 +86,8 @@ open class TextMessagePresenterBuilder<ViewModelBuilderT, InteractionHandlerT>
sizingCell: TextMessageCollectionViewCell,
baseCellStyle: BaseMessageCollectionViewCellStyleProtocol,
textCellStyle: TextMessageCollectionViewCellStyleProtocol,
layoutCache: NSCache<AnyObject, AnyObject>) -> TextMessagePresenter<ViewModelBuilderT, InteractionHandlerT> {
layoutCache: NSCache<AnyObject, AnyObject>,
menuPresenter: TextMessageMenuItemPresenterProtocol?) -> TextMessagePresenter<ViewModelBuilderT, InteractionHandlerT> {
assert(self.canHandleChatItem(chatItem))
return TextMessagePresenter<ViewModelBuilderT, InteractionHandlerT>(
messageModel: chatItem as! ModelT,
Expand All @@ -90,7 +96,8 @@ open class TextMessagePresenterBuilder<ViewModelBuilderT, InteractionHandlerT>
sizingCell: sizingCell,
baseCellStyle: baseCellStyle,
textCellStyle: textCellStyle,
layoutCache: layoutCache
layoutCache: layoutCache,
menuPresenter: menuPresenter
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ class TextMessagePresenterTests: XCTestCase, UICollectionViewDataSource {
let baseStyle = BaseMessageCollectionViewCellDefaultStyle()
let messageModel = MessageModel(uid: "uid", senderId: "senderId", type: "text-message", isIncoming: true, date: NSDate() as Date, status: .success)
let textMessageModel = TextMessageModel(messageModel: messageModel, text: "Some text")
self.presenter = TextMessagePresenter(messageModel: textMessageModel, viewModelBuilder: viewModelBuilder, interactionHandler: TextMessageTestHandler(), sizingCell: sizingCell, baseCellStyle: baseStyle, textCellStyle: textStyle, layoutCache: NSCache())
self.presenter = TextMessagePresenter(
messageModel: textMessageModel,
viewModelBuilder: viewModelBuilder,
interactionHandler: TextMessageTestHandler(),
sizingCell: sizingCell,
baseCellStyle: baseStyle,
textCellStyle: textStyle,
layoutCache: NSCache(),
menuPresenter: TextMessageMenuItemPresenter()
)
}

func testThat_RegistersAndDequeuesCells() {
Expand Down