diff --git a/Session/Calls/Call Management/SessionCall.swift b/Session/Calls/Call Management/SessionCall.swift index 1d106c38b..0d9c03563 100644 --- a/Session/Calls/Call Management/SessionCall.swift +++ b/Session/Calls/Call Management/SessionCall.swift @@ -397,7 +397,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate { let webRTCSession: WebRTCSession = self.webRTCSession GRDBStorage.shared - .write { db in webRTCSession.sendOffer(db, to: sessionId, isRestartingICEConnection: true) } + .read { db in webRTCSession.sendOffer(db, to: sessionId, isRestartingICEConnection: true) } .retainUntilComplete() } diff --git a/Session/Closed Groups/EditClosedGroupVC.swift b/Session/Closed Groups/EditClosedGroupVC.swift index a6c2e0731..7ed1b4b7b 100644 --- a/Session/Closed Groups/EditClosedGroupVC.swift +++ b/Session/Closed Groups/EditClosedGroupVC.swift @@ -417,7 +417,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat ModalActivityIndicatorViewController.present(fromViewController: navigationController) { _ in GRDBStorage.shared - .write { db in + .writeAsync { db in if !updatedMemberIds.contains(userPublicKey) { return try MessageSender.leave(db, groupPublicKey: threadId) } diff --git a/Session/Closed Groups/NewClosedGroupVC.swift b/Session/Closed Groups/NewClosedGroupVC.swift index 3e9b60e43..7ec2c4b57 100644 --- a/Session/Closed Groups/NewClosedGroupVC.swift +++ b/Session/Closed Groups/NewClosedGroupVC.swift @@ -196,27 +196,28 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate let selectedContacts = self.selectedContacts let message: String? = (selectedContacts.count > 20) ? "Please wait while the group is created..." : nil ModalActivityIndicatorViewController.present(fromViewController: navigationController!, message: message) { [weak self] _ in - let promise: Promise = GRDBStorage.shared.write { db in - try MessageSender.createClosedGroup(db, name: name, members: selectedContacts) - } - - let _ = promise.done(on: DispatchQueue.main) { thread in - GRDBStorage.shared.write { db in - try? MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete() + GRDBStorage.shared + .writeAsync { db in + try MessageSender.createClosedGroup(db, name: name, members: selectedContacts) } - - self?.presentingViewController?.dismiss(animated: true, completion: nil) - SessionApp.presentConversation(for: thread.id, action: .compose, animated: false) - } - promise.catch(on: DispatchQueue.main) { [weak self] _ in - self?.dismiss(animated: true, completion: nil) // Dismiss the loader - - let title = "Couldn't Create Group" - let message = "Please check your internet connection and try again." - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil)) - self?.presentAlert(alert) - } + .done(on: DispatchQueue.main) { thread in + GRDBStorage.shared.writeAsync { db in + try? MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete() + } + + self?.presentingViewController?.dismiss(animated: true, completion: nil) + SessionApp.presentConversation(for: thread.id, action: .compose, animated: false) + } + .catch(on: DispatchQueue.main) { [weak self] _ in + self?.dismiss(animated: true, completion: nil) // Dismiss the loader + + let title = "Couldn't Create Group" + let message = "Please check your internet connection and try again." + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil)) + self?.presentAlert(alert) + } + .retainUntilComplete() } } diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 10b4cf8d5..99a1e31be 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -1680,7 +1680,7 @@ extension ConversationVC { return promise .then { _ -> Promise in - GRDBStorage.shared.write { db in + GRDBStorage.shared.writeAsync { db in try MessageSender.sendNonDurably( db, message: messageRequestResponse, diff --git a/Session/Conversations/ConversationViewModel.swift b/Session/Conversations/ConversationViewModel.swift index a41b2642c..6b46e6d34 100644 --- a/Session/Conversations/ConversationViewModel.swift +++ b/Session/Conversations/ConversationViewModel.swift @@ -359,7 +359,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { // MARK: - Functions public func updateDraft(to draft: String) { - GRDBStorage.shared.write { db in + GRDBStorage.shared.writeAsync { db in try SessionThread .filter(id: self.threadId) .updateAll(db, SessionThread.Columns.messageDraft.set(to: draft)) diff --git a/Session/Conversations/Views & Modals/JoinOpenGroupModal.swift b/Session/Conversations/Views & Modals/JoinOpenGroupModal.swift index 87db2390d..0335df69c 100644 --- a/Session/Conversations/Views & Modals/JoinOpenGroupModal.swift +++ b/Session/Conversations/Views & Modals/JoinOpenGroupModal.swift @@ -92,24 +92,26 @@ final class JoinOpenGroupModal: Modal { presentingViewController.dismiss(animated: true, completion: nil) - GRDBStorage.shared.write { db in - OpenGroupManager.shared.add( - db, - roomToken: room, - server: server, - publicKey: publicKey, - isConfigMessage: false - ) - } - .done(on: DispatchQueue.main) { _ in - GRDBStorage.shared.write { db in - try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...) + GRDBStorage.shared + .writeAsync { db in + OpenGroupManager.shared.add( + db, + roomToken: room, + server: server, + publicKey: publicKey, + isConfigMessage: false + ) } - } - .catch(on: DispatchQueue.main) { error in - let alert = UIAlertController(title: "Couldn't Join", message: error.localizedDescription, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil)) - presentingViewController.present(alert, animated: true, completion: nil) - } + .done(on: DispatchQueue.main) { _ in + GRDBStorage.shared.writeAsync { db in + try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...) + } + } + .catch(on: DispatchQueue.main) { error in + let alert = UIAlertController(title: "Couldn't Join", message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil)) + presentingViewController.present(alert, animated: true, completion: nil) + } + .retainUntilComplete() } } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 82bee6cff..3ab8107ac 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -591,18 +591,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD guard Date().timeIntervalSince(lastSync) > (7 * 24 * 60 * 60) else { return } // Sync every 2 days - GRDBStorage.shared.write { db in - try MessageSender.syncConfiguration(db, forceSyncNow: false) - .done { - // Only update the 'lastConfigurationSync' timestamp if we have done the - // first sync (Don't want a new device config sync to override config - // syncs from other devices) - if UserDefaults.standard[.hasSyncedInitialConfiguration] { - UserDefaults.standard[.lastConfigurationSync] = Date() - } + GRDBStorage.shared + .writeAsync { db in try MessageSender.syncConfiguration(db, forceSyncNow: false) } + .done { + // Only update the 'lastConfigurationSync' timestamp if we have done the + // first sync (Don't want a new device config sync to override config + // syncs from other devices) + if UserDefaults.standard[.hasSyncedInitialConfiguration] { + UserDefaults.standard[.lastConfigurationSync] = Date() } - .retainUntilComplete() - } + } + .retainUntilComplete() } - } diff --git a/Session/Notifications/AppNotifications.swift b/Session/Notifications/AppNotifications.swift index 50fe281b6..146685c77 100644 --- a/Session/Notifications/AppNotifications.swift +++ b/Session/Notifications/AppNotifications.swift @@ -444,7 +444,9 @@ class NotificationActionHandler { throw NotificationError.failDebug("unable to find thread with id: \(threadId)") } - let promise: Promise = GRDBStorage.shared.write { db in + let (promise, seal) = Promise.pending() + + GRDBStorage.shared.writeAsync { db in let interaction: Interaction = try Interaction( threadId: thread.id, authorId: getUserHexEncodedPublicKey(db), @@ -468,12 +470,15 @@ class NotificationActionHandler { in: thread ) } - - promise.catch { [weak self] error in - GRDBStorage.shared.read { db in + .done { seal.fulfill(()) } + .catch { error in + GRDBStorage.shared.read { [weak self] db in self?.notificationPresenter.notifyForFailedSend(db, in: thread) } + + seal.reject(error) } + .retainUntilComplete() return promise } diff --git a/Session/Open Groups/JoinOpenGroupVC.swift b/Session/Open Groups/JoinOpenGroupVC.swift index aca0f2ce6..866f2442b 100644 --- a/Session/Open Groups/JoinOpenGroupVC.swift +++ b/Session/Open Groups/JoinOpenGroupVC.swift @@ -163,7 +163,7 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC ModalActivityIndicatorViewController.present(fromViewController: navigationController, canCancel: false) { [weak self] _ in GRDBStorage.shared - .write { db in + .writeAsync { db in OpenGroupManager.shared.add( db, roomToken: roomToken, diff --git a/Session/Settings/NukeDataModal.swift b/Session/Settings/NukeDataModal.swift index 54ccf1d22..06a35c38c 100644 --- a/Session/Settings/NukeDataModal.swift +++ b/Session/Settings/NukeDataModal.swift @@ -154,17 +154,16 @@ final class NukeDataModal: Modal { @objc private func clearDeviceOnly() { ModalActivityIndicatorViewController.present(fromViewController: self, canCancel: false) { [weak self] _ in - GRDBStorage.shared.write { db in - try MessageSender.syncConfiguration(db, forceSyncNow: true) - .ensure(on: DispatchQueue.main) { - self?.dismiss(animated: true, completion: nil) // Dismiss the loader - - UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later - General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access - NotificationCenter.default.post(name: .dataNukeRequested, object: nil) - } - .retainUntilComplete() - } + GRDBStorage.shared + .writeAsync { db in try MessageSender.syncConfiguration(db, forceSyncNow: true) } + .ensure(on: DispatchQueue.main) { + self?.dismiss(animated: true, completion: nil) // Dismiss the loader + + UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later + General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access + NotificationCenter.default.post(name: .dataNukeRequested, object: nil) + } + .retainUntilComplete() } } diff --git a/SessionMessagingKit/Calls/WebRTCSession.swift b/SessionMessagingKit/Calls/WebRTCSession.swift index b73558450..20345393c 100644 --- a/SessionMessagingKit/Calls/WebRTCSession.swift +++ b/SessionMessagingKit/Calls/WebRTCSession.swift @@ -170,7 +170,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { } } GRDBStorage.shared - .write { db in + .writeAsync { db in try MessageSender .sendNonDurably( db, diff --git a/SessionMessagingKit/Database/Models/ControlMessageProcessRecord.swift b/SessionMessagingKit/Database/Models/ControlMessageProcessRecord.swift index 5d13f758b..b2efb6b98 100644 --- a/SessionMessagingKit/Database/Models/ControlMessageProcessRecord.swift +++ b/SessionMessagingKit/Database/Models/ControlMessageProcessRecord.swift @@ -71,6 +71,13 @@ public struct ControlMessageProcessRecord: Codable, FetchableRecord, Persistable // the unique constraints on that table prevent duplicate messages if message is VisibleMessage { return nil } + // Allow duplicates for UnsendRequest messages, if a user received an UnsendRequest + // as a push notification the it wouldn't include a serverHash and, as a result, + // wouldn't get deleted from the server - since the logic only runs if we find a + // matching message the safest option is to allow duplicate handling to avoid an + // edge-case where a message doesn't get deleted + if message is UnsendRequest { return nil } + // Allow duplicates for all call messages, the double checking will be done on // message handling to make sure the messages are for the same ongoing call if message is CallMessage { return nil } diff --git a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift index 96436c6f4..1f9dda368 100644 --- a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift +++ b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift @@ -130,7 +130,7 @@ public enum MessageSendJob: JobExecutor { details.message.threadId = (details.message.threadId ?? job.threadId) // Perform the actual message sending - GRDBStorage.shared.write { db -> Promise in + GRDBStorage.shared.writeAsync { db -> Promise in try MessageSender.sendImmediate( db, message: details.message, @@ -170,6 +170,7 @@ public enum MessageSendJob: JobExecutor { failure(job, error, false) } } + .retainUntilComplete() } } diff --git a/SessionMessagingKit/Jobs/Types/SendReadReceiptsJob.swift b/SessionMessagingKit/Jobs/Types/SendReadReceiptsJob.swift index c1194a920..429bda7e6 100644 --- a/SessionMessagingKit/Jobs/Types/SendReadReceiptsJob.swift +++ b/SessionMessagingKit/Jobs/Types/SendReadReceiptsJob.swift @@ -35,7 +35,7 @@ public enum SendReadReceiptsJob: JobExecutor { } GRDBStorage.shared - .write { db in + .writeAsync { db in try MessageSender.sendImmediate( db, message: ReadReceipt( diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index e9e96e518..a2e99cac6 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -194,7 +194,7 @@ public final class OpenGroupManager: NSObject { // Note: We don't do this after the db commit as it can fail (resulting in endless loading) OpenGroupAPI.workQueue.async { dependencies.storage - .write { db in + .writeAsync { db in OpenGroupAPI .capabilitiesAndRoom( db, @@ -809,7 +809,7 @@ public final class OpenGroupManager: NSObject { // Trigger the download on a background queue DispatchQueue.global(qos: .background).async { dependencies.storage - .write { db in + .writeAsync { db in OpenGroupAPI .downloadFile( db, @@ -832,6 +832,7 @@ public final class OpenGroupManager: NSObject { seal.fulfill(imageData) } .catch { seal.reject($0) } + .retainUntilComplete() } dependencies.mutableCache.mutate { cache in diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift index bd6f0952f..c0e0b0115 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift @@ -164,8 +164,7 @@ extension MessageSender { } if let error: Error = errors.first { return Promise(error: error) } - - return GRDBStorage.shared.write { db in + return GRDBStorage.shared.writeAsync { db in try MessageSender.sendImmediate( db, message: message, diff --git a/SessionShareExtension/ThreadPickerVC.swift b/SessionShareExtension/ThreadPickerVC.swift index e444bbe48..82d4f99ab 100644 --- a/SessionShareExtension/ThreadPickerVC.swift +++ b/SessionShareExtension/ThreadPickerVC.swift @@ -221,7 +221,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView ModalActivityIndicatorViewController.present(fromViewController: shareVC!, canCancel: false, message: "vc_share_sending_message".localized()) { activityIndicator in GRDBStorage.shared - .write { [weak self] db -> Promise in + .writeAsync { [weak self] db -> Promise in guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else { activityIndicator.dismiss { } self?.shareVC?.shareViewFailed(error: MessageSenderError.noThread)