|  |  |  | @ -41,7 +41,21 @@ public final class MultiDeviceProtocol : NSObject { | 
		
	
		
			
				|  |  |  |  |         return !(message is DeviceLinkMessage) && !message.thread.isGroupThread() | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     private static func sendMessage(_ messageSend: OWSMessageSend, to destination: MultiDeviceDestination, in transaction: YapDatabaseReadTransaction) { | 
		
	
		
			
				|  |  |  |  |     private static func copy(_ messageSend: OWSMessageSend, for destination: MultiDeviceDestination, with seal: Resolver<TSContactThread>) -> OWSMessageSend { | 
		
	
		
			
				|  |  |  |  |         var recipient: SignalRecipient! | 
		
	
		
			
				|  |  |  |  |         storage.dbReadConnection.read { transaction in | 
		
	
		
			
				|  |  |  |  |             recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.hexEncodedPublicKey, transaction: transaction) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         // TODO: Apparently it's okay that the thread, sender certificate, etc. don't get changed? | 
		
	
		
			
				|  |  |  |  |         return OWSMessageSend(message: messageSend.message, thread: messageSend.thread, recipient: recipient, | 
		
	
		
			
				|  |  |  |  |             senderCertificate: messageSend.senderCertificate, udAccess: messageSend.udAccess, localNumber: messageSend.localNumber, success: { | 
		
	
		
			
				|  |  |  |  |             seal.fulfill(messageSend.thread as! TSContactThread) | 
		
	
		
			
				|  |  |  |  |         }, failure: { error in | 
		
	
		
			
				|  |  |  |  |             seal.reject(error) | 
		
	
		
			
				|  |  |  |  |         }) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     private static func sendMessage(_ messageSend: OWSMessageSend, to destination: MultiDeviceDestination, in transaction: YapDatabaseReadTransaction) -> Promise<Void> { | 
		
	
		
			
				|  |  |  |  |         let (promise, seal) = Promise<TSContactThread>.pending() | 
		
	
		
			
				|  |  |  |  |         let message = messageSend.message | 
		
	
		
			
				|  |  |  |  |         let messageSender = SSKEnvironment.shared.messageSender | 
		
	
	
		
			
				
					|  |  |  | @ -50,7 +64,7 @@ public final class MultiDeviceProtocol : NSObject { | 
		
	
		
			
				|  |  |  |  |             let shouldSendAutoGeneratedFR = !thread.isContactFriend && !(message is FriendRequestMessage) | 
		
	
		
			
				|  |  |  |  |                 && !(message is SessionRequestMessage) && !isSessionResetMessage && !(message is TypingIndicatorMessage) | 
		
	
		
			
				|  |  |  |  |             if !shouldSendAutoGeneratedFR { | 
		
	
		
			
				|  |  |  |  |                 let messageSendCopy = messageSend.copy(with: destination) | 
		
	
		
			
				|  |  |  |  |                 let messageSendCopy = copy(messageSend, for: destination, with: seal) | 
		
	
		
			
				|  |  |  |  |                 messageSender.sendMessage(messageSendCopy) | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 DispatchQueue.main.async { | 
		
	
	
		
			
				
					|  |  |  | @ -76,6 +90,7 @@ public final class MultiDeviceProtocol : NSObject { | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return promise.map { _ in } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     @objc(sendMessageToDestinationAndLinkedDevices:in:) | 
		
	
	
		
			
				
					|  |  |  | @ -92,16 +107,31 @@ public final class MultiDeviceProtocol : NSObject { | 
		
	
		
			
				|  |  |  |  |         print("[Loki] Sending \(type(of: message)) message using multi device routing.") | 
		
	
		
			
				|  |  |  |  |         let recipientID = messageSend.recipient.recipientId() | 
		
	
		
			
				|  |  |  |  |         getMultiDeviceDestinations(for: recipientID, in: transaction).done(on: OWSDispatch.sendingQueue()) { destinations in | 
		
	
		
			
				|  |  |  |  |             var promises: [Promise<Void>] = [] | 
		
	
		
			
				|  |  |  |  |             let masterDestination = destinations.first { $0.isMaster } | 
		
	
		
			
				|  |  |  |  |             if let masterDestination = masterDestination { | 
		
	
		
			
				|  |  |  |  |                 storage.dbReadConnection.read { transaction in | 
		
	
		
			
				|  |  |  |  |                     sendMessage(messageSend, to: masterDestination, in: transaction) | 
		
	
		
			
				|  |  |  |  |                     promises.append(sendMessage(messageSend, to: masterDestination, in: transaction)) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             let slaveDestinations = destinations.filter { !$0.isMaster } | 
		
	
		
			
				|  |  |  |  |             slaveDestinations.forEach { slaveDestination in | 
		
	
		
			
				|  |  |  |  |                 storage.dbReadConnection.read { transaction in | 
		
	
		
			
				|  |  |  |  |                     sendMessage(messageSend, to: slaveDestination, in: transaction) | 
		
	
		
			
				|  |  |  |  |                     promises.append(sendMessage(messageSend, to: slaveDestination, in: transaction)) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             when(resolved: promises).done(on: OWSDispatch.sendingQueue()) { results in | 
		
	
		
			
				|  |  |  |  |                 let errors = results.compactMap { result -> Error? in | 
		
	
		
			
				|  |  |  |  |                     if case Result.rejected(let error) = result { | 
		
	
		
			
				|  |  |  |  |                         return error | 
		
	
		
			
				|  |  |  |  |                     } else { | 
		
	
		
			
				|  |  |  |  |                         return nil | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |                 if errors.isEmpty { | 
		
	
		
			
				|  |  |  |  |                     messageSend.success() | 
		
	
		
			
				|  |  |  |  |                 } else { | 
		
	
		
			
				|  |  |  |  |                     messageSend.failure(errors.first!) | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         }.catch(on: OWSDispatch.sendingQueue()) { error in | 
		
	
	
		
			
				
					|  |  |  | 
 |