Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalUI/AttachmentMultisend/AttachmentMultisend+OversizeText.swift
1 views
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
public import SignalServiceKit

extension AttachmentMultisend {

    public struct Destination {
        public let conversationItem: ConversationItem
        public let thread: TSThread
        // Message bodies are re-generated _per destination_,
        // as mentions must be hydrated based on participants.
        public let messageBody: ValidatedMessageBody?
    }

    public static func prepareDestinations(
        forSendingMessageBody messageBody: MessageBody?,
        toConversations conversations: [ConversationItem],
    ) async throws -> [Destination] {
        // If the message body has no mentions, we can "hydrate" once across all threads
        // and share it. We only need to re-generate per-thread if there are mentions.
        let canShareMessageBody = !(messageBody?.ranges.hasMentions ?? false)

        struct PreDestination {
            let conversationItem: ConversationItem
            let thread: TSThread
            let messageBody: HydratedMessageBody?
        }

        let preDestinations: [PreDestination] = try await deps.databaseStorage.awaitableWrite { tx in
            return try conversations.map { conversation in
                guard let thread = conversation.getOrCreateThread(transaction: tx) else {
                    throw OWSAssertionError("Missing thread for conversation")
                }
                let hydratedMessageBody: HydratedMessageBody?
                if canShareMessageBody {
                    // Don't set per-destination message bodies.
                    hydratedMessageBody = nil
                } else {
                    hydratedMessageBody = messageBody?.forForwarding(to: thread, transaction: tx)
                }
                return .init(
                    conversationItem: conversation,
                    thread: thread,
                    messageBody: hydratedMessageBody,
                )
            }
        }

        guard !canShareMessageBody else {
            // We only prepare the single shared body.
            let validatedMessageBody: ValidatedMessageBody?
            if let messageBody {
                validatedMessageBody = try await deps.attachmentValidator.prepareOversizeTextIfNeeded(
                    messageBody,
                )
            } else {
                validatedMessageBody = nil
            }
            return preDestinations.map {
                .init(
                    conversationItem: $0.conversationItem,
                    thread: $0.thread,
                    messageBody: validatedMessageBody,
                )
            }
        }

        // Prepare the message body per-thread.
        var destinations = [Destination]()
        for preDestination in preDestinations {
            guard let hydratedMessageBody = preDestination.messageBody else {
                destinations.append(.init(
                    conversationItem: preDestination.conversationItem,
                    thread: preDestination.thread,
                    messageBody: nil,
                ))
                continue
            }
            let validatedMessageBody = try await deps.attachmentValidator.prepareOversizeTextIfNeeded(
                hydratedMessageBody.asMessageBodyForForwarding(),
            )
            destinations.append(.init(
                conversationItem: preDestination.conversationItem,
                thread: preDestination.thread,
                messageBody: validatedMessageBody,
            ))
        }
        return destinations
    }
}