Clean up system message cells, make them tappable, etc.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 6a6d71db6c
commit 15074cdb8f

@ -67,14 +67,19 @@ NS_ASSUME_NONNULL_BEGIN
= (TSUnreadIndicatorInteraction *)((TSMessageAdapter *)messageData).interaction; = (TSUnreadIndicatorInteraction *)((TSMessageAdapter *)messageData).interaction;
return [self sizeForUnreadIndicator:interaction cacheKey:cacheKey layout:layout]; return [self sizeForUnreadIndicator:interaction cacheKey:cacheKey layout:layout];
} }
case TSIncomingMessageAdapter:
case TSOutgoingMessageAdapter:
break;
default: default:
// TODO: we need to examine the other cases. OWSFail(@"---- Unknown sizing interaction: %@", [((TSMessageAdapter *)messageData).interaction class]);
break; break;
} }
} else if ([messageData isKindOfClass:[OWSCall class]]) { } else if ([messageData isKindOfClass:[OWSCall class]]) {
id cacheKey = [self cacheKeyForMessageData:messageData]; id cacheKey = [self cacheKeyForMessageData:messageData];
TSInteraction *interaction = ((OWSCall *)messageData).interaction; TSInteraction *interaction = ((OWSCall *)messageData).interaction;
return [self sizeForSystemMessage:interaction cacheKey:cacheKey layout:layout]; return [self sizeForSystemMessage:interaction cacheKey:cacheKey layout:layout];
} else {
OWSFail(@"Can't size unknown message data type: %@", [messageData class]);
} }
// BEGIN HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368 // BEGIN HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368

@ -27,4 +27,8 @@ import Foundation
static let rejectedCallWithUnseenIdentityChangeNotificationBody = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY", comment: "notification action") static let rejectedCallWithUnseenIdentityChangeNotificationBody = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY", comment: "notification action")
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITHOUT_CALLER_NAME", comment: "notification action") static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITHOUT_CALLER_NAME", comment: "notification action")
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITH_CALLER_NAME", comment: "notification action") static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITH_CALLER_NAME", comment: "notification action")
static let callBackAlertTitle = NSLocalizedString("CALL_USER_ALERT_TITLE", comment: "Title for alert offering to call a user.")
static let callBackAlertMessageFormat = NSLocalizedString("CALL_USER_ALERT_MESSAGE_FORMAT", comment: "Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}.")
static let callBackAlertCallButton = NSLocalizedString("CALL_USER_ALERT_CALL_BUTTON", comment: "Label for call button for alert offering to call a user.")
} }

