Make link previews work again

pull/313/head
nielsandriesse 4 years ago
parent f7bfa5c4d7
commit d39e155e1c

@ -3753,22 +3753,29 @@ typedef enum : NSUInteger {
message.sentTimestamp = [NSDate millisecondTimestamp]; message.sentTimestamp = [NSDate millisecondTimestamp];
message.text = text; message.text = text;
message.quote = [SNQuote from:self.inputToolbar.quotedReply]; message.quote = [SNQuote from:self.inputToolbar.quotedReply];
TSThread *thread = self.thread; OWSLinkPreviewDraft *linkPreviewDraft = self.inputToolbar.linkPreviewDraft;
TSOutgoingMessage *tsMessage = [TSOutgoingMessage from:message associatedWith:thread];
[self.conversationViewModel appendUnsavedOutgoingTextMessage:tsMessage];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[tsMessage saveWithTransaction:transaction]; message.linkPreview = [SNLinkPreview from:linkPreviewDraft using:transaction];
}]; } completion:^{
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { dispatch_async(dispatch_get_main_queue(), ^{
[SNMessageSender send:message withAttachments:@[] inThread:thread usingTransaction:transaction]; TSThread *thread = self.thread;
[thread setDraft:@"" transaction:transaction]; TSOutgoingMessage *tsMessage = [TSOutgoingMessage from:message associatedWith:thread];
[self.conversationViewModel appendUnsavedOutgoingTextMessage:tsMessage];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[tsMessage saveWithTransaction:transaction];
}];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[SNMessageSender send:message withAttachments:@[] inThread:thread usingTransaction:transaction];
[thread setDraft:@"" transaction:transaction];
}];
[self messageWasSent:tsMessage];
[self.inputToolbar clearTextMessageAnimated:YES];
[self resetMentions];
dispatch_async(dispatch_get_main_queue(), ^{
[[weakSelf inputToolbar] toggleDefaultKeyboard];
});
});
}]; }];
[self messageWasSent:tsMessage];
[self.inputToolbar clearTextMessageAnimated:YES];
[self resetMentions];
dispatch_async(dispatch_get_main_queue(), ^{
[[weakSelf inputToolbar] toggleDefaultKeyboard];
});
} }
- (void)voiceMemoGestureDidStart - (void)voiceMemoGestureDidStart

@ -31,11 +31,11 @@ extension Storage {
} }
/// Returns the ID of the `TSIncomingMessage` that was constructed. /// Returns the ID of the `TSIncomingMessage` that was constructed.
public func persist(_ message: VisibleMessage, withQuotedMessage quotedMessage: TSQuotedMessage?, groupPublicKey: String?, using transaction: Any) -> String? { public func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, using transaction: Any) -> String? {
let transaction = transaction as! YapDatabaseReadWriteTransaction let transaction = transaction as! YapDatabaseReadWriteTransaction
guard let threadID = getOrCreateThread(for: message.sender!, groupPublicKey: groupPublicKey, using: transaction), guard let threadID = getOrCreateThread(for: message.sender!, groupPublicKey: groupPublicKey, using: transaction),
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return nil } let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return nil }
let message = TSIncomingMessage.from(message, withQuotedMessage: quotedMessage, associatedWith: thread) let message = TSIncomingMessage.from(message, quotedMessage: quotedMessage, linkPreview: linkPreview, associatedWith: thread)
message.save(with: transaction) message.save(with: transaction)
DispatchQueue.main.async { message.touch() } // FIXME: Hack for a thread updating issue DispatchQueue.main.async { message.touch() } // FIXME: Hack for a thread updating issue
return message.uniqueId! return message.uniqueId!

