mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Renamed the OpenGroupPollerV2 and OpenGroupManagerV2
This commit is contained in:
parent
cd3dffcff9
commit
8cc9caa0fd
|
@ -752,8 +752,8 @@
|
||||||
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */; };
|
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */; };
|
||||||
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */; };
|
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */; };
|
||||||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
||||||
C3DB66AC260ACA42001EFC55 /* OpenGroupManagerV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */; };
|
C3DB66AC260ACA42001EFC55 /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */; };
|
||||||
C3DB66C3260ACCE6001EFC55 /* OpenGroupPollerV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66C2260ACCE6001EFC55 /* OpenGroupPollerV2.swift */; };
|
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */; };
|
||||||
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */; };
|
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */; };
|
||||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
||||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
||||||
|
@ -1859,8 +1859,8 @@
|
||||||
C3D9E43025676D3D0040E4F3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
|
C3D9E43025676D3D0040E4F3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
|
||||||
C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMessage.swift; sourceTree = "<group>"; };
|
C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMessage.swift; sourceTree = "<group>"; };
|
||||||
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
|
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
|
||||||
C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManagerV2.swift; sourceTree = "<group>"; };
|
C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManager.swift; sourceTree = "<group>"; };
|
||||||
C3DB66C2260ACCE6001EFC55 /* OpenGroupPollerV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupPollerV2.swift; sourceTree = "<group>"; };
|
C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupPoller.swift; sourceTree = "<group>"; };
|
||||||
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupAPI+ObjC.swift"; sourceTree = "<group>"; };
|
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupAPI+ObjC.swift"; sourceTree = "<group>"; };
|
||||||
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
|
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
|
||||||
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
|
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2748,7 +2748,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */,
|
C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */,
|
||||||
C3DB66C2260ACCE6001EFC55 /* OpenGroupPollerV2.swift */,
|
C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */,
|
||||||
C33FDB3A255A580B00E217F9 /* Poller.swift */,
|
C33FDB3A255A580B00E217F9 /* Poller.swift */,
|
||||||
);
|
);
|
||||||
path = Pollers;
|
path = Pollers;
|
||||||
|
@ -3357,7 +3357,7 @@
|
||||||
FDC4380727B31D3A00C60D73 /* Types */,
|
FDC4380727B31D3A00C60D73 /* Types */,
|
||||||
B88FA7B726045D100049422F /* OpenGroupAPI.swift */,
|
B88FA7B726045D100049422F /* OpenGroupAPI.swift */,
|
||||||
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */,
|
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */,
|
||||||
C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */,
|
C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */,
|
||||||
);
|
);
|
||||||
path = "Open Groups";
|
path = "Open Groups";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -5131,7 +5131,7 @@
|
||||||
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */,
|
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */,
|
||||||
C32C5E75256DE020003C73A2 /* YapDatabaseTransaction+OWS.m in Sources */,
|
C32C5E75256DE020003C73A2 /* YapDatabaseTransaction+OWS.m in Sources */,
|
||||||
C3BBE0802554CDD70050F1E3 /* Storage.swift in Sources */,
|
C3BBE0802554CDD70050F1E3 /* Storage.swift in Sources */,
|
||||||
C3DB66AC260ACA42001EFC55 /* OpenGroupManagerV2.swift in Sources */,
|
C3DB66AC260ACA42001EFC55 /* OpenGroupManager.swift in Sources */,
|
||||||
B8F5F61B25EDE4BF003BF8D4 /* DataExtractionNotificationInfoMessage.swift in Sources */,
|
B8F5F61B25EDE4BF003BF8D4 /* DataExtractionNotificationInfoMessage.swift in Sources */,
|
||||||
FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */,
|
FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */,
|
||||||
C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */,
|
C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */,
|
||||||
|
@ -5230,7 +5230,7 @@
|
||||||
C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */,
|
C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */,
|
||||||
FDC438C327BB512200C60D73 /* SodiumProtocols.swift in Sources */,
|
FDC438C327BB512200C60D73 /* SodiumProtocols.swift in Sources */,
|
||||||
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
|
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
|
||||||
C3DB66C3260ACCE6001EFC55 /* OpenGroupPollerV2.swift in Sources */,
|
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */,
|
||||||
FDC438AE27BB148700C60D73 /* UserDeleteMessagesResponse.swift in Sources */,
|
FDC438AE27BB148700C60D73 /* UserDeleteMessagesResponse.swift in Sources */,
|
||||||
C3C2A75F2553A3C500C340D1 /* VisibleMessage+LinkPreview.swift in Sources */,
|
C3C2A75F2553A3C500C340D1 /* VisibleMessage+LinkPreview.swift in Sources */,
|
||||||
C32C5FBB256E0206003C73A2 /* OWSBackgroundTask.m in Sources */,
|
C32C5FBB256E0206003C73A2 /* OWSBackgroundTask.m in Sources */,
|
||||||
|
|
|
@ -63,23 +63,24 @@ final class JoinOpenGroupModal : Modal {
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
@objc private func joinOpenGroup() {
|
@objc private func joinOpenGroup() {
|
||||||
guard let (room, server, publicKey) = OpenGroupManagerV2.parseV2OpenGroup(from: url) else {
|
guard let (room, server, publicKey) = OpenGroupManager.parseV2OpenGroup(from: url) else {
|
||||||
let alert = UIAlertController(title: "Couldn't Join", message: nil, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Couldn't Join", message: nil, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||||
return presentingViewController!.present(alert, animated: true, completion: nil)
|
return presentingViewController!.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
presentingViewController!.dismiss(animated: true, completion: nil)
|
presentingViewController!.dismiss(animated: true, completion: nil)
|
||||||
Storage.shared.write { [presentingViewController = self.presentingViewController!] transaction in
|
Storage.shared.write { [presentingViewController = self.presentingViewController!] transaction in
|
||||||
OpenGroupManagerV2.shared.add(room: room, server: server, publicKey: publicKey, using: transaction)
|
OpenGroupManager.shared
|
||||||
.done(on: DispatchQueue.main) { _ in
|
.add(room: room, server: server, publicKey: publicKey, using: transaction)
|
||||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
.done(on: DispatchQueue.main) { _ in
|
||||||
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...)
|
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||||
}
|
appDelegate.forceSyncConfigurationNowIfNeeded().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)
|
.catch(on: DispatchQueue.main) { error in
|
||||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
let alert = UIAlertController(title: "Couldn't Join", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
presentingViewController.present(alert, animated: true, completion: nil)
|
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||||
}
|
presentingViewController.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -525,7 +525,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
|
||||||
Storage.write { transaction in
|
Storage.write { transaction in
|
||||||
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
|
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
|
||||||
if let openGroupV2 = openGroupV2 {
|
if let openGroupV2 = openGroupV2 {
|
||||||
OpenGroupManagerV2.shared.delete(openGroupV2, associatedWith: thread, using: transaction)
|
OpenGroupManager.shared.delete(openGroupV2, associatedWith: thread, using: transaction)
|
||||||
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true {
|
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true {
|
||||||
let groupID = thread.groupModel.groupId
|
let groupID = thread.groupModel.groupId
|
||||||
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
|
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
|
||||||
|
|
|
@ -697,11 +697,11 @@ static NSTimeInterval launchStartedAt;
|
||||||
|
|
||||||
- (void)startOpenGroupPollersIfNeeded
|
- (void)startOpenGroupPollersIfNeeded
|
||||||
{
|
{
|
||||||
[SNOpenGroupManagerV2.shared startPolling];
|
[SNOpenGroupManager.shared startPolling];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopOpenGroupPollers {
|
- (void)stopOpenGroupPollers {
|
||||||
[SNOpenGroupManagerV2.shared stopPolling];
|
[SNOpenGroupManager.shared stopPolling];
|
||||||
}
|
}
|
||||||
|
|
||||||
# pragma mark - App Mode
|
# pragma mark - App Mode
|
||||||
|
|
|
@ -127,7 +127,7 @@ final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageView
|
||||||
fileprivate func joinOpenGroup(with string: String) {
|
fileprivate func joinOpenGroup(with string: String) {
|
||||||
// A V2 open group URL will look like: <optional scheme> + <host> + <optional port> + <room> + <public key>
|
// A V2 open group URL will look like: <optional scheme> + <host> + <optional port> + <room> + <public key>
|
||||||
// The host doesn't parse if no explicit scheme is provided
|
// The host doesn't parse if no explicit scheme is provided
|
||||||
if let (room, server, publicKey) = OpenGroupManagerV2.parseV2OpenGroup(from: string) {
|
if let (room, server, publicKey) = OpenGroupManager.parseV2OpenGroup(from: string) {
|
||||||
joinV2OpenGroup(room: room, server: server, publicKey: publicKey)
|
joinV2OpenGroup(room: room, server: server, publicKey: publicKey)
|
||||||
} else {
|
} else {
|
||||||
let title = NSLocalizedString("invalid_url", comment: "")
|
let title = NSLocalizedString("invalid_url", comment: "")
|
||||||
|
@ -141,7 +141,7 @@ final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageView
|
||||||
isJoining = true
|
isJoining = true
|
||||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
|
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
|
||||||
Storage.shared.write { transaction in
|
Storage.shared.write { transaction in
|
||||||
OpenGroupManagerV2.shared.add(room: room, server: server, publicKey: publicKey, using: transaction)
|
OpenGroupManager.shared.add(room: room, server: server, publicKey: publicKey, using: transaction)
|
||||||
.done(on: DispatchQueue.main) { [weak self] _ in
|
.done(on: DispatchQueue.main) { [weak self] _ in
|
||||||
self?.presentingViewController?.dismiss(animated: true, completion: nil)
|
self?.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||||
|
|
|
@ -2,7 +2,7 @@ import PromiseKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
|
|
||||||
@objc(LKBackgroundPoller)
|
@objc(LKBackgroundPoller)
|
||||||
public final class BackgroundPoller : NSObject {
|
public final class BackgroundPoller: NSObject {
|
||||||
private static var closedGroupPoller: ClosedGroupPoller!
|
private static var closedGroupPoller: ClosedGroupPoller!
|
||||||
private static var promises: [Promise<Void>] = []
|
private static var promises: [Promise<Void>] = []
|
||||||
|
|
||||||
|
@ -11,20 +11,26 @@ public final class BackgroundPoller : NSObject {
|
||||||
@objc(pollWithCompletionHandler:)
|
@objc(pollWithCompletionHandler:)
|
||||||
public static func poll(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
public static func poll(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||||
promises = []
|
promises = []
|
||||||
promises.append(pollForMessages())
|
.appending(pollForMessages())
|
||||||
promises.append(contentsOf: pollForClosedGroupMessages())
|
.appending(pollForClosedGroupMessages())
|
||||||
let v2OpenGroupServers = Set(Storage.shared.getAllV2OpenGroups().values.map { $0.server })
|
.appending(
|
||||||
v2OpenGroupServers.forEach { server in
|
Set(Storage.shared.getAllV2OpenGroups().values.map { $0.server })
|
||||||
let poller = OpenGroupPollerV2(for: server)
|
.map { server in
|
||||||
poller.stop()
|
let poller = OpenGroupAPI.Poller(for: server)
|
||||||
promises.append(poller.poll(isBackgroundPoll: true))
|
poller.stop()
|
||||||
}
|
|
||||||
when(resolved: promises).done { _ in
|
return poller.poll(isBackgroundPoll: true)
|
||||||
completionHandler(.newData)
|
}
|
||||||
}.catch { error in
|
)
|
||||||
SNLog("Background poll failed due to error: \(error)")
|
|
||||||
completionHandler(.failed)
|
when(resolved: promises)
|
||||||
}
|
.done { _ in
|
||||||
|
completionHandler(.newData)
|
||||||
|
}
|
||||||
|
.catch { error in
|
||||||
|
SNLog("Background poll failed due to error: \(error)")
|
||||||
|
completionHandler(.failed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func pollForMessages() -> Promise<Void> {
|
private static func pollForMessages() -> Promise<Void> {
|
||||||
|
@ -38,22 +44,30 @@ public final class BackgroundPoller : NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getMessages(for publicKey: String) -> Promise<Void> {
|
private static func getMessages(for publicKey: String) -> Promise<Void> {
|
||||||
return SnodeAPI.getSwarm(for: publicKey).then(on: DispatchQueue.main) { swarm -> Promise<Void> in
|
return SnodeAPI.getSwarm(for: publicKey)
|
||||||
guard let snode = swarm.randomElement() else { throw SnodeAPI.Error.generic }
|
.then(on: DispatchQueue.main) { swarm -> Promise<Void> in
|
||||||
return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) {
|
guard let snode = swarm.randomElement() else { throw SnodeAPI.Error.generic }
|
||||||
return SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey).then(on: DispatchQueue.main) { rawResponse -> Promise<Void> in
|
|
||||||
let messages = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) {
|
||||||
let promises = messages.compactMap { json -> Promise<Void>? in
|
return SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey)
|
||||||
// Use a best attempt approach here; we don't want to fail the entire process if one of the
|
.then(on: DispatchQueue.main) { rawResponse -> Promise<Void> in
|
||||||
// messages failed to parse.
|
let messages = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
||||||
guard let envelope = SNProtoEnvelope.from(json),
|
let promises = messages
|
||||||
let data = try? envelope.serializedData() else { return nil }
|
.compactMap { json -> Promise<Void>? in
|
||||||
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
// Use a best attempt approach here; we don't want to fail
|
||||||
return job.execute()
|
// the entire process if one of the messages failed to parse.
|
||||||
}
|
guard let envelope = SNProtoEnvelope.from(json), let data = try? envelope.serializedData() else {
|
||||||
return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
||||||
|
|
||||||
|
return job.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Handle response (maybe in the poller or the OpenGroupManagerV2?).
|
// TODO: Handle response (maybe in the poller or the OpenGroupManager?)
|
||||||
return batch(server, requests: requestResponseType, using: dependencies)
|
return batch(server, requests: requestResponseType, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
let rooms: [String] = dependencies.storage.getAllV2OpenGroups().values
|
let rooms: [String] = dependencies.storage.getAllV2OpenGroups().values
|
||||||
.filter { $0.server == server }
|
.filter { $0.server == server }
|
||||||
.map { $0.room }
|
.map { $0.room }
|
||||||
let useMessageLimit = (hasPerformedInitialPoll[server] != true && timeSinceLastOpen > OpenGroupPollerV2.maxInactivityPeriod)
|
let useMessageLimit = (hasPerformedInitialPoll[server] != true && timeSinceLastOpen > OpenGroupAPI.Poller.maxInactivityPeriod)
|
||||||
|
|
||||||
hasPerformedInitialPoll[server] = true
|
hasPerformedInitialPoll[server] = true
|
||||||
|
|
||||||
|
@ -666,7 +666,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
|
|
||||||
// MARK: - General
|
// MARK: - General
|
||||||
|
|
||||||
// TODO: Shift this to the OpenGroupManagerV2? (seems more at place there than in the API).
|
// TODO: Shift this to the OpenGroupManager? (seems more at place there than in the API)
|
||||||
public static func getDefaultRoomsIfNeeded(using dependencies: Dependencies = Dependencies()) {
|
public static func getDefaultRoomsIfNeeded(using dependencies: Dependencies = Dependencies()) {
|
||||||
Storage.shared.write(
|
Storage.shared.write(
|
||||||
with: { transaction in
|
with: { transaction in
|
||||||
|
@ -922,7 +922,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
.filter { $0.server == server }
|
.filter { $0.server == server }
|
||||||
.map { $0.room }
|
.map { $0.room }
|
||||||
var getAuthTokenPromises: [String: Promise<String>] = [:]
|
var getAuthTokenPromises: [String: Promise<String>] = [:]
|
||||||
let useMessageLimit = (hasPerformedInitialPoll[server] != true && timeSinceLastOpen > OpenGroupPollerV2.maxInactivityPeriod)
|
let useMessageLimit = (hasPerformedInitialPoll[server] != true && timeSinceLastOpen > OpenGroupAPI.Poller.maxInactivityPeriod)
|
||||||
|
|
||||||
hasPerformedInitialPoll[server] = true
|
hasPerformedInitialPoll[server] = true
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
|
||||||
@objc(SNOpenGroupManagerV2)
|
@objc(SNOpenGroupManager)
|
||||||
public final class OpenGroupManagerV2 : NSObject {
|
public final class OpenGroupManager: NSObject {
|
||||||
private var pollers: [String:OpenGroupPollerV2] = [:] // One for each server
|
@objc public static let shared = OpenGroupManager()
|
||||||
|
|
||||||
|
private var pollers: [String: OpenGroupAPI.Poller] = [:] // One for each server
|
||||||
private var isPolling = false
|
private var isPolling = false
|
||||||
|
|
||||||
// MARK: Initialization
|
// MARK: - Polling
|
||||||
@objc public static let shared = OpenGroupManagerV2()
|
|
||||||
|
|
||||||
private override init() { }
|
|
||||||
|
|
||||||
// MARK: Polling
|
|
||||||
@objc public func startPolling() {
|
@objc public func startPolling() {
|
||||||
guard !isPolling else { return }
|
guard !isPolling else { return }
|
||||||
|
|
||||||
isPolling = true
|
isPolling = true
|
||||||
let servers = Set(Storage.shared.getAllV2OpenGroups().values.map { $0.server })
|
pollers = Set(Storage.shared.getAllV2OpenGroups().values.map { $0.server })
|
||||||
servers.forEach { server in
|
.reduce(into: [:]) { prev, server in
|
||||||
if let poller = pollers[server] { poller.stop() } // Should never occur
|
pollers[server]?.stop() // Should never occur
|
||||||
let poller = OpenGroupPollerV2(for: server)
|
|
||||||
poller.startIfNeeded()
|
let poller = OpenGroupAPI.Poller(for: server)
|
||||||
pollers[server] = poller
|
poller.startIfNeeded()
|
||||||
}
|
|
||||||
|
prev[server] = poller
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func stopPolling() {
|
@objc public func stopPolling() {
|
||||||
|
@ -28,17 +28,22 @@ public final class OpenGroupManagerV2 : NSObject {
|
||||||
pollers.removeAll()
|
pollers.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Adding & Removing
|
// MARK: - Adding & Removing
|
||||||
|
|
||||||
public func add(room: String, server: String, publicKey: String, using transaction: Any) -> Promise<Void> {
|
public func add(room: String, server: String, publicKey: String, using transaction: Any) -> Promise<Void> {
|
||||||
let storage = Storage.shared
|
let storage = Storage.shared
|
||||||
|
|
||||||
// Clear any existing data if needed
|
// Clear any existing data if needed
|
||||||
storage.removeLastMessageServerID(for: room, on: server, using: transaction)
|
storage.removeLastMessageServerID(for: room, on: server, using: transaction)
|
||||||
storage.removeLastDeletionServerID(for: room, on: server, using: transaction)
|
storage.removeLastDeletionServerID(for: room, on: server, using: transaction)
|
||||||
storage.removeAuthToken(for: room, on: server, using: transaction)
|
storage.removeAuthToken(for: room, on: server, using: transaction)
|
||||||
|
|
||||||
// Store the public key
|
// Store the public key
|
||||||
storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction)
|
storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction)
|
||||||
|
|
||||||
let (promise, seal) = Promise<Void>.pending()
|
let (promise, seal) = Promise<Void>.pending()
|
||||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||||
|
|
||||||
transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) {
|
transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) {
|
||||||
// Get the group info
|
// Get the group info
|
||||||
// TODO: Remove this legacy method
|
// TODO: Remove this legacy method
|
||||||
|
@ -56,10 +61,10 @@ public final class OpenGroupManagerV2 : NSObject {
|
||||||
// storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
|
// storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
|
||||||
// }, completion: {
|
// }, completion: {
|
||||||
// // Start the poller if needed
|
// // Start the poller if needed
|
||||||
// if OpenGroupManagerV2.shared.pollers[server] == nil {
|
// if OpenGroupManager.shared.pollers[server] == nil {
|
||||||
// let poller = OpenGroupPollerV2(for: server)
|
// let poller = OpenGroupPollerV2(for: server)
|
||||||
// poller.startIfNeeded()
|
// poller.startIfNeeded()
|
||||||
// OpenGroupManagerV2.shared.pollers[server] = poller
|
// OpenGroupManager.shared.pollers[server] = poller
|
||||||
// }
|
// }
|
||||||
// // Fetch the group image
|
// // Fetch the group image
|
||||||
// OpenGroupAPI.legacyGetGroupImage(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { data in
|
// OpenGroupAPI.legacyGetGroupImage(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { data in
|
||||||
|
@ -110,15 +115,15 @@ public final class OpenGroupManagerV2 : NSObject {
|
||||||
},
|
},
|
||||||
completion: {
|
completion: {
|
||||||
// Start the poller if needed
|
// Start the poller if needed
|
||||||
if OpenGroupManagerV2.shared.pollers[server] == nil {
|
if OpenGroupManager.shared.pollers[server] == nil {
|
||||||
let poller = OpenGroupPollerV2(for: server)
|
let poller = OpenGroupAPI.Poller(for: server)
|
||||||
poller.startIfNeeded()
|
poller.startIfNeeded()
|
||||||
OpenGroupManagerV2.shared.pollers[server] = poller
|
OpenGroupManager.shared.pollers[server] = poller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the group image (if there is one)
|
// Fetch the group image (if there is one)
|
||||||
// TODO: Need to test this
|
// TODO: Need to test this.
|
||||||
// TODO: Clean this up (can we avoid the if/else with fancy promise wrangling?)
|
// TODO: Clean this up (can we avoid the if/else with fancy promise wrangling?).
|
||||||
if let imageId: Int64 = room.imageId {
|
if let imageId: Int64 = room.imageId {
|
||||||
OpenGroupAPI.roomImage(imageId, for: room.token, on: server)
|
OpenGroupAPI.roomImage(imageId, for: room.token, on: server)
|
||||||
.done(on: DispatchQueue.global(qos: .userInitiated)) { data in
|
.done(on: DispatchQueue.global(qos: .userInitiated)) { data in
|
|
@ -233,8 +233,8 @@ extension MessageReceiver {
|
||||||
}
|
}
|
||||||
// Open groups
|
// Open groups
|
||||||
for openGroupURL in message.openGroups {
|
for openGroupURL in message.openGroups {
|
||||||
if let (room, server, publicKey) = OpenGroupManagerV2.parseV2OpenGroup(from: openGroupURL) {
|
if let (room, server, publicKey) = OpenGroupManager.parseV2OpenGroup(from: openGroupURL) {
|
||||||
OpenGroupManagerV2.shared.add(room: room, server: server, publicKey: publicKey, using: transaction).retainUntilComplete()
|
OpenGroupManager.shared.add(room: room, server: server, publicKey: publicKey, using: transaction).retainUntilComplete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
import PromiseKit
|
||||||
|
import SessionSnodeKit
|
||||||
|
|
||||||
|
extension OpenGroupAPI {
|
||||||
|
public final class Poller {
|
||||||
|
private let server: String
|
||||||
|
private var timer: Timer? = nil
|
||||||
|
private var hasStarted = false
|
||||||
|
private var isPolling = false
|
||||||
|
|
||||||
|
// MARK: - Settings
|
||||||
|
|
||||||
|
internal static let maxInactivityPeriod: Double = (14 * 24 * 60 * 60)
|
||||||
|
private static let pollInterval: TimeInterval = 4
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
public init(for server: String) {
|
||||||
|
self.server = server
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public func startIfNeeded() {
|
||||||
|
guard !hasStarted else { return }
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in // Timers don't do well on background queues
|
||||||
|
self?.hasStarted = true
|
||||||
|
self?.timer = Timer.scheduledTimer(withTimeInterval: Poller.pollInterval, repeats: true) { _ in
|
||||||
|
self?.poll().retainUntilComplete()
|
||||||
|
}
|
||||||
|
self?.poll().retainUntilComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public func stop() {
|
||||||
|
timer?.invalidate()
|
||||||
|
hasStarted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Polling
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func poll() -> Promise<Void> {
|
||||||
|
return poll(isBackgroundPoll: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func poll(isBackgroundPoll: Bool) -> Promise<Void> {
|
||||||
|
guard !self.isPolling else { return Promise.value(()) }
|
||||||
|
|
||||||
|
self.isPolling = true
|
||||||
|
let (promise, seal) = Promise<Void>.pending()
|
||||||
|
promise.retainUntilComplete()
|
||||||
|
|
||||||
|
OpenGroupAPI.poll(server)
|
||||||
|
.done(on: OpenGroupAPI.workQueue) { [weak self] response in
|
||||||
|
self?.isPolling = false
|
||||||
|
self?.handlePollResponse(response, isBackgroundPoll: isBackgroundPoll)
|
||||||
|
seal.fulfill(())
|
||||||
|
}
|
||||||
|
.catch(on: OpenGroupAPI.workQueue) { [weak self] error in
|
||||||
|
SNLog("Open group polling failed due to error: \(error).")
|
||||||
|
self?.isPolling = false
|
||||||
|
seal.fulfill(()) // The promise is just used to keep track of when we're done
|
||||||
|
}
|
||||||
|
// OpenGroupAPI.compactPoll(server)
|
||||||
|
// OpenGroupAPI.legacyCompactPoll(server)
|
||||||
|
// .done(on: OpenGroupAPI.workQueue) { [weak self] response in
|
||||||
|
// guard let self = self else { return }
|
||||||
|
// self.isPolling = false
|
||||||
|
// response.results.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) }
|
||||||
|
// seal.fulfill(())
|
||||||
|
// }
|
||||||
|
// .catch(on: OpenGroupAPI.workQueue) { error in
|
||||||
|
// SNLog("Open group polling failed due to error: \(error).")
|
||||||
|
// self.isPolling = false
|
||||||
|
// seal.fulfill(()) // The promise is just used to keep track of when we're done
|
||||||
|
// }
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handlePollResponse(_ response: [OpenGroupAPI.Endpoint: (info: OnionRequestResponseInfoType, data: Codable)], isBackgroundPoll: Bool) {
|
||||||
|
let storage = SNMessagingKitConfiguration.shared.storage
|
||||||
|
|
||||||
|
response.forEach { endpoint, response in
|
||||||
|
switch endpoint {
|
||||||
|
case .roomMessagesRecent(let roomToken), .roomMessagesBefore(let roomToken, _), .roomMessagesSince(let roomToken, _):
|
||||||
|
guard let responseData: [OpenGroupAPI.Message] = response.data as? [OpenGroupAPI.Message] else {
|
||||||
|
//SNLog("Open group polling failed due to error: \(error).")
|
||||||
|
return // TODO: Throw error?
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMessages(responseData, roomToken: roomToken, isBackgroundPoll: isBackgroundPoll, using: storage)
|
||||||
|
|
||||||
|
case .roomPollInfo(let roomToken, _):
|
||||||
|
guard let responseData: OpenGroupAPI.RoomPollInfo = response.data as? OpenGroupAPI.RoomPollInfo else {
|
||||||
|
//SNLog("Open group polling failed due to error: \(error).")
|
||||||
|
return // TODO: Throw error?
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePollInfo(responseData, roomToken: roomToken, isBackgroundPoll: isBackgroundPoll, using: storage)
|
||||||
|
|
||||||
|
default: break // No custom handling needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Custom response handling
|
||||||
|
// TODO: Shift this logic to the OpenGroupManager? (seems like the place it should belong?)
|
||||||
|
|
||||||
|
private func handleMessages(_ messages: [OpenGroupAPI.Message], roomToken: String, isBackgroundPoll: Bool, using storage: SessionMessagingKitStorageProtocol) {
|
||||||
|
// Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages
|
||||||
|
let openGroupID = "\(server).\(roomToken)"
|
||||||
|
let sortedMessages: [OpenGroupAPI.Message] = messages
|
||||||
|
.sorted { lhs, rhs in lhs.seqNo < rhs.seqNo }
|
||||||
|
|
||||||
|
storage.write { transaction in
|
||||||
|
var messageServerIDsToRemove: [UInt64] = []
|
||||||
|
|
||||||
|
sortedMessages.forEach { message in
|
||||||
|
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString), let sender: String = message.sender else {
|
||||||
|
// A message with no data has been deleted so add it to the list to remove
|
||||||
|
messageServerIDsToRemove.append(UInt64(message.seqNo))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted)))
|
||||||
|
envelope.setContent(data)
|
||||||
|
envelope.setSource(sender)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try envelope.buildSerializedData()
|
||||||
|
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.seqNo), isRetry: false, using: transaction)
|
||||||
|
try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
SNLog("Couldn't receive open group message due to error: \(error).")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle any deletions that are needed
|
||||||
|
guard !messageServerIDsToRemove.isEmpty else { return }
|
||||||
|
guard let transaction: YapDatabaseReadWriteTransaction = transaction as? YapDatabaseReadWriteTransaction else { return }
|
||||||
|
guard let threadID = storage.v2GetThreadID(for: openGroupID), let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var messagesToRemove: [TSMessage] = []
|
||||||
|
|
||||||
|
thread.enumerateInteractions(with: transaction) { interaction, stop in
|
||||||
|
guard let message: TSMessage = interaction as? TSMessage, messageServerIDsToRemove.contains(message.openGroupServerMessageID) else { return }
|
||||||
|
messagesToRemove.append(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
messagesToRemove.forEach { $0.remove(with: transaction) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handlePollInfo(_ pollInfo: OpenGroupAPI.RoomPollInfo, roomToken: String, isBackgroundPoll: Bool, using storage: SessionMessagingKitStorageProtocol) {
|
||||||
|
// TODO: Handle other properties???.
|
||||||
|
|
||||||
|
// public let token: String?
|
||||||
|
// public let created: TimeInterval?
|
||||||
|
// public let name: String?
|
||||||
|
// public let description: String?
|
||||||
|
// public let imageId: Int64?
|
||||||
|
//
|
||||||
|
// public let infoUpdates: Int64?
|
||||||
|
// public let messageSequence: Int64?
|
||||||
|
// public let activeUsers: Int64?
|
||||||
|
// public let activeUsersCutoff: Int64?
|
||||||
|
// public let pinnedMessages: [PinnedMessage]?
|
||||||
|
//
|
||||||
|
// public let admin: Bool?
|
||||||
|
// public let globalAdmin: Bool?
|
||||||
|
// public let admins: [String]?
|
||||||
|
// public let hiddenAdmins: [String]?
|
||||||
|
//
|
||||||
|
// public let moderator: Bool?
|
||||||
|
// public let globalModerator: Bool?
|
||||||
|
// public let moderators: [String]?
|
||||||
|
// public let hiddenModerators: [String]?
|
||||||
|
|
||||||
|
// - Moderators
|
||||||
|
OpenGroupAPI.moderators[server] = (OpenGroupAPI.moderators[server] ?? [:])
|
||||||
|
.setting(roomToken, Set(pollInfo.moderators ?? []))
|
||||||
|
|
||||||
|
// public let read: Bool?
|
||||||
|
// public let defaultRead: Bool?
|
||||||
|
// public let write: Bool?
|
||||||
|
// public let defaultWrite: Bool?
|
||||||
|
// public let upload: Bool?
|
||||||
|
// public let defaultUpload: Bool?
|
||||||
|
//
|
||||||
|
// /// Only populated and different if the `info_updates` counter differs from the provided `info_updated` value
|
||||||
|
// public let details: Room?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Legacy Handling
|
||||||
|
|
||||||
|
private func handleCompactPollBody(_ body: OpenGroupAPI.LegacyCompactPollResponse.Result, isBackgroundPoll: Bool) {
|
||||||
|
let storage = SNMessagingKitConfiguration.shared.storage
|
||||||
|
// - Messages
|
||||||
|
// Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages
|
||||||
|
let openGroupID = "\(server).\(body.room)"
|
||||||
|
let messages = (body.messages ?? []).sorted { ($0.serverID ?? 0) < ($1.serverID ?? 0) }
|
||||||
|
|
||||||
|
storage.write { transaction in
|
||||||
|
messages.forEach { message in
|
||||||
|
guard let data = Data(base64Encoded: message.base64EncodedData) else {
|
||||||
|
return SNLog("Ignoring open group message with invalid encoding.")
|
||||||
|
}
|
||||||
|
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: message.sentTimestamp)
|
||||||
|
envelope.setContent(data)
|
||||||
|
envelope.setSource(message.sender!) // Safe because messages with a nil sender are filtered out
|
||||||
|
do {
|
||||||
|
let data = try envelope.buildSerializedData()
|
||||||
|
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.serverID!), isRetry: false, using: transaction)
|
||||||
|
try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
|
||||||
|
} catch {
|
||||||
|
SNLog("Couldn't receive open group message due to error: \(error).")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Moderators
|
||||||
|
if var x = OpenGroupAPI.moderators[server] {
|
||||||
|
x[body.room] = Set(body.moderators ?? [])
|
||||||
|
OpenGroupAPI.moderators[server] = x
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
OpenGroupAPI.moderators[server] = [ body.room : Set(body.moderators ?? []) ]
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Deletions
|
||||||
|
let deletedMessageServerIDs = Set((body.deletions ?? []).map { UInt64($0.deletedMessageID) })
|
||||||
|
storage.write { transaction in
|
||||||
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||||
|
guard let threadID = storage.v2GetThreadID(for: openGroupID),
|
||||||
|
let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
|
||||||
|
var messagesToRemove: [TSMessage] = []
|
||||||
|
|
||||||
|
thread.enumerateInteractions(with: transaction) { interaction, stop in
|
||||||
|
guard let message = interaction as? TSMessage, deletedMessageServerIDs.contains(message.openGroupServerMessageID) else { return }
|
||||||
|
messagesToRemove.append(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
messagesToRemove.forEach { $0.remove(with: transaction) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,204 +0,0 @@
|
||||||
import PromiseKit
|
|
||||||
import SessionSnodeKit
|
|
||||||
|
|
||||||
@objc(SNOpenGroupPollerV2)
|
|
||||||
public final class OpenGroupPollerV2 : NSObject {
|
|
||||||
private let server: String
|
|
||||||
private var timer: Timer? = nil
|
|
||||||
private var hasStarted = false
|
|
||||||
private var isPolling = false
|
|
||||||
|
|
||||||
// MARK: Settings
|
|
||||||
private let pollInterval: TimeInterval = 4
|
|
||||||
static let maxInactivityPeriod: Double = 14 * 24 * 60 * 60
|
|
||||||
|
|
||||||
// MARK: Lifecycle
|
|
||||||
public init(for server: String) {
|
|
||||||
self.server = server
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func startIfNeeded() {
|
|
||||||
guard !hasStarted else { return }
|
|
||||||
DispatchQueue.main.async { [weak self] in // Timers don't do well on background queues
|
|
||||||
guard let strongSelf = self else { return }
|
|
||||||
strongSelf.hasStarted = true
|
|
||||||
strongSelf.timer = Timer.scheduledTimer(withTimeInterval: strongSelf.pollInterval, repeats: true) { _ in
|
|
||||||
self?.poll().retainUntilComplete()
|
|
||||||
}
|
|
||||||
strongSelf.poll().retainUntilComplete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func stop() {
|
|
||||||
timer?.invalidate()
|
|
||||||
hasStarted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Polling
|
|
||||||
@discardableResult
|
|
||||||
public func poll() -> Promise<Void> {
|
|
||||||
return poll(isBackgroundPoll: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public func poll(isBackgroundPoll: Bool) -> Promise<Void> {
|
|
||||||
guard !self.isPolling else { return Promise.value(()) }
|
|
||||||
self.isPolling = true
|
|
||||||
let (promise, seal) = Promise<Void>.pending()
|
|
||||||
promise.retainUntilComplete()
|
|
||||||
|
|
||||||
OpenGroupAPI.poll(server)
|
|
||||||
.done(on: OpenGroupAPI.workQueue) { [weak self] response in
|
|
||||||
self?.isPolling = false
|
|
||||||
self?.handlePollResponse(response, isBackgroundPoll: isBackgroundPoll)
|
|
||||||
seal.fulfill(())
|
|
||||||
}
|
|
||||||
.catch(on: OpenGroupAPI.workQueue) { [weak self] error in
|
|
||||||
SNLog("Open group polling failed due to error: \(error).")
|
|
||||||
self?.isPolling = false
|
|
||||||
seal.fulfill(()) // The promise is just used to keep track of when we're done
|
|
||||||
}
|
|
||||||
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handlePollResponse(_ response: [OpenGroupAPI.Endpoint: (info: OnionRequestResponseInfoType, data: Codable)], isBackgroundPoll: Bool) {
|
|
||||||
let storage = SNMessagingKitConfiguration.shared.storage
|
|
||||||
|
|
||||||
response.forEach { endpoint, response in
|
|
||||||
switch endpoint {
|
|
||||||
case .roomMessagesRecent(let roomToken), .roomMessagesBefore(let roomToken, _), .roomMessagesSince(let roomToken, _):
|
|
||||||
guard let responseData: [OpenGroupAPI.Message] = response.data as? [OpenGroupAPI.Message] else {
|
|
||||||
//SNLog("Open group polling failed due to error: \(error).")
|
|
||||||
return // TODO: Throw error?
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMessages(responseData, roomToken: roomToken, isBackgroundPoll: isBackgroundPoll, using: storage)
|
|
||||||
|
|
||||||
case .roomPollInfo(let roomToken, _):
|
|
||||||
guard let responseData: OpenGroupAPI.RoomPollInfo = response.data as? OpenGroupAPI.RoomPollInfo else {
|
|
||||||
//SNLog("Open group polling failed due to error: \(error).")
|
|
||||||
return // TODO: Throw error?
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePollInfo(responseData, roomToken: roomToken, isBackgroundPoll: isBackgroundPoll, using: storage)
|
|
||||||
|
|
||||||
default: break // No custom handling needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Custom response handling
|
|
||||||
// TODO: Shift this logic to the OpenGroupManagerV2? (seems like the place it should belong?)
|
|
||||||
|
|
||||||
private func handleMessages(_ messages: [OpenGroupAPI.Message], roomToken: String, isBackgroundPoll: Bool, using storage: SessionMessagingKitStorageProtocol) {
|
|
||||||
// Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages
|
|
||||||
let openGroupID = "\(server).\(roomToken)"
|
|
||||||
let sortedMessages: [OpenGroupAPI.Message] = messages
|
|
||||||
.sorted { lhs, rhs in lhs.seqNo < rhs.seqNo }
|
|
||||||
|
|
||||||
storage.write { transaction in
|
|
||||||
var messageServerIDsToRemove: [UInt64] = []
|
|
||||||
|
|
||||||
sortedMessages.forEach { message in
|
|
||||||
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString), let sender: String = message.sender else {
|
|
||||||
// A message with no data has been deleted so add it to the list to remove
|
|
||||||
messageServerIDsToRemove.append(UInt64(message.seqNo))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted)))
|
|
||||||
envelope.setContent(data)
|
|
||||||
envelope.setSource(sender)
|
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try envelope.buildSerializedData()
|
|
||||||
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.seqNo), isRetry: false, using: transaction)
|
|
||||||
try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
SNLog("Couldn't receive open group message due to error: \(error).")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle any deletions that are needed
|
|
||||||
guard !messageServerIDsToRemove.isEmpty else { return }
|
|
||||||
guard let transaction: YapDatabaseReadWriteTransaction = transaction as? YapDatabaseReadWriteTransaction else { return }
|
|
||||||
guard let threadID = storage.v2GetThreadID(for: openGroupID), let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var messagesToRemove: [TSMessage] = []
|
|
||||||
|
|
||||||
thread.enumerateInteractions(with: transaction) { interaction, stop in
|
|
||||||
guard let message: TSMessage = interaction as? TSMessage, messageServerIDsToRemove.contains(message.openGroupServerMessageID) else { return }
|
|
||||||
messagesToRemove.append(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesToRemove.forEach { $0.remove(with: transaction) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handlePollInfo(_ pollInfo: OpenGroupAPI.RoomPollInfo, roomToken: String, isBackgroundPoll: Bool, using storage: SessionMessagingKitStorageProtocol) {
|
|
||||||
// TODO: Handle other properties???
|
|
||||||
|
|
||||||
// - Moderators
|
|
||||||
OpenGroupAPI.moderators[server] = (OpenGroupAPI.moderators[server] ?? [:])
|
|
||||||
.setting(roomToken, Set(pollInfo.moderators ?? []))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Legacy Handling
|
|
||||||
|
|
||||||
private func handleCompactPollBody(_ body: OpenGroupAPI.LegacyCompactPollResponse.Result, isBackgroundPoll: Bool) {
|
|
||||||
let storage = SNMessagingKitConfiguration.shared.storage
|
|
||||||
// - Messages
|
|
||||||
// Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages
|
|
||||||
let openGroupID = "\(server).\(body.room)"
|
|
||||||
let messages = (body.messages ?? []).sorted { ($0.serverID ?? 0) < ($1.serverID ?? 0) }
|
|
||||||
|
|
||||||
storage.write { transaction in
|
|
||||||
messages.forEach { message in
|
|
||||||
guard let data = Data(base64Encoded: message.base64EncodedData) else {
|
|
||||||
return SNLog("Ignoring open group message with invalid encoding.")
|
|
||||||
}
|
|
||||||
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: message.sentTimestamp)
|
|
||||||
envelope.setContent(data)
|
|
||||||
envelope.setSource(message.sender!) // Safe because messages with a nil sender are filtered out
|
|
||||||
do {
|
|
||||||
let data = try envelope.buildSerializedData()
|
|
||||||
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.serverID!), isRetry: false, using: transaction)
|
|
||||||
try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
|
|
||||||
} catch {
|
|
||||||
SNLog("Couldn't receive open group message due to error: \(error).")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Moderators
|
|
||||||
if var x = OpenGroupAPI.moderators[server] {
|
|
||||||
x[body.room] = Set(body.moderators ?? [])
|
|
||||||
OpenGroupAPI.moderators[server] = x
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
OpenGroupAPI.moderators[server] = [ body.room : Set(body.moderators ?? []) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Deletions
|
|
||||||
let deletedMessageServerIDs = Set((body.deletions ?? []).map { UInt64($0.deletedMessageID) })
|
|
||||||
storage.write { transaction in
|
|
||||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
|
||||||
guard let threadID = storage.v2GetThreadID(for: openGroupID),
|
|
||||||
let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
|
|
||||||
var messagesToRemove: [TSMessage] = []
|
|
||||||
|
|
||||||
thread.enumerateInteractions(with: transaction) { interaction, stop in
|
|
||||||
guard let message = interaction as? TSMessage, deletedMessageServerIDs.contains(message.openGroupServerMessageID) else { return }
|
|
||||||
messagesToRemove.append(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesToRemove.forEach { $0.remove(with: transaction) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue