Merge branch 'charlesmchen/quotedReplies6'

pull/1/head
Matthew Chen 7 years ago
commit 93820e7bcb

@ -154,6 +154,7 @@
347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3478506F1FDAEB16007B8332 /* OWSUserProfile.m */; };
347850721FDAEB17007B8332 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 347850701FDAEB16007B8332 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496744C2076768700080B5F /* OWSMessageBubbleView.m */; };
3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; };
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */; };
34A910601FFEB114000C4745 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A9105F1FFEB114000C4745 /* OWSBackup.m */; };
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; };
@ -729,6 +730,7 @@
3495BC911F1426B800B478F5 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = translations/ar.lproj/Localizable.strings; sourceTree = "<group>"; };
3496744B2076768600080B5F /* OWSMessageBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageBubbleView.h; sourceTree = "<group>"; };
3496744C2076768700080B5F /* OWSMessageBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageBubbleView.m; sourceTree = "<group>"; };
3496744E2076ACCE00080B5F /* LongTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongTextViewController.swift; sourceTree = "<group>"; };
34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWS2FARegistrationViewController.m; sourceTree = "<group>"; };
34A55F3620485464002CC6DE /* OWS2FARegistrationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWS2FARegistrationViewController.h; sourceTree = "<group>"; };
34A9105E1FFEB113000C4745 /* OWSBackup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackup.h; sourceTree = "<group>"; };
@ -1590,8 +1592,10 @@
34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */,
34B3F8541E8DF1700035BE1A /* NewGroupViewController.h */,
34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */,
3496744E2076ACCE00080B5F /* LongTextViewController.swift */,
34A55F3620485464002CC6DE /* OWS2FARegistrationViewController.h */,
34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */,
45D2AC01204885170033C692 /* OWS2FAReminderViewController.swift */,
345BC30A2047030600257B7C /* OWS2FASettingsViewController.h */,
345BC30B2047030600257B7C /* OWS2FASettingsViewController.m */,
34C42D591F45F7A80072EC04 /* OWSNavigationController.h */,
@ -1604,7 +1608,6 @@
34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */,
340FC897204DAC8D007AEB0F /* ThreadSettings */,
34D1F0BE1F8EC1760066283D /* Utils */,
45D2AC01204885170033C692 /* OWS2FAReminderViewController.swift */,
);
path = ViewControllers;
sourceTree = "<group>";
@ -3183,6 +3186,7 @@
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */,
340FC8AD204DAC8D007AEB0F /* OWSLinkedDevicesTableViewController.m in Sources */,
340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */,
3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */,
34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */,
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */,
34D1F0AC1F867BFC0066283D /* OWSExpirationTimerView.m in Sources */,

@ -25,6 +25,7 @@
#import "OWSBubbleView.h"
#import "OWSCallNotificationsAdaptee.h"
#import "OWSDatabaseMigration.h"
#import "OWSMessageBubbleView.h"
#import "OWSMessageCell.h"
#import "OWSNavigationController.h"
#import "OWSProgressView.h"

