Rework handling of oversize text messages.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 97bab48a93
commit bcf83a4c8e

@ -50,7 +50,6 @@
34B3F8851E8DF1700035BE1A /* NewGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */; }; 34B3F8851E8DF1700035BE1A /* NewGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */; };
34B3F8861E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */; }; 34B3F8861E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */; };
34B3F8871E8DF1700035BE1A /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */; }; 34B3F8871E8DF1700035BE1A /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */; };
34B3F8881E8DF1700035BE1A /* OversizeTextMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85A1E8DF1700035BE1A /* OversizeTextMessageViewController.swift */; };
34B3F8891E8DF1700035BE1A /* OWSConversationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85C1E8DF1700035BE1A /* OWSConversationSettingsViewController.m */; }; 34B3F8891E8DF1700035BE1A /* OWSConversationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85C1E8DF1700035BE1A /* OWSConversationSettingsViewController.m */; };
34B3F88A1E8DF1700035BE1A /* OWSLinkDeviceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85E1E8DF1700035BE1A /* OWSLinkDeviceViewController.m */; }; 34B3F88A1E8DF1700035BE1A /* OWSLinkDeviceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85E1E8DF1700035BE1A /* OWSLinkDeviceViewController.m */; };
34B3F88B1E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8601E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m */; }; 34B3F88B1E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8601E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m */; };
@ -477,7 +476,6 @@
34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = "<group>"; }; 34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = "<group>"; };
34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = "<group>"; }; 34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = "<group>"; };
34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsViewController.m; sourceTree = "<group>"; }; 34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsViewController.m; sourceTree = "<group>"; };
34B3F85A1E8DF1700035BE1A /* OversizeTextMessageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OversizeTextMessageViewController.swift; sourceTree = "<group>"; };
34B3F85B1E8DF1700035BE1A /* OWSConversationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsViewController.h; sourceTree = "<group>"; }; 34B3F85B1E8DF1700035BE1A /* OWSConversationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsViewController.h; sourceTree = "<group>"; };
34B3F85C1E8DF1700035BE1A /* OWSConversationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSConversationSettingsViewController.m; sourceTree = "<group>"; }; 34B3F85C1E8DF1700035BE1A /* OWSConversationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSConversationSettingsViewController.m; sourceTree = "<group>"; };
34B3F85D1E8DF1700035BE1A /* OWSLinkDeviceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkDeviceViewController.h; sourceTree = "<group>"; }; 34B3F85D1E8DF1700035BE1A /* OWSLinkDeviceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkDeviceViewController.h; sourceTree = "<group>"; };
@ -1048,7 +1046,6 @@
34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */, 34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */,
34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */, 34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */,
34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */, 34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */,
34B3F85A1E8DF1700035BE1A /* OversizeTextMessageViewController.swift */,
34CCAF391F0C2748004084F4 /* OWSAddToContactViewController.h */, 34CCAF391F0C2748004084F4 /* OWSAddToContactViewController.h */,
34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */, 34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */,
34533F161EA8D2070006114F /* OWSAudioAttachmentPlayer.h */, 34533F161EA8D2070006114F /* OWSAudioAttachmentPlayer.h */,
@ -2291,7 +2288,6 @@
34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */, 34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */,
76EB068618170B34006006FC /* ContactTableViewCell.m in Sources */, 76EB068618170B34006006FC /* ContactTableViewCell.m in Sources */,
3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */, 3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */,
34B3F8881E8DF1700035BE1A /* OversizeTextMessageViewController.swift in Sources */,
452314A01F7E9E18003A428C /* DirectionalPanGestureRecognizer.swift in Sources */, 452314A01F7E9E18003A428C /* DirectionalPanGestureRecognizer.swift in Sources */,
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */, 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */,
34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */, 34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */,

