165 lines
8.5 KiB
Swift
165 lines
8.5 KiB
Swift
//
|
|
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SignalMessaging
|
|
|
|
public class AttachmentMultisend: Dependencies {
|
|
|
|
public class func sendApprovedMedia(conversations: [ConversationItem],
|
|
approvalMessageBody: MessageBody?,
|
|
approvedAttachments: [SignalAttachment]) -> Promise<[TSThread]> {
|
|
return firstly(on: ThreadUtil.enqueueSendQueue) {
|
|
// Duplicate attachments per conversation
|
|
let conversationAttachments: [(ConversationItem, [SignalAttachment])] =
|
|
try conversations.map { conversation in
|
|
return (conversation, try approvedAttachments.map { try $0.cloneAttachment() })
|
|
}
|
|
|
|
// We only upload one set of attachments, and then copy the upload details into
|
|
// each conversation before sending.
|
|
let attachmentsToUpload: [OutgoingAttachmentInfo] = approvedAttachments.map { attachment in
|
|
return OutgoingAttachmentInfo(dataSource: attachment.dataSource,
|
|
contentType: attachment.mimeType,
|
|
sourceFilename: attachment.filenameOrDefault,
|
|
caption: attachment.captionText,
|
|
albumMessageId: nil,
|
|
isBorderless: attachment.isBorderless,
|
|
isLoopingVideo: attachment.isLoopingVideo)
|
|
}
|
|
|
|
var threads: [TSThread] = []
|
|
self.databaseStorage.write { transaction in
|
|
var messages: [TSOutgoingMessage] = []
|
|
|
|
for (conversation, attachments) in conversationAttachments {
|
|
guard let thread = conversation.thread(transaction: transaction) else {
|
|
owsFailDebug("Missing thread for conversation")
|
|
continue
|
|
}
|
|
|
|
// If this thread has a pending message request, treat it as accepted.
|
|
ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer(thread: thread,
|
|
transaction: transaction)
|
|
|
|
let message = try! ThreadUtil.createUnsentMessage(body: approvalMessageBody,
|
|
mediaAttachments: attachments,
|
|
thread: thread,
|
|
quotedReplyModel: nil,
|
|
linkPreviewDraft: nil,
|
|
transaction: transaction)
|
|
messages.append(message)
|
|
threads.append(thread)
|
|
|
|
thread.donateSendMessageIntent(transaction: transaction)
|
|
}
|
|
|
|
// map of attachments we'll upload to their copies in each recipient thread
|
|
var attachmentIdMap: [String: [String]] = [:]
|
|
let correspondingAttachmentIds = transpose(messages.map { $0.attachmentIds })
|
|
for (index, attachmentInfo) in attachmentsToUpload.enumerated() {
|
|
do {
|
|
let attachmentToUpload = try attachmentInfo.asStreamConsumingDataSource(withIsVoiceMessage: false)
|
|
attachmentToUpload.anyInsert(transaction: transaction)
|
|
|
|
attachmentIdMap[attachmentToUpload.uniqueId] = correspondingAttachmentIds[index]
|
|
} catch {
|
|
owsFailDebug("error: \(error)")
|
|
}
|
|
}
|
|
|
|
self.broadcastMediaMessageJobQueue.add(attachmentIdMap: attachmentIdMap,
|
|
transaction: transaction)
|
|
}
|
|
return threads
|
|
}
|
|
}
|
|
|
|
public class func sendApprovedMediaFromShareExtension(
|
|
conversations: [ConversationItem],
|
|
approvalMessageBody: MessageBody?,
|
|
approvedAttachments: [SignalAttachment],
|
|
messagesReadyToSend: (([TSOutgoingMessage]) -> Void)? = nil
|
|
) -> Promise<[TSThread]> {
|
|
return firstly(on: .sharedUserInitiated) { () -> (Promise<[TSThread]>) in
|
|
// Duplicate attachments per conversation
|
|
let conversationAttachments: [(ConversationItem, [SignalAttachment])] =
|
|
try conversations.map { conversation in
|
|
return (conversation, try approvedAttachments.map { try $0.cloneAttachment() })
|
|
}
|
|
|
|
// We only upload one set of attachments, and then copy the upload details into
|
|
// each conversation before sending.
|
|
let attachmentsToUpload: [OutgoingAttachmentInfo] = approvedAttachments.map { attachment in
|
|
return OutgoingAttachmentInfo(dataSource: attachment.dataSource,
|
|
contentType: attachment.mimeType,
|
|
sourceFilename: attachment.filenameOrDefault,
|
|
caption: attachment.captionText,
|
|
albumMessageId: nil,
|
|
isBorderless: attachment.isBorderless,
|
|
isLoopingVideo: attachment.isLoopingVideo)
|
|
}
|
|
|
|
var threads: [TSThread] = []
|
|
var attachmentIdMap: [String: [String]] = [:]
|
|
var messages: [TSOutgoingMessage] = []
|
|
|
|
self.databaseStorage.write { transaction in
|
|
|
|
for (conversation, attachments) in conversationAttachments {
|
|
guard let thread = conversation.thread(transaction: transaction) else {
|
|
owsFailDebug("Missing thread for conversation")
|
|
continue
|
|
}
|
|
|
|
// If this thread has a pending message request, treat it as accepted.
|
|
ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer(thread: thread,
|
|
transaction: transaction)
|
|
|
|
let message = try! ThreadUtil.createUnsentMessage(body: approvalMessageBody,
|
|
mediaAttachments: attachments,
|
|
thread: thread,
|
|
quotedReplyModel: nil,
|
|
linkPreviewDraft: nil,
|
|
transaction: transaction)
|
|
messages.append(message)
|
|
threads.append(thread)
|
|
|
|
thread.donateSendMessageIntent(transaction: transaction)
|
|
}
|
|
|
|
// map of attachments we'll upload to their copies in each recipient thread
|
|
let correspondingAttachmentIds = transpose(messages.map { $0.attachmentIds })
|
|
for (index, attachmentInfo) in attachmentsToUpload.enumerated() {
|
|
do {
|
|
let attachmentToUpload = try attachmentInfo.asStreamConsumingDataSource(withIsVoiceMessage: false)
|
|
attachmentToUpload.anyInsert(transaction: transaction)
|
|
|
|
attachmentIdMap[attachmentToUpload.uniqueId] = correspondingAttachmentIds[index]
|
|
} catch {
|
|
owsFailDebug("error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
messagesReadyToSend?(messages)
|
|
|
|
let outgoingMessages = try BroadcastMediaUploader.upload(attachmentIdMap: attachmentIdMap)
|
|
|
|
var messageSendPromises = [Promise<Void>]()
|
|
databaseStorage.write { transaction in
|
|
for message in outgoingMessages {
|
|
messageSendPromises.append(ThreadUtil.enqueueMessagePromise(
|
|
message: message,
|
|
isHighPriority: true,
|
|
transaction: transaction
|
|
))
|
|
}
|
|
}
|
|
|
|
return Promise.when(fulfilled: messageSendPromises).map { threads }
|
|
}
|
|
}
|
|
}
|