@ -24,6 +24,8 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) {
@property (nonatomic, nullable, readonly) UIView *bodyMediaView;
@property (nonatomic) BOOL alwaysShowBubbleTail;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
@ -35,7 +37,7 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) {
- (void)loadContent;
- (void)unloadContent;
- (CGSize)sizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth;
- (CGSize)sizeForContentWidth:(int)contentWidth;
- (void)prepareForReuse;

@ -234,7 +234,7 @@ NS_ASSUME_NONNULL_BEGIN
CGSize bodyTextContentSize = [self bodyTextSizeForContentWidth:self.contentWidth includeMargins:NO];
self.bubbleView.isOutgoing = self.isOutgoing;
self.bubbleView.hideTail = self.viewItem.shouldHideBubbleTail;
self.bubbleView.hideTail = self.viewItem.shouldHideBubbleTail && !self.alwaysShowBubbleTail;
if ([self.viewItem.interaction isKindOfClass:[TSMessage class]] && self.hasNonImageBodyContent) {
TSMessage *message = (TSMessage *)self.viewItem.interaction;
@ -315,7 +315,6 @@ NS_ASSUME_NONNULL_BEGIN
if (bodyMediaView) {
OWSAssert(self.loadCellContentBlock);
OWSAssert(self.unloadCellContentBlock);
OWSAssert(!lastSubview);
bodyMediaView.clipsToBounds = YES;
@ -898,9 +897,7 @@ NS_ASSUME_NONNULL_BEGIN
return (int)floor(contentWidth * 0.8f);
}
- (CGSize)quotedMessageSizeForViewWidth:(int)viewWidth
contentWidth:(int)contentWidth
includeMargins:(BOOL)includeMargins
- (CGSize)quotedMessageSizeForContentWidth:(int)contentWidth includeMargins:(BOOL)includeMargins
{
OWSAssert(self.viewItem);
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
@ -920,15 +917,14 @@ NS_ASSUME_NONNULL_BEGIN
return result;
}
- (CGSize)sizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
- (CGSize)sizeForContentWidth:(int)contentWidth
{
OWSAssert(self.viewItem);
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
CGSize cellSize = CGSizeZero;
CGSize quotedMessageSize =
[self quotedMessageSizeForViewWidth:viewWidth contentWidth:contentWidth includeMargins:YES];
CGSize quotedMessageSize = [self quotedMessageSizeForContentWidth:contentWidth includeMargins:YES];
cellSize.width = MAX(cellSize.width, quotedMessageSize.width);
cellSize.height += quotedMessageSize.height;

@ -381,7 +381,7 @@ NS_ASSUME_NONNULL_BEGIN
self.messageBubbleView.viewItem = self.viewItem;
self.messageBubbleView.contentWidth = self.contentWidth;
self.messageBubbleView.cellMediaCache = self.delegate.cellMediaCache;
CGSize messageBubbleSize = [self.messageBubbleView sizeForViewWidth:viewWidth contentWidth:contentWidth];
CGSize messageBubbleSize = [self.messageBubbleView sizeForContentWidth:contentWidth];
CGSize cellSize = messageBubbleSize;

@ -2101,11 +2101,7 @@ typedef enum : NSUInteger {
OWSAssert(conversationItem);
OWSAssert([conversationItem.interaction isKindOfClass:[TSMessage class]]);
TSMessage *message = (TSMessage *)conversationItem.interaction;
MessageDetailViewController *view =
[[MessageDetailViewController alloc] initWithViewItem:conversationItem
message:message
mode:MessageMetadataViewModeFocusOnMessage];
LongTextViewController *view = [[LongTextViewController alloc] initWithViewItem:conversationItem];
[self.navigationController pushViewController:view animated:YES];
}

@ -0,0 +1,100 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalServiceKit
import SignalMessaging
@objc
public class LongTextViewController: OWSViewController {
// MARK: Properties
let viewItem: ConversationViewItem
let messageBody: String
var messageTextView: UITextView?
// MARK: Initializers
@available(*, unavailable, message:"use other constructor instead.")
public required init?(coder aDecoder: NSCoder) {
fatalError("\(#function) is unimplemented.")
}
public required init(viewItem: ConversationViewItem) {
self.viewItem = viewItem
self.messageBody = LongTextViewController.displayableText(viewItem: viewItem)
super.init(nibName: nil, bundle: nil)
}
private class func displayableText(viewItem: ConversationViewItem) -> String {
guard viewItem.hasBodyText else {
return ""
}
guard let displayableText = viewItem.displayableBodyText() else {
return ""
}
let messageBody = displayableText.fullText
return messageBody
}
// MARK: View Lifecycle
public override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = NSLocalizedString("LONG_TEXT_VIEW_TITLE",
comment: "Title for the 'long text message' view.")
createViews()
}
// MARK: - Create Views
private func createViews() {
view.backgroundColor = UIColor.white
let messageTextView = UITextView()
self.messageTextView = messageTextView
messageTextView.font = UIFont.ows_dynamicTypeBody
messageTextView.backgroundColor = UIColor.white
messageTextView.isOpaque = true
messageTextView.isEditable = false
messageTextView.isSelectable = true
messageTextView.isScrollEnabled = true
messageTextView.showsHorizontalScrollIndicator = false
messageTextView.showsVerticalScrollIndicator = true
messageTextView.isUserInteractionEnabled = true
messageTextView.textColor = UIColor.black
messageTextView.text = messageBody
view.addSubview(messageTextView)
messageTextView.autoPinLeadingToSuperviewMargin()
messageTextView.autoPinTrailingToSuperviewMargin()
messageTextView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
let footer = UIToolbar()
footer.barTintColor = UIColor.ows_materialBlue
view.addSubview(footer)
footer.autoPinWidthToSuperview(withMargin: 0)
footer.autoPinEdge(.top, to: .bottom, of: messageTextView)
footer.autoPin(toBottomLayoutGuideOf: self, withInset: 0)
footer.items = [
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareButtonPressed)),
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
]
}
// MARK: - Actions
func shareButtonPressed() {
AttachmentSharing.showShareUI(forText: messageBody)
}
}