@ -151,6 +151,7 @@ typedef enum : NSUInteger {
JSQMessagesComposerTextViewPasteDelegate, JSQMessagesComposerTextViewPasteDelegate,
OWSConversationSettingsViewDelegate, OWSConversationSettingsViewDelegate,
OWSMessagesCollectionViewFlowLayoutDelegate, OWSMessagesCollectionViewFlowLayoutDelegate,
OWSSystemMessageCellDelegate,
OWSTextViewPasteDelegate, OWSTextViewPasteDelegate,
OWSVoiceMemoGestureDelegate, OWSVoiceMemoGestureDelegate,
UIDocumentMenuDelegate, UIDocumentMenuDelegate,
@ -1578,6 +1579,7 @@ typedef enum : NSUInteger {
[self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier] [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier]
forIndexPath:indexPath]; forIndexPath:indexPath];
[cell configureWithInteraction:interaction]; [cell configureWithInteraction:interaction];
cell.systemMessageCellDelegate = self;
return cell; return cell;
} }
@ -1795,7 +1797,6 @@ typedef enum : NSUInteger {
[self showConversationSettings]; [self showConversationSettings];
} }
- (void)collectionView:(JSQMessagesCollectionView *)collectionView - (void)collectionView:(JSQMessagesCollectionView *)collectionView
didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath
{ {
@ -1968,13 +1969,10 @@ typedef enum : NSUInteger {
} }
} break; } break;
case TSErrorMessageAdapter: case TSErrorMessageAdapter:
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
break;
case TSInfoMessageAdapter: case TSInfoMessageAdapter:
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
break;
case TSCallAdapter: case TSCallAdapter:
case TSUnreadIndicatorAdapter: case TSUnreadIndicatorAdapter:
OWSFail(@"Unexpected tap for system message.");
break; break;
default: default:
DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction); DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction);
@ -2218,17 +2216,38 @@ typedef enum : NSUInteger {
- (void)handleErrorMessageTap:(TSErrorMessage *)message - (void)handleErrorMessageTap:(TSErrorMessage *)message
{ {
if ([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) { OWSAssert(message);
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
} else if ([message isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) { switch (message.errorType) {
[self tappedUnknownContactBlockOfferMessage:(OWSUnknownContactBlockOfferMessage *)message]; case TSErrorMessageInvalidKeyException:
} else if (message.errorType == TSErrorMessageInvalidMessage) { break;
[self tappedCorruptedMessage:message]; case TSErrorMessageNonBlockingIdentityChange:
} else if (message.errorType == TSErrorMessageNonBlockingIdentityChange) { [self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId];
[self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId]; return;
} else { case TSErrorMessageWrongTrustedIdentityKey:
DDLogWarn(@"%@ Unhandled tap for error message:%@", self.tag, message); OWSAssert([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]);
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
return;
case TSErrorMessageMissingKeyId:
// Unused.
break;
case TSErrorMessageNoSession:
break;
case TSErrorMessageInvalidMessage:
[self tappedCorruptedMessage:message];
return;
case TSErrorMessageDuplicateMessage:
// Unused.
break;
case TSErrorMessageInvalidVersion:
break;
case TSErrorMessageUnknownContactBlockOffer:
OWSAssert([message isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]);
[self tappedUnknownContactBlockOfferMessage:(OWSUnknownContactBlockOfferMessage *)message];
return;
} }
DDLogWarn(@"%@ Unhandled tap for error message:%@", self.tag, message);
} }
- (void)tappedNonBlockingIdentityChangeForRecipientId:(NSString *)signalId - (void)tappedNonBlockingIdentityChangeForRecipientId:(NSString *)signalId
@ -2251,16 +2270,35 @@ typedef enum : NSUInteger {
- (void)handleInfoMessageTap:(TSInfoMessage *)message - (void)handleInfoMessageTap:(TSInfoMessage *)message
{ {
if ([message isKindOfClass:[OWSAddToContactsOfferMessage class]]) { OWSAssert(message);
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
} else { switch (message.messageType) {
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message); case TSInfoMessageUserNotRegistered:
break;
case TSInfoMessageTypeSessionDidEnd:
break;
case TSInfoMessageTypeUnsupportedMessage:
// Unused.
break;
case TSInfoMessageAddToContactsOffer:
OWSAssert([message isKindOfClass:[OWSAddToContactsOfferMessage class]]);
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
return;
case TSInfoMessageTypeGroupUpdate:
[self showConversationSettings];
return;
case TSInfoMessageTypeGroupQuit:
break;
case TSInfoMessageTypeDisappearingMessagesUpdate:
[self showConversationSettings];
return;
} }
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message);
} }
- (void)tappedCorruptedMessage:(TSErrorMessage *)message - (void)tappedCorruptedMessage:(TSErrorMessage *)message
{ {
NSString *alertMessage = [NSString NSString *alertMessage = [NSString
stringWithFormat:NSLocalizedString(@"CORRUPTED_SESSION_DESCRIPTION", @"ActionSheet title"), self.thread.name]; stringWithFormat:NSLocalizedString(@"CORRUPTED_SESSION_DESCRIPTION", @"ActionSheet title"), self.thread.name];
@ -2394,6 +2432,59 @@ typedef enum : NSUInteger {
editImmediately:YES]; editImmediately:YES];
} }
- (void)handleCallTap:(TSCall *)call
{
OWSAssert(call);
if (![self.thread isKindOfClass:[TSContactThread class]]) {
DDLogError(@"%@ unexpected thread: %@ in %s", self.tag, self.thread, __PRETTY_FUNCTION__);
OWSAssert(NO);
return;
}
TSContactThread *contactThread = (TSContactThread *)self.thread;
NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:contactThread.contactIdentifier];
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:[CallStrings callBackAlertTitle]
message:[NSString stringWithFormat:[CallStrings callBackAlertMessageFormat], displayName]
preferredStyle:UIAlertControllerStyleAlert];
__weak MessagesViewController *weakSelf = self;
UIAlertAction *callAction = [UIAlertAction actionWithTitle:[CallStrings callBackAlertCallButton]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[weakSelf callAction:nil];
}];
[alertController addAction:callAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
style:UIAlertActionStyleCancel
handler:nil];
[alertController addAction:dismissAction];
[[UIApplication sharedApplication].frontmostViewController presentViewController:alertController
animated:YES
completion:nil];
}
#pragma mark - OWSSystemMessageCellDelegate
- (void)didTapSystemMessageWithInteraction:(TSInteraction *)interaction
{
OWSAssert([NSThread isMainThread]);
OWSAssert(interaction);
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
} else if ([interaction isKindOfClass:[TSCall class]]) {
[self handleCallTap:(TSCall *)interaction];
} else {
OWSFail(@"Tap for system messages of unknown type: %@", [interaction class]);
}
}
#pragma mark - ContactEditingDelegate #pragma mark - ContactEditingDelegate
- (void)didFinishEditingContact - (void)didFinishEditingContact

