diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index e9d291775..d41e19fdf 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -295,6 +295,7 @@ B8CA011525A293800091AF73 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011425A293800091AF73 /* Configuration.swift */; }; B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */; }; B8CA014125A293EE0091AF73 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA014025A293EE0091AF73 /* Storage.swift */; }; + B8CADAE925AFADF400AAFA15 /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */; }; B8CCF6352396005F0091D419 /* SpaceMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */; }; B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */; }; B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */; }; @@ -475,7 +476,6 @@ C33FDC95255A582000E217F9 /* OWSFailedMessagesJob.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDADB255A580400E217F9 /* OWSFailedMessagesJob.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC96255A582000E217F9 /* NSObject+Casting.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDADC255A580400E217F9 /* NSObject+Casting.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; }; - C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADF255A580400E217F9 /* PublicChatManager.swift */; }; C33FDC9A255A582000E217F9 /* ByteParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAE0255A580400E217F9 /* ByteParser.m */; }; C33FDCA2255A582000E217F9 /* OWSMessageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33FDCC7255A582000E217F9 /* NSArray+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB0D255A580800E217F9 /* NSArray+OWS.m */; }; @@ -718,7 +718,6 @@ C3A7229C2558E4310043A11F /* OpenGroupMessage+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */; }; C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Description.swift */; }; C3AAFFC225AE916D0089E6DD /* OpenGroupManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFC125AE916D0089E6DD /* OpenGroupManagerProtocol.swift */; }; - C3AAFFD525AE92860089E6DD /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */; }; C3AAFFE825AE975D0089E6DD /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFDE25AE96FF0089E6DD /* ConfigurationMessage+Convenience.swift */; }; C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFF125AE99710089E6DD /* AppDelegate.swift */; }; C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0752554CDA60050F1E3 /* Configuration.swift */; }; @@ -1391,7 +1390,6 @@ C33FDADC255A580400E217F9 /* NSObject+Casting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+Casting.h"; sourceTree = ""; }; C33FDADD255A580400E217F9 /* TSInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInfoMessage.h; sourceTree = ""; }; C33FDADE255A580400E217F9 /* SwiftSingletons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSingletons.swift; sourceTree = ""; }; - C33FDADF255A580400E217F9 /* PublicChatManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicChatManager.swift; sourceTree = ""; }; C33FDAE0255A580400E217F9 /* ByteParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ByteParser.m; sourceTree = ""; }; C33FDAE1255A580400E217F9 /* OWSReadTracking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSReadTracking.h; sourceTree = ""; }; C33FDAE4255A580400E217F9 /* TSAttachmentStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAttachmentStream.h; sourceTree = ""; }; @@ -3118,8 +3116,6 @@ isa = PBXGroup; children = ( C33FDB19255A580900E217F9 /* GroupUtilities.swift */, - C33FDADF255A580400E217F9 /* PublicChatManager.swift */, - C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */, C38EF3E5255B6DF4007E1867 /* ContactCellView.h */, C38EF3D6255B6DEF007E1867 /* ContactCellView.m */, C38EF3E6255B6DF4007E1867 /* ContactTableViewCell.h */, @@ -3197,6 +3193,7 @@ C3A721362558BDFA0043A11F /* OpenGroupInfo.swift */, C3A721342558BDF90043A11F /* OpenGroupMessage.swift */, C3A7229B2558E4310043A11F /* OpenGroupMessage+Conversion.swift */, + C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */, ); path = "Open Groups"; sourceTree = ""; @@ -4640,7 +4637,6 @@ C38EF3C3255B6DE7007E1867 /* ImageEditorTextItem.swift in Sources */, C33FDC7D255A582000E217F9 /* OWSDispatch.m in Sources */, C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */, - C33FDC99255A582000E217F9 /* PublicChatManager.swift in Sources */, C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */, C38EF35F255B6DCC007E1867 /* SelectRecipientViewController.m in Sources */, C38EF3C5255B6DE7007E1867 /* OWSViewController+ImageEditor.swift in Sources */, @@ -4709,7 +4705,6 @@ C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */, C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */, C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */, - C3AAFFD525AE92860089E6DD /* OpenGroupManager.swift in Sources */, C38EF408255B6DF7007E1867 /* OWSSearchBar.m in Sources */, C38EF327255B6DBF007E1867 /* BlockListUIUtils.m in Sources */, C38EF310255B6DBF007E1867 /* DebugLogger.m in Sources */, @@ -4927,6 +4922,7 @@ C32C5EDC256DF501003C73A2 /* YapDatabaseConnection+OWS.m in Sources */, C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */, B8B32033258B235D0020074B /* Storage+Contacts.swift in Sources */, + B8CADAE925AFADF400AAFA15 /* OpenGroupManager.swift in Sources */, B8856D69256F141F001CE70E /* OWSWindowManager.m in Sources */, C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */, C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */, diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index cb18de22a..ccbaee39e 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -395,45 +395,14 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { guard let thread = self.thread(at: indexPath.row) else { return [] } - let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in var message = NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: "") if let thread = thread as? TSGroupThread, thread.isClosedGroup, thread.groupModel.groupAdminIds.contains(getUserHexEncodedPublicKey()) { message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone." } let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in - Storage.write { transaction in - Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction) - if let openGroup = openGroup { - var messageIDs: Set = [] - thread.enumerateInteractions(with: transaction) { interaction, _ in - messageIDs.insert(interaction.uniqueId!) - } - OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction) - transaction.removeObject(forKey: "\(openGroup.server).\(openGroup.channel)", inCollection: Storage.lastMessageServerIDCollection) - transaction.removeObject(forKey: "\(openGroup.server).\(openGroup.channel)", inCollection: Storage.lastDeletionServerIDCollection) - let _ = OpenGroupAPI.leave(openGroup.channel, on: openGroup.server) - thread.removeAllThreadInteractions(with: transaction) - thread.remove(with: transaction) - } else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true { - let groupID = thread.groupModel.groupId - let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID) - do { - try MessageSender.leave(groupPublicKey, using: transaction) - } catch { - // TODO: Handle - } - Storage.write { transaction in - thread.removeAllThreadInteractions(with: transaction) - thread.remove(with: transaction) - } - } else { - thread.removeAllThreadInteractions(with: transaction) - thread.remove(with: transaction) - } - } - NotificationCenter.default.post(name: .threadDeleted, object: nil, userInfo: [ "threadId" : thread.uniqueId! ]) + alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { [weak self] _ in + self?.delete(thread) }) alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .default) { _ in }) guard let self = self else { return } @@ -460,6 +429,29 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol } } + private func delete(_ thread: TSThread) { + let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) + Storage.write { transaction in + Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction) + if let openGroup = openGroup { + OpenGroupManager.shared.delete(openGroup, associatedWith: thread, using: transaction) + } else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true { + let groupID = thread.groupModel.groupId + let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID) + do { + try MessageSender.leave(groupPublicKey, using: transaction) + } catch { + // TODO: Handle + } + thread.removeAllThreadInteractions(with: transaction) + thread.remove(with: transaction) + } else { + thread.removeAllThreadInteractions(with: transaction) + thread.remove(with: transaction) + } + } + } + @objc private func openSettings() { let settingsVC = SettingsVC() let navigationController = OWSNavigationController(rootViewController: settingsVC) diff --git a/Session/Meta/AppDelegate.m b/Session/Meta/AppDelegate.m index 2c5c6dc3e..952f58dfe 100644 --- a/Session/Meta/AppDelegate.m +++ b/Session/Meta/AppDelegate.m @@ -756,10 +756,10 @@ static NSTimeInterval launchStartedAt; - (void)startOpenGroupPollersIfNeeded { - [LKPublicChatManager.shared startPollersIfNeeded]; + [SNOpenGroupManager.shared startPolling]; } -- (void)stopOpenGroupPollers { [LKPublicChatManager.shared stopPollers]; } +- (void)stopOpenGroupPollers { [SNOpenGroupManager.shared stopPolling]; } # pragma mark - App Mode @@ -813,7 +813,7 @@ static NSTimeInterval launchStartedAt; [self stopPoller]; [self stopClosedGroupPoller]; [self stopOpenGroupPollers]; - [LKPublicChatManager.shared stopPollers]; + [SNOpenGroupManager.shared stopPolling]; BOOL wasUnlinked = [NSUserDefaults.standardUserDefaults boolForKey:@"wasUnlinked"]; [SignalApp resetAppData:^{ // Resetting the data clears the old user defaults. We need to restore the unlink default. diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index e23f8c834..8da88149d 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -13,6 +13,7 @@ extension AppDelegate { let job = MessageSendJob(message: configurationMessage, destination: destination) JobQueue.shared.add(job, using: transaction) } + userDefaults[.lastConfigurationSync] = Date() } func forceSyncConfigurationNowIfNeeded() -> Promise { diff --git a/Session/Open Groups/JoinPublicChatVC.swift b/Session/Open Groups/JoinPublicChatVC.swift index 203820574..267a6618e 100644 --- a/Session/Open Groups/JoinPublicChatVC.swift +++ b/Session/Open Groups/JoinPublicChatVC.swift @@ -132,11 +132,9 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie } isJoining = true ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in - Storage.shared.write { transaction in - OpenGroupManager.shared.addOpenGroup(with: urlAsString, using: transaction) + Storage.shared.write(with: { transaction in + OpenGroupManager.shared.add(with: urlAsString, using: transaction) .done(on: DispatchQueue.main) { [weak self] _ in - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...) self?.presentingViewController!.dismiss(animated: true, completion: nil) } .catch(on: DispatchQueue.main) { [weak self] error in @@ -150,7 +148,10 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie self?.isJoining = false self?.showError(title: title, message: message) } - } + }, completion: { + let appDelegate = UIApplication.shared.delegate as! AppDelegate + appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...) + }) } } diff --git a/SessionMessagingKit/Database/Storage+OpenGroups.swift b/SessionMessagingKit/Database/Storage+OpenGroups.swift index a6b87a1b1..7797c3ee5 100644 --- a/SessionMessagingKit/Database/Storage+OpenGroups.swift +++ b/SessionMessagingKit/Database/Storage+OpenGroups.swift @@ -116,16 +116,6 @@ extension Storage { (transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: server, inCollection: Storage.openGroupPublicKeyCollection) } - - - // MARK: - Deletion - - public func clearAllData(for group: UInt64, on server: String, using transaction: Any) { - removeLastMessageServerID(for: group, on: server, using: transaction) - removeLastDeletionServerID(for: group, on: server, using: transaction) - removeOpenGroupPublicKey(for: server, using: transaction) - } - // MARK: - Last Message Server ID diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift new file mode 100644 index 000000000..aa9b9e593 --- /dev/null +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -0,0 +1,96 @@ +import PromiseKit + +@objc(SNOpenGroupManager) +public final class OpenGroupManager : NSObject, OpenGroupManagerProtocol { + private var pollers: [String:OpenGroupPoller] = [:] + private var isPolling = false + + // MARK: Error + public enum Error : LocalizedError { + case invalidURL + + public var errorDescription: String? { + switch self { + case .invalidURL: return "Invalid URL." + } + } + } + + // MARK: Initialization + @objc public static let shared = OpenGroupManager() + + private override init() { } + + // MARK: Polling + @objc public func startPolling() { + guard !isPolling else { return } + isPolling = true + let openGroups = Storage.shared.getAllUserOpenGroups() + for (_, openGroup) in openGroups { + if let poller = pollers[openGroup.id] { poller.stop() } // Should never occur + let poller = OpenGroupPoller(for: openGroup) + poller.startIfNeeded() + pollers[openGroup.id] = poller + } + } + + @objc public func stopPolling() { + pollers.forEach { (_, openGroupPoller) in openGroupPoller.stop() } + pollers.removeAll() + } + + // MARK: Adding & Removing + public func add(with url: String, using transaction: Any) -> Promise { + guard let url = URL(string: url), let scheme = url.scheme, scheme == "https", url.host != nil else { + return Promise(error: Error.invalidURL) + } + let channel: UInt64 = 1 + let server = url.absoluteString + let userPublicKey = getUserHexEncodedPublicKey() + let profileManager = SSKEnvironment.shared.profileManager + let displayName = profileManager.profileNameForRecipient(withID: userPublicKey) + let profilePictureURL = profileManager.profilePictureURL() + let profileKey = profileManager.localProfileKey().keyData + Storage.shared.removeLastMessageServerID(for: channel, on: server, using: transaction) + Storage.shared.removeLastDeletionServerID(for: channel, on: server, using: transaction) + return OpenGroupAPI.getInfo(for: channel, on: server).done { info in + let openGroup = OpenGroup(channel: channel, server: server, displayName: info.displayName, isDeletable: true)! + let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id) + let model = TSGroupModel(title: openGroup.displayName, memberIds: [ userPublicKey ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: []) + Storage.shared.write(with: { transaction in + let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction as! YapDatabaseReadWriteTransaction) + Storage.shared.setOpenGroup(openGroup, for: thread.uniqueId!, using: transaction) + }, completion: { + let _ = OpenGroupAPI.setDisplayName(to: displayName, on: server) + let _ = OpenGroupAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: server) + let _ = OpenGroupAPI.join(channel, on: server) + if let poller = OpenGroupManager.shared.pollers[openGroup.id] { + poller.stop() + OpenGroupManager.shared.pollers[openGroup.id] = nil + } + let poller = OpenGroupPoller(for: openGroup) + poller.startIfNeeded() + OpenGroupManager.shared.pollers[openGroup.id] = poller + }) + } + } + + public func delete(_ openGroup: OpenGroup, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) { + if let poller = pollers[openGroup.id] { + poller.stop() + pollers[openGroup.id] = nil + } + var messageIDs: Set = [] + thread.enumerateInteractions(with: transaction) { interaction, _ in + messageIDs.insert(interaction.uniqueId!) + } + SNMessagingKitConfiguration.shared.storage.updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, using: transaction) + Storage.shared.removeLastMessageServerID(for: openGroup.channel, on: openGroup.server, using: transaction) + Storage.shared.removeLastDeletionServerID(for: openGroup.channel, on: openGroup.server, using: transaction) + let _ = OpenGroupAPI.leave(openGroup.channel, on: openGroup.server) + Storage.shared.removeOpenGroupPublicKey(for: openGroup.server, using: transaction) + thread.removeAllThreadInteractions(with: transaction) + thread.remove(with: transaction) + Storage.shared.removeOpenGroup(for: thread.uniqueId!, using: transaction) + } +} diff --git a/SessionMessagingKit/OpenGroupManagerProtocol.swift b/SessionMessagingKit/OpenGroupManagerProtocol.swift index 2617d8e85..2a640ae35 100644 --- a/SessionMessagingKit/OpenGroupManagerProtocol.swift +++ b/SessionMessagingKit/OpenGroupManagerProtocol.swift @@ -2,5 +2,5 @@ import PromiseKit public protocol OpenGroupManagerProtocol { - func addOpenGroup(with url: String, using transaction: Any) -> Promise + func add(with url: String, using transaction: Any) -> Promise } diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index ca08ac18a..9db13b83a 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -92,22 +92,6 @@ extension MessageReceiver { } } - private static func handleConfigurationMessage(_ message: ConfigurationMessage, using transaction: Any) { - guard message.sender == getUserHexEncodedPublicKey() else { return } - let storage = SNMessagingKitConfiguration.shared.storage - let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys() - for closedGroup in message.closedGroups { - guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue } - handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair, - members: [String](closedGroup.members), admins: [String](closedGroup.admins), using: transaction) - } - let allOpenGroups = Set(storage.getAllUserOpenGroups().keys) - for openGroupURL in message.openGroups { - guard !allOpenGroups.contains(openGroupURL) else { continue } - SNMessagingKitConfiguration.shared.openGroupManager.addOpenGroup(with: openGroupURL, using: transaction).retainUntilComplete() - } - } - public static func setExpirationTimer(to duration: UInt32, for senderPublicKey: String, groupPublicKey: String?, using transaction: Any) { let transaction = transaction as! YapDatabaseReadWriteTransaction var isGroup = false @@ -151,6 +135,22 @@ extension MessageReceiver { message.save(with: transaction) SSKEnvironment.shared.disappearingMessagesJob.startIfNecessary() } + + private static func handleConfigurationMessage(_ message: ConfigurationMessage, using transaction: Any) { + guard message.sender == getUserHexEncodedPublicKey() else { return } + let storage = SNMessagingKitConfiguration.shared.storage + let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys() + for closedGroup in message.closedGroups { + guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue } + handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair, + members: [String](closedGroup.members), admins: [String](closedGroup.admins), using: transaction) + } + let allOpenGroups = Set(storage.getAllUserOpenGroups().keys) + for openGroupURL in message.openGroups { + guard !allOpenGroups.contains(openGroupURL) else { continue } + SNMessagingKitConfiguration.shared.openGroupManager.add(with: openGroupURL, using: transaction).retainUntilComplete() + } + } @discardableResult public static func handleVisibleMessage(_ message: VisibleMessage, associatedWithProto proto: SNProtoContent, openGroupID: String?, isBackgroundPoll: Bool, using transaction: Any) throws -> String { diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index 0af9f9e63..c35bb3d13 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -53,6 +53,7 @@ public protocol SessionMessagingKitStorageProtocol { func getAllUserOpenGroups() -> [String:OpenGroup] func getOpenGroup(for threadID: String) -> OpenGroup? func getThreadID(for openGroupID: String) -> String? + func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set, using transaction: Any) // MARK: - Open Group Public Keys diff --git a/SignalUtilitiesKit/Database/Storage+Conformances.swift b/SignalUtilitiesKit/Database/Storage+Conformances.swift index c42dcc40f..2834f277d 100644 --- a/SignalUtilitiesKit/Database/Storage+Conformances.swift +++ b/SignalUtilitiesKit/Database/Storage+Conformances.swift @@ -1,2 +1,8 @@ -extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol { } +extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol { + + public func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set, using transaction: Any) { + let transaction = transaction as! YapDatabaseReadWriteTransaction + OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction) + } +} diff --git a/SignalUtilitiesKit/To Do/OpenGroupManager.swift b/SignalUtilitiesKit/To Do/OpenGroupManager.swift deleted file mode 100644 index 1c230aedb..000000000 --- a/SignalUtilitiesKit/To Do/OpenGroupManager.swift +++ /dev/null @@ -1,39 +0,0 @@ -import PromiseKit - -public final class OpenGroupManager : OpenGroupManagerProtocol { - - public enum Error : LocalizedError { - case invalidURL - - public var errorDescription: String? { - switch self { - case .invalidURL: return "Invalid URL." - } - } - } - - public static let shared = OpenGroupManager() - - private init() { } - - public func addOpenGroup(with url: String, using transaction: Any) -> Promise { - guard let url = URL(string: url), let scheme = url.scheme, scheme == "https", url.host != nil else { - return Promise(error: Error.invalidURL) - } - let channelID: UInt64 = 1 - let urlAsString = url.absoluteString - let userPublicKey = getUserHexEncodedPublicKey() - let profileManager = OWSProfileManager.shared() - let displayName = profileManager.profileNameForRecipient(withID: userPublicKey) - let profilePictureURL = profileManager.profilePictureURL() - let profileKey = profileManager.localProfileKey().keyData - let transaction = transaction as! YapDatabaseReadWriteTransaction - transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: Storage.lastMessageServerIDCollection) - transaction.removeObject(forKey: "\(urlAsString).\(channelID)", inCollection: Storage.lastDeletionServerIDCollection) - return PublicChatManager.shared.addChat(server: urlAsString, channel: channelID).done(on: DispatchQueue.main) { _ in - let _ = OpenGroupAPI.setDisplayName(to: displayName, on: urlAsString) - let _ = OpenGroupAPI.setProfilePictureURL(to: profilePictureURL, using: profileKey, on: urlAsString) - let _ = OpenGroupAPI.join(channelID, on: urlAsString) - } - } -} diff --git a/SignalUtilitiesKit/To Do/PublicChatManager.swift b/SignalUtilitiesKit/To Do/PublicChatManager.swift deleted file mode 100644 index 624ea7509..000000000 --- a/SignalUtilitiesKit/To Do/PublicChatManager.swift +++ /dev/null @@ -1,131 +0,0 @@ -import PromiseKit - -// TODO: Clean - -@objc(LKPublicChatManager) -public final class PublicChatManager : NSObject { - private let storage = OWSPrimaryStorage.shared() - @objc public var chats: [String:OpenGroup] = [:] - private var pollers: [String:OpenGroupPoller] = [:] - private var isPolling = false - - private var userHexEncodedPublicKey: String? { - return OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey - } - - public enum Error : Swift.Error { - case chatCreationFailed - case userPublicKeyNotFound - } - - @objc public static let shared = PublicChatManager() - - private override init() { - super.init() - NotificationCenter.default.addObserver(self, selector: #selector(onThreadDeleted(_:)), name: .threadDeleted, object: nil) - refreshChatsAndPollers() - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - @objc public func startPollersIfNeeded() { - for (threadID, publicChat) in chats { - if let poller = pollers[threadID] { - poller.startIfNeeded() - } else { - let poller = OpenGroupPoller(for: publicChat) - poller.startIfNeeded() - pollers[threadID] = poller - } - } - isPolling = true - } - - @objc public func stopPollers() { - for poller in pollers.values { poller.stop() } - isPolling = false - } - - public func addChat(server: String, channel: UInt64) -> Promise { - if let existingChat = getChat(server: server, channel: channel) { - if let newChat = self.addChat(server: server, channel: channel, name: existingChat.displayName) { - return Promise.value(newChat) - } else { - return Promise(error: Error.chatCreationFailed) - } - } - return OpenGroupAPI.getInfo(for: channel, on: server).map2 { channelInfo -> OpenGroup in - guard let chat = self.addChat(server: server, channel: channel, name: channelInfo.displayName) else { throw Error.chatCreationFailed } - return chat - } - } - - @discardableResult - @objc(addChatWithServer:channel:name:) - public func addChat(server: String, channel: UInt64, name: String) -> OpenGroup? { - guard let chat = OpenGroup(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil } - let model = TSGroupModel(title: chat.displayName, memberIds: [userHexEncodedPublicKey!, chat.server], image: nil, groupId: LKGroupUtilities.getEncodedOpenGroupIDAsData(chat.id), groupType: .openGroup, adminIds: []) - - // Store the group chat mapping - Storage.writeSync { transaction in - let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction) - - // Save the group chat - Storage.shared.setOpenGroup(chat, for: thread.uniqueId!, using: transaction) - } - - // Update chats and pollers - self.refreshChatsAndPollers() - - return chat - } - - @objc(addChatWithServer:channel:) - public func objc_addChat(server: String, channel: UInt64) -> AnyPromise { - return AnyPromise.from(addChat(server: server, channel: channel)) - } - - @objc func refreshChatsAndPollers() { - let newChats = Storage.shared.getAllUserOpenGroups() - - // Remove any chats that don't exist in the database - let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) } - removedChatThreadIds.forEach { threadID in - let poller = self.pollers.removeValue(forKey: threadID) - poller?.stop() - } - - // Only append to chats if we have a thread for the chat - self.chats = newChats.filter { (threadID, group) in - return TSGroupThread.fetch(uniqueId: threadID) != nil - } - - if (isPolling) { startPollersIfNeeded() } - } - - @objc private func onThreadDeleted(_ notification: Notification) { - guard let threadId = notification.userInfo?["threadId"] as? String else { return } - - // Reset the last message cache - if let chat = self.chats[threadId] { - Storage.write { transaction in - Storage.shared.clearAllData(for: chat.channel, on: chat.server, using: transaction) - } - } - - // Remove the chat from the db - Storage.write { transaction in - Storage.shared.removeOpenGroup(for: threadId, using: transaction) - } - - refreshChatsAndPollers() - } - - public func getChat(server: String, channel: UInt64) -> OpenGroup? { - return chats.values.first { chat in - return chat.server == server && chat.channel == channel - } - } -}