Signal-iOS/SignalServiceKit/Messages/Attachments/V2/DataSource/QuotedReplyAttachmentDataSource.swift

131 lines
5.2 KiB
Swift

//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
/// A DataSource for a quoted reply thumbnail attachment to be created locally, with
/// additional required metadata.
public struct QuotedReplyAttachmentDataSource {
/// The row id of the original message being quoted, if found locally.
public let originalMessageRowId: Int64?
public let source: Source
public enum Source {
/// We took the original message's attachment, transcoded/resized it as needed,
/// and have prepared it for use as a totally independent attachment.
/// No reference to the original attachment is needed.
/// (It may even have been a legacy attachment!)
case pendingAttachment(PendingAttachment)
/// The original message's attachment.
/// Used only if we were unable to transcode and create a PendingAttachment.
/// This could be because it came from the message receive flow, or because
/// the original is/was only a pointer and not available locally to transcode.
/// The original can be used at download time as the "source", if it is a stream by then.
case originalAttachment(OriginalAttachmentSource)
/// The best source we have is a pointer proto unassociated with any existing attachment.
/// Typically this came from the sender of the reply when we were unable to find the original message.
case pointer(SSKProtoAttachmentPointer)
}
/// Reference to an existing attachment to use as the source for the quoted reply.
public struct OriginalAttachmentSource {
public let id: Attachment.IDType
public let mimeType: String
/// We copy the transit tier info for downloading purposes, as a fallback
/// if the original is undownloaded.
public let transitTierInfo: Attachment.TransitTierInfo?
/// Note: these come from the original's AttachmentReference.
public let renderingFlag: AttachmentReference.RenderingFlag
public let sourceFilename: String?
public let sourceUnencryptedByteCount: UInt32?
public let sourceMediaSizePixels: CGSize?
}
internal init(originalMessageRowId: Int64?, source: Source) {
self.originalMessageRowId = originalMessageRowId
self.source = source
}
public static func fromPendingAttachment(
_ pendingAttachment: PendingAttachment,
originalMessageRowId: Int64?
) -> Self {
return .init(originalMessageRowId: originalMessageRowId, source: .pendingAttachment(pendingAttachment))
}
public static func fromOriginalAttachment(
_ originalAttachment: Attachment,
originalReference: AttachmentReference
) -> Self {
let originalMessageRowId: Int64?
switch originalReference.owner.id {
case
.messageBodyAttachment(let messageRowId),
.messageOversizeText(let messageRowId),
.messageLinkPreview(let messageRowId),
.quotedReplyAttachment(let messageRowId),
.messageSticker(let messageRowId),
.messageContactAvatar(let messageRowId):
originalMessageRowId = messageRowId
case
.storyMessageMedia,
.storyMessageLinkPreview,
.threadWallpaperImage,
.globalThreadWallpaperImage:
owsFailDebug("Shouldn't be creating a quoted reply from a non message attachment")
originalMessageRowId = nil
}
return .init(
originalMessageRowId: originalMessageRowId,
source: .originalAttachment(.init(
id: originalAttachment.id,
mimeType: originalAttachment.mimeType,
transitTierInfo: originalAttachment.transitTierInfo,
renderingFlag: originalReference.renderingFlag,
sourceFilename: originalReference.sourceFilename,
sourceUnencryptedByteCount: originalReference.sourceUnencryptedByteCount,
sourceMediaSizePixels: originalReference.sourceMediaSizePixels
))
)
}
public static func fromPointerProto(
_ proto: SSKProtoAttachmentPointer
) -> Self {
return .init(originalMessageRowId: nil, source: .pointer(proto))
}
}
extension QuotedReplyAttachmentDataSource.Source {
var renderingFlag: AttachmentReference.RenderingFlag {
switch self {
case .pendingAttachment(let pendingAttachment):
return pendingAttachment.renderingFlag
case .originalAttachment(let originalAttachmentSource):
return originalAttachmentSource.renderingFlag
case .pointer(let proto):
return .fromProto(proto)
}
}
}
public struct OwnedQuotedReplyAttachmentDataSource {
public let source: QuotedReplyAttachmentDataSource
/// The owner is the reply message, NOT the original message being quoted.
public let owner: AttachmentReference.OwnerBuilder.MessageAttachmentBuilder
public init(
dataSource: QuotedReplyAttachmentDataSource,
owner: AttachmentReference.OwnerBuilder.MessageAttachmentBuilder
) {
self.source = dataSource
self.owner = owner
}
}