Make LinkPreviewView reusable.

pull/1/head
Matthew Chen 6 years ago
parent c7053aa36d
commit ca8a4b3751

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ConversationViewItem;
@class OWSContact;
@class OWSLinkPreview;
@class OWSQuotedReplyModel;
@class TSAttachmentPointer;
@class TSAttachmentStream;
@ -21,6 +22,7 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) {
OWSMessageGestureLocation_OversizeText,
OWSMessageGestureLocation_Media,
OWSMessageGestureLocation_QuotedReply,
OWSMessageGestureLocation_LinkPreview,
};
extern const UIDataDetectorTypes kOWSAllowedDataDetectorTypes;
@ -46,6 +48,8 @@ extern const UIDataDetectorTypes kOWSAllowedDataDetectorTypes;
quotedReply:(OWSQuotedReplyModel *)quotedReply
failedThumbnailDownloadAttachmentPointer:(TSAttachmentPointer *)attachmentPointer;
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem linkPreview:(OWSLinkPreview *)linkPreview;
- (void)didTapContactShareViewItem:(id<ConversationViewItem>)viewItem;
- (void)didTapSendMessageToContactShare:(ContactShareViewModel *)contactShare

@ -40,6 +40,8 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
@property (nonatomic, nullable) UIView *bodyMediaView;
@property (nonatomic, nullable) LinkPreviewView *linkPreviewView;
// Should lazy-load expensive view contents (images, etc.).
// Should do nothing if view is already loaded.
@property (nonatomic, nullable) dispatch_block_t loadCellContentBlock;
@ -1158,6 +1160,13 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
}
}
if (self.viewItem.linkPreview) {
CGSize linkPreviewSize = [LinkPreviewView measureWithConversationViewItem:self.viewItem];
linkPreviewSize.width = MIN(linkPreviewSize.width, self.conversationStyle.maxMessageWidth);
cellSize.width = MAX(cellSize.width, linkPreviewSize.width);
cellSize.height += linkPreviewSize.height;
}
NSValue *_Nullable bodyTextSize = [self bodyTextSize];
if (bodyTextSize) {
[textViewSizes addObject:bodyTextSize];
@ -1284,6 +1293,9 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
[self.contactShareButtonsView removeFromSuperview];
self.contactShareButtonsView = nil;
[self.linkPreviewView removeFromSuperview];
self.linkPreviewView = nil;
}
#pragma mark - Gestures
@ -1338,6 +1350,13 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
OWSFailDebug(@"Missing quoted message.");
}
break;
case OWSMessageGestureLocation_LinkPreview:
if (self.viewItem.linkPreview) {
[self.delegate didTapConversationItem:self.viewItem linkPreview:self.viewItem.linkPreview];
} else {
OWSFailDebug(@"Missing link preview.");
}
break;
}
}
@ -1426,6 +1445,17 @@ const UIDataDetectorTypes kOWSAllowedDataDetectorTypes
}
}
if (self.linkPreviewView) {
// Treat this as a "quoted reply" gesture if:
//
// * There is a "quoted reply" view.
// * The gesture occured within or above the "quoted reply" view.
CGPoint location = [self convertPoint:locationInMessageBubble toView:self.linkPreviewView];
if (CGRectContainsPoint(self.linkPreviewView.bounds, location)) {
return OWSMessageGestureLocation_LinkPreview;
}
}
if (self.bodyMediaView) {
// Treat this as a "body media" gesture if:
//

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSMessageCell.h"
@ -438,7 +438,8 @@ NS_ASSUME_NONNULL_BEGIN
CGPoint locationInMessageBubble = [sender locationInView:self.messageBubbleView];
switch ([self.messageBubbleView gestureLocationForLocation:locationInMessageBubble]) {
case OWSMessageGestureLocation_Default:
case OWSMessageGestureLocation_OversizeText: {
case OWSMessageGestureLocation_OversizeText:
case OWSMessageGestureLocation_LinkPreview: {
[self.delegate conversationCell:self
shouldAllowReply:shouldAllowReply
didLongpressTextViewItem:self.viewItem];

@ -765,7 +765,8 @@ const CGFloat kMaxTextViewHeight = 98;
[self clearLinkPreviewView];
LinkPreviewView *linkPreviewView = [[LinkPreviewView alloc] initWithState:state delegate:self];
LinkPreviewView *linkPreviewView = [[LinkPreviewView alloc] initWithDelegate:self];
linkPreviewView.state = state;
self.linkPreviewView = linkPreviewView;
// TODO: Revisit once we have a separate quoted reply view.
[self.contentRows insertArrangedSubview:linkPreviewView atIndex:0];

@ -2338,6 +2338,13 @@ typedef enum : NSUInteger {
// TODO: Highlight the quoted message?
}
- (void)didTapConversationItem:(id<ConversationViewItem>)viewItem linkPreview:(OWSLinkPreview *)linkPreview
{
OWSAssertIsOnMainThread();
// TODO:
}
- (void)showDetailViewForViewItem:(id<ConversationViewItem>)conversationItem
{
OWSAssertIsOnMainThread();

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -710,6 +710,12 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
// no - op
}
func didTapConversationItem(_ viewItem: ConversationViewItem, linkPreview: OWSLinkPreview) {
// no - op
// TODO:
}
@objc func didLongPressSent(sender: UIGestureRecognizer) {
guard sender.state == .began else {
return

@ -197,7 +197,16 @@ public protocol LinkPreviewViewDelegate {
@objc
public class LinkPreviewView: UIStackView {
private weak var delegate: LinkPreviewViewDelegate?
private let state: LinkPreviewState
@objc
public var state: LinkPreviewState? {
didSet {
AssertIsOnMainThread()
assert(oldValue == nil)
updateContents()
}
}
@available(*, unavailable, message:"use other constructor instead.")
required init(coder aDecoder: NSCoder) {
@ -209,30 +218,44 @@ public class LinkPreviewView: UIStackView {
notImplemented()
}
private let imageView = UIImageView()
private let titleLabel = UILabel()
private let domainLabel = UILabel()
private var cancelButton: UIButton?
private var layoutConstraints = [NSLayoutConstraint]()
@objc
public init(state: LinkPreviewState,
delegate: LinkPreviewViewDelegate?) {
self.state = state
public init(delegate: LinkPreviewViewDelegate?) {
self.delegate = delegate
super.init(frame: .zero)
createContents()
self.isUserInteractionEnabled = true
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped)))
}
private var isApproval: Bool {
return delegate != nil
}
private func createContents() {
private func resetContents() {
for subview in subviews {
subview.removeFromSuperview()
}
self.axis = .horizontal
self.alignment = .center
self.distribution = .fill
self.spacing = 0
self.isUserInteractionEnabled = true
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped)))
cancelButton = nil
NSLayoutConstraint.deactivate(layoutConstraints)
layoutConstraints = []
}
private func updateContents() {
resetContents()
guard let state = state else {
return
}
guard state.isLoaded() else {
createLoadingContents()
return
@ -241,7 +264,7 @@ public class LinkPreviewView: UIStackView {
createMessageContents()
return
}
createApprovalContents()
createApprovalContents(state: state)
}
private func createMessageContents() {
@ -250,9 +273,7 @@ public class LinkPreviewView: UIStackView {
private let approvalHeight: CGFloat = 76
private var cancelButton: UIButton?
private func createApprovalContents() {
private func createApprovalContents(state: LinkPreviewState) {
self.axis = .horizontal
self.alignment = .fill
self.distribution = .equalSpacing
@ -260,7 +281,7 @@ public class LinkPreviewView: UIStackView {
// Image
if let imageView = createImageView() {
if let imageView = createImageView(state: state) {
imageView.contentMode = .scaleAspectFill
imageView.autoPinToSquareAspectRatio()
let imageSize = approvalHeight
@ -346,7 +367,7 @@ public class LinkPreviewView: UIStackView {
strokeView.autoSetDimension(.height, toSize: CGHairlineWidth())
}
private func createImageView() -> UIImageView? {
private func createImageView(state: LinkPreviewState) -> UIImageView? {
guard state.isLoaded() else {
owsFailDebug("State not loaded.")
return nil
@ -367,7 +388,7 @@ public class LinkPreviewView: UIStackView {
private func createLoadingContents() {
self.axis = .vertical
self.alignment = .center
self.autoSetDimension(.height, toSize: approvalHeight)
self.layoutConstraints.append(self.autoSetDimension(.height, toSize: approvalHeight))
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator.startAnimating()
@ -379,6 +400,9 @@ public class LinkPreviewView: UIStackView {
// MARK: Events
@objc func wasTapped(sender: UIGestureRecognizer) {
guard let state = state else {
return
}
guard sender.state == .recognized else {
return
}
@ -392,7 +416,15 @@ public class LinkPreviewView: UIStackView {
return
}
}
self.delegate?.linkPreviewDidTap?(urlString: self.state.urlString())
self.delegate?.linkPreviewDidTap?(urlString: state.urlString())
}
// MARK: Measurement
@objc
public class func measure(withConversationViewItem item: ConversationViewItem) -> CGSize {
// TODO:
return CGSize.zero
}
@objc func didTapCancel(sender: UIButton) {

Loading…
Cancel
Save