mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
11df899db3
It's not necessary because we can always get the messages again from the server, and it helps with performance
108 lines
4.9 KiB
Swift
108 lines
4.9 KiB
Swift
import PromiseKit
|
|
|
|
@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()
|
|
OpenGroupAPIV2.compactPoll(server).done(on: OpenGroupAPIV2.workQueue) { [weak self] bodies in
|
|
guard let self = self else { return }
|
|
self.isPolling = false
|
|
bodies.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) }
|
|
seal.fulfill(())
|
|
}.catch(on: OpenGroupAPIV2.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 handleCompactPollBody(_ body: OpenGroupAPIV2.CompactPollResponseBody, 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! < $1.serverID! } // Safe because messages with a nil serverID are filtered out
|
|
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
|
|
envelope.setServerTimestamp(message.sentTimestamp)
|
|
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 = OpenGroupAPIV2.moderators[server] {
|
|
x[body.room] = Set(body.moderators)
|
|
OpenGroupAPIV2.moderators[server] = x
|
|
} else {
|
|
OpenGroupAPIV2.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) }
|
|
}
|
|
}
|
|
}
|