Ensure constant bubble sizes for oversize text.

This commit is contained in:
Matthew Chen 2019-03-15 13:21:54 -04:00
parent dc168270c2
commit 67c89cb4e3
5 changed files with 107 additions and 209 deletions

View File

@ -369,7 +369,6 @@
452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452C468E1E427E200087B011 /* OutboundCallInitiator.swift */; };
452C7CA72037628B003D51A5 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170D51E315310003FC1F2 /* Weak.swift */; };
452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */; };
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */; };
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */; };
452EC6E1205FF5DC000E787C /* Bench.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EC6E0205FF5DC000E787C /* Bench.swift */; };
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
@ -1093,7 +1092,6 @@
452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactShareToExistingContactViewController.swift; sourceTree = "<group>"; };
452C468E1E427E200087B011 /* OutboundCallInitiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutboundCallInitiator.swift; sourceTree = "<group>"; };
452D1AF02081059C00A67F7F /* StringAdditionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAdditionsTest.swift; sourceTree = "<group>"; };
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentPointerView.swift; sourceTree = "<group>"; };
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryViewController.swift; sourceTree = "<group>"; };
452EC6E0205FF5DC000E787C /* Bench.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bench.swift; sourceTree = "<group>"; };
452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFetcherJob.swift; sourceTree = "<group>"; };
@ -2419,7 +2417,6 @@
76EB052B18170B33006006FC /* Views */ = {
isa = PBXGroup;
children = (
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */,
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */,
4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */,
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
@ -3602,7 +3599,6 @@
34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */,
3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */,
34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */,
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */,
45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */,
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */,
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,

View File

@ -274,15 +274,16 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
case OWSMessageCellType_GenericAttachment:
bodyMediaView = [self loadViewForGenericAttachment];
break;
case OWSMessageCellType_DownloadingAttachment:
bodyMediaView = [self loadViewForDownloadingAttachment];
break;
case OWSMessageCellType_ContactShare:
bodyMediaView = [self loadViewForContactShare];
break;
case OWSMessageCellType_MediaMessage:
bodyMediaView = [self loadViewForMediaAlbum];
break;
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
bodyMediaView = [self loadViewForOversizeTextDownload];
break;
}
if (bodyMediaView) {
@ -564,8 +565,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_ContactShare:
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
return NO;
case OWSMessageCellType_MediaMessage:
return YES;
@ -579,9 +581,10 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return NO;
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_ContactShare:
case OWSMessageCellType_MediaMessage:
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
return YES;
}
}
@ -870,31 +873,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return attachmentView;
}
- (UIView *)loadViewForDownloadingAttachment
{
OWSAssertDebug(self.viewItem.attachmentPointer);
// TODO: We probably want to do something different for attachments
// being restored from backup.
AttachmentPointerView *downloadView =
[[AttachmentPointerView alloc] initWithAttachmentPointer:self.viewItem.attachmentPointer
isIncoming:self.isIncoming
conversationStyle:self.conversationStyle];
UIView *wrapper = [UIView new];
[wrapper addSubview:downloadView];
[downloadView autoPinEdgesToSuperviewEdges];
self.loadCellContentBlock = ^{
// Do nothing.
};
self.unloadCellContentBlock = ^{
// Do nothing.
};
return wrapper;
}
- (UIView *)loadViewForContactShare
{
OWSAssertDebug(self.viewItem.contactShare);
@ -915,6 +893,26 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
return contactShareView;
}
- (UIView *)loadViewForOversizeTextDownload
{
BOOL isFailed = self.cellType == OWSMessageCellType_OversizeTextFailed;
// We can use an empty view. The progress views will display download
// progress or tap-to-retry UI.
UIView *attachmentView = [UIView new];
[self addProgressViewsIfNecessary:attachmentView];
self.loadCellContentBlock = ^{
// Do nothing.
};
self.unloadCellContentBlock = ^{
// Do nothing.
};
return attachmentView;
}
- (void)addProgressViewsIfNecessary:(UIView *)bodyMediaView
{
if (self.viewItem.attachmentStream) {
@ -1075,9 +1073,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
result = [attachmentView measureSizeWithMaxMessageWidth:maxMessageWidth];
break;
}
case OWSMessageCellType_DownloadingAttachment:
result = CGSizeMake(MIN(200, maxMessageWidth), [AttachmentPointerView measureHeight]);
break;
case OWSMessageCellType_ContactShare:
OWSAssertDebug(self.viewItem.contactShare);
@ -1122,6 +1117,12 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
}
}
break;
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
// There's no way to predict the size of the oversize text,
// so we just use a square bubble.
result = CGSizeMake(maxMessageWidth, maxMessageWidth);
break;
}
OWSAssertDebug(result.width <= maxMessageWidth);
@ -1462,6 +1463,8 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
switch (self.cellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
break;
case OWSMessageCellType_Audio:
if (self.viewItem.attachmentStream) {
@ -1473,8 +1476,6 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
[AttachmentSharing showShareUIForAttachment:self.viewItem.attachmentStream];
}
break;
case OWSMessageCellType_DownloadingAttachment:
break;
case OWSMessageCellType_ContactShare:
[self.delegate didTapContactShareViewItem:self.viewItem];
break;

View File

@ -12,9 +12,10 @@ typedef NS_ENUM(NSInteger, OWSMessageCellType) {
OWSMessageCellType_TextOnlyMessage,
OWSMessageCellType_Audio,
OWSMessageCellType_GenericAttachment,
OWSMessageCellType_DownloadingAttachment,
OWSMessageCellType_ContactShare,
OWSMessageCellType_MediaMessage,
OWSMessageCellType_OversizeTextDownloading,
OWSMessageCellType_OversizeTextFailed,
};
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);

