Send link previews

pull/347/head
nielsandriesse 4 years ago
parent 810aa42f03
commit 2805742d5d

@ -42,13 +42,13 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
message.sentTimestamp = NSDate.millisecondTimestamp()
message.text = text
message.quote = VisibleMessage.Quote.from(snInputView.quoteDraftInfo?.model)
// TODO: Link previews
let linkPreviewDraft = snInputView.linkPreviewInfo?.draft
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
Storage.shared.write(with: { transaction in
// TODO: Link previews
Storage.write(with: { transaction in
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
}, completion: { [weak self] in
// TODO: Link previews
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
Storage.shared.write { transaction in
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
}

@ -1,7 +1,8 @@
final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate, QuoteViewDelegate, BodyTextViewDelegate, UITextViewDelegate {
final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate, QuoteViewDelegate, LinkPreviewViewV2Delegate {
private let delegate: InputViewDelegate
var quoteDraftInfo: (model: OWSQuotedReplyModel, isOutgoing: Bool)? { didSet { handleQuoteDraftChanged() } }
var linkPreviewInfo: (url: String, draft: OWSLinkPreviewDraft?)?
private lazy var linkPreviewView: LinkPreviewViewV2 = {
let maxWidth = self.additionalContentContainer.bounds.width - InputView.linkPreviewViewInset
@ -103,6 +104,7 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate,
private func handleQuoteDraftChanged() {
additionalContentContainer.subviews.forEach { $0.removeFromSuperview() }
linkPreviewInfo = nil
guard let quoteDraftInfo = quoteDraftInfo else { return }
let direction: QuoteView.Direction = quoteDraftInfo.isOutgoing ? .outgoing : .incoming
let hInset: CGFloat = 6
@ -117,7 +119,9 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate,
private func doLinkPreviewThingies() {
additionalContentContainer.subviews.forEach { $0.removeFromSuperview() }
quoteDraftInfo = nil
// Suggest that the user enable link previews if they haven't already, and we haven't
// told them about link previews yet
let text = inputTextView.text!
let userDefaults = UserDefaults.standard
if !OWSLinkPreview.allPreviewUrls(forMessageBodyText: text).isEmpty && !SSKPreferences.areLinkPreviewsEnabled
@ -125,25 +129,33 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate,
// TODO: Show suggestion
userDefaults[.hasSeenLinkPreviewSuggestion] = true
}
// Check that link previews are enabled
guard SSKPreferences.areLinkPreviewsEnabled else { return }
// Check that a valid URL is present
guard let linkPreviewURL = OWSLinkPreview.previewUrl(forRawBodyText: text, selectedRange: inputTextView.selectedRange) else {
return
}
// Guard against obsolete updates
guard linkPreviewURL != self.linkPreviewInfo?.url else { return }
// Set the state to loading
linkPreviewInfo = (url: linkPreviewURL, draft: nil)
linkPreviewView.linkPreviewState = LinkPreviewLoading()
// Add the link preview view
additionalContentContainer.addSubview(linkPreviewView)
linkPreviewView.pin(.left, to: .left, of: additionalContentContainer, withInset: InputView.linkPreviewViewInset)
linkPreviewView.pin(.top, to: .top, of: additionalContentContainer, withInset: 10)
linkPreviewView.pin(.right, to: .right, of: additionalContentContainer)
linkPreviewView.pin(.bottom, to: .bottom, of: additionalContentContainer, withInset: -4)
// Build the link preview
OWSLinkPreview.tryToBuildPreviewInfo(previewUrl: linkPreviewURL).done { [weak self] draft in
guard let self = self else { return }
guard self.linkPreviewInfo?.url == linkPreviewURL else { return } // Obsolete
self.linkPreviewInfo = (url: linkPreviewURL, draft: draft)
self.linkPreviewView.linkPreviewState = LinkPreviewDraft(linkPreviewDraft: draft)
}.catch { _ in
guard self.linkPreviewInfo?.url == linkPreviewURL else { return } // Obsolete
self.linkPreviewInfo = nil
self.additionalContentContainer.subviews.forEach { $0.removeFromSuperview() }
}.retainUntilComplete()
}
@ -167,6 +179,11 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate,
func handleLongPress() {
// Not relevant in this case
}
func handleLinkPreviewCanceled() {
linkPreviewInfo = nil
additionalContentContainer.subviews.forEach { $0.removeFromSuperview() }
}
}
// MARK: Delegate

@ -3,7 +3,7 @@ import NVActivityIndicatorView
final class LinkPreviewViewV2 : UIView {
private let viewItem: ConversationViewItem?
private let maxWidth: CGFloat
private let delegate: UITextViewDelegate & BodyTextViewDelegate
private let delegate: LinkPreviewViewV2Delegate
var linkPreviewState: LinkPreviewState? { didSet { update() } }
private lazy var imageViewContainerWidthConstraint = imageView.set(.width, to: 100)
private lazy var imageViewContainerHeightConstraint = imageView.set(.height, to: 100)
@ -45,11 +45,25 @@ final class LinkPreviewViewV2 : UIView {
private lazy var hStackViewContainer = UIView()
private lazy var hStackView = UIStackView()
private lazy var cancelButton: UIButton = {
let result = UIButton(type: .custom)
let tint: UIColor = isLightMode ? .black : .white
result.setImage(UIImage(named: "X")?.withTint(tint), for: UIControl.State.normal)
let cancelButtonSize = LinkPreviewViewV2.cancelButtonSize
result.set(.width, to: cancelButtonSize)
result.set(.height, to: cancelButtonSize)
result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside)
return result
}()
// MARK: Settings
private static let loaderSize: CGFloat = 24
private static let cancelButtonSize: CGFloat = 45
// MARK: Lifecycle
init(for viewItem: ConversationViewItem?, maxWidth: CGFloat, delegate: UITextViewDelegate & BodyTextViewDelegate) {
init(for viewItem: ConversationViewItem?, maxWidth: CGFloat, delegate: LinkPreviewViewV2Delegate) {
self.viewItem = viewItem
self.maxWidth = maxWidth
self.delegate = delegate
@ -76,7 +90,8 @@ final class LinkPreviewViewV2 : UIView {
titleLabelContainer.addSubview(titleLabel)
titleLabel.pin(to: titleLabelContainer, withInset: Values.smallSpacing)
// Horizontal stack view
let hStackView = UIStackView(arrangedSubviews: [ imageViewContainer, titleLabelContainer ])
hStackView.addArrangedSubview(imageViewContainer)
hStackView.addArrangedSubview(titleLabelContainer)
hStackView.axis = .horizontal
hStackView.alignment = .center
hStackViewContainer.addSubview(hStackView)
@ -96,6 +111,7 @@ final class LinkPreviewViewV2 : UIView {
// MARK: Updating
private func update() {
cancelButton.removeFromSuperview()
guard let linkPreviewState = linkPreviewState else { return }
// Image view
let imageViewContainerSize: CGFloat = (linkPreviewState is LinkPreviewSent) ? 100 : 80
@ -112,12 +128,15 @@ final class LinkPreviewViewV2 : UIView {
loader.alpha = (linkPreviewState.image() != nil) ? 0 : 1
if linkPreviewState.image() != nil { loader.stopAnimating() } else { loader.startAnimating() }
// Title
switch linkPreviewState {
case is LinkPreviewSent: titleLabel.textColor = sentLinkPreviewTextColor
default:
let textColor: UIColor = isDarkMode ? .white : .black
titleLabel.textColor = textColor
let isSent = (linkPreviewState is LinkPreviewSent)
let isOutgoing = (viewItem?.interaction.interactionType() == .outgoingMessage)
let textColor: UIColor
if isSent && isOutgoing && isLightMode {
textColor = .white
} else {
textColor = isDarkMode ? .white : .black
}
titleLabel.textColor = textColor
titleLabel.text = linkPreviewState.title()
// Horizontal stack view
switch linkPreviewState {
@ -131,5 +150,19 @@ final class LinkPreviewViewV2 : UIView {
bodyTextViewContainer.addSubview(bodyTextView)
bodyTextView.pin(to: bodyTextViewContainer, withInset: 12)
}
if linkPreviewState is LinkPreviewDraft {
hStackView.addArrangedSubview(cancelButton)
}
}
// MARK: Interaction
@objc private func cancel() {
delegate.handleLinkPreviewCanceled()
}
}
// MARK: Delegate
protocol LinkPreviewViewV2Delegate : UITextViewDelegate & BodyTextViewDelegate {
func handleLinkPreviewCanceled()
}

@ -1,5 +1,5 @@
final class VisibleMessageCell : MessageCell, UITextViewDelegate, BodyTextViewDelegate {
final class VisibleMessageCell : MessageCell, LinkPreviewViewV2Delegate {
private var unloadContent: (() -> Void)?
private var previousX: CGFloat = 0
var albumView: MediaAlbumView?
@ -438,6 +438,10 @@ final class VisibleMessageCell : MessageCell, UITextViewDelegate, BodyTextViewDe
resetReply()
delegate?.handleReplyButtonTapped(for: viewItem)
}
func handleLinkPreviewCanceled() {
// Not relevant in this case
}
// MARK: Convenience
private func getCornersToRound() -> UIRectCorner {

Loading…
Cancel
Save