@ -1,7 +1,7 @@
public extension TSIncomingMessage { public extension TSIncomingMessage {
static func from(_ visibleMessage: VisibleMessage, withQuotedMessage quotedMessage: TSQuotedMessage?, associatedWith thread: TSThread) -> TSIncomingMessage { static func from(_ visibleMessage: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, associatedWith thread: TSThread) -> TSIncomingMessage {
let sender = visibleMessage.sender! let sender = visibleMessage.sender!
var expiration: UInt32 = 0 var expiration: UInt32 = 0
Storage.read { transaction in Storage.read { transaction in
@ -16,7 +16,7 @@ public extension TSIncomingMessage {
attachmentIds: visibleMessage.attachmentIDs, attachmentIds: visibleMessage.attachmentIDs,
expiresInSeconds: expiration, expiresInSeconds: expiration,
quotedMessage: quotedMessage, quotedMessage: quotedMessage,
linkPreview: OWSLinkPreview.from(visibleMessage.linkPreview), linkPreview: linkPreview,
serverTimestamp: nil, serverTimestamp: nil,
wasReceivedByUD: true wasReceivedByUD: true
) )

@ -6,37 +6,49 @@ public extension VisibleMessage {
class LinkPreview : NSObject, NSCoding { class LinkPreview : NSObject, NSCoding {
public var title: String? public var title: String?
public var url: String? public var url: String?
public var attachmentID: String?
public var isValid: Bool { title != nil && url != nil } public var isValid: Bool { title != nil && url != nil && attachmentID != nil }
internal init(title: String?, url: String) { internal init(title: String?, url: String, attachmentID: String?) {
self.title = title self.title = title
self.url = url self.url = url
self.attachmentID = attachmentID
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
if let title = coder.decodeObject(forKey: "title") as! String? { self.title = title } if let title = coder.decodeObject(forKey: "title") as! String? { self.title = title }
if let url = coder.decodeObject(forKey: "urlString") as! String? { self.url = url } if let url = coder.decodeObject(forKey: "urlString") as! String? { self.url = url }
if let attachmentID = coder.decodeObject(forKey: "attachmentID") as! String? { self.attachmentID = attachmentID }
} }
public func encode(with coder: NSCoder) { public func encode(with coder: NSCoder) {
coder.encode(title, forKey: "title") coder.encode(title, forKey: "title")
coder.encode(url, forKey: "urlString") coder.encode(url, forKey: "urlString")
coder.encode(attachmentID, forKey: "attachmentID")
} }
public static func fromProto(_ proto: SNProtoDataMessagePreview) -> LinkPreview? { public static func fromProto(_ proto: SNProtoDataMessagePreview) -> LinkPreview? {
let title = proto.title let title = proto.title
let url = proto.url let url = proto.url
return LinkPreview(title: title, url: url) return LinkPreview(title: title, url: url, attachmentID: nil)
} }
public func toProto() -> SNProtoDataMessagePreview? { public func toProto() -> SNProtoDataMessagePreview? {
preconditionFailure("Use toProto(using:) instead.")
}
public func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoDataMessagePreview? {
guard let url = url else { guard let url = url else {
SNLog("Couldn't construct link preview proto from: \(self).") SNLog("Couldn't construct link preview proto from: \(self).")
return nil return nil
} }
let linkPreviewProto = SNProtoDataMessagePreview.builder(url: url) let linkPreviewProto = SNProtoDataMessagePreview.builder(url: url)
if let title = title { linkPreviewProto.setTitle(title) } if let title = title { linkPreviewProto.setTitle(title) }
if let attachmentID = attachmentID, let stream = TSAttachmentStream.fetch(uniqueId: attachmentID, transaction: transaction),
let attachmentProto = stream.buildProto() {
linkPreviewProto.setImage(attachmentProto)
}
do { do {
return try linkPreviewProto.build() return try linkPreviewProto.build()
} catch { } catch {

@ -60,17 +60,27 @@ public final class VisibleMessage : Message {
public func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? { public func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? {
let proto = SNProtoContent.builder() let proto = SNProtoContent.builder()
var attachmentIDs = self.attachmentIDs
let dataMessage: SNProtoDataMessage.SNProtoDataMessageBuilder let dataMessage: SNProtoDataMessage.SNProtoDataMessageBuilder
// Profile
if let profile = profile, let profileProto = profile.toProto() { if let profile = profile, let profileProto = profile.toProto() {
dataMessage = profileProto.asBuilder() dataMessage = profileProto.asBuilder()
} else { } else {
dataMessage = SNProtoDataMessage.builder() dataMessage = SNProtoDataMessage.builder()
} }
// Text
if let text = text { dataMessage.setBody(text) } if let text = text { dataMessage.setBody(text) }
var attachmentIDs = self.attachmentIDs // Quote
if let quotedAttachmentID = quote?.attachmentID, let index = attachmentIDs.firstIndex(of: quotedAttachmentID) { if let quotedAttachmentID = quote?.attachmentID, let index = attachmentIDs.firstIndex(of: quotedAttachmentID) {
attachmentIDs.remove(at: index) attachmentIDs.remove(at: index)
} }
if let quote = quote, let quoteProto = quote.toProto(using: transaction) { dataMessage.setQuote(quoteProto) }
// Link preview
if let linkPreviewAttachmentID = linkPreview?.attachmentID, let index = attachmentIDs.firstIndex(of: linkPreviewAttachmentID) {
attachmentIDs.remove(at: index)
}
if let linkPreview = linkPreview, let linkPreviewProto = linkPreview.toProto(using: transaction) { dataMessage.setPreview([ linkPreviewProto ]) }
// Attachments
let attachments = attachmentIDs.compactMap { TSAttachmentStream.fetch(uniqueId: $0, transaction: transaction) } let attachments = attachmentIDs.compactMap { TSAttachmentStream.fetch(uniqueId: $0, transaction: transaction) }
if !attachments.allSatisfy({ $0.isUploaded }) { if !attachments.allSatisfy({ $0.isUploaded }) {
#if DEBUG #if DEBUG
@ -79,9 +89,8 @@ public final class VisibleMessage : Message {
} }
let attachmentProtos = attachments.compactMap { $0.buildProto() } let attachmentProtos = attachments.compactMap { $0.buildProto() }
dataMessage.setAttachments(attachmentProtos) dataMessage.setAttachments(attachmentProtos)
if let quote = quote, let quoteProto = quote.toProto(using: transaction) { dataMessage.setQuote(quoteProto) }
if let linkPreview = linkPreview, let linkPreviewProto = linkPreview.toProto() { dataMessage.setPreview([ linkPreviewProto ]) }
// TODO: Contact // TODO: Contact
// Build
do { do {
proto.setDataMessage(try dataMessage.build()) proto.setDataMessage(try dataMessage.build())
return try proto.build() return try proto.build()

@ -2,6 +2,21 @@
extension OWSLinkPreview { extension OWSLinkPreview {
public static func from(_ linkPreview: VisibleMessage.LinkPreview?) -> OWSLinkPreview? { public static func from(_ linkPreview: VisibleMessage.LinkPreview?) -> OWSLinkPreview? {
return nil // TODO: Implement guard let linkPreview = linkPreview else { return nil }
return OWSLinkPreview(urlString: linkPreview.url!, title: linkPreview.title, imageAttachmentId: linkPreview.attachmentID)
}
}
extension VisibleMessage.LinkPreview {
@objc(from:using:)
public static func from(_ linkPreview: OWSLinkPreviewDraft?, using transaction: YapDatabaseReadWriteTransaction) -> VisibleMessage.LinkPreview? {
guard let linkPreview = linkPreview else { return nil }
do {
let linkPreview = try OWSLinkPreview.buildValidatedLinkPreview(fromInfo: linkPreview, transaction: transaction)
return VisibleMessage.LinkPreview(title: linkPreview.title, url: linkPreview.urlString!, attachmentID: linkPreview.imageAttachmentId)
} catch {
return nil
}
} }
} }

@ -167,8 +167,17 @@ extension MessageReceiver {
attachmentIDs.append(id) attachmentIDs.append(id)
} }
} }
// Parse link preview if needed
var owsLinkPreview: OWSLinkPreview?
if message.linkPreview != nil && proto.dataMessage?.preview.isEmpty == false {
owsLinkPreview = try? OWSLinkPreview.buildValidatedLinkPreview(dataMessage: proto.dataMessage!, body: message.text, transaction: transaction)
if let id = owsLinkPreview?.imageAttachmentId {
attachmentIDs.append(id)
}
}
// Persist the message // Persist the message
guard let tsIncomingMessageID = storage.persist(message, withQuotedMessage: tsQuotedMessage, groupPublicKey: message.groupPublicKey, using: transaction) else { throw Error.noThread } guard let tsIncomingMessageID = storage.persist(message, quotedMessage: tsQuotedMessage, linkPreview: owsLinkPreview,
groupPublicKey: message.groupPublicKey, using: transaction) else { throw Error.noThread }
message.threadID = threadID message.threadID = threadID
// Start attachment downloads if needed // Start attachment downloads if needed
storage.withAsync({ transaction in storage.withAsync({ transaction in

@ -76,7 +76,7 @@ public protocol SessionMessagingKitStorageProtocol {
/// Returns the ID of the thread. /// Returns the ID of the thread.
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, using transaction: Any) -> String? func getOrCreateThread(for publicKey: String, groupPublicKey: String?, using transaction: Any) -> String?
/// Returns the ID of the `TSIncomingMessage` that was constructed. /// Returns the ID of the `TSIncomingMessage` that was constructed.
func persist(_ message: VisibleMessage, withQuotedMessage quotedMessage: TSQuotedMessage?, groupPublicKey: String?, using transaction: Any) -> String? func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, using transaction: Any) -> String?
/// Returns the IDs of the saved attachments. /// Returns the IDs of the saved attachments.
func persist(_ attachments: [VisibleMessage.Attachment], using transaction: Any) -> [String] func persist(_ attachments: [VisibleMessage.Attachment], using transaction: Any) -> [String]
/// Also touches the associated message. /// Also touches the associated message.

@ -21,12 +21,15 @@ extension MessageSender : SharedSenderKeysDelegate {
stream.save(with: transaction) stream.save(with: transaction)
} }
tsMessage.quotedMessage?.createThumbnailAttachmentsIfNecessary(with: transaction) tsMessage.quotedMessage?.createThumbnailAttachmentsIfNecessary(with: transaction)
if let linkPreviewAttachmentID = tsMessage.linkPreview?.imageAttachmentId, var linkPreviewAttachmentID: String?
let stream = TSAttachment.fetch(uniqueId: linkPreviewAttachmentID, transaction: transaction) as? TSAttachmentStream { if let id = tsMessage.linkPreview?.imageAttachmentId,
let stream = TSAttachment.fetch(uniqueId: id, transaction: transaction) as? TSAttachmentStream {
linkPreviewAttachmentID = id
streams.append(stream) streams.append(stream)
} }
message.attachmentIDs = streams.map { $0.uniqueId! } message.attachmentIDs = streams.map { $0.uniqueId! }
tsMessage.attachmentIds.addObjects(from: message.attachmentIDs) tsMessage.attachmentIds.addObjects(from: message.attachmentIDs)
if let id = linkPreviewAttachmentID { tsMessage.attachmentIds.remove(id) }
tsMessage.save(with: transaction) tsMessage.save(with: transaction)
} }

Loading…
Cancel
Save