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);
switch (message.errorType) {
case TSErrorMessageInvalidKeyException:
break;
case TSErrorMessageNonBlockingIdentityChange:
[self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId];
return;
case TSErrorMessageWrongTrustedIdentityKey:
OWSAssert([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]);
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message]; [self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
} else if ([message isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) { return;
[self tappedUnknownContactBlockOfferMessage:(OWSUnknownContactBlockOfferMessage *)message]; case TSErrorMessageMissingKeyId:
} else if (message.errorType == TSErrorMessageInvalidMessage) { // Unused.
break;
case TSErrorMessageNoSession:
break;
case TSErrorMessageInvalidMessage:
[self tappedCorruptedMessage:message]; [self tappedCorruptedMessage:message];
} else if (message.errorType == TSErrorMessageNonBlockingIdentityChange) { return;
[self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId]; case TSErrorMessageDuplicateMessage:
} else { // Unused.
DDLogWarn(@"%@ Unhandled tap for error message:%@", self.tag, message); 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);
switch (message.messageType) {
case TSInfoMessageUserNotRegistered:
break;
case TSInfoMessageTypeSessionDidEnd:
break;
case TSInfoMessageTypeUnsupportedMessage:
// Unused.
break;
case TSInfoMessageAddToContactsOffer:
OWSAssert([message isKindOfClass:[OWSAddToContactsOfferMessage class]]);
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message]; [self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
} else { return;
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message); 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,20 +26,32 @@
@implementation OWSSystemMessageCell @implementation OWSSystemMessageCell
+ (NSString *)cellReuseIdentifier - (instancetype)initWithFrame:(CGRect)frame
{ {
return NSStringFromClass([self class]); if (self = [super initWithFrame:frame]) {
[self commontInit];
}
return self;
} }
- (void)configureWithInteraction:(TSInteraction *)interaction; - (instancetype)init
{ {
OWSAssert(interaction); if (self = [super init]) {
[self commontInit];
}
_interaction = interaction; return self;
}
- (void)commontInit
{
OWSAssert(!self.imageView);
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
if (!self.titleLabel) {
self.imageView = [UIImageView new]; self.imageView = [UIImageView new];
[self.contentView addSubview:self.imageView]; [self.contentView addSubview:self.imageView];
@ -49,7 +61,22 @@
self.titleLabel.numberOfLines = 0; self.titleLabel.numberOfLines = 0;
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.titleLabel]; [self.contentView addSubview:self.titleLabel];
}
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self addGestureRecognizer:tap];
}
+ (NSString *)cellReuseIdentifier
{
return NSStringFromClass([self class]);
}
- (void)configureWithInteraction:(TSInteraction *)interaction;
{
OWSAssert(interaction);
_interaction = interaction;
UIImage *icon = [self iconForInteraction:self.interaction]; UIImage *icon = [self iconForInteraction:self.interaction];
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
@ -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,20 +27,32 @@
@implementation OWSUnreadIndicatorCell @implementation OWSUnreadIndicatorCell
+ (NSString *)cellReuseIdentifier - (instancetype)initWithFrame:(CGRect)frame
{ {
return NSStringFromClass([self class]); if (self = [super initWithFrame:frame]) {
[self commontInit];
}
return self;
} }
- (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction; - (instancetype)init
{ {
OWSAssert(interaction); if (self = [super init]) {
[self commontInit];
}
_interaction = interaction; return self;
}
- (void)commontInit
{
OWSAssert(!self.bannerView);
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
if (!self.titleLabel) {
self.bannerView = [UIView new]; self.bannerView = [UIView new];
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3]; self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
[self.contentView addSubview:self.bannerView]; [self.contentView addSubview:self.bannerView];
@ -71,7 +83,22 @@
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping; self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.subtitleLabel.textAlignment = NSTextAlignmentCenter; self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:self.subtitleLabel]; [self.contentView addSubview:self.subtitleLabel];
} }
+ (NSString *)cellReuseIdentifier
{
return NSStringFromClass([self class]);
}
- (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction;
{
OWSAssert(interaction);
_interaction = interaction;
self.backgroundColor = [UIColor whiteColor];
[self setNeedsLayout];
} }
+ (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