TM-SGNL-iOS/SignalServiceKit/Messages/Edit/OutgoingEditMessage.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

184 lines
5.8 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
// This needs to reflect the edit as represented (and sourced) from the db.
@objc
public class OutgoingEditMessage: TSOutgoingMessage {
// MARK: - Edit target data
@objc
private(set) var editedMessage: TSOutgoingMessage
@objc
private(set) var targetMessageTimestamp: UInt64 = 0
// MARK: - Overrides
@objc
public override var isUrgent: Bool { false }
@objc
public override var shouldBeSaved: Bool { false }
@objc
public override var debugDescription: String { "editMessage" }
@objc
override var shouldRecordSendLog: Bool { true }
@objc
override var contentHint: SealedSenderContentHint { .implicit }
// MARK: - Initialization
@objc
public init(
thread: TSThread,
targetMessageTimestamp: UInt64,
editMessage: TSOutgoingMessage,
transaction: SDSAnyReadTransaction
) {
self.targetMessageTimestamp = targetMessageTimestamp
self.editedMessage = editMessage
let builder: TSOutgoingMessageBuilder = .withDefaultValues(
thread: thread,
timestamp: editMessage.timestamp
)
super.init(
outgoingMessageWith: builder,
additionalRecipients: [],
explicitRecipients: [],
skippedRecipients: [],
transaction: transaction
)
}
/// Note on `init?(coder:)` and `init(dictionary:)`: Both are implemented as seeming
/// no-ops here and initialize `editMessage` to an empty value. However, these methods
/// are subtly important.
///
/// 1. `OutgoingEditMessage` is a subclass of `TSOutgoingMessage`, which is in turn,
/// an ancestor of `MTLModel`. `MTLModel` uses both of these methods to provide
/// reflection based encoding/decoding of it's subclasses, which is used when serializing
/// messages into the MessageSendingQueue
///
/// 2. Since this is Swift, once a custom initializer is added, Swift requires implementing any
/// required initializers.
///
/// So, long story short, these empty methods keep the compiler happy, while allowing the
/// `MTLModel` base class to properly serialize `OutgoingEditMessage` and all it's
/// inherited properties
@objc
required init?(coder: NSCoder) {
// Placeholder message to appease the compiler. The message
do {
self.editedMessage = try TSOutgoingMessage(dictionary: [:])
} catch {
owsFailDebug("Failed to create placeholder message")
return nil
}
super.init(coder: coder)
}
@objc
required init(dictionary dictionaryValue: [String: Any]!) throws {
do {
self.editedMessage = try TSOutgoingMessage(dictionary: [:])
} catch {
owsFailDebug("Failed to create placeholder message")
throw error
}
try super.init(dictionary: dictionaryValue)
}
// MARK: - Builders
public override func contentBuilder(
thread: TSThread,
transaction: SDSAnyReadTransaction
) -> SSKProtoContentBuilder? {
let editBuilder = SSKProtoEditMessage.builder()
let contentBuilder = SSKProtoContent.builder()
guard let targetDataMessageBuilder = editedMessage.dataMessageBuilder(
with: thread,
transaction: transaction
) else {
owsFailDebug("failed to build outgoing edit data message")
return nil
}
do {
editBuilder.setDataMessage(try targetDataMessageBuilder.build())
editBuilder.setTargetSentTimestamp(self.targetMessageTimestamp)
contentBuilder.setEditMessage(try editBuilder.build())
return contentBuilder
} catch {
owsFailDebug("failed to build protobuf: \(error)")
return nil
}
}
public override func dataMessageBuilder(
with thread: TSThread,
transaction: SDSAnyReadTransaction
) -> SSKProtoDataMessageBuilder? {
editedMessage.dataMessageBuilder(
with: thread,
transaction: transaction
)
}
public override func buildTranscriptSyncMessage(
localThread: TSThread,
transaction: SDSAnyWriteTransaction
) -> OWSOutgoingSyncMessage? {
guard let thread = thread(tx: transaction) else {
owsFailDebug("Missing thread for interaction.")
return nil
}
let transcript = OutgoingEditMessageSyncTranscript(
localThread: localThread,
messageThread: thread,
outgoingMessage: self,
isRecipientUpdate: false,
transaction: transaction
)
return transcript
}
/// This override is required to properly update the correct interaction row when delivery
/// receipts are processed. Without this, the deliviery is registered against the
/// OutgoingEditMessage, which doesn't have a backing entry in the interactions table.
/// Instead, when updating this message, ensure that the `recipientAddressStates` are
/// in sync between the OutgoingEditMesasge and it's wrapped TSOutgoingMessage
public override func anyUpdateOutgoingMessage(
transaction: SDSAnyWriteTransaction,
block: (TSOutgoingMessage) -> Void
) {
super.anyUpdateOutgoingMessage(transaction: transaction, block: block)
if let editedMessage = TSOutgoingMessage.anyFetchOutgoingMessage(
uniqueId: editedMessage.uniqueId,
transaction: transaction
) {
editedMessage.anyUpdateOutgoingMessage(
transaction: transaction,
block: block
)
}
}
}