From 0c4cae133ba6e35af1945c4c4f07a15e2b5aeb32 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 11 Jul 2018 20:56:24 -0600 Subject: [PATCH] milestone: route one real action (copy text) --- Signal.xcodeproj/project.pbxproj | 6 +- .../Cells/ConversationViewCell.h | 9 +- .../ConversationView/Cells/OWSMessageCell.m | 271 +++++++++--------- .../ConversationViewController.m | 16 +- .../MessageActionsViewController.swift | 168 ++++++++--- 5 files changed, 283 insertions(+), 187 deletions(-) rename {SignalMessaging => Signal/src}/ViewControllers/MessageActionsViewController.swift (58%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 70e42084b..219d691b9 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -419,8 +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 */; }; 4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */; }; - 4CFF4C0B20F56104005DA313 /* MessageActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */; }; 70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; }; 768A1A2B17FC9CD300E00ED8 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 768A1A2A17FC9CD300E00ED8 /* libz.dylib */; }; 76C87F19181EFCE600C4ACAB /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */; }; @@ -1691,6 +1691,7 @@ 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */, 45F32C1D205718B000A300D5 /* MediaPageViewController.swift */, 454A84032059C787008B8C75 /* MediaTileViewController.swift */, + 4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */, 34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */, 34B3F84F1E8DF1700035BE1A /* NewContactThreadViewController.h */, 34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */, @@ -1888,7 +1889,6 @@ 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */, 34D99C8A1F27B13B00D284D6 /* OWSViewController.h */, 34D99C8B1F27B13B00D284D6 /* OWSViewController.m */, - 4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */, 45A60E7220AC674100FB1ABF /* ReturnToCallViewController.swift */, ); path = ViewControllers; @@ -3129,7 +3129,6 @@ 344D6CEC20069E070042AF96 /* NewNonContactConversationViewController.m in Sources */, 346129FB1FD5F31400532771 /* OWS101ExistingUsersBlockOnIdentityChange.m in Sources */, 344F248D2007CCD600CFB4F4 /* DisplayableText.swift in Sources */, - 4CFF4C0B20F56104005DA313 /* MessageActionsViewController.swift in Sources */, 450998651FD8A34D00D89EB3 /* DeviceSleepManager.swift in Sources */, 3466087220E550F400AFFE73 /* ConversationStyle.swift in Sources */, 3478506B1FD9B78A007B8332 /* NoopCallMessageHandler.swift in Sources */, @@ -3265,6 +3264,7 @@ 340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */, 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */, 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, + 4CB5F26720F6E1E2004D1B42 /* MessageActionsViewController.swift in Sources */, 451686AB1F520CDA00AC3D4B /* MultiDeviceProfileKeyUpdateJob.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h index b0edb8c4d..40d725cee 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h @@ -22,13 +22,16 @@ NS_ASSUME_NONNULL_BEGIN @protocol ConversationViewCellDelegate -- (void)conversationCellDidLongpressText:(ConversationViewCell *)cell viewItem:(ConversationViewItem *)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem; +- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem; - (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer viewItem:(ConversationViewItem *)conversationItem; -- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem; -- (void)conversationCell:(ConversationViewCell *)cell didTapReplyForViewItem:(ConversationViewItem *)conversationItem; +// MJK move these to MessageActionDelegate +//- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem; +//- (void)conversationCell:(ConversationViewCell *)cell didTapReplyForViewItem:(ConversationViewItem *)conversationItem; #pragma mark - System Cell diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 60e5605db..701759716 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -377,7 +377,7 @@ NS_ASSUME_NONNULL_BEGIN [self.sendFailureBadgeView removeFromSuperview]; self.sendFailureBadgeView = nil; - [self hideMenuControllerIfNecessary]; + // [self hideMenuControllerIfNecessary]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -395,9 +395,9 @@ NS_ASSUME_NONNULL_BEGIN [self ensureMediaLoadState]; - if (!isCellVisible) { - [self hideMenuControllerIfNecessary]; - } + // if (!isCellVisible) { + // [self hideMenuControllerIfNecessary]; + // } } #pragma mark - Gesture recognizers @@ -448,17 +448,18 @@ NS_ASSUME_NONNULL_BEGIN switch ([self.messageBubbleView gestureLocationForLocation:locationInMessageBubble]) { case OWSMessageGestureLocation_Default: case OWSMessageGestureLocation_OversizeText: { - [self.delegate conversationCellDidLongpressText:self viewItem:self.viewItem]; + [self.delegate conversationCell:self didLongpressTextViewItem:self.viewItem]; break; } case OWSMessageGestureLocation_Media: { CGPoint location = [sender locationInView:self]; - [self showMediaMenuController:location]; + [self.delegate conversationCell:self didLongpressMediaViewItem:self.viewItem]; + break; } case OWSMessageGestureLocation_QuotedReply: { CGPoint location = [sender locationInView:self]; - [self showDefaultMenuController:location]; + [self.delegate conversationCell:self didLongpressQuoteViewItem:self.viewItem]; break; } } @@ -471,135 +472,135 @@ NS_ASSUME_NONNULL_BEGIN [self.delegate didPanWithGestureRecognizer:panRecognizer viewItem:self.viewItem]; } -#pragma mark - UIMenuController +//#pragma mark - UIMenuController +// +//- (void)showTextMenuController:(CGPoint)fromLocation +//{ +// [self showMenuController:fromLocation menuItems:self.viewItem.textMenuControllerItems]; +//} +// +//- (void)showMediaMenuController:(CGPoint)fromLocation +//{ +// [self showMenuController:fromLocation menuItems:self.viewItem.mediaMenuControllerItems]; +//} +// +//- (void)showDefaultMenuController:(CGPoint)fromLocation +//{ +// [self showMenuController:fromLocation menuItems:self.viewItem.defaultMenuControllerItems]; +//} +// +//- (void)showMenuController:(CGPoint)fromLocation menuItems:(NSArray *)menuItems +//{ +// if (menuItems.count < 1) { +// OWSFail(@"%@ No menu items to present.", self.logTag); +// return; +// } +// +// // We don't want taps on messages to hide the keyboard, +// // so we only let messages become first responder +// // while they are trying to present the menu controller. +// self.isPresentingMenuController = YES; +// +// [self becomeFirstResponder]; +// +// if ([UIMenuController sharedMenuController].isMenuVisible) { +// [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; +// } +// +// // We use custom action selectors so that we can control +// // the ordering of the actions in the menu. +// [UIMenuController sharedMenuController].menuItems = menuItems; +// CGRect targetRect = CGRectMake(fromLocation.x, fromLocation.y, 1, 1); +// [[UIMenuController sharedMenuController] setTargetRect:targetRect inView:self]; +// [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES]; +//} +// +//- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender +//{ +// return [self.viewItem canPerformAction:action]; +//} +// +//- (void)copyTextAction:(nullable id)sender +//{ +// [self.viewItem copyTextAction]; +//} +// +//- (void)copyMediaAction:(nullable id)sender +//{ +// [self.viewItem copyMediaAction]; +//} +// +//- (void)shareTextAction:(nullable id)sender +//{ +// [self.viewItem shareTextAction]; +//} +// +//- (void)shareMediaAction:(nullable id)sender +//{ +// [self.viewItem shareMediaAction]; +//} +// +//- (void)saveMediaAction:(nullable id)sender +//{ +// [self.viewItem saveMediaAction]; +//} +// +//- (void)deleteAction:(nullable id)sender +//{ +// [self.viewItem deleteAction]; +//} +// +//- (void)metadataAction:(nullable id)sender +//{ +// OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); +// +// [self.delegate showMetadataViewForViewItem:self.viewItem]; +//} +// +//- (void)replyAction:(nullable id)sender +//{ +// OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); +// +// [self.delegate conversationCell:self didTapReplyForViewItem:self.viewItem]; +//} -- (void)showTextMenuController:(CGPoint)fromLocation -{ - [self showMenuController:fromLocation menuItems:self.viewItem.textMenuControllerItems]; -} - -- (void)showMediaMenuController:(CGPoint)fromLocation -{ - [self showMenuController:fromLocation menuItems:self.viewItem.mediaMenuControllerItems]; -} - -- (void)showDefaultMenuController:(CGPoint)fromLocation -{ - [self showMenuController:fromLocation menuItems:self.viewItem.defaultMenuControllerItems]; -} - -- (void)showMenuController:(CGPoint)fromLocation menuItems:(NSArray *)menuItems -{ - if (menuItems.count < 1) { - OWSFail(@"%@ No menu items to present.", self.logTag); - return; - } - - // We don't want taps on messages to hide the keyboard, - // so we only let messages become first responder - // while they are trying to present the menu controller. - self.isPresentingMenuController = YES; - - [self becomeFirstResponder]; - - if ([UIMenuController sharedMenuController].isMenuVisible) { - [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; - } - - // We use custom action selectors so that we can control - // the ordering of the actions in the menu. - [UIMenuController sharedMenuController].menuItems = menuItems; - CGRect targetRect = CGRectMake(fromLocation.x, fromLocation.y, 1, 1); - [[UIMenuController sharedMenuController] setTargetRect:targetRect inView:self]; - [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES]; -} - -- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender -{ - return [self.viewItem canPerformAction:action]; -} - -- (void)copyTextAction:(nullable id)sender -{ - [self.viewItem copyTextAction]; -} - -- (void)copyMediaAction:(nullable id)sender -{ - [self.viewItem copyMediaAction]; -} - -- (void)shareTextAction:(nullable id)sender -{ - [self.viewItem shareTextAction]; -} - -- (void)shareMediaAction:(nullable id)sender -{ - [self.viewItem shareMediaAction]; -} - -- (void)saveMediaAction:(nullable id)sender -{ - [self.viewItem saveMediaAction]; -} - -- (void)deleteAction:(nullable id)sender -{ - [self.viewItem deleteAction]; -} - -- (void)metadataAction:(nullable id)sender -{ - OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - - [self.delegate showMetadataViewForViewItem:self.viewItem]; -} - -- (void)replyAction:(nullable id)sender -{ - OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - - [self.delegate conversationCell:self didTapReplyForViewItem:self.viewItem]; -} - -- (BOOL)canBecomeFirstResponder -{ - return self.isPresentingMenuController; -} - -- (void)didHideMenuController:(NSNotification *)notification -{ - self.isPresentingMenuController = NO; -} - -- (void)setIsPresentingMenuController:(BOOL)isPresentingMenuController -{ - if (_isPresentingMenuController == isPresentingMenuController) { - return; - } - - _isPresentingMenuController = isPresentingMenuController; - - if (isPresentingMenuController) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didHideMenuController:) - name:UIMenuControllerDidHideMenuNotification - object:nil]; - } else { - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIMenuControllerDidHideMenuNotification - object:nil]; - } -} - -- (void)hideMenuControllerIfNecessary -{ - if (self.isPresentingMenuController) { - [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; - } - self.isPresentingMenuController = NO; -} +//- (BOOL)canBecomeFirstResponder +//{ +// return self.isPresentingMenuController; +//} +// +//- (void)didHideMenuController:(NSNotification *)notification +//{ +// self.isPresentingMenuController = NO; +//} +// +//- (void)setIsPresentingMenuController:(BOOL)isPresentingMenuController +//{ +// if (_isPresentingMenuController == isPresentingMenuController) { +// return; +// } +// +// _isPresentingMenuController = isPresentingMenuController; +// +// if (isPresentingMenuController) { +// [[NSNotificationCenter defaultCenter] addObserver:self +// selector:@selector(didHideMenuController:) +// name:UIMenuControllerDidHideMenuNotification +// object:nil]; +// } else { +// [[NSNotificationCenter defaultCenter] removeObserver:self +// name:UIMenuControllerDidHideMenuNotification +// object:nil]; +// } +//} +// +//- (void)hideMenuControllerIfNecessary +//{ +// if (self.isPresentingMenuController) { +// [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; +// } +// self.isPresentingMenuController = NO; +//} @end diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 1f756a25b..ce452ba96 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1990,7 +1990,17 @@ typedef enum : NSUInteger { #pragma mark - ConversationViewCellDelegate -- (void)conversationCellDidLongpressText:(ConversationViewCell *)cell viewItem:(ConversationViewItem *)viewItem +- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem +{ + DDLogDebug(@"%@ in %s TODO", self.logTag, __PRETTY_FUNCTION__); +} + +- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem +{ + DDLogDebug(@"%@ in %s TODO", self.logTag, __PRETTY_FUNCTION__); +} + +- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem { // TODO Interpolate from 0->0.3 depending on distance to make visible. NSTimeInterval animationDuration = 0.3; @@ -2003,8 +2013,10 @@ typedef enum : NSUInteger { } completion:^(BOOL finished) { // TODO pass in real actions + + NSArray *messageActions = [viewItem textActionsWithDelegate:self]; MessageActionsViewController *messageActionsViewController = - [[MessageActionsViewController alloc] initWithFocusedView:cell actions:@[]]; + [[MessageActionsViewController alloc] initWithFocusedView:cell actions:messageActions]; messageActionsViewController.delegate = self; diff --git a/SignalMessaging/ViewControllers/MessageActionsViewController.swift b/Signal/src/ViewControllers/MessageActionsViewController.swift similarity index 58% rename from SignalMessaging/ViewControllers/MessageActionsViewController.swift rename to Signal/src/ViewControllers/MessageActionsViewController.swift index 017819271..1bd75c2ef 100644 --- a/SignalMessaging/ViewControllers/MessageActionsViewController.swift +++ b/Signal/src/ViewControllers/MessageActionsViewController.swift @@ -7,10 +7,85 @@ import Foundation @objc protocol MessageActionsDelegate: class { func messageActionsDidHide(_ messageActionsViewController: MessageActionsViewController) + func messageActions(_ messageActionsViewController: MessageActionsViewController, showInfoForItem conversationViewItem: ConversationViewItem) + func messageActions(_ messageActionsViewController: MessageActionsViewController, replyToItem conversationViewItem: ConversationViewItem) +} + +struct MessageActionBuilder { + static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction { + return MessageAction(image: #imageLiteral(resourceName: "table_ic_verify"), + title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"), + subtitle: nil, + block: { (_) in + + }) + } + + static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction { + return MessageAction(image: #imageLiteral(resourceName: "generic-attachment-small"), + title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"), + subtitle: nil, + block: { (_) in + conversationViewItem.copyTextAction() + }) + } +} + +extension ConversationViewItem { + + var deleteMessageAction: MessageAction { + return MessageAction(image: #imageLiteral(resourceName: "message_status_failed_large"), + title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"), + subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"), + block: { (action) in + Logger.debug("\(self.logTag) in \(#function) action: \(action)") + }) + } + + var infoAction: MessageAction { + return MessageAction(image: #imageLiteral(resourceName: "system_message_security"), + title: NSLocalizedString("MESSAGE_ACTION_TITLE_INFO", comment: "Action sheet button title"), + subtitle: nil, + block: { (action) in + Logger.debug("\(self.logTag) in \(#function) action: \(action)") + }) + } + + @objc + func textActions(delegate: MessageActionsDelegate) -> [MessageAction] { + var actions: [MessageAction] = [] + if self.hasBodyText { + actions.append(MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)) + } + + return actions +// switch self.messageCellType() { +// case .unknown: +// return actions +// case .textMessage: +// return [self.copyTextAction] +// case .oversizeTextMessage: +// return [self.copyTextAction] +// case .stillImage: +// return [] +// case .animatedImage: +// return [] +// case .audio: +// return [] +// case .video: +// return [] +// case .genericAttachment: +// return [] +// case .downloadingAttachment: +// return [] +// case .contactShare: +// return [] +// } + } } @objc -class MessageActionsViewController: UIViewController { +class MessageActionsViewController: UIViewController, MessageActionSheetDelegate { @objc weak var delegate: MessageActionsDelegate? @@ -18,49 +93,14 @@ class MessageActionsViewController: UIViewController { private let focusedView: UIView private let actionSheetView: MessageActionSheetView - static let replyAction = MessageAction(block: { (action) in - Logger.debug("\(logTag) in \(#function) action: \(action)") - }, - image: #imageLiteral(resourceName: "table_ic_verify"), - title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"), - subtitle: nil) - - static let copyTextAction = MessageAction(block: { (action) in - Logger.debug("\(logTag) in \(#function) action: \(action)") - }, - image: #imageLiteral(resourceName: "generic-attachment-small"), - title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"), - subtitle: nil) - - static let deleteMessageAction = MessageAction(block: { (action) in - Logger.debug("\(logTag) in \(#function) action: \(action)") - }, - image: #imageLiteral(resourceName: "message_status_failed_large"), - title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"), - subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle")) - - static let infoAction = MessageAction(block: { (action) in - Logger.debug("\(logTag) in \(#function) action: \(action)") - }, - image: #imageLiteral(resourceName: "system_message_info"), - title: NSLocalizedString("MESSAGE_ACTION_TITLE_INFO", comment: "Action sheet button title"), - subtitle: nil) - - static let testActions: [MessageAction] = [ - replyAction, - copyTextAction, - deleteMessageAction, - infoAction - ] - @objc required init(focusedView: UIView, actions: [MessageAction]) { self.focusedView = focusedView - // FIXME - self.actionSheetView = MessageActionSheetView(actions: MessageActionsViewController.testActions) - + self.actionSheetView = MessageActionSheetView(actions: actions) super.init(nibName: nil, bundle: nil) + + actionSheetView.delegate = self } required init?(coder aDecoder: NSCoder) { @@ -156,33 +196,49 @@ class MessageActionsViewController: UIViewController { self.delegate?.messageActionsDidHide(self) }) } + + // MARK: MessageActionSheetDelegate + + func actionSheet(_ actionSheet: MessageActionSheetView, didCompleteAction action: MessageAction) { + animateDismiss() + } } // MARK: ActionView @objc -class MessageAction: NSObject { +public class MessageAction: NSObject { let block: (MessageAction) -> Void let image: UIImage let title: String let subtitle: String? - init(block: @escaping (MessageAction) -> Void, image: UIImage, title: String, subtitle: String?) { - self.block = block + public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MessageAction) -> Void) { self.image = image self.title = title self.subtitle = subtitle + self.block = block } } +protocol MessageActionSheetDelegate: class { + func actionSheet(_ actionSheet: MessageActionSheetView, didCompleteAction action: MessageAction) +} + +protocol MessageActionViewDelegate: class { + func actionView(_ actionView: MessageActionView, didCompleteAction action: MessageAction) +} + class MessageActionView: UIView { - let action: MessageAction + public weak var delegate: MessageActionViewDelegate? + private let action: MessageAction required init(action: MessageAction) { self.action = action super.init(frame: CGRect.zero) + isUserInteractionEnabled = true backgroundColor = .white let imageView = UIImageView(image: action.image) @@ -213,6 +269,21 @@ class MessageActionView: UIView { self.addSubview(contentRow) contentRow.autoPinToSuperviewMargins() contentRow.autoSetDimension(.height, toSize: 56, relation: .greaterThanOrEqual) + + // 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)") + self.action.block(action) + self.delegate?.actionView(self, didCompleteAction: action) } required init?(coder aDecoder: NSCoder) { @@ -220,10 +291,11 @@ class MessageActionView: UIView { } } -class MessageActionSheetView: UIView { +class MessageActionSheetView: UIView, MessageActionViewDelegate { private let actionStackView: UIStackView private var actions: [MessageAction] + weak var delegate: MessageActionSheetDelegate? override var bounds: CGRect { didSet { @@ -258,10 +330,18 @@ class MessageActionSheetView: UIView { 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, didCompleteAction action: MessageAction) { + self.delegate?.actionSheet(self, didCompleteAction: action) + } + + // MARK: private func updateMask() { let cornerRadius: CGFloat = 16 let path: UIBezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))