Split out generic action sheet components

Keep the message specific components separte, so we could re-use the
MenuActionsViewController.
This commit is contained in:
Michael Kirk 2018-07-12 12:47:54 -06:00
parent 093a5eaa68
commit 82fdd5b883
6 changed files with 283 additions and 273 deletions

View File

@ -419,7 +419,8 @@
4C20B2B720CA0034001BAC90 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; };
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */; };
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */; };
4CB5F26720F6E1E2004D1B42 /* MessageActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */; };
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; };
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; };
4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */; };
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
768A1A2B17FC9CD300E00ED8 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 768A1A2A17FC9CD300E00ED8 /* libz.dylib */; };
@ -1083,8 +1084,9 @@
4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = "<group>"; };
4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearchViewController.swift; sourceTree = "<group>"; };
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissableTextField.swift; sourceTree = "<group>"; };
4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = "<group>"; };
4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationConfigurationSyncOperation.swift; sourceTree = "<group>"; };
4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActionsViewController.swift; sourceTree = "<group>"; };
4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuActionsViewController.swift; sourceTree = "<group>"; };
69349DE607F5BA6036C9AC60 /* Pods-SignalShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
748A5CAEDD7C919FC64C6807 /* Pods_SignalTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1691,7 +1693,7 @@
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */,
45F32C1D205718B000A300D5 /* MediaPageViewController.swift */,
454A84032059C787008B8C75 /* MediaTileViewController.swift */,
4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */,
4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */,
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */,
34B3F84F1E8DF1700035BE1A /* NewContactThreadViewController.h */,
34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */,
@ -1989,6 +1991,7 @@
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
4CB5F26820F7D060004D1B42 /* MessageActions.swift */,
);
path = Models;
sourceTree = "<group>";
@ -3264,7 +3267,7 @@
340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */,
34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */,
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
4CB5F26720F6E1E2004D1B42 /* MessageActionsViewController.swift in Sources */,
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
451686AB1F520CDA00AC3D4B /* MultiDeviceProfileKeyUpdateJob.swift in Sources */,
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */,
@ -3350,6 +3353,7 @@
76EB054018170B33006006FC /* AppDelegate.m in Sources */,
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
34BECE2B1F74C12700D7438D /* DebugUIStress.m in Sources */,
340FC8B9204DAC8D007AEB0F /* UpdateGroupViewController.m in Sources */,

View File

@ -0,0 +1,131 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
protocol MessageActionsDelegate: class {
func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem)
func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem)
}
struct MessageActionBuilder {
static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_reply"),
title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"),
subtitle: nil,
block: { [weak delegate] (_) in
delegate?.messageActionsReplyToItem(conversationViewItem)
})
}
static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_copy"),
title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"),
subtitle: nil,
block: { (_) in
conversationViewItem.copyTextAction()
})
}
static func showDetails(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_info"),
title: NSLocalizedString("MESSAGE_ACTION_DETAILS", comment: "Action sheet button title"),
subtitle: nil,
block: { [weak delegate] (_) in
delegate?.messageActionsShowDetailsForItem(conversationViewItem)
})
}
static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_trash"),
title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"),
subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"),
block: { (_) in
conversationViewItem.deleteAction()
})
}
static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_copy"),
title: NSLocalizedString("MESSAGE_ACTION_COPY_MEDIA", comment: "Action sheet button title"),
subtitle: nil,
block: { (_) in
conversationViewItem.copyMediaAction()
})
}
static func saveMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
return MenuAction(image: #imageLiteral(resourceName: "ic_download"),
title: NSLocalizedString("MESSAGE_ACTION_SAVE_MEDIA", comment: "Action sheet button title"),
subtitle: nil,
block: { (_) in
conversationViewItem.saveMediaAction()
})
}
}
extension ConversationViewItem {
@objc
func textActions(delegate: MessageActionsDelegate) -> [MenuAction] {
var actions: [MenuAction] = []
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
actions.append(replyAction)
if self.hasBodyTextActionContent {
let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)
actions.append(copyTextAction)
}
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
actions.append(deleteAction)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
actions.append(showDetailsAction)
return actions
}
@objc
func mediaActions(delegate: MessageActionsDelegate) -> [MenuAction] {
var actions: [MenuAction] = []
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)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
actions.append(showDetailsAction)
return actions
}
@objc
func quotedMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] {
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
return [replyAction, deleteAction, showDetailsAction]
}
@objc
func infoMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] {
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
return [deleteAction]
}
}

View File

