mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'charlesmchen/jsqRewriteOversizeText'
This commit is contained in:
commit
e1526876a6
|
@ -50,7 +50,6 @@
|
|||
34B3F8851E8DF1700035BE1A /* NewGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */; };
|
||||
34B3F8861E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.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 */; };
|
||||
34B3F88A1E8DF1700035BE1A /* OWSLinkDeviceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F85E1E8DF1700035BE1A /* OWSLinkDeviceViewController.m */; };
|
||||
34B3F88B1E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8601E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m */; };
|
||||
|
@ -76,7 +75,7 @@
|
|||
34C6B0AC1FA0E46F00D35993 /* test-mp4.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0A81FA0E46F00D35993 /* test-mp4.mp4 */; };
|
||||
34C6B0AE1FA0E4AA00D35993 /* test-jpg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */; };
|
||||
34CA1C251F706B5400E51C51 /* NSAttributedString+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CA1C241F706B5400E51C51 /* NSAttributedString+OWS.m */; };
|
||||
34CA1C271F7156F300E51C51 /* MessageMetadataViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CA1C261F7156F300E51C51 /* MessageMetadataViewController.swift */; };
|
||||
34CA1C271F7156F300E51C51 /* MessageDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */; };
|
||||
34CA1C291F7164F700E51C51 /* MediaMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CA1C281F7164F700E51C51 /* MediaMessageView.swift */; };
|
||||
34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; };
|
||||
34CCAF3B1F0C2748004084F4 /* OWSAddToContactViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */; };
|
||||
|
@ -240,8 +239,8 @@
|
|||
45DF5DF31DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */; };
|
||||
45E2E9201E153B3D00457AA0 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E2E91F1E153B3D00457AA0 /* Strings.swift */; };
|
||||
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
|
||||
45E615161E8C590B0018AD52 /* DisplayableTextFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */; };
|
||||
45E615171E8C59100018AD52 /* DisplayableTextFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */; };
|
||||
45E615161E8C590B0018AD52 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E615151E8C590B0018AD52 /* DisplayableText.swift */; };
|
||||
45E615171E8C59100018AD52 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E615151E8C590B0018AD52 /* DisplayableText.swift */; };
|
||||
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */; };
|
||||
45F170AC1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
|
||||
45F170AD1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
|
||||
|
@ -477,7 +476,6 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -521,7 +519,7 @@
|
|||
34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "test-jpg.jpg"; sourceTree = "<group>"; };
|
||||
34CA1C231F706B5400E51C51 /* NSAttributedString+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+OWS.h"; sourceTree = "<group>"; };
|
||||
34CA1C241F706B5400E51C51 /* NSAttributedString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+OWS.m"; sourceTree = "<group>"; };
|
||||
34CA1C261F7156F300E51C51 /* MessageMetadataViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageMetadataViewController.swift; sourceTree = "<group>"; };
|
||||
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDetailViewController.swift; sourceTree = "<group>"; };
|
||||
34CA1C281F7164F700E51C51 /* MediaMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaMessageView.swift; sourceTree = "<group>"; };
|
||||
34CCAF361F0C0599004084F4 /* AppUpdateNag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppUpdateNag.h; sourceTree = "<group>"; };
|
||||
34CCAF371F0C0599004084F4 /* AppUpdateNag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppUpdateNag.m; sourceTree = "<group>"; };
|
||||
|
@ -709,7 +707,7 @@
|
|||
45E2E91E1E13EE3500457AA0 /* OWSCallNotificationsAdaptee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = OWSCallNotificationsAdaptee.h; path = UserInterface/OWSCallNotificationsAdaptee.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
45E2E91F1E153B3D00457AA0 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = Strings.swift; path = UserInterface/Strings.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarqueeLabel.swift; sourceTree = "<group>"; };
|
||||
45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilter.swift; sourceTree = "<group>"; };
|
||||
45E615151E8C590B0018AD52 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableText.swift; sourceTree = "<group>"; };
|
||||
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilterTest.swift; sourceTree = "<group>"; };
|
||||
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSession.swift; sourceTree = "<group>"; };
|
||||
45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSessionTest.swift; sourceTree = "<group>"; };
|
||||
|
@ -1036,7 +1034,7 @@
|
|||
34B3F84D1E8DF1700035BE1A /* LockInteractionController.h */,
|
||||
34B3F84E1E8DF1700035BE1A /* LockInteractionController.m */,
|
||||
34CA1C281F7164F700E51C51 /* MediaMessageView.swift */,
|
||||
34CA1C261F7156F300E51C51 /* MessageMetadataViewController.swift */,
|
||||
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */,
|
||||
34D9134C1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift */,
|
||||
34B3F84F1E8DF1700035BE1A /* NewContactThreadViewController.h */,
|
||||
34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */,
|
||||
|
@ -1048,7 +1046,6 @@
|
|||
34B3F8571E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m */,
|
||||
34B3F8581E8DF1700035BE1A /* NotificationSettingsViewController.h */,
|
||||
34B3F8591E8DF1700035BE1A /* NotificationSettingsViewController.m */,
|
||||
34B3F85A1E8DF1700035BE1A /* OversizeTextMessageViewController.swift */,
|
||||
34CCAF391F0C2748004084F4 /* OWSAddToContactViewController.h */,
|
||||
34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */,
|
||||
34533F161EA8D2070006114F /* OWSAudioAttachmentPlayer.h */,
|
||||
|
@ -1460,7 +1457,7 @@
|
|||
B90418E4183E9DD40038554A /* DateUtil.h */,
|
||||
B90418E5183E9DD40038554A /* DateUtil.m */,
|
||||
348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */,
|
||||
45E615151E8C590B0018AD52 /* DisplayableTextFilter.swift */,
|
||||
45E615151E8C590B0018AD52 /* DisplayableText.swift */,
|
||||
76EB04EA18170B33006006FC /* FunctionalUtil.h */,
|
||||
76EB04EB18170B33006006FC /* FunctionalUtil.m */,
|
||||
455AC69A1F4F79E500134004 /* ImageCache.swift */,
|
||||
|
@ -2286,16 +2283,15 @@
|
|||
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */,
|
||||
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */,
|
||||
45666EC61D99483D008FE134 /* OWSAvatarBuilder.m in Sources */,
|
||||
45E615161E8C590B0018AD52 /* DisplayableTextFilter.swift in Sources */,
|
||||
45E615161E8C590B0018AD52 /* DisplayableText.swift in Sources */,
|
||||
34B3F88A1E8DF1700035BE1A /* OWSLinkDeviceViewController.m in Sources */,
|
||||
34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */,
|
||||
76EB068618170B34006006FC /* ContactTableViewCell.m in Sources */,
|
||||
3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */,
|
||||
34B3F8881E8DF1700035BE1A /* OversizeTextMessageViewController.swift in Sources */,
|
||||
452314A01F7E9E18003A428C /* DirectionalPanGestureRecognizer.swift in Sources */,
|
||||
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */,
|
||||
34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */,
|
||||
34CA1C271F7156F300E51C51 /* MessageMetadataViewController.swift in Sources */,
|
||||
34CA1C271F7156F300E51C51 /* MessageDetailViewController.swift in Sources */,
|
||||
34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */,
|
||||
45F170AC1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */,
|
||||
34D1F0B71F87F8850066283D /* OWSGenericAttachmentView.m in Sources */,
|
||||
|
@ -2470,7 +2466,7 @@
|
|||
451DA3CB1F148AAD008E2423 /* CallViewController.swift in Sources */,
|
||||
456F6E201E2411A000FD2210 /* CallService.swift in Sources */,
|
||||
45A663C61F92EC760027B59E /* GroupTableViewCell.swift in Sources */,
|
||||
45E615171E8C59100018AD52 /* DisplayableTextFilter.swift in Sources */,
|
||||
45E615171E8C59100018AD52 /* DisplayableText.swift in Sources */,
|
||||
B660F6BB1C29868000687D6E /* OWSContactsManagerTest.m in Sources */,
|
||||
45A6DAD71EBBF85500893231 /* ReminderView.swift in Sources */,
|
||||
B660F6D21C29868000687D6E /* PushManagerTest.m in Sources */,
|
||||
|
|
|
@ -20,14 +20,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
imageView:(UIView *)imageView;
|
||||
- (void)didTapVideoViewItem:(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
|
||||
attachmentPointer:(TSAttachmentPointer *)attachmentPointer;
|
||||
- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message;
|
||||
- (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
|
||||
viewItem:(ConversationViewItem *)conversationItem;
|
||||
|
||||
- (void)showMetadataViewForMessage:(TSMessage *)message;
|
||||
- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem;
|
||||
|
||||
#pragma mark - System Cell
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic) BubbleMaskingView *payloadView;
|
||||
@property (nonatomic) UILabel *dateHeaderLabel;
|
||||
@property (nonatomic) UITextView *textView;
|
||||
@property (nonatomic, nullable) UILabel *tapForMoreLabel;
|
||||
@property (nonatomic, nullable) UIImageView *bubbleImageView;
|
||||
@property (nonatomic, nullable) AttachmentUploadView *attachmentUploadView;
|
||||
@property (nonatomic, nullable) UIImageView *stillImageView;
|
||||
|
@ -130,7 +131,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.textView = [UITextView new];
|
||||
// Honor dynamic type in the message bodies.
|
||||
self.textView.font = [self textMessageFont];
|
||||
self.textView.font = [UIFont ows_regularFontWithSize:16.f];
|
||||
self.textView.backgroundColor = [UIColor clearColor];
|
||||
self.textView.opaque = NO;
|
||||
self.textView.editable = NO;
|
||||
|
@ -183,17 +183,27 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [UIFont ows_dynamicTypeBodyFont];
|
||||
}
|
||||
|
||||
- (UIFont *)tapForMoreFont
|
||||
{
|
||||
return [UIFont ows_regularFontWithSize:12.f];
|
||||
}
|
||||
|
||||
- (CGFloat)tapForMoreHeight
|
||||
{
|
||||
return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25);
|
||||
}
|
||||
|
||||
- (OWSMessageCellType)cellType
|
||||
{
|
||||
return self.viewItem.messageCellType;
|
||||
}
|
||||
|
||||
- (nullable NSString *)textMessage
|
||||
- (nullable DisplayableText *)displayableText
|
||||
{
|
||||
// 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
|
||||
|
@ -571,7 +581,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
self.bubbleImageView.hidden = NO;
|
||||
self.textView.hidden = NO;
|
||||
self.textView.text = self.textMessage;
|
||||
self.textView.text = self.displayableText.displayText;
|
||||
UIColor *textColor = [self textColor];
|
||||
self.textView.textColor = textColor;
|
||||
self.textView.font = [self textMessageFont];
|
||||
|
@ -592,12 +602,34 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
= (UIDataDetectorTypeLink | UIDataDetectorTypeAddress | UIDataDetectorTypeCalendarEvent);
|
||||
}
|
||||
|
||||
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],
|
||||
];
|
||||
if (self.displayableText.isTextTruncated) {
|
||||
self.tapForMoreLabel = [UILabel new];
|
||||
self.tapForMoreLabel.text = NSLocalizedString(@"CONVERSATION_VIEW_OVERSIZE_TEXT_TAP_FOR_MORE",
|
||||
@"Indicator on truncated text messages that they can be tapped to see the entire text message.");
|
||||
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
|
||||
|
@ -772,11 +804,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
CGFloat textVMargin = self.textVMargin;
|
||||
const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin));
|
||||
|
||||
self.textView.text = self.textMessage;
|
||||
self.textView.text = self.displayableText.displayText;
|
||||
self.textView.font = [self textMessageFont];
|
||||
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),
|
||||
(CGFloat)ceil(textSize.height + textVMargin * 2));
|
||||
(CGFloat)ceil(textSize.height + textVMargin * 2 + tapForMoreHeight));
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_StillImage:
|
||||
|
@ -907,6 +940,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.textView.text = nil;
|
||||
self.textView.hidden = YES;
|
||||
self.textView.dataDetectorTypes = UIDataDetectorTypeNone;
|
||||
[self.tapForMoreLabel removeFromSuperview];
|
||||
self.tapForMoreLabel = nil;
|
||||
self.footerLabel.text = nil;
|
||||
self.footerLabel.hidden = YES;
|
||||
self.bubbleImageView.image = nil;
|
||||
|
@ -990,9 +1025,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
switch (self.cellType) {
|
||||
case OWSMessageCellType_TextMessage:
|
||||
break;
|
||||
case OWSMessageCellType_OversizeTextMessage:
|
||||
[self.delegate didTapOversizeTextMessage:self.textMessage attachmentStream:self.attachmentStream];
|
||||
if (self.displayableText.isTextTruncated) {
|
||||
[self.delegate didTapTruncatedTextMessage:self.viewItem];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case OWSMessageCellType_StillImage:
|
||||
[self.delegate didTapImageViewItem:self.viewItem
|
||||
|
@ -1093,7 +1130,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
[self.delegate showMetadataViewForMessage:self.message];
|
||||
[self.delegate showMetadataViewForViewItem:self.viewItem];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
|
|
|
@ -2040,18 +2040,18 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
|||
[self.audioAttachmentPlayer play];
|
||||
}
|
||||
|
||||
- (void)didTapOversizeTextMessage:(NSString *)displayableText attachmentStream:(TSAttachmentStream *)attachmentStream
|
||||
- (void)didTapTruncatedTextMessage:(ConversationViewItem *)conversationItem
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
OWSAssert(displayableText);
|
||||
OWSAssert(attachmentStream);
|
||||
OWSAssert(conversationItem);
|
||||
OWSAssert([conversationItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
// Tapping on incoming and outgoing "oversize text messages" should show the
|
||||
// "oversize text message" view.
|
||||
OversizeTextMessageViewController *messageVC =
|
||||
[[OversizeTextMessageViewController alloc] initWithDisplayableText:displayableText
|
||||
attachmentStream:attachmentStream];
|
||||
[self.navigationController pushViewController:messageVC animated:YES];
|
||||
TSMessage *message = (TSMessage *)conversationItem.interaction;
|
||||
MessageDetailViewController *view =
|
||||
[[MessageDetailViewController alloc] initWithViewItem:conversationItem
|
||||
message:message
|
||||
mode:MessageMetadataViewModeFocusOnMessage];
|
||||
[self.navigationController pushViewController:view animated:YES];
|
||||
}
|
||||
|
||||
- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem
|
||||
|
@ -2074,12 +2074,17 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
|||
[self handleUnsentMessageTap:message];
|
||||
}
|
||||
|
||||
- (void)showMetadataViewForMessage:(TSMessage *)message
|
||||
- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
OWSAssert(message);
|
||||
OWSAssert(conversationItem);
|
||||
OWSAssert([conversationItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
MessageMetadataViewController *view = [[MessageMetadataViewController alloc] initWithMessage:message];
|
||||
TSMessage *message = (TSMessage *)conversationItem.interaction;
|
||||
MessageDetailViewController *view =
|
||||
[[MessageDetailViewController alloc] initWithViewItem:conversationItem
|
||||
message:message
|
||||
mode:MessageMetadataViewModeFocusOnMetadata];
|
||||
[self.navigationController pushViewController:view animated:YES];
|
||||
}
|
||||
|
||||
|
@ -4085,7 +4090,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
|
|||
// want to inadvertently clobber it here.
|
||||
OWSAssert(self.navigationController.delegate == nil) self.navigationController.delegate = self;
|
||||
TSMessage *message = (TSMessage *)interaction;
|
||||
MessageMetadataViewController *view = [[MessageMetadataViewController alloc] initWithMessage:message];
|
||||
MessageDetailViewController *view =
|
||||
[[MessageDetailViewController alloc] initWithViewItem:conversationItem
|
||||
message:message
|
||||
mode:MessageMetadataViewModeFocusOnMetadata];
|
||||
[self.navigationController pushViewController:view animated:YES];
|
||||
} else {
|
||||
OWSFail(@"%@ Can't show message metadata for message of type: %@", self.tag, [interaction class]);
|
||||
|
|
|
@ -22,7 +22,10 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
|
|||
|
||||
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@class ConversationViewCell;
|
||||
@class DisplayableText;
|
||||
@class OWSAudioMessageView;
|
||||
@class TSAttachmentPointer;
|
||||
@class TSAttachmentStream;
|
||||
|
@ -72,19 +75,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
|||
|
||||
- (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
|
||||
|
||||
// These methods only apply to text & attachment messages.
|
||||
- (OWSMessageCellType)messageCellType;
|
||||
- (nullable NSString *)textMessage;
|
||||
- (nullable DisplayableText *)displayableText;
|
||||
- (nullable TSAttachmentStream *)attachmentStream;
|
||||
- (nullable TSAttachmentPointer *)attachmentPointer;
|
||||
- (CGSize)contentSize;
|
||||
|
@ -93,13 +88,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
|||
// if a load has previously failed.
|
||||
@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
|
||||
|
||||
- (NSArray<UIMenuItem *> *)menuControllerItems;
|
||||
|
|
|
@ -37,6 +37,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface ConversationViewItem ()
|
||||
|
||||
@property (nonatomic, nullable) NSValue *cachedCellSize;
|
||||
|
@ -50,7 +52,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
|
||||
@property (nonatomic) BOOL hasViewState;
|
||||
@property (nonatomic) OWSMessageCellType messageCellType;
|
||||
@property (nonatomic, nullable) NSString *textMessage;
|
||||
@property (nonatomic, nullable) DisplayableText *displayableText;
|
||||
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
|
||||
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
|
||||
@property (nonatomic) CGSize contentSize;
|
||||
|
@ -85,7 +87,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
|
||||
self.hasViewState = NO;
|
||||
self.messageCellType = OWSMessageCellType_Unknown;
|
||||
self.textMessage = nil;
|
||||
self.displayableText = nil;
|
||||
self.attachmentStream = nil;
|
||||
self.attachmentPointer = nil;
|
||||
self.contentSize = CGSizeZero;
|
||||
|
@ -266,47 +268,72 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
return cache;
|
||||
}
|
||||
|
||||
- (NSString *)displayableTextForText:(NSString *)text interactionId:(NSString *)interactionId
|
||||
- (DisplayableText *)displayableTextForText:(NSString *)text interactionId:(NSString *)interactionId
|
||||
{
|
||||
OWSAssert(text);
|
||||
OWSAssert(interactionId.length > 0);
|
||||
|
||||
NSString *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId];
|
||||
if (!displayableText) {
|
||||
// Only show up to 2kb of text.
|
||||
const NSUInteger kMaxTextDisplayLength = 2 * 1024;
|
||||
text = [text ows_stripped];
|
||||
displayableText = [[DisplayableTextFilter new] displayableText:text];
|
||||
if (displayableText.length > kMaxTextDisplayLength) {
|
||||
// Trim whitespace before _AND_ after slicing the snipper from the string.
|
||||
NSString *snippet = [
|
||||
[[displayableText ows_stripped] substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] ows_stripped];
|
||||
displayableText = [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT",
|
||||
@"A display format for oversize text messages."),
|
||||
snippet];
|
||||
}
|
||||
if (!displayableText) {
|
||||
displayableText = @"";
|
||||
}
|
||||
[[self displayableTextCache] setObject:displayableText forKey:interactionId];
|
||||
}
|
||||
return displayableText;
|
||||
return [self displayableTextForInteractionId:interactionId
|
||||
textBlock:^{
|
||||
return text;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)displayableTextForAttachmentStream:(TSAttachmentStream *)attachmentStream
|
||||
interactionId:(NSString *)interactionId
|
||||
- (DisplayableText *)displayableTextForAttachmentStream:(TSAttachmentStream *)attachmentStream
|
||||
interactionId:(NSString *)interactionId
|
||||
{
|
||||
OWSAssert(attachmentStream);
|
||||
OWSAssert(interactionId.length > 0);
|
||||
|
||||
NSString *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId];
|
||||
if (displayableText) {
|
||||
return displayableText;
|
||||
}
|
||||
return [self displayableTextForInteractionId:interactionId
|
||||
textBlock:^{
|
||||
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
|
||||
NSString *text =
|
||||
[[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
|
||||
return text;
|
||||
}];
|
||||
}
|
||||
|
||||
NSData *textData = [NSData dataWithContentsOfURL:attachmentStream.mediaURL];
|
||||
NSString *text = [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
|
||||
return [self displayableTextForText:text interactionId:interactionId];
|
||||
- (DisplayableText *)displayableTextForInteractionId:(NSString *)interactionId
|
||||
textBlock:(NSString * (^_Nonnull)())textBlock
|
||||
{
|
||||
OWSAssert(interactionId.length > 0);
|
||||
|
||||
DisplayableText *_Nullable displayableText = [[self displayableTextCache] objectForKey:interactionId];
|
||||
if (!displayableText) {
|
||||
NSString *text = textBlock();
|
||||
|
||||
// Only show up to 2kb of text.
|
||||
const NSUInteger kMaxTextDisplayLength = 2 * 1024;
|
||||
text = [text ows_stripped];
|
||||
NSString *_Nullable fullText = [DisplayableText displayableText:text];
|
||||
BOOL isTextTruncated = NO;
|
||||
if (!fullText) {
|
||||
fullText = @"";
|
||||
} else {
|
||||
fullText = fullText;
|
||||
}
|
||||
NSString *_Nullable displayText = fullText;
|
||||
if (displayText.length > kMaxTextDisplayLength) {
|
||||
// Trim whitespace before _AND_ after slicing the snipper from the string.
|
||||
NSString *snippet = [[displayText substringWithRange:NSMakeRange(0, kMaxTextDisplayLength)] ows_stripped];
|
||||
displayText = [NSString stringWithFormat:NSLocalizedString(@"OVERSIZE_TEXT_DISPLAY_FORMAT",
|
||||
@"A display format for oversize text messages."),
|
||||
snippet];
|
||||
isTextTruncated = YES;
|
||||
}
|
||||
if (!displayText) {
|
||||
displayText = @"";
|
||||
} else {
|
||||
displayText = displayText;
|
||||
}
|
||||
|
||||
displayableText =
|
||||
[[DisplayableText alloc] initWithFullText:fullText displayText:displayText isTextTruncated:isTextTruncated];
|
||||
|
||||
[[self displayableTextCache] setObject:displayableText forKey:interactionId];
|
||||
}
|
||||
return displayableText;
|
||||
}
|
||||
|
||||
- (void)ensureViewState
|
||||
|
@ -321,7 +348,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
TSMessage *interaction = (TSMessage *)self.interaction;
|
||||
if (interaction.body != nil) {
|
||||
self.messageCellType = OWSMessageCellType_TextMessage;
|
||||
self.textMessage = [self displayableTextForText:interaction.body interactionId:interaction.uniqueId];
|
||||
self.displayableText = [self displayableTextForText:interaction.body interactionId:interaction.uniqueId];
|
||||
return;
|
||||
} else {
|
||||
NSString *_Nullable attachmentId = interaction.attachmentIds.firstObject;
|
||||
|
@ -332,8 +359,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
|
||||
if ([attachment.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]) {
|
||||
self.messageCellType = OWSMessageCellType_OversizeTextMessage;
|
||||
self.textMessage = [self displayableTextForAttachmentStream:self.attachmentStream
|
||||
interactionId:interaction.uniqueId];
|
||||
self.displayableText = [self displayableTextForAttachmentStream:self.attachmentStream
|
||||
interactionId:interaction.uniqueId];
|
||||
return;
|
||||
} else if ([self.attachmentStream isAnimated] || [self.attachmentStream isImage] ||
|
||||
[self.attachmentStream isVideo]) {
|
||||
|
@ -387,13 +414,17 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
return _messageCellType;
|
||||
}
|
||||
|
||||
- (nullable NSString *)textMessage
|
||||
- (nullable DisplayableText *)displayableText
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
|
||||
[self ensureViewState];
|
||||
|
||||
return _textMessage;
|
||||
OWSAssert(_displayableText);
|
||||
OWSAssert(_displayableText.displayText);
|
||||
OWSAssert(_displayableText.fullText);
|
||||
|
||||
return _displayableText;
|
||||
}
|
||||
|
||||
- (nullable TSAttachmentStream *)attachmentStream
|
||||
|
@ -495,7 +526,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
switch (self.messageCellType) {
|
||||
case OWSMessageCellType_TextMessage:
|
||||
case OWSMessageCellType_OversizeTextMessage:
|
||||
[UIPasteboard.generalPasteboard setString:self.textMessage];
|
||||
OWSAssert(self.displayableText);
|
||||
[UIPasteboard.generalPasteboard setString:self.displayableText.fullText];
|
||||
break;
|
||||
case OWSMessageCellType_StillImage:
|
||||
case OWSMessageCellType_AnimatedImage:
|
||||
|
@ -527,7 +559,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
switch (self.messageCellType) {
|
||||
case OWSMessageCellType_TextMessage:
|
||||
case OWSMessageCellType_OversizeTextMessage:
|
||||
[AttachmentSharing showShareUIForText:self.textMessage];
|
||||
OWSAssert(self.displayableText);
|
||||
[AttachmentSharing showShareUIForText:self.displayableText.fullText];
|
||||
break;
|
||||
case OWSMessageCellType_StillImage:
|
||||
case OWSMessageCellType_AnimatedImage:
|
||||
|
@ -618,7 +651,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
switch (self.messageCellType) {
|
||||
case OWSMessageCellType_TextMessage:
|
||||
case OWSMessageCellType_OversizeTextMessage:
|
||||
return self.textMessage.length > 0;
|
||||
OWSAssert(self.displayableText);
|
||||
return self.displayableText.fullText.length > 0;
|
||||
case OWSMessageCellType_StillImage:
|
||||
case OWSMessageCellType_AnimatedImage:
|
||||
case OWSMessageCellType_Audio:
|
||||
|
|
|
@ -101,15 +101,9 @@ class CropScaleImageViewController: OWSViewController {
|
|||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use srcImage:successCompletion: constructor instead.")
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.srcImage = UIImage(named:"fail")!
|
||||
self.successCompletion = { _ in
|
||||
}
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
|
||||
configureCropAndScale()
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
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 "
|
||||
@"dui. Vivamus efficitur pulvinar erat vitae congue. Proin vehicula turpis non felis "
|
||||
@"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];
|
||||
|
|
|
@ -22,8 +22,7 @@ class GifPickerLayout: UICollectionViewLayout {
|
|||
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
override init() {
|
||||
|
|
|
@ -188,7 +188,7 @@ const NSUInteger kAvatarViewDiameter = 52;
|
|||
: [UIColor lightGrayColor]),
|
||||
}]];
|
||||
}
|
||||
NSString *displayableText = [[DisplayableTextFilter new] displayableText:thread.lastMessageLabel];
|
||||
NSString *displayableText = [DisplayableText displayableText:thread.lastMessageLabel];
|
||||
if (displayableText) {
|
||||
[snippetText appendAttributedString:[[NSAttributedString alloc]
|
||||
initWithString:displayableText
|
||||
|
|
|
@ -4,10 +4,16 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
class MessageMetadataViewController: OWSViewController {
|
||||
@objc
|
||||
enum MessageMetadataViewMode: UInt {
|
||||
case focusOnMessage
|
||||
case focusOnMetadata
|
||||
}
|
||||
|
||||
static let TAG = "[MessageMetadataViewController]"
|
||||
let TAG = "[MessageMetadataViewController]"
|
||||
class MessageDetailViewController: OWSViewController {
|
||||
|
||||
static let TAG = "[MessageDetailViewController]"
|
||||
let TAG = "[MessageDetailViewController]"
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
@ -18,6 +24,8 @@ class MessageMetadataViewController: OWSViewController {
|
|||
let bubbleFactory = OWSMessagesBubbleImageFactory()
|
||||
var bubbleView: UIView?
|
||||
|
||||
let mode: MessageMetadataViewMode
|
||||
let viewItem: ConversationViewItem
|
||||
var message: TSMessage
|
||||
|
||||
var mediaMessageView: MediaMessageView?
|
||||
|
@ -32,18 +40,16 @@ class MessageMetadataViewController: OWSViewController {
|
|||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use message: constructor instead.")
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.contactsManager = Environment.getCurrent().contactsManager
|
||||
self.message = TSMessage()
|
||||
self.databaseConnection = TSStorageManager.shared().newDatabaseConnection()!
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
required init(message: TSMessage) {
|
||||
required init(viewItem: ConversationViewItem, message: TSMessage, mode: MessageMetadataViewMode) {
|
||||
self.contactsManager = Environment.getCurrent().contactsManager
|
||||
self.viewItem = viewItem
|
||||
self.message = message
|
||||
self.mode = mode
|
||||
self.databaseConnection = TSStorageManager.shared().newDatabaseConnection()!
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
@ -62,12 +68,15 @@ class MessageMetadataViewController: OWSViewController {
|
|||
createViews()
|
||||
|
||||
self.view.layoutIfNeeded()
|
||||
if let bubbleView = self.bubbleView {
|
||||
let showAtLeast: CGFloat = 50
|
||||
let middleCenter = CGPoint(x: bubbleView.frame.origin.x + bubbleView.frame.width / 2,
|
||||
y: bubbleView.frame.origin.y + bubbleView.frame.height - showAtLeast)
|
||||
let offset = bubbleView.superview!.convert(middleCenter, to: scrollView)
|
||||
self.scrollView!.setContentOffset(offset, animated: false)
|
||||
|
||||
if mode == .focusOnMetadata {
|
||||
if let bubbleView = self.bubbleView {
|
||||
let showAtLeast: CGFloat = 50
|
||||
let middleCenter = CGPoint(x: bubbleView.frame.origin.x + bubbleView.frame.width / 2,
|
||||
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,
|
||||
|
@ -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] {
|
||||
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()
|
||||
} else if let messageBody = message.body {
|
||||
// TODO: We should also display "oversize text messages" in a
|
||||
// similar way.
|
||||
if messageBody.characters.count > 0 {
|
||||
self.messageBody = messageBody
|
||||
|
||||
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: ""))
|
||||
}
|
||||
} 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()
|
|
@ -23,11 +23,9 @@ class ModalActivityIndicatorViewController: OWSViewController {
|
|||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use canCancel:completion: constructor instead.")
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.canCancel = false
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
|
@ -4,12 +4,26 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
@objc class DisplayableTextFilter: NSObject {
|
||||
@objc class DisplayableText: NSObject {
|
||||
|
||||
let TAG = "[DisplayableTextFilter]"
|
||||
static let TAG = "[DisplayableText]"
|
||||
|
||||
let fullText: String
|
||||
let displayText: String
|
||||
let isTextTruncated: Bool
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
init(fullText: String, displayText: String, isTextTruncated: Bool) {
|
||||
self.fullText = fullText
|
||||
self.displayText = displayText
|
||||
self.isTextTruncated = isTextTruncated
|
||||
}
|
||||
|
||||
// MARK: Filter Methods
|
||||
|
||||
@objc
|
||||
func displayableText(_ text: String?) -> String? {
|
||||
class func displayableText(_ text: String?) -> String? {
|
||||
guard let text = text else {
|
||||
return nil
|
||||
}
|
||||
|
@ -17,13 +31,13 @@ import Foundation
|
|||
if (self.hasExcessiveDiacriticals(text: text)) {
|
||||
Logger.warn("\(TAG) filtering text for excessive diacriticals.")
|
||||
let filteredText = text.folding(options: .diacriticInsensitive, locale: .current)
|
||||
return filteredText
|
||||
return filteredText.ows_stripped()
|
||||
}
|
||||
|
||||
return text
|
||||
return text.ows_stripped()
|
||||
}
|
||||
|
||||
private func hasExcessiveDiacriticals(text: String) -> Bool {
|
||||
private class func hasExcessiveDiacriticals(text: String) -> Bool {
|
||||
// discard any zalgo style text, by detecting maximum number of glyphs per character
|
||||
for char in text.characters.enumerated() {
|
||||
let scalarCount = String(char.element).unicodeScalars.count
|
|
@ -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) {
|
||||
self.horizontalBarLayer = CAShapeLayer()
|
||||
self.progressLayer = CAShapeLayer()
|
||||
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
owsFail("\(self.tag) Invalid constructor")
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
public required init() {
|
||||
|
|
|
@ -30,13 +30,9 @@ import Foundation
|
|||
createContent()
|
||||
}
|
||||
|
||||
@available(*, unavailable, message:"use default constructor instead.")
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
button = UIButton(type:.custom)
|
||||
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
fatalError("\(#function) is unimplemented.")
|
||||
}
|
||||
|
||||
private func createContent() {
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
import XCTest
|
||||
|
||||
class DisplayableTextFilterTest: XCTestCase {
|
||||
|
||||
class DisplayableTextTest: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
|
@ -18,7 +18,7 @@ class DisplayableTextFilterTest: XCTestCase {
|
|||
|
||||
func testDisplayableText() {
|
||||
// Ignore default byte size limitations to test other filtering behaviors
|
||||
let filter = DisplayableTextFilter()
|
||||
let filter = DisplayableText()
|
||||
|
||||
// show plain text
|
||||
let boringText = "boring text"
|
||||
|
|
|
@ -76,18 +76,12 @@
|
|||
/* No comment provided by engineer. */
|
||||
"ATTACHMENT" = "Attachment";
|
||||
|
||||
/* Title for the 'attachment approval' dialog. */
|
||||
"ATTACHMENT_APPROVAL_DIALOG_TITLE" = "Attachment";
|
||||
|
||||
/* Format string for file extension label in call interstitial view */
|
||||
"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'}}. */
|
||||
"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 */
|
||||
"ATTACHMENT_DEFAULT_FILENAME" = "Attachment";
|
||||
|
||||
|
@ -238,6 +232,9 @@
|
|||
/* Accessibility label for hang up 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. */
|
||||
"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 */
|
||||
"CALL_VIEW_SWITCH_TO_VIDEO_LABEL" = "Switch to video call";
|
||||
|
||||
/* Accessibility label for muting the microphone */
|
||||
"CALL_VIEW_MUTE_LABEL" = "Mute";
|
||||
|
||||
/* notification action */
|
||||
"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. */
|
||||
"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. */
|
||||
"CONVERSATION_VIEW_UNKNOWN_CONTACT_BLOCK_OFFER" = "Block This User";
|
||||
|
||||
|
@ -818,10 +815,10 @@
|
|||
"LIST_GROUP_MEMBERS_ACTION" = "Group Members";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"LOGGING_SECTION" = "Logging";
|
||||
"load_earlier_messages" = "Load Earlier Messages";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"ME_STRING" = "Me";
|
||||
"LOGGING_SECTION" = "Logging";
|
||||
|
||||
/* media picker option to take photo or video */
|
||||
"MEDIA_FROM_CAMERA_BUTTON" = "Camera";
|
||||
|
@ -883,8 +880,7 @@
|
|||
/* Title for the 'message metadata' view. */
|
||||
"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 footer for failed messages */
|
||||
|
@ -958,24 +954,9 @@
|
|||
Alert title when camera is not authorized */
|
||||
"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}} */
|
||||
"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. */
|
||||
"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. */
|
||||
"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 */
|
||||
"NEW_NONCONTACT_CONVERSATION_VIEW_BUTTON" = "Search";
|
||||
|
||||
|
@ -1092,7 +1076,7 @@
|
|||
"OUTGOING_INCOMPLETE_CALL" = "Unanswered outgoing call";
|
||||
|
||||
/* 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. */
|
||||
"OVERSIZE_TEXT_MESSAGE_VIEW_TITLE" = "Message";
|
||||
|
|
Loading…
Reference in a new issue