@ -12,7 +12,7 @@ enum MessageMetadataViewMode: UInt {
case focusOnMetadata
}
class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, MediaDetailPresenter, MediaGalleryDataSourceDelegate {
class MessageDetailViewController: OWSViewController, MediaDetailPresenter, MediaGalleryDataSourceDelegate {
// MARK: Properties
@ -28,15 +28,9 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
var message: TSMessage
var wasDeleted: Bool = false
var mediaMessageView: MediaMessageView?
// See comments on updateTextLayout.
var messageTextView: UITextView?
var messageTextProxyView: UIView?
var messageTextTopConstraint: NSLayoutConstraint?
var messageTextHeightLayoutConstraint: NSLayoutConstraint?
var messageTextProxyViewHeightConstraint: NSLayoutConstraint?
var bubbleViewWidthConstraint: NSLayoutConstraint?
var messageBubbleView: OWSMessageBubbleView?
var messageBubbleViewWidthLayoutConstraint: NSLayoutConstraint?
var messageBubbleViewHeightLayoutConstraint: NSLayoutConstraint?
var scrollView: UIScrollView!
var contentView: UIView?
@ -87,7 +81,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateTextLayout()
updateMessageBubbleViewLayout()
if mode == .focusOnMetadata {
if let bubbleView = self.bubbleView {
@ -121,7 +115,6 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
view.backgroundColor = UIColor.white
let scrollView = UIScrollView()
scrollView.delegate = self
self.scrollView = scrollView
view.addSubview(scrollView)
scrollView.autoPinWidthToSuperview(withMargin: 0)
@ -218,7 +211,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
}
for recipientId in thread.recipientIdentifiers {
let (recipientStatus, shortStatusMessage, longStatusMessage) = MessageRecipientStatusUtils.recipientStatusAndStatusMessage(outgoingMessage: outgoingMessage, recipientId: recipientId, referenceView: self.view)
let (recipientStatus, shortStatusMessage, _) = MessageRecipientStatusUtils.recipientStatusAndStatusMessage(outgoingMessage: outgoingMessage, recipientId: recipientId, referenceView: self.view)
guard recipientStatus == recipientStatusGroup else {
continue
@ -299,11 +292,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
lastRow.autoPinEdge(toSuperviewEdge: .bottom, withInset: 20)
}
if let mediaMessageView = mediaMessageView {
mediaMessageView.autoMatch(.height, to: .width, of: mediaMessageView, withOffset: 0, relation: .lessThanOrEqual)
}
updateTextLayout()
updateMessageBubbleViewLayout()
}
private func displayableTextIfText() -> String? {
@ -321,8 +310,6 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
}
let bubbleViewHMargin: CGFloat = 10
let messageTailEdgeMargin: CGFloat = 15
let messageNoTailEdgeMargin: CGFloat = 10
private func contentRows() -> [UIView] {
var rows = [UIView]()
@ -331,60 +318,28 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
rows += addAttachmentRows()
}
if let messageBody = displayableTextIfText() {
if true {
let messageBubbleView = OWSMessageBubbleView(frame: CGRect.zero)
self.messageBubbleView = messageBubbleView
messageBubbleView.viewItem = viewItem
messageBubbleView.cellMediaCache = NSCache()
messageBubbleView.contentWidth = contentWidth()
messageBubbleView.alwaysShowBubbleTail = true
messageBubbleView.configureViews()
messageBubbleView.loadContent()
self.messageBody = messageBody
messageBubbleView.isUserInteractionEnabled = true
messageBubbleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(messageBubbleTapped)))
let isIncoming = self.message as? TSIncomingMessage != nil
let row = UIView()
row.addSubview(messageBubbleView)
messageBubbleView.autoPinHeightToSuperview()
// 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.
//
// Therefore we're doing something unusual here.
// See comments on updateTextLayout.
let messageTextView = UITextView()
self.messageTextView = messageTextView
messageTextView.font = UIFont.ows_dynamicTypeBody
messageTextView.backgroundColor = UIColor.clear
messageTextView.isOpaque = false
messageTextView.isEditable = false
messageTextView.isSelectable = true
messageTextView.textContainerInset = UIEdgeInsets.zero
messageTextView.contentInset = UIEdgeInsets.zero
messageTextView.isScrollEnabled = true
messageTextView.showsHorizontalScrollIndicator = false
messageTextView.showsVerticalScrollIndicator = false
messageTextView.isUserInteractionEnabled = false
messageTextView.textColor = isIncoming ? UIColor.black : UIColor.white
messageTextView.text = messageBody
let bubbleImageData = bubbleFactory.bubble(message: message)
let messageTextProxyView = UIView()
messageTextProxyView.layoutMargins = UIEdgeInsets.zero
self.messageTextProxyView = messageTextProxyView
messageTextProxyView.addSubview(messageTextView)
messageTextView.autoPinWidthToSuperview()
self.messageTextTopConstraint = messageTextView.autoPinEdge(toSuperviewEdge: .top, withInset: 0)
self.messageTextHeightLayoutConstraint = messageTextView.autoSetDimension(.height, toSize: 0)
let bubbleView = UIImageView(image: bubbleImageData.messageBubbleImage)
self.bubbleView = bubbleView
bubbleView.layer.cornerRadius = 10
bubbleView.addSubview(messageTextProxyView)
messageTextProxyView.autoPinEdge(toSuperviewEdge: isIncoming ? .leading : .trailing, withInset: messageTailEdgeMargin)
messageTextProxyView.autoPinEdge(toSuperviewEdge: isIncoming ? .trailing : .leading, withInset: messageNoTailEdgeMargin)
messageTextProxyView.autoPinHeightToSuperview(withMargin: 10)
self.messageTextProxyViewHeightConstraint = messageTextProxyView.autoSetDimension(.height, toSize: 0)
let isIncoming = self.message as? TSIncomingMessage != nil
messageBubbleView.autoPinEdge(toSuperviewEdge: isIncoming ? .leading : .trailing, withInset: bubbleViewHMargin)
let row = UIView()
row.addSubview(bubbleView)
bubbleView.autoPinHeightToSuperview()
bubbleView.autoPinEdge(toSuperviewEdge: isIncoming ? .leading : .trailing, withInset: bubbleViewHMargin)
self.bubbleViewWidthConstraint = bubbleView.autoSetDimension(.width, toSize: 0)
self.messageBubbleViewWidthLayoutConstraint = messageBubbleView.autoSetDimension(.width, toSize: 0)
self.messageBubbleViewHeightLayoutConstraint = messageBubbleView.autoSetDimension(.height, toSize: 0)
rows.append(row)
}
@ -432,26 +387,6 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
}
self.attachmentStream = attachmentStream
if let filePath = attachmentStream.filePath() {
dataSource = DataSourcePath.dataSource(withFilePath: filePath)
}
guard let dataSource = dataSource else {
rows.append(valueRow(name: NSLocalizedString("MESSAGE_METADATA_VIEW_ATTACHMENT_MISSING_FILE",
comment: "Label for 'missing' attachments in the 'message metadata' view."),
value: ""))
return rows
}
let contentType = attachment.contentType
if let dataUTI = MIMETypeUtil.utiType(forMIMEType: contentType) {
let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: dataUTI, imageQuality: .original)
let mediaMessageView = MediaMessageView(attachment: attachment, mode: .small, mediaDetailPresenter: self)
mediaMessageView.backgroundColor = UIColor.white
self.mediaMessageView = mediaMessageView
rows.append(mediaMessageView)
}
return rows
}
@ -559,28 +494,6 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
AttachmentSharing.showShareUI(forAttachment: attachmentStream)
}
func copyToPasteboard() {
if let messageBody = messageBody {
UIPasteboard.general.string = messageBody
return
}
guard let attachmentStream = attachmentStream else {
Logger.error("\(logTag) Message has neither attachment nor message body.")
return
}
guard let utiType = MIMETypeUtil.utiType(forMIMEType: attachmentStream.contentType) else {
Logger.error("\(logTag) Attachment has invalid MIME type: \(attachmentStream.contentType).")
return
}
guard let dataSource = dataSource else {
Logger.error("\(logTag) Attachment missing data source.")
return
}
let data = dataSource.data()
UIPasteboard.general.setData(data, forPasteboardType: utiType)
}
// MARK: - Actions
// This method should be called after self.databaseConnection.beginLongLivedReadTransaction().
@ -650,110 +563,64 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
}
}
// MARK: - Text Layout
// 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. Therefore if we do the naive
// thing and embed a full-size UITextView inside our UIScrollView, it will
// fail to render any text if the text message is sufficiently long.
//
// Therefore we're doing something unusual.
//
// * We use an empty UIView "messageTextProxyView" as a placeholder for the
// the UITextView. It has the size and position of where the UITextView
// would be normally.
// * We use a UITextView inside that proxy that is just large enough to
// render the content onscreen. We then move it around within the proxy
// bounds to render the parts of the proxy which are onscreen.
private func updateTextLayout() {
guard let messageTextView = messageTextView else {
return
}
guard let messageTextProxyView = messageTextProxyView else {
owsFail("\(logTag) Missing messageTextProxyView")
return
}
guard let scrollView = scrollView else {
owsFail("\(logTag) Missing scrollView")
return
}
guard let contentView = contentView else {
owsFail("\(logTag) Missing contentView")
return
}
guard let bubbleView = bubbleView else {
owsFail("\(logTag) Missing bubbleView")
// MARK: - Message Bubble Layout
private func contentWidth() -> Int32 {
return Int32(round(self.view.width() - (2 * bubbleViewHMargin)))
}
private func updateMessageBubbleViewLayout() {
guard let messageBubbleView = messageBubbleView else {
return
}
guard let bubbleSuperview = bubbleView.superview else {
owsFail("\(logTag) Missing bubbleSuperview")
guard let messageBubbleViewWidthLayoutConstraint = messageBubbleViewWidthLayoutConstraint else {
return
}
guard let messageTextTopConstraint = messageTextTopConstraint else {
owsFail("\(logTag) Missing messageTextTopConstraint")
guard let messageBubbleViewHeightLayoutConstraint = messageBubbleViewHeightLayoutConstraint else {
return
}
guard let messageTextHeightLayoutConstraint = messageTextHeightLayoutConstraint else {
owsFail("\(logTag) Missing messageTextHeightLayoutConstraint")
messageBubbleView.contentWidth = contentWidth()
let messageBubbleSize = messageBubbleView.size(forContentWidth: contentWidth())
messageBubbleViewWidthLayoutConstraint.constant = messageBubbleSize.width
messageBubbleViewHeightLayoutConstraint.constant = messageBubbleSize.height
}
// MARK: - Event Handlers
func messageBubbleTapped(sender: UIGestureRecognizer) {
guard let messageBubbleView = messageBubbleView else {
return
}
guard let messageTextProxyViewHeightConstraint = messageTextProxyViewHeightConstraint else {
owsFail("\(logTag) Missing messageTextProxyViewHeightConstraint")
guard sender.state == .recognized else {
return
}
guard let bubbleViewWidthConstraint = bubbleViewWidthConstraint else {
owsFail("\(logTag) Missing bubbleViewWidthConstraint")
return
if let outgoingMessage = viewItem.interaction as? TSOutgoingMessage {
switch outgoingMessage.messageState {
case .attemptingOut,
.unsent:
// Ignore taps on "unsent" and "sending" messages.
return
default:
break
}
}
if messageTextView.width() != messageTextProxyView.width() {
owsFail("\(logTag) messageTextView.width \(messageTextView.width) != messageTextProxyView.width \(messageTextProxyView.width)")
}
let maxBubbleWidth = bubbleSuperview.width() - (bubbleViewHMargin * 2)
let maxTextWidth = maxBubbleWidth - (messageTailEdgeMargin + messageNoTailEdgeMargin)
// Measure the total text size.
let textSize = messageTextView.sizeThatFits(CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude))
// Measure the size of the scroll view viewport.
let scrollViewSize = scrollView.frame.size
// Obtain the current scroll view content offset (scroll state).
let scrollViewContentOffset = scrollView.contentOffset
// Obtain the location of the text view proxy relative to the content view.
let textProxyOffset = contentView.convert(CGPoint.zero, from: messageTextProxyView)
// 1. The bubble view's width should fit the text content.
let bubbleViewWidth = ceil(textSize.width + messageTailEdgeMargin + messageNoTailEdgeMargin)
bubbleViewWidthConstraint.constant = bubbleViewWidth
// 2. The text proxy's height should reflect the entire text content.
let messageTextProxyViewHeight = ceil(textSize.height)
messageTextProxyViewHeightConstraint.constant = messageTextProxyViewHeight
// 3. We only want to render a single screenful of text content at a time.
// The height of the text view should reflect the height of the scrollview's
// viewport.
let messageTextViewHeight = ceil(min(textSize.height, scrollViewSize.height))
messageTextHeightLayoutConstraint.constant = messageTextViewHeight
// 4. We want to move the text view around within the proxy in response to
// scroll state changes so that it can render the part of the proxy which
// is on screen.
let minMessageTextViewY = CGFloat(0)
let maxMessageTextViewY = messageTextProxyViewHeight - messageTextViewHeight
let rawMessageTextViewY = -textProxyOffset.y + scrollViewContentOffset.y
let messageTextViewY = max(minMessageTextViewY, min(maxMessageTextViewY, rawMessageTextViewY))
messageTextTopConstraint.constant = messageTextViewY
// 5. We want to scroll the text view's content so that the text view
// renders the appropriate content for the scrollview's scroll state.
messageTextView.contentOffset = CGPoint(x: 0, y: messageTextViewY)
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
Logger.verbose("\(logTag) scrollViewDidScroll")
updateTextLayout()
let locationInMessageBubble = sender.location(in: messageBubbleView)
switch messageBubbleView.gestureLocation(forLocation: locationInMessageBubble) {
case .default:
break
case .oversizeText:
let viewController = LongTextViewController(viewItem: viewItem)
self.navigationController?.pushViewController(viewController, animated: true)
break
case .media:
// TODO: We could show MediaGalleryViewController?
break
case .quotedReply:
break
}
}
// MediaGalleryDataSourceDelegate

@ -971,6 +971,9 @@
/* No comment provided by engineer. */
"LOGGING_SECTION" = "Logging";
/* Title for the 'long text message' view. */
"LONG_TEXT_VIEW_TITLE" = "Message";
/* nav bar button item */
"MEDIA_DETAIL_VIEW_ALL_MEDIA_BUTTON" = "All Media";
@ -1010,9 +1013,6 @@
/* Label for the MIME type of attachments in the 'message metadata' view. */
"MESSAGE_METADATA_VIEW_ATTACHMENT_MIME_TYPE" = "MIME type";
/* Label for 'missing' attachments in the 'message metadata' view. */
"MESSAGE_METADATA_VIEW_ATTACHMENT_MISSING_FILE" = "Missing Attachment";
/* Label for 'not yet downloaded' attachments in the 'message metadata' view. */
"MESSAGE_METADATA_VIEW_ATTACHMENT_NOT_YET_DOWNLOADED" = "Not yet downloaded";

@ -217,20 +217,20 @@ extension String {
@objc
public class func displayableText(_ rawText: String) -> DisplayableText {
// Only show up to N characters of text.
let kMaxTextDisplayLength = 1024
let kMaxTextDisplayLength = 512
let fullText = rawText.filterStringForDisplay()
var isTextTruncated = false
var displayText = fullText
if displayText.count > kMaxTextDisplayLength {
// Trim whitespace before _AND_ after slicing the snipper from the string.
let snippet = String(displayText.prefix(kMaxTextDisplayLength)).ows_stripped()
displayText = String(format:NSLocalizedString("OVERSIZE_TEXT_DISPLAY_FORMAT", comment:
displayText = String(format: NSLocalizedString("OVERSIZE_TEXT_DISPLAY_FORMAT", comment:
"A display format for oversize text messages."),
snippet)
isTextTruncated = true
}
let displayableText = DisplayableText(fullText: fullText, displayText: displayText, isTextTruncated:isTextTruncated)
let displayableText = DisplayableText(fullText: fullText, displayText: displayText, isTextTruncated: isTextTruncated)
return displayableText
}
}

Loading…
Cancel
Save