@ -132,6 +132,7 @@ typedef enum : NSUInteger {
ConversationViewCellDelegate,
ConversationInputTextViewDelegate,
MessageActionsDelegate,
MenuActionsViewControllerDelegate,
OWSMessageBubbleViewDelegate,
UICollectionViewDelegate,
UICollectionViewDataSource,
@ -1981,13 +1982,6 @@ typedef enum : NSUInteger {
#pragma mark - MessageActionsDelegate
- (void)messageActionsDidHide:(MessageActionsViewController *)messageActionsViewController
{
[[OWSWindowManager sharedManager] hideMessageActionsWindow:messageActionsViewController];
[self updateShouldObserveDBModifications];
}
- (void)messageActionsShowDetailsForItem:(ConversationViewItem *)conversationViewItem
{
[self showDetailViewForViewItem:conversationViewItem];
@ -1998,7 +1992,16 @@ typedef enum : NSUInteger {
[self populateReplyForViewItem:conversationViewItem];
}
- (void)messageActions:(MessageActionsViewController *)messageActionsViewController
#pragma mark - MenuActionsViewControllerDelegate
- (void)menuActionsDidHide:(MenuActionsViewController *)menuActionsViewController
{
[[OWSWindowManager sharedManager] hideMenuActionsWindow];
[self updateShouldObserveDBModifications];
}
- (void)menuActions:(MenuActionsViewController *)menuActionsViewController
isPresentingWithVerticalFocusChange:(CGFloat)verticalChange
{
UIEdgeInsets oldInset = self.collectionView.contentInset;
@ -2026,7 +2029,7 @@ typedef enum : NSUInteger {
self.collectionView.contentInset = newInset;
}
- (void)messageActions:(MessageActionsViewController *)messageActionsViewController
- (void)menuActions:(MenuActionsViewController *)menuActionsViewController
isDismissingWithVerticalFocusChange:(CGFloat)verticalChange
{
UIEdgeInsets oldInset = self.collectionView.contentInset;
@ -2058,36 +2061,36 @@ typedef enum : NSUInteger {
- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem
{
NSArray<MessageAction *> *messageActions = [viewItem mediaActionsWithDelegate:self];
NSArray<MenuAction *> *messageActions = [viewItem mediaActionsWithDelegate:self];
[self presentMessageActions:messageActions withFocusedCell:cell];
}
- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem
{
NSArray<MessageAction *> *messageActions = [viewItem textActionsWithDelegate:self];
NSArray<MenuAction *> *messageActions = [viewItem textActionsWithDelegate:self];
[self presentMessageActions:messageActions withFocusedCell:cell];
}
- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem
{
NSArray<MessageAction *> *messageActions = [viewItem quotedMessageActionsWithDelegate:self];
NSArray<MenuAction *> *messageActions = [viewItem quotedMessageActionsWithDelegate:self];
[self presentMessageActions:messageActions withFocusedCell:cell];
}
- (void)conversationCell:(ConversationViewCell *)cell didLongpressSystemMessageViewItem:(ConversationViewItem *)viewItem
{
NSArray<MessageAction *> *messageActions = [viewItem infoMessageActionsWithDelegate:self];
NSArray<MenuAction *> *messageActions = [viewItem infoMessageActionsWithDelegate:self];
[self presentMessageActions:messageActions withFocusedCell:cell];
}
- (void)presentMessageActions:(NSArray<MessageAction *> *)messageActions withFocusedCell:(ConversationViewCell *)cell
- (void)presentMessageActions:(NSArray<MenuAction *> *)messageActions withFocusedCell:(ConversationViewCell *)cell
{
MessageActionsViewController *messageActionsViewController =
[[MessageActionsViewController alloc] initWithFocusedView:cell actions:messageActions];
MenuActionsViewController *menuActionsViewController =
[[MenuActionsViewController alloc] initWithFocusedView:cell actions:messageActions];
messageActionsViewController.delegate = self;
menuActionsViewController.delegate = self;
[[OWSWindowManager sharedManager] showMessageActionsWindow:messageActionsViewController];
[[OWSWindowManager sharedManager] showMenuActionsWindow:menuActionsViewController];
[self updateShouldObserveDBModifications];
}
@ -4550,7 +4553,7 @@ typedef enum : NSUInteger {
return;
}
if (OWSWindowManager.sharedManager.isPresentingMessageActions) {
if (OWSWindowManager.sharedManager.isPresentingMenuActions) {
self.shouldObserveDBModifications = NO;
return;
}

View File

@ -5,148 +5,41 @@
import Foundation
@objc
protocol MessageActionsDelegate: class {
func messageActionsDidHide(_ messageActionsViewController: MessageActionsViewController)
func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem)
func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem)
func messageActions(_ messageActionsViewController: MessageActionsViewController, isPresentingWithVerticalFocusChange: CGFloat)
func messageActions(_ messageActionsViewController: MessageActionsViewController, isDismissingWithVerticalFocusChange: CGFloat)
}
public class MenuAction: NSObject {
let block: (MenuAction) -> Void
let image: UIImage
let title: String
let subtitle: String?
struct MessageActionBuilder {
static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
return MessageAction(image: #imageLiteral(resourceName: "ic_reply"),
title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"),
subtitle: nil,
block: { [weak delegate] (_) in
delegate?.messageActionsReplyToItem(conversationViewItem)
})
}
static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"),
subtitle: nil,
block: { (_) in
conversationViewItem.copyTextAction()
})
}
static func showDetails(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
return MessageAction(image: #imageLiteral(resourceName: "ic_info"),
title: NSLocalizedString("MESSAGE_ACTION_DETAILS", comment: "Action sheet button title"),
subtitle: nil,
block: { [weak delegate] (_) in
delegate?.messageActionsShowDetailsForItem(conversationViewItem)
})
}
static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
return MessageAction(image: #imageLiteral(resourceName: "ic_trash"),
title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"),
subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"),
block: { (_) in
conversationViewItem.deleteAction()
})
}
static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
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 {
return MessageAction(image: #imageLiteral(resourceName: "ic_download"),
title: NSLocalizedString("MESSAGE_ACTION_SAVE_MEDIA", comment: "Action sheet button title"),
subtitle: nil,
block: { (_) in
conversationViewItem.saveMediaAction()
})
}
}
extension ConversationViewItem {
@objc
func textActions(delegate: MessageActionsDelegate) -> [MessageAction] {
var actions: [MessageAction] = []
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
actions.append(replyAction)
if self.hasBodyTextActionContent {
let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)
actions.append(copyTextAction)
}
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
actions.append(deleteAction)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
actions.append(showDetailsAction)
return actions
}
@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)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
actions.append(showDetailsAction)
return actions
}
@objc
func quotedMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
return [replyAction, deleteAction, showDetailsAction]
}
@objc
func infoMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
return [deleteAction]
public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MenuAction) -> Void) {
self.image = image
self.title = title
self.subtitle = subtitle
self.block = block
}
}
@objc
class MessageActionsViewController: UIViewController, MessageActionSheetDelegate {
protocol MenuActionsViewControllerDelegate: class {
func menuActionsDidHide(_ menuActionsViewController: MenuActionsViewController)
func menuActions(_ menuActionsViewController: MenuActionsViewController, isPresentingWithVerticalFocusChange: CGFloat)
func menuActions(_ menuActionsViewController: MenuActionsViewController, isDismissingWithVerticalFocusChange: CGFloat)
}
@objc
class MenuActionsViewController: UIViewController, MenuActionSheetDelegate {
@objc
weak var delegate: MessageActionsDelegate?
weak var delegate: MenuActionsViewControllerDelegate?
private let focusedView: UIView
private let actionSheetView: MessageActionSheetView
private let actionSheetView: MenuActionSheetView
@objc
required init(focusedView: UIView, actions: [MessageAction]) {
required init(focusedView: UIView, actions: [MenuAction]) {
self.focusedView = focusedView
self.actionSheetView = MessageActionSheetView(actions: actions)
self.actionSheetView = MenuActionSheetView(actions: actions)
super.init(nibName: nil, bundle: nil)
actionSheetView.delegate = self
@ -206,8 +99,6 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
}
private func animatePresentation() {
// TODO first time only?
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
owsFail("\(self.logTag) in \(#function) actionSheetViewVerticalConstraint was unexpectedly nil")
return
@ -255,27 +146,27 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
let offset = -overlap
self.presentationFocusOffset = offset
self.delegate?.messageActions(self, isPresentingWithVerticalFocusChange: offset)
self.delegate?.menuActions(self, isPresentingWithVerticalFocusChange: offset)
},
completion: nil)
}
private func animateDismiss(action: MessageAction?) {
private func animateDismiss(action: MenuAction?) {
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
owsFail("\(self.logTag) in \(#function) actionSheetVerticalConstraint was unexpectedly nil")
self.delegate?.messageActionsDidHide(self)
self.delegate?.menuActionsDidHide(self)
return
}
guard let snapshotView = self.snapshotView else {
owsFail("\(self.logTag) in \(#function) snapshotView was unexpectedly nil")
self.delegate?.messageActionsDidHide(self)
self.delegate?.menuActionsDidHide(self)
return
}
guard let presentationFocusOffset = self.presentationFocusOffset else {
owsFail("\(self.logTag) in \(#function) presentationFocusOffset was unexpectedly nil")
self.delegate?.messageActionsDidHide(self)
self.delegate?.menuActionsDidHide(self)
return
}
@ -293,11 +184,11 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
snapshotView.frame.origin.y -= presentationFocusOffset
// this helps when focused view is above navbars, etc.
snapshotView.alpha = 0
self.delegate?.messageActions(self, isDismissingWithVerticalFocusChange: presentationFocusOffset)
self.delegate?.menuActions(self, isDismissingWithVerticalFocusChange: presentationFocusOffset)
},
completion: { _ in
self.view.isHidden = true
self.delegate?.messageActionsDidHide(self)
self.delegate?.menuActionsDidHide(self)
if let action = action {
action.block(action)
}
@ -316,43 +207,92 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
animateDismiss(action: nil)
}
// MARK: MessageActionSheetDelegate
// MARK: MenuActionSheetDelegate
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction) {
func actionSheet(_ actionSheet: MenuActionSheetView, didSelectAction action: MenuAction) {
animateDismiss(action: action)
}
}
// MARK: ActionView
protocol MenuActionSheetDelegate: class {
func actionSheet(_ actionSheet: MenuActionSheetView, didSelectAction action: MenuAction)
}
@objc
public class MessageAction: NSObject {
let block: (MessageAction) -> Void
let image: UIImage
let title: String
let subtitle: String?
class MenuActionSheetView: UIView, MenuActionViewDelegate {
public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MessageAction) -> Void) {
self.image = image
self.title = title
self.subtitle = subtitle
self.block = block
private let actionStackView: UIStackView
private var actions: [MenuAction]
weak var delegate: MenuActionSheetDelegate?
override var bounds: CGRect {
didSet {
updateMask()
}
}
convenience init(actions: [MenuAction]) {
self.init(frame: CGRect.zero)
actions.forEach { self.addAction($0) }
}
override init(frame: CGRect) {
actionStackView = UIStackView()
actionStackView.axis = .vertical
actionStackView.spacing = CGHairlineWidth()
actions = []
super.init(frame: frame)
backgroundColor = UIColor.ows_light10
addSubview(actionStackView)
actionStackView.autoPinToSuperviewEdges()
self.clipsToBounds = true
// Prevent panning from percolating to the superview, which would
// cause us to dismiss
let panGestureSink = UIPanGestureRecognizer(target: nil, action: nil)
self.addGestureRecognizer(panGestureSink)
}
required init?(coder aDecoder: NSCoder) {
fatalError("not implemented")
}
public func addAction(_ action: MenuAction) {
let actionView = MenuActionView(action: action)
actionView.delegate = self
actions.append(action)
self.actionStackView.addArrangedSubview(actionView)
}
// MARK: MenuActionViewDelegate
func actionView(_ actionView: MenuActionView, didSelectAction action: MenuAction) {
self.delegate?.actionSheet(self, didSelectAction: action)
}
// MARK:
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
}
}
protocol MessageActionSheetDelegate: class {
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction)
protocol MenuActionViewDelegate: class {
func actionView(_ actionView: MenuActionView, didSelectAction action: MenuAction)
}
protocol MessageActionViewDelegate: class {
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction)
}
class MenuActionView: UIButton {
public weak var delegate: MenuActionViewDelegate?
private let action: MenuAction
class MessageActionView: UIButton {
public weak var delegate: MessageActionViewDelegate?
private let action: MessageAction
required init(action: MessageAction) {
required init(action: MenuAction) {
self.action = action
super.init(frame: CGRect.zero)
@ -413,69 +353,3 @@ class MessageActionView: UIButton {
fatalError("not implemented")
}
}
class MessageActionSheetView: UIView, MessageActionViewDelegate {
private let actionStackView: UIStackView
private var actions: [MessageAction]
weak var delegate: MessageActionSheetDelegate?
override var bounds: CGRect {
didSet {
updateMask()
}
}
convenience init(actions: [MessageAction]) {
self.init(frame: CGRect.zero)
actions.forEach { self.addAction($0) }
}
override init(frame: CGRect) {
actionStackView = UIStackView()
actionStackView.axis = .vertical
actionStackView.spacing = CGHairlineWidth()
actions = []
super.init(frame: frame)
backgroundColor = UIColor.ows_light10
addSubview(actionStackView)
actionStackView.autoPinToSuperviewEdges()
self.clipsToBounds = true
// Prevent panning from percolating to the superview, which would
// cause us to dismiss
let panGestureSink = UIPanGestureRecognizer(target: nil, action: nil)
self.addGestureRecognizer(panGestureSink)
}
required init?(coder aDecoder: NSCoder) {
fatalError("not implemented")
}
public func addAction(_ action: MessageAction) {
let actionView = MessageActionView(action: action)
actionView.delegate = self
actions.append(action)
self.actionStackView.addArrangedSubview(actionView)
}
// MARK: MessageActionViewDelegate
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction) {
self.delegate?.actionSheet(self, didSelectAction: action)
}
// MARK:
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
}
}

View File

@ -29,10 +29,10 @@ extern const UIWindowLevel UIWindowLevel_Background;
#pragma mark - Message Actions
@property (nonatomic, readonly) BOOL isPresentingMessageActions;
@property (nonatomic, readonly) BOOL isPresentingMenuActions;
- (void)showMessageActionsWindow:(UIViewController *)messageActionsViewController;
- (void)hideMessageActionsWindow:(UIViewController *)messageActionsViewController;
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController;
- (void)hideMenuActionsWindow;
#pragma mark - Calls

View File

@ -101,8 +101,8 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
@property (nonatomic) UINavigationController *callNavigationController;
// UIWindowLevel_MessageActions
@property (nonatomic) UIWindow *messageActionsWindow;
@property (nonatomic, nullable) UIViewController *messageActionsViewController;
@property (nonatomic) UIWindow *menuActionsWindow;
@property (nonatomic, nullable) UIViewController *menuActionsViewController;
// UIWindowLevel_Background if inactive,
// UIWindowLevel_ScreenBlocking() if active.
@ -157,7 +157,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
self.returnToCallWindow = [self createReturnToCallWindow:rootWindow];
self.callViewWindow = [self createCallViewWindow:rootWindow];
self.messageActionsWindow = [self createMessageActionsWindowWithRoowWindow:rootWindow];
self.menuActionsWindow = [self createMenuActionsWindowWithRoowWindow:rootWindow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangeStatusBarFrame:)
@ -200,7 +200,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
return window;
}
- (UIWindow *)createMessageActionsWindowWithRoowWindow:(UIWindow *)rootWindow
- (UIWindow *)createMenuActionsWindowWithRoowWindow:(UIWindow *)rootWindow
{
UIWindow *window;
if (@available(iOS 11, *)) {
@ -262,25 +262,23 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
#pragma mark - Message Actions
- (BOOL)isPresentingMessageActions
- (BOOL)isPresentingMenuActions
{
return self.messageActionsViewController != nil;
return self.menuActionsViewController != nil;
}
- (void)showMessageActionsWindow:(UIViewController *)messageActionsViewController
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController
{
self.messageActionsViewController = messageActionsViewController;
self.messageActionsWindow.rootViewController = messageActionsViewController;
self.menuActionsViewController = menuActionsViewController;
self.menuActionsWindow.rootViewController = menuActionsViewController;
[self ensureWindowState];
}
- (void)hideMessageActionsWindow:(UIViewController *)messageActionsViewController
- (void)hideMenuActionsWindow
{
OWSAssert(self.messageActionsViewController == messageActionsViewController);
self.messageActionsWindow.rootViewController = nil;
self.messageActionsViewController = nil;
self.menuActionsWindow.rootViewController = nil;
self.menuActionsViewController = nil;
[self ensureWindowState];
}
@ -404,7 +402,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
[self ensureCallViewWindowHidden];
[self ensureMessageActionsWindowHidden];
[self ensureScreenBlockWindowHidden];
} else if (self.messageActionsViewController) {
} else if (self.menuActionsViewController) {
// Show Message Actions
[self ensureRootWindowShown];
@ -503,23 +501,23 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
{
OWSAssertIsOnMainThread();
if (self.messageActionsWindow.hidden) {
if (self.menuActionsWindow.hidden) {
DDLogInfo(@"%@ showing message actions window.", self.logTag);
}
// Do not make key, we want the keyboard to stay popped.
self.messageActionsWindow.hidden = NO;
self.menuActionsWindow.hidden = NO;
}
- (void)ensureMessageActionsWindowHidden
{
OWSAssertIsOnMainThread();
if (!self.messageActionsWindow.hidden) {
if (!self.menuActionsWindow.hidden) {
DDLogInfo(@"%@ hiding message actions window.", self.logTag);
}
self.messageActionsWindow.hidden = YES;
self.menuActionsWindow.hidden = YES;
}
- (void)ensureScreenBlockWindowShown