From 7abd51838fab5d8b6f3f2bce0ee4e08ebd6d586e Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 11 Apr 2018 10:59:40 -0400 Subject: [PATCH] Move bubble actions to new bubble delegate // FREEBIE --- .../Cells/ConversationViewCell.h | 12 -- .../Cells/OWSMessageBubbleView.h | 29 ++++ .../Cells/OWSMessageBubbleView.m | 151 +++++++++++++++++- .../ConversationView/Cells/OWSMessageCell.h | 4 + .../ConversationView/Cells/OWSMessageCell.m | 145 +++++------------ .../ConversationViewController.m | 7 +- 6 files changed, 233 insertions(+), 115 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h index 3172388f2..c4e735817 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h @@ -16,18 +16,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol ConversationViewCellDelegate -- (void)didTapImageViewItem:(ConversationViewItem *)viewItem - attachmentStream:(TSAttachmentStream *)attachmentStream - imageView:(UIView *)imageView; -- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem - attachmentStream:(TSAttachmentStream *)attachmentStream - imageView:(UIView *)imageView; -- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; -- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem; -- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem - attachmentPointer:(TSAttachmentPointer *)attachmentPointer; -- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message; -- (void)didTapQuotedMessage:(ConversationViewItem *)viewItem quotedMessage:(TSQuotedMessage *)quotedMessage; - (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer viewItem:(ConversationViewItem *)conversationItem; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h index d3fb6a1d7..9313616dd 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.h @@ -5,6 +5,10 @@ NS_ASSUME_NONNULL_BEGIN @class ConversationViewItem; +@class TSAttachmentPointer; +@class TSAttachmentStream; +@class TSOutgoingMessage; +@class TSQuotedMessage; typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) { // Message text, etc. @@ -14,6 +18,29 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) { OWSMessageGestureLocation_QuotedReply, }; +@protocol OWSMessageBubbleViewDelegate + +- (void)didTapImageViewItem:(ConversationViewItem *)viewItem + attachmentStream:(TSAttachmentStream *)attachmentStream + imageView:(UIView *)imageView; + +- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem + attachmentStream:(TSAttachmentStream *)attachmentStream + imageView:(UIView *)imageView; + +- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; + +- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem; + +- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem + attachmentPointer:(TSAttachmentPointer *)attachmentPointer; + +- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message; + +- (void)didTapQuotedMessage:(ConversationViewItem *)viewItem quotedMessage:(TSQuotedMessage *)quotedMessage; + +@end + @interface OWSMessageBubbleView : UIView @property (nonatomic, nullable) ConversationViewItem *viewItem; @@ -26,6 +53,8 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) { @property (nonatomic) BOOL alwaysShowBubbleTail; +@property (nonatomic, weak) id delegate; + - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 47e630db0..5dc38b40c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -59,13 +59,17 @@ NS_ASSUME_NONNULL_BEGIN _viewConstraints = [NSMutableArray new]; self.layoutMargins = UIEdgeInsetsZero; - self.userInteractionEnabled = NO; + self.userInteractionEnabled = YES; self.bubbleView = [OWSBubbleView new]; self.bubbleView.layoutMargins = UIEdgeInsetsZero; [self addSubview:self.bubbleView]; [self.bubbleView autoPinEdgesToSuperviewEdges]; + UITapGestureRecognizer *tap = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + [self addGestureRecognizer:tap]; + self.bodyTextView = [self newTextView]; // Setting dataDetectorTypes is expensive. Do it just once. self.bodyTextView.dataDetectorTypes @@ -150,6 +154,13 @@ NS_ASSUME_NONNULL_BEGIN return self.viewItem.attachmentPointer; } +- (TSMessage *)message +{ + OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); + + return (TSMessage *)self.viewItem.interaction; +} + - (CGSize)mediaSize { // This should always be valid for the appropriate cell types. @@ -1017,6 +1028,8 @@ NS_ASSUME_NONNULL_BEGIN [NSLayoutConstraint deactivateConstraints:self.viewConstraints]; self.viewConstraints = [NSMutableArray new]; + self.delegate = nil; + [self.bodyTextView removeFromSuperview]; self.bodyTextView.text = nil; self.bodyTextView.hidden = YES; @@ -1043,6 +1056,142 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Gestures +- (void)handleTapGesture:(UITapGestureRecognizer *)sender +{ + OWSAssert(self.delegate); + + if (sender.state != UIGestureRecognizerStateRecognized) { + DDLogVerbose(@"%@ Ignoring tap on message: %@", self.logTag, self.viewItem.interaction.debugDescription); + return; + } + + if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; + if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + [self.delegate didTapFailedOutgoingMessage:outgoingMessage]; + return; + } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { + // Ignore taps on outgoing messages being sent. + return; + } + } + + CGPoint locationInMessageBubble = [sender locationInView:self]; + switch ([self gestureLocationForLocation:locationInMessageBubble]) { + case OWSMessageGestureLocation_Default: + // Do nothing. + return; + case OWSMessageGestureLocation_OversizeText: + [self.delegate didTapTruncatedTextMessage:self.viewItem]; + return; + case OWSMessageGestureLocation_Media: + [self handleMediaTapGesture]; + break; + case OWSMessageGestureLocation_QuotedReply: + if (self.message.quotedMessage) { + [self.delegate didTapQuotedMessage:self.viewItem quotedMessage:self.message.quotedMessage]; + } else { + OWSFail(@"%@ Missing quoted message.", self.logTag) + } + break; + } +} +// +//- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender +//{ +// OWSAssert(self.delegate); +// +// if (sender.state != UIGestureRecognizerStateBegan) { +// return; +// } +// +// if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { +// TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; +// if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { +// // Ignore long press on unsent messages. +// return; +// } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { +// // Ignore long press on outgoing messages being sent. +// return; +// } +// } +// +// CGPoint locationInMessageBubble = [sender locationInView:self]; +// switch ([self gestureLocationForLocation:locationInMessageBubble]) { +// case OWSMessageGestureLocation_Default: +// case OWSMessageGestureLocation_OversizeText: { +// CGPoint location = [sender locationInView:self]; +// [self showTextMenuController:location]; +// break; +// } +// case OWSMessageGestureLocation_Media: { +// CGPoint location = [sender locationInView:self]; +// [self showMediaMenuController:location]; +// break; +// } +// case OWSMessageGestureLocation_QuotedReply: +// // TODO: +// break; +// } +//} + +- (void)handleMediaTapGesture +{ + OWSAssert(self.delegate); + + TSAttachmentStream *_Nullable attachmentStream = self.viewItem.attachmentStream; + + switch (self.cellType) { + case OWSMessageCellType_Unknown: + case OWSMessageCellType_TextMessage: + case OWSMessageCellType_OversizeTextMessage: + break; + case OWSMessageCellType_StillImage: + OWSAssert(self.bodyMediaView); + OWSAssert(attachmentStream); + + [self.delegate didTapImageViewItem:self.viewItem + attachmentStream:attachmentStream + imageView:self.bodyMediaView]; + break; + case OWSMessageCellType_AnimatedImage: + OWSAssert(self.bodyMediaView); + OWSAssert(attachmentStream); + + [self.delegate didTapImageViewItem:self.viewItem + attachmentStream:attachmentStream + imageView:self.bodyMediaView]; + break; + case OWSMessageCellType_Audio: + OWSAssert(attachmentStream); + + [self.delegate didTapAudioViewItem:self.viewItem attachmentStream:attachmentStream]; + return; + case OWSMessageCellType_Video: + OWSAssert(self.bodyMediaView); + OWSAssert(attachmentStream); + + [self.delegate didTapVideoViewItem:self.viewItem + attachmentStream:attachmentStream + imageView:self.bodyMediaView]; + return; + case OWSMessageCellType_GenericAttachment: + OWSAssert(attachmentStream); + + [AttachmentSharing showShareUIForAttachment:attachmentStream]; + break; + case OWSMessageCellType_DownloadingAttachment: { + TSAttachmentPointer *_Nullable attachmentPointer = self.viewItem.attachmentPointer; + OWSAssert(attachmentPointer); + + if (attachmentPointer.state == TSAttachmentPointerStateFailed) { + [self.delegate didTapFailedIncomingAttachment:self.viewItem attachmentPointer:attachmentPointer]; + } + break; + } + } +} + - (OWSMessageGestureLocation)gestureLocationForLocation:(CGPoint)locationInMessageBubble { if (self.quotedMessageView) { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h index 789b94fbb..4d52718b5 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h @@ -4,10 +4,14 @@ #import "ConversationViewCell.h" +@class OWSMessageBubbleView; + NS_ASSUME_NONNULL_BEGIN @interface OWSMessageCell : ConversationViewCell +@property (nonatomic, readonly) OWSMessageBubbleView *messageBubbleView; + + (NSString *)cellReuseIdentifier; @end diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 3a950c471..1c0b3c581 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -83,9 +83,9 @@ NS_ASSUME_NONNULL_BEGIN self.contentView.userInteractionEnabled = YES; - UITapGestureRecognizer *tap = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; - [self.contentView addGestureRecognizer:tap]; + // UITapGestureRecognizer *tap = + // [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + // [self.contentView addGestureRecognizer:tap]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)]; @@ -483,104 +483,47 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Gesture recognizers -- (void)handleTapGesture:(UITapGestureRecognizer *)sender -{ - OWSAssert(self.delegate); - - if (sender.state != UIGestureRecognizerStateRecognized) { - DDLogVerbose(@"%@ Ignoring tap on message: %@", self.logTag, self.viewItem.interaction.debugDescription); - return; - } - - if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { - TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; - if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { - [self.delegate didTapFailedOutgoingMessage:outgoingMessage]; - return; - } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { - // Ignore taps on outgoing messages being sent. - return; - } - } - - CGPoint locationInMessageBubble = [sender locationInView:self.messageBubbleView]; - switch ([self.messageBubbleView gestureLocationForLocation:locationInMessageBubble]) { - case OWSMessageGestureLocation_Default: - // Do nothing. - return; - case OWSMessageGestureLocation_OversizeText: - [self.delegate didTapTruncatedTextMessage:self.viewItem]; - return; - case OWSMessageGestureLocation_Media: - [self handleMediaTapGesture]; - break; - case OWSMessageGestureLocation_QuotedReply: - if (self.message.quotedMessage) { - [self.delegate didTapQuotedMessage:self.viewItem quotedMessage:self.message.quotedMessage]; - } else { - OWSFail(@"%@ Missing quoted message.", self.logTag) - } - break; - } -} - -- (void)handleMediaTapGesture -{ - OWSAssert(self.delegate); - - TSAttachmentStream *_Nullable attachmentStream = self.viewItem.attachmentStream; - - switch (self.cellType) { - case OWSMessageCellType_Unknown: - case OWSMessageCellType_TextMessage: - case OWSMessageCellType_OversizeTextMessage: - break; - case OWSMessageCellType_StillImage: - OWSAssert(self.messageBubbleView.bodyMediaView); - OWSAssert(attachmentStream); - - [self.delegate didTapImageViewItem:self.viewItem - attachmentStream:attachmentStream - imageView:self.messageBubbleView.bodyMediaView]; - break; - case OWSMessageCellType_AnimatedImage: - OWSAssert(self.messageBubbleView.bodyMediaView); - OWSAssert(attachmentStream); - - [self.delegate didTapImageViewItem:self.viewItem - attachmentStream:attachmentStream - imageView:self.messageBubbleView.bodyMediaView]; - break; - case OWSMessageCellType_Audio: - OWSAssert(attachmentStream); - - [self.delegate didTapAudioViewItem:self.viewItem attachmentStream:attachmentStream]; - return; - case OWSMessageCellType_Video: - OWSAssert(self.messageBubbleView.bodyMediaView); - OWSAssert(attachmentStream); - - [self.delegate didTapVideoViewItem:self.viewItem - attachmentStream:attachmentStream - imageView:self.messageBubbleView.bodyMediaView]; - return; - case OWSMessageCellType_GenericAttachment: - OWSAssert(attachmentStream); - - [AttachmentSharing showShareUIForAttachment:attachmentStream]; - break; - case OWSMessageCellType_DownloadingAttachment: { - TSAttachmentPointer *_Nullable attachmentPointer = self.viewItem.attachmentPointer; - OWSAssert(attachmentPointer); - - if (attachmentPointer.state == TSAttachmentPointerStateFailed) { - [self.delegate didTapFailedIncomingAttachment:self.viewItem attachmentPointer:attachmentPointer]; - } - break; - } - } -} - +//- (void)handleTapGesture:(UITapGestureRecognizer *)sender +//{ +// OWSAssert(self.delegate); +// +// if (sender.state != UIGestureRecognizerStateRecognized) { +// DDLogVerbose(@"%@ Ignoring tap on message: %@", self.logTag, self.viewItem.interaction.debugDescription); +// return; +// } +// +// if (self.viewItem.interaction.interactionType == OWSInteractionType_OutgoingMessage) { +// TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction; +// if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { +// [self.delegate didTapFailedOutgoingMessage:outgoingMessage]; +// return; +// } else if (outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { +// // Ignore taps on outgoing messages being sent. +// return; +// } +// } +// +// CGPoint locationInMessageBubble = [sender locationInView:self.messageBubbleView]; +// switch ([self.messageBubbleView gestureLocationForLocation:locationInMessageBubble]) { +// case OWSMessageGestureLocation_Default: +// // Do nothing. +// return; +// case OWSMessageGestureLocation_OversizeText: +// [self.delegate didTapTruncatedTextMessage:self.viewItem]; +// return; +// case OWSMessageGestureLocation_Media: +// [self handleMediaTapGesture]; +// break; +// case OWSMessageGestureLocation_QuotedReply: +// if (self.message.quotedMessage) { +// [self.delegate didTapQuotedMessage:self.viewItem quotedMessage:self.message.quotedMessage]; +// } else { +// OWSFail(@"%@ Missing quoted message.", self.logTag) +// } +// break; +// } +//} +// - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender { OWSAssert(self.delegate); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index e8cdac495..8db321fdf 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -128,6 +128,7 @@ typedef enum : NSUInteger { ConversationViewLayoutDelegate, ConversationViewCellDelegate, ConversationInputTextViewDelegate, + OWSMessageBubbleViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UIDocumentMenuDelegate, @@ -2020,7 +2021,7 @@ typedef enum : NSUInteger { success:successHandler]; } -#pragma mark - Message Events +#pragma mark - OWSMessageBubbleViewDelegate - (void)didTapImageViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream @@ -4718,6 +4719,10 @@ typedef enum : NSUInteger { } cell.viewItem = viewItem; cell.delegate = self; + if ([cell isKindOfClass:[OWSMessageCell class]]) { + OWSMessageCell *messageCell = (OWSMessageCell *)cell; + messageCell.messageBubbleView.delegate = self; + } cell.contentWidth = self.layout.contentWidth; [cell loadForDisplay];