2018-07-11 00:13:33 +02:00
|
|
|
//
|
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
2018-07-11 01:02:43 +02:00
|
|
|
@objc
|
|
|
|
protocol MessageActionsDelegate: class {
|
|
|
|
func messageActionsDidHide(_ messageActionsViewController: MessageActionsViewController)
|
2018-07-12 05:29:35 +02:00
|
|
|
func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem)
|
|
|
|
func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem)
|
2018-07-12 18:25:23 +02:00
|
|
|
func messageActions(_ messageActionsViewController: MessageActionsViewController, isPresentingWithVerticalFocusChange: CGFloat)
|
|
|
|
func messageActions(_ messageActionsViewController: MessageActionsViewController, isDismissingWithVerticalFocusChange: CGFloat)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct MessageActionBuilder {
|
|
|
|
static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_reply"),
|
2018-07-12 04:56:24 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"),
|
|
|
|
subtitle: nil,
|
2018-07-12 05:29:35 +02:00
|
|
|
block: { [weak delegate] (_) in
|
|
|
|
delegate?.messageActionsReplyToItem(conversationViewItem)
|
2018-07-12 04:56:24 +02:00
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
|
2018-07-12 04:56:24 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"),
|
|
|
|
subtitle: nil,
|
|
|
|
block: { (_) in
|
|
|
|
conversationViewItem.copyTextAction()
|
|
|
|
})
|
|
|
|
}
|
2018-07-12 05:29:35 +02:00
|
|
|
|
|
|
|
static func showDetails(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_info"),
|
2018-07-12 05:29:35 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_DETAILS", comment: "Action sheet button title"),
|
|
|
|
subtitle: nil,
|
|
|
|
block: { [weak delegate] (_) in
|
|
|
|
delegate?.messageActionsShowDetailsForItem(conversationViewItem)
|
|
|
|
})
|
|
|
|
}
|
2018-07-12 04:56:24 +02:00
|
|
|
|
2018-07-12 05:55:04 +02:00
|
|
|
static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_trash"),
|
2018-07-12 04:56:24 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"),
|
|
|
|
subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"),
|
2018-07-12 05:55:04 +02:00
|
|
|
block: { (_) in
|
|
|
|
conversationViewItem.deleteAction()
|
2018-07-12 04:56:24 +02:00
|
|
|
})
|
|
|
|
}
|
2018-07-12 06:11:36 +02:00
|
|
|
|
|
|
|
static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
|
2018-07-12 06:11:36 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_COPY_MEDIA", comment: "Action sheet button title"),
|
|
|
|
subtitle: nil,
|
|
|
|
block: { (_) in
|
|
|
|
conversationViewItem.copyMediaAction()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
static func saveMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
2018-07-12 18:36:20 +02:00
|
|
|
return MessageAction(image: #imageLiteral(resourceName: "ic_download"),
|
2018-07-12 06:11:36 +02:00
|
|
|
title: NSLocalizedString("MESSAGE_ACTION_SAVE_MEDIA", comment: "Action sheet button title"),
|
|
|
|
subtitle: nil,
|
|
|
|
block: { (_) in
|
|
|
|
conversationViewItem.saveMediaAction()
|
|
|
|
})
|
|
|
|
}
|
2018-07-12 05:55:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
extension ConversationViewItem {
|
2018-07-12 04:56:24 +02:00
|
|
|
|
|
|
|
@objc
|
|
|
|
func textActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
|
|
|
var actions: [MessageAction] = []
|
2018-07-12 05:29:35 +02:00
|
|
|
|
2018-07-12 05:47:37 +02:00
|
|
|
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(replyAction)
|
2018-07-12 05:29:35 +02:00
|
|
|
|
|
|
|
if self.hasBodyTextActionContent {
|
|
|
|
let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(copyTextAction)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
2018-07-12 05:55:04 +02:00
|
|
|
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(deleteAction)
|
|
|
|
|
2018-07-12 06:17:53 +02:00
|
|
|
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(showDetailsAction)
|
2018-07-12 05:29:35 +02:00
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
return actions
|
|
|
|
}
|
2018-07-12 06:11:36 +02:00
|
|
|
|
|
|
|
@objc
|
|
|
|
func mediaActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
|
|
|
var actions: [MessageAction] = []
|
|
|
|
|
|
|
|
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(replyAction)
|
|
|
|
|
|
|
|
if self.hasMediaActionContent {
|
|
|
|
let copyMediaAction = MessageActionBuilder.copyMedia(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(copyMediaAction)
|
|
|
|
let saveMediaAction = MessageActionBuilder.saveMedia(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(saveMediaAction)
|
|
|
|
}
|
|
|
|
|
|
|
|
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(deleteAction)
|
|
|
|
|
2018-07-12 06:17:53 +02:00
|
|
|
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
|
|
|
actions.append(showDetailsAction)
|
2018-07-12 06:11:36 +02:00
|
|
|
|
|
|
|
return actions
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func quotedMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
2018-07-12 06:17:53 +02:00
|
|
|
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
|
|
|
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
|
|
|
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
2018-07-12 06:11:36 +02:00
|
|
|
|
2018-07-12 06:17:53 +02:00
|
|
|
return [replyAction, deleteAction, showDetailsAction]
|
2018-07-12 06:11:36 +02:00
|
|
|
}
|
2018-07-12 06:48:48 +02:00
|
|
|
|
|
|
|
@objc
|
|
|
|
func infoMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
|
|
|
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
|
|
|
|
|
|
|
return [deleteAction]
|
|
|
|
}
|
2018-07-11 01:02:43 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 00:13:33 +02:00
|
|
|
@objc
|
2018-07-12 04:56:24 +02:00
|
|
|
class MessageActionsViewController: UIViewController, MessageActionSheetDelegate {
|
2018-07-11 00:13:33 +02:00
|
|
|
|
|
|
|
@objc
|
|
|
|
weak var delegate: MessageActionsDelegate?
|
|
|
|
|
2018-07-11 03:04:11 +02:00
|
|
|
private let focusedView: UIView
|
|
|
|
private let actionSheetView: MessageActionSheetView
|
|
|
|
|
2018-07-11 00:43:20 +02:00
|
|
|
@objc
|
2018-07-11 03:04:11 +02:00
|
|
|
required init(focusedView: UIView, actions: [MessageAction]) {
|
2018-07-11 00:43:20 +02:00
|
|
|
self.focusedView = focusedView
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
self.actionSheetView = MessageActionSheetView(actions: actions)
|
2018-07-11 00:43:20 +02:00
|
|
|
super.init(nibName: nil, bundle: nil)
|
2018-07-12 04:56:24 +02:00
|
|
|
|
|
|
|
actionSheetView.delegate = self
|
2018-07-11 00:43:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:53:02 +02:00
|
|
|
var actionSheetViewVerticalConstraint: NSLayoutConstraint?
|
|
|
|
|
2018-07-11 00:13:33 +02:00
|
|
|
override func loadView() {
|
|
|
|
self.view = UIView()
|
2018-07-11 00:43:20 +02:00
|
|
|
|
2018-07-11 03:04:11 +02:00
|
|
|
view.addSubview(actionSheetView)
|
2018-07-11 18:53:02 +02:00
|
|
|
|
|
|
|
actionSheetView.autoPinWidthToSuperview()
|
2018-07-11 03:04:11 +02:00
|
|
|
actionSheetView.setContentHuggingVerticalHigh()
|
|
|
|
actionSheetView.setCompressionResistanceHigh()
|
2018-07-11 18:53:02 +02:00
|
|
|
self.actionSheetViewVerticalConstraint = actionSheetView.autoPinEdge(.top, to: .bottom, of: self.view)
|
2018-07-11 03:04:11 +02:00
|
|
|
|
2018-07-11 00:13:33 +02:00
|
|
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapBackground))
|
|
|
|
self.view.addGestureRecognizer(tapGesture)
|
|
|
|
}
|
|
|
|
|
2018-07-12 18:25:23 +02:00
|
|
|
var snapshotView: UIView?
|
|
|
|
|
2018-07-11 18:53:02 +02:00
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
super.viewDidAppear(true)
|
|
|
|
|
|
|
|
// TODO first time only?
|
|
|
|
|
|
|
|
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) actionSheetViewVerticalConstraint was unexpectedly nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-12 18:25:23 +02:00
|
|
|
guard let focusedViewSuperview = focusedView.superview else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) focusedViewSuperview was unexpectedly nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:53:02 +02:00
|
|
|
// darken background
|
2018-07-12 18:25:23 +02:00
|
|
|
guard let snapshotView = addSnapshotFocusedView() else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) snapshotView was unexpectedly nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
self.snapshotView = snapshotView
|
|
|
|
snapshotView.superview?.layoutIfNeeded()
|
|
|
|
|
2018-07-11 18:53:02 +02:00
|
|
|
let backgroundDuration: TimeInterval = 0.1
|
|
|
|
UIView.animate(withDuration: backgroundDuration) {
|
|
|
|
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
|
|
|
|
}
|
|
|
|
|
2018-07-12 18:25:23 +02:00
|
|
|
// layout actionsheet and snapshot view with initial frame
|
|
|
|
self.view.layoutIfNeeded()
|
|
|
|
|
|
|
|
let oldFocusFrame = self.view.convert(focusedView.frame, from: focusedViewSuperview)
|
2018-07-11 18:53:02 +02:00
|
|
|
NSLayoutConstraint.deactivate([actionSheetViewVerticalConstraint])
|
|
|
|
self.actionSheetViewVerticalConstraint = self.actionSheetView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
UIView.animate(withDuration: 0.3,
|
|
|
|
delay: backgroundDuration,
|
|
|
|
options: .curveEaseOut,
|
|
|
|
animations: {
|
2018-07-12 18:25:23 +02:00
|
|
|
self.actionSheetView.superview?.layoutIfNeeded()
|
|
|
|
let newSheetFrame = self.actionSheetView.frame
|
|
|
|
|
|
|
|
var newFocusFrame = oldFocusFrame
|
|
|
|
|
|
|
|
// Position focused item just over the action sheet.
|
|
|
|
let padding: CGFloat = 10
|
|
|
|
let overlap: CGFloat = (oldFocusFrame.maxY + padding) - newSheetFrame.minY
|
|
|
|
newFocusFrame.origin.y = oldFocusFrame.origin.y - overlap
|
|
|
|
|
|
|
|
snapshotView.frame = newFocusFrame
|
|
|
|
|
|
|
|
let offset = -overlap
|
|
|
|
self.presentationFocusOffset = offset
|
|
|
|
self.delegate?.messageActions(self, isPresentingWithVerticalFocusChange: offset)
|
|
|
|
},
|
2018-07-11 18:53:02 +02:00
|
|
|
completion: nil)
|
|
|
|
}
|
|
|
|
|
2018-07-12 18:25:23 +02:00
|
|
|
var presentationFocusOffset: CGFloat?
|
|
|
|
|
|
|
|
private func addSnapshotFocusedView() -> UIView? {
|
2018-07-11 00:43:20 +02:00
|
|
|
guard let snapshotView = self.focusedView.snapshotView(afterScreenUpdates: false) else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) snapshotView was unexpectedly nil")
|
2018-07-12 18:25:23 +02:00
|
|
|
return nil
|
2018-07-11 00:43:20 +02:00
|
|
|
}
|
|
|
|
view.addSubview(snapshotView)
|
|
|
|
|
|
|
|
guard let focusedViewSuperview = focusedView.superview else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) focusedViewSuperview was unexpectedly nil")
|
2018-07-12 18:25:23 +02:00
|
|
|
return nil
|
2018-07-11 00:43:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
let convertedFrame = view.convert(focusedView.frame, from: focusedViewSuperview)
|
|
|
|
snapshotView.frame = convertedFrame
|
2018-07-12 18:25:23 +02:00
|
|
|
|
|
|
|
return snapshotView
|
2018-07-11 00:43:20 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 00:13:33 +02:00
|
|
|
@objc
|
|
|
|
func didTapBackground() {
|
2018-07-12 05:29:35 +02:00
|
|
|
animateDismiss(action: nil)
|
2018-07-11 18:53:02 +02:00
|
|
|
}
|
|
|
|
|
2018-07-12 05:29:35 +02:00
|
|
|
func animateDismiss(action: MessageAction?) {
|
2018-07-12 18:25:23 +02:00
|
|
|
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
|
2018-07-11 18:53:02 +02:00
|
|
|
owsFail("\(self.logTag) in \(#function) actionSheetVerticalConstraint was unexpectedly nil")
|
2018-07-12 18:25:23 +02:00
|
|
|
self.delegate?.messageActionsDidHide(self)
|
|
|
|
return
|
2018-07-11 18:53:02 +02:00
|
|
|
}
|
|
|
|
|
2018-07-12 18:25:23 +02:00
|
|
|
guard let snapshotView = self.snapshotView else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) snapshotView was unexpectedly nil")
|
|
|
|
self.delegate?.messageActionsDidHide(self)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let presentationFocusOffset = self.presentationFocusOffset else {
|
|
|
|
owsFail("\(self.logTag) in \(#function) presentationFocusOffset was unexpectedly nil")
|
|
|
|
self.delegate?.messageActionsDidHide(self)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
self.actionSheetView.superview?.layoutIfNeeded()
|
|
|
|
NSLayoutConstraint.deactivate([actionSheetViewVerticalConstraint])
|
|
|
|
|
2018-07-12 05:29:35 +02:00
|
|
|
let dismissDuration: TimeInterval = 0.2
|
2018-07-11 18:53:02 +02:00
|
|
|
self.actionSheetViewVerticalConstraint = self.actionSheetView.autoPinEdge(.top, to: .bottom, of: self.view)
|
2018-07-12 05:29:35 +02:00
|
|
|
UIView.animate(withDuration: dismissDuration,
|
2018-07-11 18:53:02 +02:00
|
|
|
delay: 0,
|
|
|
|
options: .curveEaseOut,
|
|
|
|
animations: {
|
|
|
|
self.view.backgroundColor = UIColor.clear
|
|
|
|
self.actionSheetView.superview?.layoutIfNeeded()
|
2018-07-12 18:25:23 +02:00
|
|
|
snapshotView.frame.origin.y -= presentationFocusOffset
|
|
|
|
// this helps when focused view is above navbars, etc.
|
|
|
|
snapshotView.alpha = 0
|
|
|
|
self.delegate?.messageActions(self, isDismissingWithVerticalFocusChange: presentationFocusOffset)
|
2018-07-11 18:53:02 +02:00
|
|
|
},
|
|
|
|
completion: { _ in
|
2018-07-12 05:29:35 +02:00
|
|
|
self.view.isHidden = true
|
2018-07-11 18:53:02 +02:00
|
|
|
self.delegate?.messageActionsDidHide(self)
|
2018-07-12 05:29:35 +02:00
|
|
|
if let action = action {
|
|
|
|
action.block(action)
|
|
|
|
}
|
2018-07-11 18:53:02 +02:00
|
|
|
})
|
2018-07-11 00:13:33 +02:00
|
|
|
}
|
2018-07-12 04:56:24 +02:00
|
|
|
|
|
|
|
// MARK: MessageActionSheetDelegate
|
|
|
|
|
2018-07-12 05:29:35 +02:00
|
|
|
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction) {
|
|
|
|
animateDismiss(action: action)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
2018-07-11 00:13:33 +02:00
|
|
|
}
|
2018-07-11 03:04:11 +02:00
|
|
|
|
|
|
|
// MARK: ActionView
|
|
|
|
|
|
|
|
@objc
|
2018-07-12 04:56:24 +02:00
|
|
|
public class MessageAction: NSObject {
|
2018-07-11 03:04:11 +02:00
|
|
|
let block: (MessageAction) -> Void
|
|
|
|
let image: UIImage
|
|
|
|
let title: String
|
|
|
|
let subtitle: String?
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MessageAction) -> Void) {
|
2018-07-11 03:04:11 +02:00
|
|
|
self.image = image
|
|
|
|
self.title = title
|
|
|
|
self.subtitle = subtitle
|
2018-07-12 04:56:24 +02:00
|
|
|
self.block = block
|
2018-07-11 03:04:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
protocol MessageActionSheetDelegate: class {
|
2018-07-12 05:29:35 +02:00
|
|
|
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protocol MessageActionViewDelegate: class {
|
2018-07-12 05:29:35 +02:00
|
|
|
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 03:04:11 +02:00
|
|
|
class MessageActionView: UIView {
|
2018-07-12 04:56:24 +02:00
|
|
|
public weak var delegate: MessageActionViewDelegate?
|
|
|
|
private let action: MessageAction
|
2018-07-11 03:04:11 +02:00
|
|
|
|
|
|
|
required init(action: MessageAction) {
|
|
|
|
self.action = action
|
|
|
|
|
|
|
|
super.init(frame: CGRect.zero)
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
isUserInteractionEnabled = true
|
2018-07-11 18:18:45 +02:00
|
|
|
backgroundColor = .white
|
|
|
|
|
2018-07-11 03:04:11 +02:00
|
|
|
let imageView = UIImageView(image: action.image)
|
|
|
|
let imageWidth: CGFloat = 24
|
|
|
|
imageView.autoSetDimensions(to: CGSize(width: imageWidth, height: imageWidth))
|
|
|
|
|
|
|
|
let titleLabel = UILabel()
|
|
|
|
titleLabel.font = UIFont.ows_dynamicTypeBody
|
|
|
|
titleLabel.textColor = UIColor.ows_light90
|
|
|
|
titleLabel.text = action.title
|
|
|
|
|
|
|
|
let subtitleLabel = UILabel()
|
|
|
|
subtitleLabel.font = UIFont.ows_dynamicTypeSubheadline
|
|
|
|
subtitleLabel.textColor = UIColor.ows_light60
|
|
|
|
subtitleLabel.text = action.subtitle
|
|
|
|
|
|
|
|
let textColumn = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
|
|
|
|
textColumn.axis = .vertical
|
|
|
|
textColumn.alignment = .leading
|
|
|
|
|
|
|
|
let contentRow = UIStackView(arrangedSubviews: [imageView, textColumn])
|
|
|
|
contentRow.axis = .horizontal
|
|
|
|
contentRow.alignment = .center
|
|
|
|
contentRow.spacing = 12
|
|
|
|
contentRow.isLayoutMarginsRelativeArrangement = true
|
2018-07-11 18:18:45 +02:00
|
|
|
contentRow.layoutMargins = UIEdgeInsets(top: 7, left: 16, bottom: 7, right: 16)
|
2018-07-11 03:04:11 +02:00
|
|
|
|
|
|
|
self.addSubview(contentRow)
|
|
|
|
contentRow.autoPinToSuperviewMargins()
|
2018-07-11 18:31:01 +02:00
|
|
|
contentRow.autoSetDimension(.height, toSize: 56, relation: .greaterThanOrEqual)
|
2018-07-12 04:56:24 +02:00
|
|
|
|
|
|
|
// TODO better mimic button
|
|
|
|
// - style with touch down
|
|
|
|
// - slide from one to the next
|
|
|
|
// - accessability hints
|
|
|
|
// - UIControl?
|
|
|
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didPress))
|
|
|
|
self.addGestureRecognizer(tapGesture)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
func didPress(event: Any) {
|
|
|
|
Logger.debug("\(logTag) in \(#function)")
|
2018-07-12 05:29:35 +02:00
|
|
|
self.delegate?.actionView(self, didSelectAction: action)
|
2018-07-11 03:04:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("not implemented")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
class MessageActionSheetView: UIView, MessageActionViewDelegate {
|
2018-07-11 03:04:11 +02:00
|
|
|
|
|
|
|
private let actionStackView: UIStackView
|
|
|
|
private var actions: [MessageAction]
|
2018-07-12 04:56:24 +02:00
|
|
|
weak var delegate: MessageActionSheetDelegate?
|
2018-07-11 03:04:11 +02:00
|
|
|
|
2018-07-11 18:18:45 +02:00
|
|
|
override var bounds: CGRect {
|
|
|
|
didSet {
|
|
|
|
updateMask()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 03:04:11 +02:00
|
|
|
convenience init(actions: [MessageAction]) {
|
|
|
|
self.init(frame: CGRect.zero)
|
|
|
|
actions.forEach { self.addAction($0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
override init(frame: CGRect) {
|
|
|
|
actionStackView = UIStackView()
|
2018-07-11 18:18:45 +02:00
|
|
|
actionStackView.axis = .vertical
|
|
|
|
actionStackView.spacing = CGHairlineWidth()
|
|
|
|
|
|
|
|
actions = []
|
2018-07-11 03:04:11 +02:00
|
|
|
|
|
|
|
super.init(frame: frame)
|
|
|
|
|
2018-07-11 18:18:45 +02:00
|
|
|
backgroundColor = UIColor.ows_light10
|
2018-07-11 03:04:11 +02:00
|
|
|
addSubview(actionStackView)
|
|
|
|
actionStackView.autoPinToSuperviewEdges()
|
|
|
|
|
2018-07-11 18:18:45 +02:00
|
|
|
self.clipsToBounds = true
|
2018-07-11 03:04:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
public func addAction(_ action: MessageAction) {
|
|
|
|
let actionView = MessageActionView(action: action)
|
2018-07-12 04:56:24 +02:00
|
|
|
actionView.delegate = self
|
2018-07-11 03:04:11 +02:00
|
|
|
actions.append(action)
|
|
|
|
self.actionStackView.addArrangedSubview(actionView)
|
|
|
|
}
|
|
|
|
|
2018-07-12 04:56:24 +02:00
|
|
|
// MARK: MessageActionViewDelegate
|
|
|
|
|
2018-07-12 05:29:35 +02:00
|
|
|
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction) {
|
|
|
|
self.delegate?.actionSheet(self, didSelectAction: action)
|
2018-07-12 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK:
|
2018-07-11 18:18:45 +02:00
|
|
|
private func updateMask() {
|
|
|
|
let cornerRadius: CGFloat = 16
|
|
|
|
let path: UIBezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
|
|
|
|
let mask = CAShapeLayer()
|
|
|
|
mask.path = path.cgPath
|
|
|
|
self.layer.mask = mask
|
2018-07-11 03:04:11 +02:00
|
|
|
}
|
|
|
|
}
|