diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index b1e812c87..bfeff2efe 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -5419,22 +5419,24 @@ typedef enum : NSUInteger { }]) { return; } - __block TSInteraction *targetInteraction; - [self.thread enumerateInteractionsUsingBlock:^(TSInteraction *interaction) { - if (interaction.timestamp == timestamp.unsignedLongLongValue) { - targetInteraction = interaction; - } - }]; - if (targetInteraction == nil || targetInteraction.interactionType != OWSInteractionType_OutgoingMessage) { return; } - NSString *hexEncodedPublicKey = targetInteraction.thread.contactIdentifier; - if (hexEncodedPublicKey == nil) { return; } - __block NSString *masterHexEncodedPublicKey; - [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:hexEncodedPublicKey in:transaction] ?: hexEncodedPublicKey; - }]; - BOOL isSlaveDevice = ![masterHexEncodedPublicKey isEqual:hexEncodedPublicKey]; - if (isSlaveDevice) { return; } dispatch_async(dispatch_get_main_queue(), ^{ + __block TSInteraction *targetInteraction; + [OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self.thread enumerateInteractionsWithTransaction:transaction usingBlock:^(TSInteraction *interaction, YapDatabaseReadTransaction *t) { + if (interaction.timestamp == timestamp.unsignedLongLongValue) { + targetInteraction = interaction; + } + }]; + }]; + if (targetInteraction == nil || targetInteraction.interactionType != OWSInteractionType_OutgoingMessage) { return; } + NSString *hexEncodedPublicKey = targetInteraction.thread.contactIdentifier; + if (hexEncodedPublicKey == nil) { return; } + __block NSString *masterHexEncodedPublicKey; + [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:hexEncodedPublicKey in:transaction] ?: hexEncodedPublicKey; + }]; + BOOL isSlaveDevice = ![masterHexEncodedPublicKey isEqual:hexEncodedPublicKey]; + if (isSlaveDevice) { return; } if (progress <= self.progressIndicatorView.progress) { return; } self.progressIndicatorView.alpha = 1; [self.progressIndicatorView setProgress:progress animated:YES]; diff --git a/SignalServiceKit/src/Loki/API/LokiPoller.swift b/SignalServiceKit/src/Loki/API/LokiPoller.swift index 257ffefc5..694a01952 100644 --- a/SignalServiceKit/src/Loki/API/LokiPoller.swift +++ b/SignalServiceKit/src/Loki/API/LokiPoller.swift @@ -81,7 +81,6 @@ public final class LokiPoller : NSObject { seal.fulfill(()) }.catch(on: LokiAPI.errorHandlingQueue) { [weak self] error in if let error = error as? Error, error == .pollLimitReached { - print("[Loki] Polling limit for \(nextSnode) reached; switching to next snode.") self?.pollCount = 0 } else { print("[Loki] Polling \(nextSnode) failed; dropping it and switching to next snode.") diff --git a/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatPoller.swift b/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatPoller.swift index 6af7e5c11..974eb814b 100644 --- a/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatPoller.swift +++ b/SignalServiceKit/src/Loki/API/Open Groups/LokiPublicChatPoller.swift @@ -12,7 +12,7 @@ public final class LokiPublicChatPoller : NSObject { // MARK: Settings private let pollForNewMessagesInterval: TimeInterval = 4 - private let pollForDeletedMessagesInterval: TimeInterval = 20 + private let pollForDeletedMessagesInterval: TimeInterval = 60 private let pollForModeratorsInterval: TimeInterval = 10 * 60 private let pollForDisplayNamesInterval: TimeInterval = 60 diff --git a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift index 3d1acd827..1d2faa4c8 100644 --- a/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Friend Requests/FriendRequestProtocol.swift @@ -60,6 +60,7 @@ public final class FriendRequestProtocol : NSObject { // Accept all outstanding friend requests associated with this user and try to establish sessions with the // subset of their devices that haven't sent a friend request. let linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: hexEncodedPublicKey, in: transaction) // This doesn't create new threads if they don't exist yet + // FIXME: Capture send failures for thread in linkedDeviceThreads { if thread.hasPendingFriendRequest { sendFriendRequestAcceptanceMessage(to: thread.contactIdentifier(), in: thread, using: transaction) // NOT hexEncodedPublicKey diff --git a/SignalServiceKit/src/Loki/Protocol/Multi Device/MultiDeviceProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Multi Device/MultiDeviceProtocol.swift index b332eea9f..725fb034f 100644 --- a/SignalServiceKit/src/Loki/Protocol/Multi Device/MultiDeviceProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Multi Device/MultiDeviceProtocol.swift @@ -41,7 +41,7 @@ public final class MultiDeviceProtocol : NSObject { return !(message is DeviceLinkMessage) && !message.thread.isGroupThread() } - private static func copy(_ messageSend: OWSMessageSend, for destination: MultiDeviceDestination, with seal: Resolver) -> OWSMessageSend { + private static func copy(_ messageSend: OWSMessageSend, for destination: MultiDeviceDestination, with seal: Resolver) -> OWSMessageSend { var recipient: SignalRecipient! storage.dbReadConnection.read { transaction in recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.hexEncodedPublicKey, transaction: transaction) @@ -49,47 +49,47 @@ public final class MultiDeviceProtocol : NSObject { // TODO: Why is it 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.message.thread as! TSContactThread) + seal.fulfill(()) }, failure: { error in seal.reject(error) }) } private static func sendMessage(_ messageSend: OWSMessageSend, to destination: MultiDeviceDestination, in transaction: YapDatabaseReadTransaction) -> Promise { - let (promise, seal) = Promise.pending() - let message = messageSend.message - let messageSender = SSKEnvironment.shared.messageSender - promise.done(on: OWSDispatch.sendingQueue()) { thread in + let (threadPromise, threadPromiseSeal) = Promise.pending() + if let thread = TSContactThread.getWithContactId(destination.hexEncodedPublicKey, transaction: transaction) { + threadPromiseSeal.fulfill(thread) + } else { + // Dispatch async on the main queue to avoid nested write transactions + DispatchQueue.main.async { + storage.dbReadWriteConnection.readWrite { transaction in + let thread = TSContactThread.getOrCreateThread(withContactId: destination.hexEncodedPublicKey, transaction: transaction) + threadPromiseSeal.fulfill(thread) + } + } + } + return threadPromise.then(on: OWSDispatch.sendingQueue()) { thread -> Promise in + let message = messageSend.message + let messageSender = SSKEnvironment.shared.messageSender + let (promise, seal) = Promise.pending() let shouldSendAutoGeneratedFR = !thread.isContactFriend && !(message is FriendRequestMessage) && message.shouldBeSaved() // shouldBeSaved indicates it isn't a transient message if !shouldSendAutoGeneratedFR { let messageSendCopy = copy(messageSend, for: destination, with: seal) messageSender.sendMessage(messageSendCopy) } else { + // Dispatch async on the main queue to avoid nested write transactions DispatchQueue.main.async { storage.dbReadWriteConnection.readWrite { transaction in - getAutoGeneratedMultiDeviceFRMessageSend(for: destination.hexEncodedPublicKey, in: transaction) + getAutoGeneratedMultiDeviceFRMessageSend(for: destination.hexEncodedPublicKey, in: transaction, seal: seal) .done(on: OWSDispatch.sendingQueue()) { autoGeneratedFRMessageSend in messageSender.sendMessage(autoGeneratedFRMessageSend) } } } } + return promise } - promise.catch(on: OWSDispatch.sendingQueue()) { error in - print("[Loki] Couldn't get thread for destination: \(destination.hexEncodedPublicKey).") - } - if let thread = TSContactThread.getWithContactId(destination.hexEncodedPublicKey, transaction: transaction) { - seal.fulfill(thread) - } else { - DispatchQueue.main.async { - storage.dbReadWriteConnection.readWrite { transaction in - let thread = TSContactThread.getOrCreateThread(withContactId: destination.hexEncodedPublicKey, transaction: transaction) - seal.fulfill(thread) - } - } - } - return promise.map { _ in } } @objc(sendMessageToDestinationAndLinkedDevices:in:) @@ -166,7 +166,7 @@ public final class MultiDeviceProtocol : NSObject { return AnyPromise.from(getAutoGeneratedMultiDeviceFRMessageSend(for: hexEncodedPublicKey, in: transaction)) } - public static func getAutoGeneratedMultiDeviceFRMessageSend(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> Promise { + public static func getAutoGeneratedMultiDeviceFRMessageSend(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction, seal externalSeal: Resolver? = nil) -> Promise { let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction) let message = getAutoGeneratedMultiDeviceFRMessage(for: hexEncodedPublicKey, in: transaction) thread.friendRequestStatus = .requestSending @@ -183,11 +183,15 @@ public final class MultiDeviceProtocol : NSObject { } let messageSend = OWSMessageSend(message: message, thread: thread, recipient: recipient, senderCertificate: senderCertificate, udAccess: recipientUDAccess, localNumber: getUserHexEncodedPublicKey(), success: { + externalSeal?.fulfill(()) + // Dispatch async on the main queue to avoid nested write transactions DispatchQueue.main.async { thread.friendRequestStatus = .requestSent thread.save() } - }, failure: { _ in + }, failure: { error in + externalSeal?.reject(error) + // Dispatch async on the main queue to avoid nested write transactions DispatchQueue.main.async { thread.friendRequestStatus = .none thread.save()