View File

@ -26,14 +26,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
return @"OWSMessageCellType_Audio";
case OWSMessageCellType_GenericAttachment:
return @"OWSMessageCellType_GenericAttachment";
case OWSMessageCellType_DownloadingAttachment:
return @"OWSMessageCellType_DownloadingAttachment";
case OWSMessageCellType_Unknown:
return @"OWSMessageCellType_Unknown";
case OWSMessageCellType_ContactShare:
return @"OWSMessageCellType_ContactShare";
case OWSMessageCellType_MediaMessage:
return @"OWSMessageCellType_MediaMessage";
case OWSMessageCellType_OversizeTextDownloading:
return @"OWSMessageCellType_OversizeTextDownloading";
case OWSMessageCellType_OversizeTextFailed:
return @"OWSMessageCellType_OversizeTextFailed";
}
}
@ -591,10 +593,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
TSAttachment *_Nullable oversizeTextAttachment = [message oversizeTextAttachmentWithTransaction:transaction];
if (oversizeTextAttachment != nil && [oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) {
if ([oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *oversizeTextAttachmentStream = (TSAttachmentStream *)oversizeTextAttachment;
self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:oversizeTextAttachmentStream
interactionId:message.uniqueId];
} else if ([oversizeTextAttachment isKindOfClass:[TSAttachmentPointer class]]) {
TSAttachmentPointer *oversizeTextAttachmentPointer = (TSAttachmentPointer *)oversizeTextAttachment;
// TODO: Handle backup restore.
self.messageCellType = (oversizeTextAttachmentPointer.state == TSAttachmentPointerStateFailed
? OWSMessageCellType_OversizeTextFailed
: OWSMessageCellType_OversizeTextDownloading);
self.attachmentPointer = (TSAttachmentPointer *)oversizeTextAttachmentPointer;
return;
} else {
NSString *_Nullable bodyText = [message bodyTextWithTransaction:transaction];
if (bodyText) {
@ -860,6 +870,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (void)copyTextAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_TextOnlyMessage:
case OWSMessageCellType_Audio:
@ -869,10 +884,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[UIPasteboard.generalPasteboard setString:self.displayableBodyText.fullText];
break;
}
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_Unknown: {
OWSFailDebug(@"No text to copy");
break;
@ -882,11 +893,20 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSFailDebug(@"Not implemented yet");
break;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
}
- (void)copyMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -899,10 +919,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[self copyAttachmentToPasteboard:self.attachmentStream];
break;
}
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
if (self.mediaAlbumItems.count == 1) {
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
@ -915,6 +931,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSFailDebug(@"Can't copy media album");
break;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
return;
}
}
@ -937,6 +957,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (void)shareMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -947,10 +972,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_GenericAttachment:
[AttachmentSharing showShareUIForAttachment:self.attachmentStream];
break;
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
// TODO: We need a "canShareMediaAction" method.
OWSAssertDebug(self.mediaAlbumItems);
@ -967,11 +988,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
[AttachmentSharing showShareUIForAttachments:attachmentStreams completion:nil];
break;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
return;
}
}
- (BOOL)canCopyMedia
{
if (self.attachmentPointer != nil) {
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -980,7 +1009,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
return NO;
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
case OWSMessageCellType_MediaMessage: {
if (self.mediaAlbumItems.count == 1) {
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
@ -990,11 +1018,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
return NO;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
return NO;
}
}
- (BOOL)canSaveMedia
{
if (self.attachmentPointer != nil) {
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -1003,7 +1038,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
return NO;
case OWSMessageCellType_GenericAttachment:
case OWSMessageCellType_DownloadingAttachment:
return NO;
case OWSMessageCellType_MediaMessage: {
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
@ -1025,11 +1059,18 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
}
return NO;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
return NO;
}
}
- (void)saveMediaAction
{
if (self.attachmentPointer != nil) {
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
return;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -1042,14 +1083,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_GenericAttachment:
OWSFailDebug(@"Cannot save media data.");
break;
case OWSMessageCellType_DownloadingAttachment: {
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
break;
}
case OWSMessageCellType_MediaMessage: {
[self saveMediaAlbumItems];
break;
}
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
return;
}
}
@ -1112,6 +1153,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
- (BOOL)hasMediaActionContent
{
if (self.attachmentPointer != nil) {
return NO;
}
switch (self.messageCellType) {
case OWSMessageCellType_Unknown:
case OWSMessageCellType_TextOnlyMessage:
@ -1120,11 +1165,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
case OWSMessageCellType_Audio:
case OWSMessageCellType_GenericAttachment:
return self.attachmentStream != nil;
case OWSMessageCellType_DownloadingAttachment: {
return NO;
}
case OWSMessageCellType_MediaMessage:
return self.firstValidAlbumAttachment != nil;
case OWSMessageCellType_OversizeTextDownloading:
case OWSMessageCellType_OversizeTextFailed:
return NO;
}
}

View File

@ -1,145 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalServiceKit
import SignalMessaging
class AttachmentPointerView: UIStackView {
let isIncoming: Bool
let attachmentPointer: TSAttachmentPointer
let conversationStyle: ConversationStyle
let progressView = OWSProgressView()
let nameLabel = UILabel()
let statusLabel = UILabel()
let filename: String
let genericFilename = NSLocalizedString("ATTACHMENT_DEFAULT_FILENAME", comment: "Generic filename for an attachment with no known name")
var progress: CGFloat = 0 {
didSet {
self.progressView.progress = progress
}
}
@objc
required init(attachmentPointer: TSAttachmentPointer, isIncoming: Bool, conversationStyle: ConversationStyle) {
self.attachmentPointer = attachmentPointer
self.isIncoming = isIncoming
self.conversationStyle = conversationStyle
let attachmentPointerFilename = attachmentPointer.sourceFilename
if let filename = attachmentPointerFilename, !filename.isEmpty {
self.filename = filename
} else {
self.filename = genericFilename
}
super.init(frame: CGRect.zero)
createSubviews()
updateViews()
if attachmentPointer.state == .downloading {
NotificationCenter.default.addObserver(self,
selector: #selector(attachmentDownloadProgress(_:)),
name: NSNotification.Name.attachmentDownloadProgress,
object: nil)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc internal func attachmentDownloadProgress(_ notification: Notification) {
guard let attachmentId = attachmentPointer.uniqueId else {
owsFailDebug("Missing attachment id.")
return
}
guard let progress = (notification as NSNotification).userInfo?[kAttachmentDownloadProgressKey] as? NSNumber else {
owsFailDebug("Attachment download notification missing progress.")
return
}
guard let notificationAttachmentId = (notification as NSNotification).userInfo?[kAttachmentDownloadAttachmentIDKey] as? String else {
owsFailDebug("Attachment download notification missing attachment id.")
return
}
guard notificationAttachmentId == attachmentId else {
return
}
self.progress = CGFloat(progress.floatValue)
}
@available(*, unavailable, message: "use init(call:) constructor instead.")
required init(coder aDecoder: NSCoder) {
notImplemented()
}
private static var vSpacing: CGFloat = 5
private class func nameFont() -> UIFont { return UIFont.ows_dynamicTypeBody }
private class func statusFont() -> UIFont { return UIFont.ows_dynamicTypeCaption1 }
private static var progressWidth: CGFloat = 80
private static var progressHeight: CGFloat = 6
func createSubviews() {
progressView.autoSetDimension(.width, toSize: AttachmentPointerView.progressWidth)
progressView.autoSetDimension(.height, toSize: AttachmentPointerView.progressHeight)
// truncate middle to be sure we include file extension
nameLabel.lineBreakMode = .byTruncatingMiddle
nameLabel.textAlignment = .center
nameLabel.textColor = self.textColor
nameLabel.font = AttachmentPointerView.nameFont()
statusLabel.textAlignment = .center
statusLabel.adjustsFontSizeToFitWidth = true
statusLabel.numberOfLines = 2
statusLabel.textColor = self.textColor
statusLabel.font = AttachmentPointerView.statusFont()
self.axis = .vertical
self.spacing = AttachmentPointerView.vSpacing
addArrangedSubview(nameLabel)
addArrangedSubview(progressView)
addArrangedSubview(statusLabel)
}
func updateViews() {
let emoji = TSAttachment.emoji(forMimeType: self.attachmentPointer.contentType)
nameLabel.text = "\(emoji) \(self.filename)"
statusLabel.text = {
switch self.attachmentPointer.state {
case .enqueued:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_QUEUED", comment: "Status label when an attachment is enqueued, but hasn't yet started downloading")
case .downloading:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_IN_PROGRESS", comment: "Status label when an attachment is currently downloading")
case .failed:
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_FAILED", comment: "Status label when an attachment download has failed.")
}
}()
if attachmentPointer.state == .downloading {
progressView.isHidden = false
progressView.autoSetDimension(.height, toSize: 8)
} else {
progressView.isHidden = true
progressView.autoSetDimension(.height, toSize: 0)
}
}
var textColor: UIColor {
return conversationStyle.bubbleTextColor(isIncoming: isIncoming)
}
@objc
public class func measureHeight() -> CGFloat {
return ceil(nameFont().lineHeight +
statusFont().lineHeight +
progressHeight +
vSpacing * 2)
}
}