@ -7,8 +7,18 @@
@class TSInteraction; @class TSInteraction;
@protocol OWSSystemMessageCellDelegate <NSObject>
- (void)didTapSystemMessageWithInteraction:(TSInteraction *)interaction;
@end
#pragma mark -
@interface OWSSystemMessageCell : JSQMessagesCollectionViewCell @interface OWSSystemMessageCell : JSQMessagesCollectionViewCell
@property (nonatomic, weak) id<OWSSystemMessageCellDelegate> systemMessageCellDelegate;
@property (nonatomic, nullable, readonly) TSInteraction *interaction; @property (nonatomic, nullable, readonly) TSInteraction *interaction;
- (void)configureWithInteraction:(TSInteraction *)interaction; - (void)configureWithInteraction:(TSInteraction *)interaction;

@ -26,6 +26,47 @@
@implementation OWSSystemMessageCell @implementation OWSSystemMessageCell
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self commontInit];
}
return self;
}
- (instancetype)init
{
if (self = [super init]) {
[self commontInit];
}
return self;
}
- (void)commontInit
{
OWSAssert(!self.imageView);
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor whiteColor];
self.imageView = [UIImageView new];
[self.contentView addSubview:self.imageView];
self.titleLabel = [UILabel new];
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
self.titleLabel.font = [OWSSystemMessageCell titleFont];
self.titleLabel.numberOfLines = 0;
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.titleLabel];
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self addGestureRecognizer:tap];
}
+ (NSString *)cellReuseIdentifier + (NSString *)cellReuseIdentifier
{ {
return NSStringFromClass([self class]); return NSStringFromClass([self class]);
@ -37,20 +78,6 @@
_interaction = interaction; _interaction = interaction;
self.backgroundColor = [UIColor whiteColor];
if (!self.titleLabel) {
self.imageView = [UIImageView new];
[self.contentView addSubview:self.imageView];
self.titleLabel = [UILabel new];
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
self.titleLabel.font = [OWSSystemMessageCell titleFont];
self.titleLabel.numberOfLines = 0;
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.titleLabel];
}
UIImage *icon = [self iconForInteraction:self.interaction]; UIImage *icon = [self iconForInteraction:self.interaction];
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
self.imageView.tintColor = [self iconColorForInteraction:self.interaction]; self.imageView.tintColor = [self iconColorForInteraction:self.interaction];
@ -213,4 +240,13 @@
self.interaction = nil; self.interaction = nil;
} }
#pragma mark - Gesture recognizers
- (void)handleTapGesture:(UITapGestureRecognizer *)tap
{
OWSAssert(self.interaction);
[self.systemMessageCellDelegate didTapSystemMessageWithInteraction:self.interaction];
}
@end @end

