diff --git a/Signal/src/network/GiphyAPI.swift b/Signal/src/network/GiphyAPI.swift index 181222191..e91441d2b 100644 --- a/Signal/src/network/GiphyAPI.swift +++ b/Signal/src/network/GiphyAPI.swift @@ -310,7 +310,7 @@ extension GiphyError: LocalizedError { let urlString = "/v1/gifs/search?api_key=\(kGiphyApiKey)&offset=\(kGiphyPageOffset)&limit=\(kGiphyPageSize)&q=\(queryEncoded)" sessionManager.get(urlString, - parameters: {}, + parameters: [String: AnyObject](), progress: nil, success: { _, value in Logger.error("search request succeeded") diff --git a/Signal/src/views/LinkPreviewView.swift b/Signal/src/views/LinkPreviewView.swift index f12e6582f..cec9c91f1 100644 --- a/Signal/src/views/LinkPreviewView.swift +++ b/Signal/src/views/LinkPreviewView.swift @@ -684,6 +684,14 @@ public class LinkPreviewView: UIStackView { addArrangedSubview(activityIndicator) let activityIndicatorSize: CGFloat = 25 activityIndicator.autoSetDimensions(to: CGSize(width: activityIndicatorSize, height: activityIndicatorSize)) + + // Stroke + let strokeView = UIView() + strokeView.backgroundColor = Theme.secondaryColor + self.addSubview(strokeView) + strokeView.autoPinWidthToSuperview(withMargin: 12) + strokeView.autoPinEdge(toSuperviewEdge: .bottom) + strokeView.autoSetDimension(.height, toSize: CGHairlineWidth()) } // MARK: Events diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m index b2436a2f2..a8f6eaa66 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSQuotedReplyModel.h" @@ -165,7 +165,6 @@ NS_ASSUME_NONNULL_BEGIN NSString *_Nullable quotedText = message.body; BOOL hasText = quotedText.length > 0; - BOOL hasAttachment = NO; TSAttachment *_Nullable attachment = [message attachmentsWithTransaction:transaction].firstObject; TSAttachmentStream *quotedAttachment; @@ -213,10 +212,16 @@ NS_ASSUME_NONNULL_BEGIN } } else { quotedAttachment = attachmentStream; - hasAttachment = YES; } } + if (!quotedAttachment && conversationItem.linkPreview && conversationItem.linkPreviewAttachment && + [conversationItem.linkPreviewAttachment isKindOfClass:[TSAttachmentStream class]]) { + + quotedAttachment = (TSAttachmentStream *)conversationItem.linkPreviewAttachment; + } + + BOOL hasAttachment = quotedAttachment != nil; if (!hasText && !hasAttachment) { OWSFailDebug(@"quoted message has neither text nor attachment"); quotedText = @""; diff --git a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift index 00ffb6167..b73725c86 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSLinkPreview.swift @@ -120,7 +120,7 @@ public class OWSLinkPreview: MTLModel { Logger.error("Discarding link preview; message has attachments.") throw LinkPreviewError.invalidInput } - let urlString = previewProto.url + let urlString = stripPossibleLinkUrl(previewProto.url) guard URL(string: urlString) != nil else { Logger.error("Could not parse preview URL.") @@ -265,7 +265,7 @@ public class OWSLinkPreview: MTLModel { let maxCharacterCount = 2048 if result.count > maxCharacterCount { let endIndex = result.index(result.startIndex, offsetBy: maxCharacterCount) - result = String(result[...endIndex]) + result = String(result[.. 0 else { + owsFailDebug("Invalid url (empty path).") + return nil + } guard let result = whitelistedDomain(forUrl: url, domainWhitelist: OWSLinkPreview.linkDomainWhitelist) else { owsFailDebug("Missing domain.") @@ -315,6 +320,16 @@ public class OWSLinkPreview: MTLModel { return result } + private class func stripPossibleLinkUrl(_ urlString: String) -> String { + var result = urlString.ows_stripped() + let suffixToStrip = "," + while result.hasSuffix(suffixToStrip) { + let endIndex = result.index(result.endIndex, offsetBy: -suffixToStrip.count) + result = String(result[.. Bool { guard let url = URL(string: urlString) else { @@ -396,11 +411,14 @@ public class OWSLinkPreview: MTLModel { } let components = body.components(separatedBy: .whitespacesAndNewlines) for component in components { - if isValidLinkUrl(component) { - previewUrlCache.setObject(component as AnyObject, forKey: body as AnyObject) - return component + let urlString = stripPossibleLinkUrl(component) + if isValidLinkUrl(urlString) { + previewUrlCache.setObject(urlString as AnyObject, forKey: body as AnyObject) + return urlString } } + // Use empty string to indicate "no preview URL" in the cache. + previewUrlCache.setObject("" as AnyObject, forKey: body as AnyObject) return nil } @@ -493,7 +511,7 @@ public class OWSLinkPreview: MTLModel { } sessionManager.get(url, - parameters: {}, + parameters: [String: AnyObject](), progress: nil, success: { _, value in @@ -546,7 +564,7 @@ public class OWSLinkPreview: MTLModel { } var title: String? - if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) { + if let rawTitle = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) { if let decodedTitle = decodeHTMLEntities(inString: rawTitle) { let normalizedTitle = OWSLinkPreview.normalizeTitle(title: decodedTitle) if normalizedTitle.count > 0 { @@ -557,12 +575,16 @@ public class OWSLinkPreview: MTLModel { Logger.verbose("title: \(String(describing: title))") - guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) else { + guard let rawImageUrlString = NSRegularExpression.parseFirstMatch(pattern: "", text: linkText) else { return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } guard let imageUrlString = decodeHTMLEntities(inString: rawImageUrlString)?.ows_stripped() else { return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) } + guard isValidMediaUrl(imageUrlString) else { + Logger.error("Invalid image URL.") + return completion(OWSLinkPreviewDraft(urlString: linkUrlString, title: title)) + } Logger.verbose("imageUrlString: \(imageUrlString)") guard let imageUrl = URL(string: imageUrlString) else { Logger.error("Could not parse image URL.")