Restore quoted reply thumbnail attachments from backup

This commit is contained in:
Harry 2024-08-20 16:50:39 -07:00 committed by GitHub
parent 07baa170f7
commit 4d320fd384
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 110 additions and 28 deletions

View File

@ -86,6 +86,45 @@ internal class MessageBackupMessageAttachmentArchiver: MessageBackupProtoArchive
)
}
public func restoreQuotedReplyThumbnailAttachment(
_ attachment: BackupProto_MessageAttachment,
chatItemId: MessageBackup.ChatItemId,
messageRowId: Int64,
message: TSMessage,
thread: MessageBackup.ChatThread,
tx: DBWriteTransaction
) -> MessageBackup.RestoreInteractionResult<Void> {
let clientUUID: UUID?
if attachment.hasClientUuid {
guard let uuid = UUID(data: attachment.clientUuid) else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.invalidAttachmentClientUUID),
chatItemId
)])
}
clientUUID = uuid
} else {
clientUUID = nil
}
let ownedAttachment = OwnedAttachmentBackupPointerProto(
proto: attachment.pointer,
renderingFlag: attachment.flag.asAttachmentFlag,
clientUUID: clientUUID,
owner: .quotedReplyAttachment(.init(
messageRowId: messageRowId,
receivedAtTimestamp: message.receivedAtTimestamp,
threadRowId: thread.threadRowId
))
)
return restoreAttachments(
[ownedAttachment],
chatItemId: chatItemId,
tx: tx
)
}
private func restoreAttachments(
_ attachments: [OwnedAttachmentBackupPointerProto],
chatItemId: MessageBackup.ChatItemId,

View File

@ -30,6 +30,7 @@ extension MessageBackup {
fileprivate let reactions: [BackupProto_Reaction]
fileprivate let bodyAttachments: [BackupProto_MessageAttachment]
fileprivate let quotedMessageThumbnail: BackupProto_MessageAttachment?
}
struct Payment {
@ -395,39 +396,47 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
)])
}
let restoreInteractionResult: RestoreInteractionResult<Void>
let restoreBodyAttachmentsResult: RestoreInteractionResult<Void>
var downstreamObjectResults = [RestoreInteractionResult<Void>]()
switch restoredContents {
case .text(let text):
restoreInteractionResult = reactionArchiver.restoreReactions(
downstreamObjectResults.append(reactionArchiver.restoreReactions(
text.reactions,
chatItemId: chatItemId,
message: message,
context: context.recipientContext,
tx: tx
)
restoreBodyAttachmentsResult = attachmentsArchiver.restoreBodyAttachments(
))
downstreamObjectResults.append(attachmentsArchiver.restoreBodyAttachments(
text.bodyAttachments,
chatItemId: chatItemId,
messageRowId: messageRowId,
message: message,
thread: thread,
tx: tx
)
))
if let quotedMessageThumbnail = text.quotedMessageThumbnail {
downstreamObjectResults.append(attachmentsArchiver.restoreQuotedReplyThumbnailAttachment(
quotedMessageThumbnail,
chatItemId: chatItemId,
messageRowId: messageRowId,
message: message,
thread: thread,
tx: tx
))
}
case .archivedPayment(let archivedPayment):
restoreInteractionResult = restoreArchivedPaymentContents(
downstreamObjectResults.append(restoreArchivedPaymentContents(
archivedPayment,
chatItemId: chatItemId,
thread: thread,
message: message,
tx: tx
)
// No body attachments to restore
restoreBodyAttachmentsResult = .success(())
))
}
return restoreInteractionResult
.combine(restoreBodyAttachmentsResult)
return downstreamObjectResults.reduce(.success(()), {
$0.combine($1)
})
}
// MARK: Helpers
@ -524,6 +533,7 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
var partialErrors = [RestoreFrameError]()
let quotedMessage: TSQuotedMessage?
let quotedMessageThumbnail: BackupProto_MessageAttachment?
if standardMessage.hasQuote {
guard
let quoteResult = restoreQuote(
@ -536,9 +546,10 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
else {
return .messageFailure(partialErrors)
}
quotedMessage = quoteResult
(quotedMessage, quotedMessageThumbnail) = quoteResult
} else {
quotedMessage = nil
quotedMessageThumbnail = nil
}
guard standardMessage.hasText else {
@ -554,7 +565,8 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
body: body,
quotedMessage: quotedMessage,
reactions: standardMessage.reactions,
bodyAttachments: standardMessage.attachments
bodyAttachments: standardMessage.attachments,
quotedMessageThumbnail: quotedMessageThumbnail
)))
case .partialRestore(let body, let partialErrors):
return .partialRestore(
@ -562,7 +574,8 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
body: body,
quotedMessage: quotedMessage,
reactions: standardMessage.reactions,
bodyAttachments: standardMessage.attachments
bodyAttachments: standardMessage.attachments,
quotedMessageThumbnail: quotedMessageThumbnail
)),
partialErrors
)
@ -655,7 +668,7 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
thread: MessageBackup.ChatThread,
context: MessageBackup.ChatRestoringContext,
tx: DBReadTransaction
) -> RestoreInteractionResult<TSQuotedMessage> {
) -> RestoreInteractionResult<(TSQuotedMessage, BackupProto_MessageAttachment?)> {
let authorAddress: MessageBackup.InteropAddress
switch context.recipientContext[quote.authorRecipientId] {
case .none:
@ -735,26 +748,48 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
isGiftBadge = true
}
guard let quoteBody else {
// Non-text not supported yet.
return .messageFailure([.restoreFrameError(.unimplemented, chatItemId)])
let quotedAttachmentInfo: OWSAttachmentInfo?
let quotedAttachmentThumbnail: BackupProto_MessageAttachment?
if let quotedAttachmentProto = quote.attachments.first {
if quotedAttachmentProto.hasThumbnail {
quotedAttachmentInfo = .init(forV2ThumbnailReference: ())
quotedAttachmentThumbnail = quotedAttachmentProto.thumbnail
} else {
let mimeType = quotedAttachmentProto.contentType.nilIfEmpty
?? MimeType.applicationOctetStream.rawValue
let sourceFilename = quotedAttachmentProto.fileName.nilIfEmpty
quotedAttachmentInfo = .init(
stubWithMimeType: mimeType,
sourceFilename: sourceFilename
)
quotedAttachmentThumbnail = nil
}
} else {
quotedAttachmentInfo = nil
quotedAttachmentThumbnail = nil
}
// TODO: [Backups] Support attachments
guard quoteBody != nil || quotedAttachmentThumbnail != nil || isGiftBadge else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.quotedMessageEmptyContent),
chatItemId
)])
}
let quotedMessage = TSQuotedMessage(
targetMessageTimestamp: targetMessageTimestamp,
authorAddress: authorAddress,
body: quoteBody.text,
bodyRanges: quoteBody.ranges,
body: quoteBody?.text,
bodyRanges: quoteBody?.ranges,
bodySource: bodySource,
quotedAttachmentInfo: quotedAttachmentInfo,
isGiftBadge: isGiftBadge
)
if partialErrors.isEmpty {
return .success(quotedMessage)
return .success((quotedMessage, quotedAttachmentThumbnail))
} else {
return .partialRestore(quotedMessage, partialErrors)
return .partialRestore((quotedMessage, quotedAttachmentThumbnail), partialErrors)
}
}