@ -12,7 +12,6 @@
@property (nonatomic, nullable, readonly) TSUnreadIndicatorInteraction *interaction; @property (nonatomic, nullable, readonly) TSUnreadIndicatorInteraction *interaction;
- (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction; - (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction;
;
+ (CGSize)cellSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction + (CGSize)cellSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction
collectionViewWidth:(CGFloat)collectionViewWidth; collectionViewWidth:(CGFloat)collectionViewWidth;

@ -27,6 +27,64 @@
@implementation OWSUnreadIndicatorCell @implementation OWSUnreadIndicatorCell
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self commontInit];
}
return self;
}
- (instancetype)init
{
if (self = [super init]) {
[self commontInit];
}
return self;
}
- (void)commontInit
{
OWSAssert(!self.bannerView);
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor whiteColor];
self.bannerView = [UIView new];
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
[self.contentView addSubview:self.bannerView];
self.bannerTopHighlightView = [UIView new];
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
[self.bannerView addSubview:self.bannerTopHighlightView];
self.bannerBottomHighlightView1 = [UIView new];
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
[self.bannerView addSubview:self.bannerBottomHighlightView1];
self.bannerBottomHighlightView2 = [UIView new];
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
[self.bannerView addSubview:self.bannerBottomHighlightView2];
self.titleLabel = [UILabel new];
self.titleLabel.text = [OWSUnreadIndicatorCell titleForInteraction:self.interaction];
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
self.titleLabel.font = [OWSUnreadIndicatorCell titleFont];
[self.bannerView addSubview:self.titleLabel];
self.subtitleLabel = [UILabel new];
self.subtitleLabel.text = [OWSUnreadIndicatorCell subtitleForInteraction:self.interaction];
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
self.subtitleLabel.font = [OWSUnreadIndicatorCell subtitleFont];
self.subtitleLabel.numberOfLines = 0;
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:self.subtitleLabel];
}
+ (NSString *)cellReuseIdentifier + (NSString *)cellReuseIdentifier
{ {
return NSStringFromClass([self class]); return NSStringFromClass([self class]);
@ -40,38 +98,7 @@
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
if (!self.titleLabel) { [self setNeedsLayout];
self.bannerView = [UIView new];
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
[self.contentView addSubview:self.bannerView];
self.bannerTopHighlightView = [UIView new];
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
[self.bannerView addSubview:self.bannerTopHighlightView];
self.bannerBottomHighlightView1 = [UIView new];
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
[self.bannerView addSubview:self.bannerBottomHighlightView1];
self.bannerBottomHighlightView2 = [UIView new];
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
[self.bannerView addSubview:self.bannerBottomHighlightView2];
self.titleLabel = [UILabel new];
self.titleLabel.text = [OWSUnreadIndicatorCell titleForInteraction:self.interaction];
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
self.titleLabel.font = [OWSUnreadIndicatorCell titleFont];
[self.bannerView addSubview:self.titleLabel];
self.subtitleLabel = [UILabel new];
self.subtitleLabel.text = [OWSUnreadIndicatorCell subtitleForInteraction:self.interaction];
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
self.subtitleLabel.font = [OWSUnreadIndicatorCell subtitleFont];
self.subtitleLabel.numberOfLines = 0;
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:self.subtitleLabel];
}
} }
+ (UIFont *)titleFont + (UIFont *)titleFont

@ -199,6 +199,15 @@
/* embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call' */ /* embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call' */
"CALL_STATUS_FORMAT" = "Signal %@"; "CALL_STATUS_FORMAT" = "Signal %@";
/* Label for call button for alert offering to call a user. */
"CALL_USER_ALERT_CALL_BUTTON" = "Call";
/* Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}. */
"CALL_USER_ALERT_MESSAGE_FORMAT" = "Would you like to call %@?";
/* Title for alert offering to call a user. */
"CALL_USER_ALERT_TITLE" = "Call?";
/* Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy. */ /* Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy. */
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL" = "You can answer calls directly from your lock screen and see the name and phone number for incoming calls if you change your settings.\n\nSee the privacy settings for details."; "CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL" = "You can answer calls directly from your lock screen and see the name and phone number for incoming calls if you change your settings.\n\nSee the privacy settings for details.";
@ -1063,7 +1072,7 @@
/* button title to confirm adding a recipient to a group when their safety number has recently changed */ /* button title to confirm adding a recipient to a group when their safety number has recently changed */
"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION" = "Confirm and Add to Group"; "SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION" = "Confirm and Add to Group";
/* button title to confirm calling a recipient whose safety number recently changed */ /* alert button text to confirm placing an outgoing call after the recipients Safety Number has changed. */
"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION" = "Confirm and Call"; "SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION" = "Confirm and Call";
/* button title to confirm sending to a recipient whose safety number recently changed */ /* button title to confirm sending to a recipient whose safety number recently changed */

Loading…
Cancel
Save