@ -20,14 +20,14 @@ NS_ASSUME_NONNULL_BEGIN
imageView:(UIView *)imageView; imageView:(UIView *)imageView;
- (void)didTapVideoViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; - (void)didTapVideoViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream;
- (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream; - (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream;
- (void)didTapOversizeTextMessage:(NSString *)displayableText attachmentStream:(TSAttachmentStream *)attachmentStream; - (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem;
- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem - (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem
attachmentPointer:(TSAttachmentPointer *)attachmentPointer; attachmentPointer:(TSAttachmentPointer *)attachmentPointer;
- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message; - (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message;
- (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer - (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
viewItem:(ConversationViewItem *)conversationItem; viewItem:(ConversationViewItem *)conversationItem;
- (void)showMetadataViewForMessage:(TSMessage *)message; - (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem;
#pragma mark - System Cell #pragma mark - System Cell

@ -73,6 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) BubbleMaskingView *payloadView; @property (nonatomic) BubbleMaskingView *payloadView;
@property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) UILabel *dateHeaderLabel;
@property (nonatomic) UITextView *textView; @property (nonatomic) UITextView *textView;
@property (nonatomic, nullable) UILabel *tapForMoreLabel;
@property (nonatomic, nullable) UIImageView *bubbleImageView; @property (nonatomic, nullable) UIImageView *bubbleImageView;
@property (nonatomic, nullable) AttachmentUploadView *attachmentUploadView; @property (nonatomic, nullable) AttachmentUploadView *attachmentUploadView;
@property (nonatomic, nullable) UIImageView *stillImageView; @property (nonatomic, nullable) UIImageView *stillImageView;
@ -130,7 +131,6 @@ NS_ASSUME_NONNULL_BEGIN
self.textView = [UITextView new]; self.textView = [UITextView new];
// Honor dynamic type in the message bodies. // Honor dynamic type in the message bodies.
self.textView.font = [self textMessageFont]; self.textView.font = [self textMessageFont];
self.textView.font = [UIFont ows_regularFontWithSize:16.f];
self.textView.backgroundColor = [UIColor clearColor]; self.textView.backgroundColor = [UIColor clearColor];
self.textView.opaque = NO; self.textView.opaque = NO;
self.textView.editable = NO; self.textView.editable = NO;
@ -183,17 +183,27 @@ NS_ASSUME_NONNULL_BEGIN
return [UIFont ows_dynamicTypeBodyFont]; return [UIFont ows_dynamicTypeBodyFont];
} }
- (UIFont *)tapForMoreFont
{
return [UIFont ows_regularFontWithSize:12.f];
}
- (CGFloat)tapForMoreHeight
{
return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25);
}
- (OWSMessageCellType)cellType - (OWSMessageCellType)cellType
{ {
return self.viewItem.messageCellType; return self.viewItem.messageCellType;
} }
- (nullable NSString *)textMessage - (nullable DisplayableText *)displayableText
{ {
// This should always be valid for the appropriate cell types. // This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem.textMessage); OWSAssert(self.viewItem.displayableText);
return self.viewItem.textMessage; return self.viewItem.displayableText;
} }
- (nullable TSAttachmentStream *)attachmentStream - (nullable TSAttachmentStream *)attachmentStream
@ -571,7 +581,7 @@ NS_ASSUME_NONNULL_BEGIN
{ {
self.bubbleImageView.hidden = NO; self.bubbleImageView.hidden = NO;
self.textView.hidden = NO; self.textView.hidden = NO;
self.textView.text = self.textMessage; self.textView.text = self.displayableText.displayText;
UIColor *textColor = [self textColor]; UIColor *textColor = [self textColor];
self.textView.textColor = textColor; self.textView.textColor = textColor;
self.textView.font = [self textMessageFont]; self.textView.font = [self textMessageFont];
@ -592,12 +602,34 @@ NS_ASSUME_NONNULL_BEGIN
= (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent); = (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent);
} }
self.contentConstraints = @[ if (self.displayableText.isTextTruncated) {
[self.textView autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin], self.tapForMoreLabel = [UILabel new];
[self.textView autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin], self.tapForMoreLabel.text = NSLocalizedString(@"CONVERSATION_VIEW_OVERSIZE_TEXT_TAP_FOR_MORE",
[self.textView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.textVMargin], @"Indicator on truncated text messages that they can be tapped to see the entire text message.");
[self.textView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.textVMargin], self.tapForMoreLabel.font = [self tapForMoreFont];
]; self.tapForMoreLabel.textColor = [textColor colorWithAlphaComponent:0.85];
self.tapForMoreLabel.textAlignment = [self.tapForMoreLabel textAlignmentUnnatural];
[self.bubbleImageView addSubview:self.tapForMoreLabel];
self.contentConstraints = @[
[self.textView autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin],
[self.textView autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin],
[self.textView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.textVMargin],
[self.tapForMoreLabel autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin],
[self.tapForMoreLabel autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin],
[self.tapForMoreLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.textView],
[self.tapForMoreLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.textVMargin],
[self.tapForMoreLabel autoSetDimension:ALDimensionHeight toSize:self.tapForMoreHeight],
];
} else {
self.contentConstraints = @[
[self.textView autoPinLeadingToSuperviewWithMargin:self.textLeadingMargin],
[self.textView autoPinTrailingToSuperviewWithMargin:self.textTrailingMargin],
[self.textView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.textVMargin],
[self.textView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.textVMargin],
];
}
} }
- (void)loadForStillImageDisplay - (void)loadForStillImageDisplay
@ -772,11 +804,12 @@ NS_ASSUME_NONNULL_BEGIN
CGFloat textVMargin = self.textVMargin; CGFloat textVMargin = self.textVMargin;
const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin)); const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin));
self.textView.text = self.textMessage; self.textView.text = self.displayableText.displayText;
self.textView.font = [self textMessageFont]; self.textView.font = [self textMessageFont];
CGSize textSize = [self.textView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]; CGSize textSize = [self.textView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)];
CGFloat tapForMoreHeight = (self.displayableText.isTextTruncated ? [self tapForMoreHeight] : 0.f);
cellSize = CGSizeMake((CGFloat)ceil(textSize.width + leftMargin + rightMargin), cellSize = CGSizeMake((CGFloat)ceil(textSize.width + leftMargin + rightMargin),
(CGFloat)ceil(textSize.height + textVMargin * 2)); (CGFloat)ceil(textSize.height + textVMargin * 2 + tapForMoreHeight));
break; break;
} }
case OWSMessageCellType_StillImage: case OWSMessageCellType_StillImage:
@ -907,6 +940,8 @@ NS_ASSUME_NONNULL_BEGIN
self.textView.text = nil; self.textView.text = nil;
self.textView.hidden = YES; self.textView.hidden = YES;
self.textView.dataDetectorTypes = UIDataDetectorTypeNone; self.textView.dataDetectorTypes = UIDataDetectorTypeNone;
[self.tapForMoreLabel removeFromSuperview];
self.tapForMoreLabel = nil;
self.footerLabel.text = nil; self.footerLabel.text = nil;
self.footerLabel.hidden = YES; self.footerLabel.hidden = YES;
self.bubbleImageView.image = nil; self.bubbleImageView.image = nil;
@ -990,9 +1025,11 @@ NS_ASSUME_NONNULL_BEGIN
switch (self.cellType) { switch (self.cellType) {
case OWSMessageCellType_TextMessage: case OWSMessageCellType_TextMessage:
break;
case OWSMessageCellType_OversizeTextMessage: case OWSMessageCellType_OversizeTextMessage:
[self.delegate didTapOversizeTextMessage:self.textMessage attachmentStream:self.attachmentStream]; if (self.displayableText.isTextTruncated) {
[self.delegate didTapTruncatedTextMessage:self.viewItem];
return;
}
break; break;
case OWSMessageCellType_StillImage: case OWSMessageCellType_StillImage:
[self.delegate didTapImageViewItem:self.viewItem [self.delegate didTapImageViewItem:self.viewItem
@ -1093,7 +1130,7 @@ NS_ASSUME_NONNULL_BEGIN
{ {
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
[self.delegate showMetadataViewForMessage:self.message]; [self.delegate showMetadataViewForViewItem:self.viewItem];
} }
- (BOOL)canBecomeFirstResponder - (BOOL)canBecomeFirstResponder

@ -2040,18 +2040,17 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
[self.audioAttachmentPlayer play]; [self.audioAttachmentPlayer play];
} }
- (void)didTapOversizeTextMessage:(NSString *)displayableText attachmentStream:(TSAttachmentStream *)attachmentStream - (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
OWSAssert(displayableText); OWSAssert(conversationItem);
OWSAssert(attachmentStream);
// Tapping on incoming and outgoing "oversize text messages" should show the TSMessage *message = (TSMessage *)conversationItem.interaction;
// "oversize text message" view. MessageMetadataViewController *view =
OversizeTextMessageViewController *messageVC = [[MessageMetadataViewController alloc] initWithViewItem:conversationItem
[[OversizeTextMessageViewController alloc] initWithDisplayableText:displayableText message:message
attachmentStream:attachmentStream]; mode:MessageMetadataViewModeFocusOnMessage];
[self.navigationController pushViewController:messageVC animated:YES]; [self.navigationController pushViewController:view animated:YES];
} }
- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem - (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem
@ -2074,12 +2073,16 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
[self handleUnsentMessageTap:message]; [self handleUnsentMessageTap:message];
} }
- (void)showMetadataViewForMessage:(TSMessage *)message - (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
OWSAssert(message); OWSAssert(conversationItem);
MessageMetadataViewController *view = [[MessageMetadataViewController alloc] initWithMessage:message]; TSMessage *message = (TSMessage *)conversationItem.interaction;
MessageMetadataViewController *view =
[[MessageMetadataViewController alloc] initWithViewItem:conversationItem
message:message
mode:MessageMetadataViewModeFocusOnMetadata];
[self.navigationController pushViewController:view animated:YES]; [self.navigationController pushViewController:view animated:YES];
} }
@ -4085,7 +4088,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
// want to inadvertently clobber it here. // want to inadvertently clobber it here.
OWSAssert(self.navigationController.delegate == nil) self.navigationController.delegate = self; OWSAssert(self.navigationController.delegate == nil) self.navigationController.delegate = self;
TSMessage *message = (TSMessage *)interaction; TSMessage *message = (TSMessage *)interaction;
MessageMetadataViewController *view = [[MessageMetadataViewController alloc] initWithMessage:message]; MessageMetadataViewController *view =
[[MessageMetadataViewController alloc] initWithViewItem:conversationItem
message:message
mode:MessageMetadataViewModeFocusOnMetadata];
[self.navigationController pushViewController:view animated:YES]; [self.navigationController pushViewController:view animated:YES];
} else { } else {
OWSFail(@"%@ Can't show message metadata for message of type: %@", self.tag, [interaction class]); OWSFail(@"%@ Can't show message metadata for message of type: %@", self.tag, [interaction class]);

@ -22,6 +22,20 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
#pragma mark -
@interface DisplayableText : NSObject
@property (nonatomic) NSString *fullText;
@property (nonatomic) NSString *displayText;
@property (nonatomic) BOOL isTextTruncated;
@end
#pragma mark -
@class ConversationViewCell; @class ConversationViewCell;
@class OWSAudioMessageView; @class OWSAudioMessageView;
@class TSAttachmentPointer; @class TSAttachmentPointer;
@ -72,19 +86,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
- (CGFloat)audioProgressSeconds; - (CGFloat)audioProgressSeconds;
#pragma mark - Expiration
// TODO:
//@property (nonatomic, readonly) BOOL isExpiringMessage;
//@property (nonatomic, readonly) BOOL shouldStartExpireTimer;
//@property (nonatomic, readonly) double expiresAtSeconds;
//@property (nonatomic, readonly) uint32_t expiresInSeconds;
#pragma mark - View State Caching #pragma mark - View State Caching
// These methods only apply to text & attachment messages. // These methods only apply to text & attachment messages.
- (OWSMessageCellType)messageCellType; - (OWSMessageCellType)messageCellType;
- (nullable NSString *)textMessage; - (nullable DisplayableText *)displayableText;
- (nullable TSAttachmentStream *)attachmentStream; - (nullable TSAttachmentStream *)attachmentStream;
- (nullable TSAttachmentPointer *)attachmentPointer; - (nullable TSAttachmentPointer *)attachmentPointer;
- (CGSize)contentSize; - (CGSize)contentSize;
@ -93,13 +99,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
// if a load has previously failed. // if a load has previously failed.
@property (nonatomic) BOOL didCellMediaFailToLoad; @property (nonatomic) BOOL didCellMediaFailToLoad;
// TODO:
//// Cells will request that this adapter clear its cached media views,
//// but the adapter should only honor requests from the last cell to
//// use its views.
//- (void)setLastPresentingCell:(nullable id)cell;
//- (void)clearCachedMediaViewsIfLastPresentingCell:(id)cell;
#pragma mark - UIMenuController #pragma mark - UIMenuController
- (NSArray<UIMenuItem *> *)menuControllerItems; - (NSArray<UIMenuItem *> *)menuControllerItems;

@ -37,6 +37,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
} }
} }
#pragma mark -
@implementation DisplayableText
@end
#pragma mark -
@interface ConversationViewItem () @interface ConversationViewItem ()
@property (nonatomic, nullable) NSValue *cachedCellSize; @property (nonatomic, nullable) NSValue *cachedCellSize;
@ -50,7 +58,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
@property (nonatomic) BOOL hasViewState; @property (nonatomic) BOOL hasViewState;
@property (nonatomic) OWSMessageCellType messageCellType; @property (nonatomic) OWSMessageCellType messageCellType;
@property (nonatomic, nullable) NSString *textMessage; @property (nonatomic, nullable) DisplayableText *displayableText;
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream; @property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer; @property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
@property (nonatomic) CGSize contentSize; @property (nonatomic) CGSize contentSize;
@ -85,7 +93,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.hasViewState = NO; self.hasViewState = NO;
self.messageCellType = OWSMessageCellType_Unknown; self.messageCellType = OWSMessageCellType_Unknown;
self.textMessage = nil; self.displayableText = nil;
self.attachmentStream = nil; self.attachmentStream = nil;
self.attachmentPointer = nil; self.attachmentPointer = nil;
self.contentSize = CGSizeZero; self.contentSize = CGSizeZero;
@ -266,49 +274,71 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return cache; return cache;
} }
- (NSString *)displayableTextForText:(NSString *)text interactionId:(NSString *)interactionId - (DisplayableText *)displayableTextForText:(NSString *)text interactionId:(NSString *)interactionId
{ {
OWSAssert(text); OWSAssert(text);
OWSAssert(interactionId.length > 0); OWSAssert(interactionId.length > 0);
NSString *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId]; return [self displayableTextForInteractionId:interactionId
textBlock:^{
return text;
}];
}
- (DisplayableText *)displayableTextForAttachmentStream:(TSAttachmentStream *)attachmentStream
interactionId:(NSString *)interactionId
{
OWSAssert(attachmentStream);
OWSAssert(interactionId.length > 0);
return [self displayableTextForInteractionId:interactionId
textBlock:^{
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
NSString *text =
[[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
return text;
}];
}
- (DisplayableText *)displayableTextForInteractionId:(NSString *)interactionId
textBlock:(NSString * (^_Nonnull)())textBlock
{
OWSAssert(interactionId.length > 0);
DisplayableText *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId];
if (!displayableText) { if (!displayableText) {
NSString *text = textBlock();
// Only show up to 2kb of text. // Only show up to 2kb of text.
const NSUInteger kMaxTextDisplayLength = 2 * 1024; const NSUInteger kMaxTextDisplayLength = 2 * 1024;
text = [text ows_stripped]; text = [text ows_stripped];
displayableText = [[DisplayableTextFilter new] displayableText:text]; NSString *fullText = [[[DisplayableTextFilter new] displayableText:text] ows_stripped];
if (displayableText.length > kMaxTextDisplayLength) { displayableText = [DisplayableText new];
if (!fullText) {
displayableText.fullText = @"";
} else {
displayableText.fullText = fullText;
}
NSString *displayText = fullText;
if (displayText.length > kMaxTextDisplayLength) {
// Trim whitespace before _AND_ after slicing the snipper from the string. // Trim whitespace before _AND_ after slicing the snipper from the string.
NSString *snippet = [ NSString *snippet = [[displayText substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] ows_stripped];
[[displayableText ows_stripped] substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] ows_stripped]; displayText = [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT",
displayableText = [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT", @"A display format for oversize text messages."),
@"A display format for oversize text messages."), snippet];
snippet]; displayableText.isTextTruncated = YES;
} }
if (!displayableText) { if (!displayText) {
displayableText = @""; displayableText.displayText = @"";
} else {
displayableText.displayText = displayText;
} }
[[self displayableTextCache] setObject:displayableText forKey:interactionId]; [[self displayableTextCache] setObject:displayableText forKey:interactionId];
} }
return displayableText; return displayableText;
} }
- (NSString *)displayableTextForAttachmentStream:(TSAttachmentStream *)attachmentStream
interactionId:(NSString *)interactionId
{
OWSAssert(attachmentStream);
OWSAssert(interactionId.length > 0);
NSString *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId];
if (displayableText) {
return displayableText;
}
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
NSString *text = [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
return [self displayableTextForText:text interactionId:interactionId];
}
- (void)ensureViewState - (void)ensureViewState
{ {
OWSAssert([self.interaction isKindOfClass:[TSMessage class]]); OWSAssert([self.interaction isKindOfClass:[TSMessage class]]);
@ -321,7 +351,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
TSMessage *interaction = (TSMessage *)self.interaction; TSMessage *interaction = (TSMessage *)self.interaction;
if (interaction.body != nil) { if (interaction.body != nil) {
self.messageCellType = OWSMessageCellType_TextMessage; self.messageCellType = OWSMessageCellType_TextMessage;
self.textMessage = [self displayableTextForText:interaction.body interactionId:interaction.uniqueId]; self.displayableText = [self displayableTextForText:interaction.body interactionId:interaction.uniqueId];
return; return;
} else { } else {
NSString *_Nullable attachmentId = interaction.attachmentIds.firstObject; NSString *_Nullable attachmentId = interaction.attachmentIds.firstObject;
@ -332,8 +362,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
if ([attachment.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]) { if ([attachment.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]) {
self.messageCellType = OWSMessageCellType_OversizeTextMessage; self.messageCellType = OWSMessageCellType_OversizeTextMessage;
self.textMessage = [self displayableTextForAttachmentStream:self.attachmentStream self.displayableText = [self displayableTextForAttachmentStream:self.attachmentStream
interactionId:interaction.uniqueId]; interactionId:interaction.uniqueId];
return; return;
} else if ([self.attachmentStream isAnimated] || [self.attachmentStream isImage] || } else if ([self.attachmentStream isAnimated] || [self.attachmentStream isImage] ||
[self.attachmentStream isVideo]) { [self.attachmentStream isVideo]) {
@ -387,13 +417,17 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return _messageCellType; return _messageCellType;
} }
- (nullable NSString *)textMessage - (nullable DisplayableText *)displayableText
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
[self ensureViewState]; [self ensureViewState];
return _textMessage; OWSAssert(_displayableText);
OWSAssert(_displayableText.displayText);
OWSAssert(_displayableText.fullText);
return _displayableText;
} }
- (nullable TSAttachmentStream *)attachmentStream - (nullable TSAttachmentStream *)attachmentStream
@ -495,7 +529,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
switch (self.messageCellType) { switch (self.messageCellType) {
case OWSMessageCellType_TextMessage: case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage: case OWSMessageCellType_OversizeTextMessage:
[UIPasteboard.generalPasteboard setString:self.textMessage]; OWSAssert(self.displayableText);
[UIPasteboard.generalPasteboard setString:self.displayableText.fullText];
break; break;
case OWSMessageCellType_StillImage: case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage: case OWSMessageCellType_AnimatedImage:
@ -527,7 +562,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
switch (self.messageCellType) { switch (self.messageCellType) {
case OWSMessageCellType_TextMessage: case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage: case OWSMessageCellType_OversizeTextMessage:
[AttachmentSharing showShareUIForText:self.textMessage]; OWSAssert(self.displayableText);
[AttachmentSharing showShareUIForText:self.displayableText.fullText];
break; break;
case OWSMessageCellType_StillImage: case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage: case OWSMessageCellType_AnimatedImage:
@ -618,7 +654,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
switch (self.messageCellType) { switch (self.messageCellType) {
case OWSMessageCellType_TextMessage: case OWSMessageCellType_TextMessage:
case OWSMessageCellType_OversizeTextMessage: case OWSMessageCellType_OversizeTextMessage:
return self.textMessage.length > 0; OWSAssert(self.displayableText);
return self.displayableText.fullText.length > 0;
case OWSMessageCellType_StillImage: case OWSMessageCellType_StillImage:
case OWSMessageCellType_AnimatedImage: case OWSMessageCellType_AnimatedImage:
case OWSMessageCellType_Audio: case OWSMessageCellType_Audio:

@ -101,15 +101,9 @@ class CropScaleImageViewController: OWSViewController {
// MARK: Initializers // MARK: Initializers
@available(*, unavailable, message:"use srcImage:successCompletion: constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
self.srcImage = UIImage(named:"fail")! fatalError("\(#function) is unimplemented.")
self.successCompletion = { _ in
}
super.init(coder: aDecoder)
owsFail("\(self.TAG) invalid constructor")
configureCropAndScale()
} }
required init(srcImage: UIImage, successCompletion : @escaping (UIImage) -> Void) { required init(srcImage: UIImage, successCompletion : @escaping (UIImage) -> Void) {

@ -627,7 +627,7 @@ NS_ASSUME_NONNULL_BEGIN
@"pulvinar a, rhoncus vitae nisl. Sed mi nunc, tempus at varius in, malesuada vitae " @"pulvinar a, rhoncus vitae nisl. Sed mi nunc, tempus at varius in, malesuada vitae "
@"dui. Vivamus efficitur pulvinar erat vitae congue. Proin vehicula turpis non felis " @"dui. Vivamus efficitur pulvinar erat vitae congue. Proin vehicula turpis non felis "
@"congue facilisis. Nullam aliquet dapibus ligula ac mollis. Etiam sit amet posuere " @"congue facilisis. Nullam aliquet dapibus ligula ac mollis. Etiam sit amet posuere "
@"lorem, in rhoncus nisi."]; @"lorem, in rhoncus nisi.\n\n"];
} }
DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:message]; DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithOversizeText:message];

@ -22,8 +22,7 @@ class GifPickerLayout: UICollectionViewLayout {
@available(*, unavailable, message:"use other constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) fatalError("\(#function) is unimplemented.")
owsFail("\(self.TAG) invalid constructor")
} }
override init() { override init() {

@ -4,6 +4,12 @@
import Foundation import Foundation
@objc
enum MessageMetadataViewMode: UInt {
case focusOnMessage
case focusOnMetadata
}
class MessageMetadataViewController: OWSViewController { class MessageMetadataViewController: OWSViewController {
static let TAG = "[MessageMetadataViewController]" static let TAG = "[MessageMetadataViewController]"
@ -18,6 +24,8 @@ class MessageMetadataViewController: OWSViewController {
let bubbleFactory = OWSMessagesBubbleImageFactory() let bubbleFactory = OWSMessagesBubbleImageFactory()
var bubbleView: UIView? var bubbleView: UIView?
let mode: MessageMetadataViewMode
let viewItem: ConversationViewItem
var message: TSMessage var message: TSMessage
var mediaMessageView: MediaMessageView? var mediaMessageView: MediaMessageView?
@ -32,18 +40,16 @@ class MessageMetadataViewController: OWSViewController {
// MARK: Initializers // MARK: Initializers
@available(*, unavailable, message:"use message: constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
self.contactsManager = Environment.getCurrent().contactsManager fatalError("\(#function) is unimplemented.")
self.message = TSMessage()
self.databaseConnection = TSStorageManager.shared().newDatabaseConnection()!
super.init(coder: aDecoder)
owsFail("\(self.TAG) invalid constructor")
} }
required init(message: TSMessage) { required init(viewItem: ConversationViewItem, message: TSMessage, mode: MessageMetadataViewMode) {
self.contactsManager = Environment.getCurrent().contactsManager self.contactsManager = Environment.getCurrent().contactsManager
self.viewItem = viewItem
self.message = message self.message = message
self.mode = mode
self.databaseConnection = TSStorageManager.shared().newDatabaseConnection()! self.databaseConnection = TSStorageManager.shared().newDatabaseConnection()!
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
@ -62,12 +68,15 @@ class MessageMetadataViewController: OWSViewController {
createViews() createViews()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
if let bubbleView = self.bubbleView {
let showAtLeast: CGFloat = 50 if mode == .focusOnMetadata {
let middleCenter = CGPoint(x: bubbleView.frame.origin.x + bubbleView.frame.width / 2, if let bubbleView = self.bubbleView {
y: bubbleView.frame.origin.y + bubbleView.frame.height - showAtLeast) let showAtLeast: CGFloat = 50
let offset = bubbleView.superview!.convert(middleCenter, to: scrollView) let middleCenter = CGPoint(x: bubbleView.frame.origin.x + bubbleView.frame.width / 2,
self.scrollView!.setContentOffset(offset, animated: false) y: bubbleView.frame.origin.y + bubbleView.frame.height - showAtLeast)
let offset = bubbleView.superview!.convert(middleCenter, to: scrollView)
self.scrollView!.setContentOffset(offset, animated: false)
}
} }
NotificationCenter.default.addObserver(self, NotificationCenter.default.addObserver(self,
@ -267,76 +276,79 @@ class MessageMetadataViewController: OWSViewController {
} }
} }
private func displayableTextIfText() -> String? {
let messageCellType = viewItem.messageCellType()
guard messageCellType == .textMessage ||
messageCellType == .oversizeTextMessage else {
return nil
}
guard let displayableText = viewItem.displayableText() else {
return nil
}
let messageBody = displayableText.fullText
guard messageBody.characters.count > 0 else {
return nil
}
return messageBody
}
private func contentRows() -> [UIView] { private func contentRows() -> [UIView] {
var rows = [UIView]() var rows = [UIView]()
if message.attachmentIds.count > 0 { if let messageBody = displayableTextIfText() {
self.messageBody = messageBody
let isIncoming = self.message as? TSIncomingMessage != nil
// UITextView can't render extremely long text due to constraints
// on the size of its backing buffer, especially when we're
// embedding it "full-size' within a UIScrollView as we do in this view.
//
// TODO: We could use CoreText instead, or we could dynamically
// manipulate the size/position of our UITextView to
// reflect scroll state.
let bodyLabel = UITextView()
bodyLabel.font = UIFont.ows_dynamicTypeBody()
bodyLabel.backgroundColor = UIColor.clear
bodyLabel.isOpaque = false
bodyLabel.isEditable = false
bodyLabel.isSelectable = true
bodyLabel.textContainerInset = UIEdgeInsets.zero
bodyLabel.contentInset = UIEdgeInsets.zero
bodyLabel.isScrollEnabled = false
bodyLabel.textColor = isIncoming ? UIColor.black : UIColor.white
bodyLabel.text = messageBody
let bubbleImageData = isIncoming ? bubbleFactory.incoming : bubbleFactory.outgoing
let leadingMargin: CGFloat = isIncoming ? 15 : 10
let trailingMargin: CGFloat = isIncoming ? 10 : 15
let bubbleView = UIImageView(image: bubbleImageData.messageBubbleImage)
self.bubbleView = bubbleView
bubbleView.layer.cornerRadius = 10
bubbleView.addSubview(bodyLabel)
bodyLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: leadingMargin)
bodyLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: trailingMargin)
bodyLabel.autoPinHeightToSuperview(withMargin: 10)
let row = UIView()
row.addSubview(bubbleView)
bubbleView.autoPinHeightToSuperview()
bubbleView.autoPinLeadingToSuperview(withMargin: 10)
bubbleView.autoPinTrailingToSuperview(withMargin: 10)
rows.append(row)
} else if message.attachmentIds.count > 0 {
rows += addAttachmentRows() rows += addAttachmentRows()
} else if let messageBody = message.body { } else {
// TODO: We should also display "oversize text messages" in a // Neither attachment nor body.
// similar way. owsFail("\(self.TAG) Message has neither attachment nor body.")
if messageBody.characters.count > 0 { rows.append(valueRow(name: NSLocalizedString("MESSAGE_METADATA_VIEW_NO_ATTACHMENT_OR_BODY",
self.messageBody = messageBody comment: "Label for messages without a body or attachment in the 'message metadata' view."),
value: ""))
let isIncoming = self.message as? TSIncomingMessage != nil
let bodyLabel = UILabel()
bodyLabel.textColor = isIncoming ? UIColor.black : UIColor.white
bodyLabel.font = UIFont.ows_regularFont(withSize: 16)
bodyLabel.text = messageBody
bodyLabel.numberOfLines = 0
bodyLabel.lineBreakMode = .byWordWrapping
let bubbleImageData = isIncoming ? bubbleFactory.incoming : bubbleFactory.outgoing
let leadingMargin: CGFloat = isIncoming ? 15 : 10
let trailingMargin: CGFloat = isIncoming ? 10 : 15
let bubbleView = UIImageView(image: bubbleImageData.messageBubbleImage)
self.bubbleView = bubbleView
bubbleView.layer.cornerRadius = 10
bubbleView.addSubview(bodyLabel)
bodyLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: leadingMargin)
bodyLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: trailingMargin)
bodyLabel.autoPinHeightToSuperview(withMargin: 10)
// Try to hug content both horizontally and vertically, but *prefer* wide and short, to narrow and tall.
// While never exceeding max width, and never cropping content.
bodyLabel.setContentHuggingPriority(UILayoutPriorityDefaultLow, for: .horizontal)
bodyLabel.setContentHuggingPriority(UILayoutPriorityDefaultHigh, for: .vertical)
bodyLabel.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
bodyLabel.autoSetDimension(.width, toSize: ScaleFromIPhone5(210), relation: .lessThanOrEqual)
let bubbleSpacer = UIView()
let row = UIView()
row.addSubview(bubbleView)
row.addSubview(bubbleSpacer)
bubbleView.autoPinHeightToSuperview()
bubbleSpacer.autoPinHeightToSuperview()
bubbleSpacer.setContentHuggingLow()
if isIncoming {
bubbleView.autoPinLeadingToSuperview(withMargin: 10)
bubbleSpacer.autoPinLeading(toTrailingOf: bubbleView)
bubbleSpacer.autoPinTrailingToSuperview(withMargin: 10)
} else {
bubbleSpacer.autoPinLeadingToSuperview(withMargin: 10)
bubbleView.autoPinLeading(toTrailingOf: bubbleSpacer)
bubbleView.autoPinTrailingToSuperview(withMargin: 10)
}
rows.append(row)
} else {
// Neither attachment nor body.
owsFail("\(self.TAG) Message has neither attachment nor body.")
rows.append(valueRow(name: NSLocalizedString("MESSAGE_METADATA_VIEW_NO_ATTACHMENT_OR_BODY",
comment: "Label for messages without a body or attachment in the 'message metadata' view."),
value: ""))
}
} }
let spacer = UIView() let spacer = UIView()

@ -23,11 +23,9 @@ class ModalActivityIndicatorViewController: OWSViewController {
// MARK: Initializers // MARK: Initializers
@available(*, unavailable, message:"use canCancel:completion: constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
self.canCancel = false fatalError("\(#function) is unimplemented.")
super.init(coder: aDecoder)
owsFail("\(self.TAG) invalid constructor")
} }
required init(canCancel: Bool) { required init(canCancel: Bool) {

@ -1,75 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
import WebRTC
import PromiseKit
class OversizeTextMessageViewController: OWSViewController {
let TAG = "[OversizeTextMessageViewController]"
let displayableText: String
let attachmentStream: TSAttachmentStream
// MARK: Initializers
@available(*, unavailable, message:"use message: constructor instead.")
required init?(coder aDecoder: NSCoder) {
displayableText = ""
attachmentStream = TSAttachmentStream(contentType:"", sourceFilename:"")
super.init(coder: aDecoder)
}
required init(displayableText: String, attachmentStream: TSAttachmentStream) {
self.displayableText = displayableText
self.attachmentStream = attachmentStream
super.init(nibName: nil, bundle: nil)
}
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = NSLocalizedString("OVERSIZE_TEXT_MESSAGE_VIEW_TITLE",
comment: "The title of the 'oversize text message' view.")
self.view.backgroundColor = UIColor.white
let textView = UITextView()
textView.textColor = UIColor.black
textView.text = displayableText
textView.font = UIFont.ows_dynamicTypeBody()
textView.isEditable = false
textView.textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 8, right: 4)
self.view.addSubview(textView)
textView.autoPinWidthToSuperview()
textView.autoPin(toTopLayoutGuideOf : self, withInset: 0)
let footerBar = UIToolbar()
footerBar.barTintColor = UIColor.ows_signalBrandBlue()
footerBar.setItems([
UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
target:nil,
action:nil),
UIBarButtonItem(barButtonSystemItem:.action,
target:self,
action:#selector(shareWasPressed)),
UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
target:nil,
action:nil)
], animated: false)
self.view.addSubview(footerBar)
footerBar.autoPinWidthToSuperview()
footerBar.autoPin(toBottomLayoutGuideOf : self, withInset: 0)
footerBar.autoPinEdge(.top, to:.bottom, of:textView)
}
func shareWasPressed(sender: UIButton) {
Logger.info("\(TAG) sharing oversize text.")
AttachmentSharing.showShareUI(for:attachmentStream.mediaURL())
}
}

@ -45,14 +45,9 @@ import UIKit
} }
} }
@available(*, unavailable, message:"use init() constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
self.horizontalBarLayer = CAShapeLayer() fatalError("\(#function) is unimplemented.")
self.progressLayer = CAShapeLayer()
super.init(coder: aDecoder)
owsFail("\(self.tag) Invalid constructor")
} }
public required init() { public required init() {

@ -30,13 +30,9 @@ import Foundation
createContent() createContent()
} }
@available(*, unavailable, message:"use default constructor instead.") @available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
button = UIButton(type:.custom) fatalError("\(#function) is unimplemented.")
super.init(coder: aDecoder)
owsFail("\(self.TAG) invalid constructor")
} }
private func createContent() { private func createContent() {

@ -76,18 +76,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"ATTACHMENT" = "Attachment"; "ATTACHMENT" = "Attachment";
/* Title for the 'attachment approval' dialog. */
"ATTACHMENT_APPROVAL_DIALOG_TITLE" = "Attachment";
/* Format string for file extension label in call interstitial view */ /* Format string for file extension label in call interstitial view */
"ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT" = "File type: %@"; "ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT" = "File type: %@";
/* Format string for file size label in call interstitial view. Embeds: {{file size as 'N mb' or 'N kb'}}. */ /* Format string for file size label in call interstitial view. Embeds: {{file size as 'N mb' or 'N kb'}}. */
"ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT" = "Size: %@"; "ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT" = "Size: %@";
/* Label for 'send' button in the 'attachment approval' dialog. */
"ATTACHMENT_APPROVAL_SEND_BUTTON" = "Send";
/* Generic filename for an attachment with no known name */ /* Generic filename for an attachment with no known name */
"ATTACHMENT_DEFAULT_FILENAME" = "Attachment"; "ATTACHMENT_DEFAULT_FILENAME" = "Attachment";
@ -238,6 +232,9 @@
/* Accessibility label for hang up call */ /* Accessibility label for hang up call */
"CALL_VIEW_HANGUP_LABEL" = "End call"; "CALL_VIEW_HANGUP_LABEL" = "End call";
/* Accessibility label for muting the microphone */
"CALL_VIEW_MUTE_LABEL" = "Mute";
/* 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.";
@ -256,9 +253,6 @@
/* Accessibility label to switch to video call */ /* Accessibility label to switch to video call */
"CALL_VIEW_SWITCH_TO_VIDEO_LABEL" = "Switch to video call"; "CALL_VIEW_SWITCH_TO_VIDEO_LABEL" = "Switch to video call";
/* Accessibility label for muting the microphone */
"CALL_VIEW_MUTE_LABEL" = "Mute";
/* notification action */ /* notification action */
"CALLBACK_BUTTON_TITLE" = "Call Back"; "CALLBACK_BUTTON_TITLE" = "Call Back";
@ -400,6 +394,9 @@
/* Title for the group of buttons show for unknown contacts offering to add them to contacts, etc. */ /* Title for the group of buttons show for unknown contacts offering to add them to contacts, etc. */
"CONVERSATION_VIEW_CONTACTS_OFFER_TITLE" = "This user is not in your contacts."; "CONVERSATION_VIEW_CONTACTS_OFFER_TITLE" = "This user is not in your contacts.";
/* Indicator on truncated text messages that they can be tapped to see the entire text message. */
"CONVERSATION_VIEW_OVERSIZE_TEXT_TAP_FOR_MORE" = "Tap For More";
/* Message shown in conversation view that offers to block an unknown user. */ /* Message shown in conversation view that offers to block an unknown user. */
"CONVERSATION_VIEW_UNKNOWN_CONTACT_BLOCK_OFFER" = "Block This User"; "CONVERSATION_VIEW_UNKNOWN_CONTACT_BLOCK_OFFER" = "Block This User";
@ -818,10 +815,10 @@
"LIST_GROUP_MEMBERS_ACTION" = "Group Members"; "LIST_GROUP_MEMBERS_ACTION" = "Group Members";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LOGGING_SECTION" = "Logging"; "load_earlier_messages" = "Load Earlier Messages";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"ME_STRING" = "Me"; "LOGGING_SECTION" = "Logging";
/* media picker option to take photo or video */ /* media picker option to take photo or video */
"MEDIA_FROM_CAMERA_BUTTON" = "Camera"; "MEDIA_FROM_CAMERA_BUTTON" = "Camera";
@ -883,8 +880,7 @@
/* Title for the 'message metadata' view. */ /* Title for the 'message metadata' view. */
"MESSAGE_METADATA_VIEW_TITLE" = "Message"; "MESSAGE_METADATA_VIEW_TITLE" = "Message";
/* message footer for delivered messages /* message status for message delivered to their recipient. */
message status for message delivered to their recipient. */
"MESSAGE_STATUS_DELIVERED" = "Delivered"; "MESSAGE_STATUS_DELIVERED" = "Delivered";
/* message footer for failed messages */ /* message footer for failed messages */
@ -958,24 +954,9 @@
Alert title when camera is not authorized */ Alert title when camera is not authorized */
"MISSING_CAMERA_PERMISSION_TITLE" = "Signal needs to access your camera."; "MISSING_CAMERA_PERMISSION_TITLE" = "Signal needs to access your camera.";
/* No comment provided by engineer. */
"MSGVIEW_MISSED_CALL_BECAUSE_OF_CHANGED_IDENTITY" = "Missed call because their safety number has changed.";
/* notification title. Embeds {{caller's name or phone number}} */ /* notification title. Embeds {{caller's name or phone number}} */
"MSGVIEW_MISSED_CALL_WITH_NAME" = "Missed call from %@."; "MSGVIEW_MISSED_CALL_WITH_NAME" = "Missed call from %@.";
/* No comment provided by engineer. */
"MSGVIEW_RECEIVED_CALL" = "You received a call from %@.";
/* No comment provided by engineer. */
"MSGVIEW_THEY_TRIED_TO_CALL_YOU" = "%@ tried to call you.";
/* No comment provided by engineer. */
"MSGVIEW_YOU_CALLED" = "You called %@.";
/* No comment provided by engineer. */
"MSGVIEW_YOU_TRIED_TO_CALL" = "You tried to call %@.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"MULTIDEVICE_PAIRING_MAX_DESC" = "You can not pair any more devices."; "MULTIDEVICE_PAIRING_MAX_DESC" = "You can not pair any more devices.";
@ -1036,6 +1017,9 @@
/* The alert title if user tries to exit the new group view without saving changes. */ /* The alert title if user tries to exit the new group view without saving changes. */
"NEW_GROUP_VIEW_UNSAVED_CHANGES_TITLE" = "Unsaved Changes"; "NEW_GROUP_VIEW_UNSAVED_CHANGES_TITLE" = "Unsaved Changes";
/* No comment provided by engineer. */
"new_message" = "New Message";
/* A label for the 'add by phone number' button in the 'new non-contact conversation' view */ /* A label for the 'add by phone number' button in the 'new non-contact conversation' view */
"NEW_NONCONTACT_CONVERSATION_VIEW_BUTTON" = "Search"; "NEW_NONCONTACT_CONVERSATION_VIEW_BUTTON" = "Search";
@ -1092,7 +1076,7 @@
"OUTGOING_INCOMPLETE_CALL" = "Unanswered outgoing call"; "OUTGOING_INCOMPLETE_CALL" = "Unanswered outgoing call";
/* A display format for oversize text messages. */ /* A display format for oversize text messages. */
"OVERSIZE_TEXT_DISPLAY_FORMAT" = "%@… [Tap For More]"; "OVERSIZE_TEXT_DISPLAY_FORMAT" = "%@…";
/* The title of the 'oversize text message' view. */ /* The title of the 'oversize text message' view. */
"OVERSIZE_TEXT_MESSAGE_VIEW_TITLE" = "Message"; "OVERSIZE_TEXT_MESSAGE_VIEW_TITLE" = "Message";

Loading…
Cancel
Save