View File

@ -364,6 +364,10 @@ extension MessageBackup {
/// A `BackupProto_BodyRange` with a missing or unrecognized style.
case unrecognizedBodyRangeStyle
/// A quoted message had no body, attachment, gift badge, or other
/// content in its representation of the original being quoted.
case quotedMessageEmptyContent
/// A `BackupProto_Group's` gv2 master key could not be parsed by libsignal.
case invalidGV2MasterKey
/// A `BackupProto_Group` was missing its group snapshot.
@ -594,6 +598,7 @@ extension MessageBackup {
.unrecognizedMessageSendStatus,
.reactionNotFromAciOrE164,
.unrecognizedBodyRangeStyle,
.quotedMessageEmptyContent,
.invalidGV2MasterKey,
.missingGV2GroupSnapshot,
.unrecognizedGV2MemberRole,

View File

@ -740,6 +740,7 @@ public class QuotedReplyManagerImpl: QuotedReplyManager {
),
bodyRanges: nil,
bodySource: .remote,
quotedAttachmentInfo: nil,
isGiftBadge: false
))
}

View File

@ -119,12 +119,12 @@ typedef NS_ENUM(NSUInteger, OWSAttachmentInfoReference) {
isGiftBadge:(BOOL)isGiftBadge;
// used when restoring quoted messages from backups
// TODO: attachments should be here too, once they are body can be made nullable.
+ (instancetype)quotedMessageWithTargetMessageTimestamp:(nullable NSNumber *)timestamp
authorAddress:(SignalServiceAddress *)authorAddress
body:(NSString *)body
body:(nullable NSString *)body
bodyRanges:(nullable MessageBodyRanges *)bodyRanges
bodySource:(TSQuotedMessageContentSource)bodySource
quotedAttachmentInfo:(nullable OWSAttachmentInfo *)attachmentInfo
isGiftBadge:(BOOL)isGiftBadge;
@end

View File

@ -210,11 +210,13 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)quotedMessageWithTargetMessageTimestamp:(nullable NSNumber *)timestamp
authorAddress:(SignalServiceAddress *)authorAddress
body:(NSString *)body
body:(nullable NSString *)body
bodyRanges:(nullable MessageBodyRanges *)bodyRanges
bodySource:(TSQuotedMessageContentSource)bodySource
quotedAttachmentInfo:(nullable OWSAttachmentInfo *)attachmentInfo
isGiftBadge:(BOOL)isGiftBadge
{
OWSAssertDebug(body != nil || attachmentInfo != nil);
OWSAssertDebug(authorAddress.isValid);
uint64_t rawTimestamp;
@ -230,7 +232,7 @@ NS_ASSUME_NONNULL_BEGIN
body:body
bodyRanges:bodyRanges
bodySource:bodySource
receivedQuotedAttachmentInfo:nil
receivedQuotedAttachmentInfo:attachmentInfo
isGiftBadge:isGiftBadge];
}