mirror of https://github.com/oxen-io/session-ios
Update other APIs for V2 open groups
parent
a5f831fd6c
commit
4c9728b4fe
@ -0,0 +1,74 @@
|
||||
import PromiseKit
|
||||
|
||||
@objc(SNOpenGroupManagerV2)
|
||||
public final class OpenGroupManagerV2 : NSObject {
|
||||
private var pollers: [String:OpenGroupPollerV2] = [:]
|
||||
private var isPolling = false
|
||||
|
||||
// MARK: Initialization
|
||||
@objc public static let shared = OpenGroupManagerV2()
|
||||
|
||||
private override init() { }
|
||||
|
||||
// MARK: Polling
|
||||
@objc public func startPolling() {
|
||||
guard !isPolling else { return }
|
||||
isPolling = true
|
||||
let openGroups = Storage.shared.getAllV2OpenGroups()
|
||||
for (_, openGroup) in openGroups {
|
||||
if let poller = pollers[openGroup.id] { poller.stop() } // Should never occur
|
||||
let poller = OpenGroupPollerV2(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(room: String, server: String, name: String, using transaction: Any) -> Promise<Void> {
|
||||
let storage = Storage.shared
|
||||
storage.removeLastMessageServerID(for: room, on: server, using: transaction)
|
||||
storage.removeLastDeletionServerID(for: room, on: server, using: transaction)
|
||||
let openGroup = OpenGroupV2(server: server, room: room, name: name)
|
||||
let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
|
||||
let model = TSGroupModel(title: openGroup.name, memberIds: [ getUserHexEncodedPublicKey() ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
|
||||
storage.write(with: { transaction in
|
||||
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction as! YapDatabaseReadWriteTransaction)
|
||||
storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
|
||||
}, completion: {
|
||||
if let poller = OpenGroupManagerV2.shared.pollers[openGroup.id] {
|
||||
poller.stop()
|
||||
OpenGroupManagerV2.shared.pollers[openGroup.id] = nil
|
||||
}
|
||||
let poller = OpenGroupPollerV2(for: openGroup)
|
||||
poller.startIfNeeded()
|
||||
OpenGroupManager.shared.pollers[openGroup.id] = poller
|
||||
})
|
||||
}
|
||||
|
||||
public func delete(_ openGroup: OpenGroupV2, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
if let poller = pollers[openGroup.id] {
|
||||
poller.stop()
|
||||
pollers[openGroup.id] = nil
|
||||
}
|
||||
var messageIDs: Set<String> = []
|
||||
var messageTimestamps: Set<UInt64> = []
|
||||
thread.enumerateInteractions(with: transaction) { interaction, _ in
|
||||
messageIDs.insert(interaction.uniqueId!)
|
||||
messageTimestamps.insert(interaction.timestamp)
|
||||
}
|
||||
SNMessagingKitConfiguration.shared.storage.updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, using: transaction)
|
||||
Storage.shared.removeReceivedMessageTimestamps(messageTimestamps, using: transaction)
|
||||
Storage.shared.removeLastMessageServerID(for: openGroup.room, on: openGroup.server, using: transaction)
|
||||
Storage.shared.removeLastDeletionServerID(for: openGroup.room, on: openGroup.server, using: transaction)
|
||||
let _ = OpenGroupAPIV2.deleteAuthToken(for: openGroup.room, on: openGroup.server)
|
||||
Storage.shared.removeOpenGroupPublicKey(for: openGroup.server, using: transaction)
|
||||
thread.removeAllThreadInteractions(with: transaction)
|
||||
thread.remove(with: transaction)
|
||||
Storage.shared.removeV2OpenGroup(for: thread.uniqueId!, using: transaction)
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
|
||||
@objc(SNOpenGroupV2)
|
||||
public final class OpenGroupV2 : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
||||
public let server: String
|
||||
public let room: String
|
||||
public let id: String
|
||||
public let name: String
|
||||
|
||||
public init(server: String, room: String, name: String) {
|
||||
self.server = server.lowercased()
|
||||
self.room = room
|
||||
self.id = "\(server).\(room)"
|
||||
self.name = name
|
||||
}
|
||||
|
||||
// MARK: Coding
|
||||
public init?(coder: NSCoder) {
|
||||
server = coder.decodeObject(forKey: "server") as! String
|
||||
room = coder.decodeObject(forKey: "room") as! String
|
||||
self.id = "\(server).\(room)"
|
||||
name = coder.decodeObject(forKey: "name") as! String
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func encode(with coder: NSCoder) {
|
||||
coder.encode(server, forKey: "server")
|
||||
coder.encode(room, forKey: "room")
|
||||
coder.encode(name, forKey: "name")
|
||||
}
|
||||
|
||||
override public var description: String { "\(name) (\(server) → \(room)" }
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
import PromiseKit
|
||||
|
||||
@objc(SNOpenGroupPollerV2)
|
||||
public final class OpenGroupPollerV2 : NSObject {
|
||||
private let openGroup: OpenGroupV2
|
||||
private var pollForNewMessagesTimer: Timer? = nil
|
||||
private var pollForDeletedMessagesTimer: Timer? = nil
|
||||
private var pollForModeratorsTimer: Timer? = nil
|
||||
private var hasStarted = false
|
||||
private var isPolling = false
|
||||
|
||||
private var isMainAppAndActive: Bool {
|
||||
var isMainAppAndActive = false
|
||||
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
|
||||
isMainAppAndActive = sharedUserDefaults.bool(forKey: "isMainAppActive")
|
||||
}
|
||||
return isMainAppAndActive
|
||||
}
|
||||
|
||||
// MARK: Settings
|
||||
private let pollForNewMessagesInterval: TimeInterval = 4
|
||||
private let pollForDeletedMessagesInterval: TimeInterval = 30
|
||||
private let pollForModeratorsInterval: TimeInterval = 10 * 60
|
||||
|
||||
// MARK: Lifecycle
|
||||
public init(for openGroup: OpenGroupV2) {
|
||||
self.openGroup = openGroup
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc public func startIfNeeded() {
|
||||
guard !hasStarted else { return }
|
||||
guard isMainAppAndActive else { stop(); return }
|
||||
DispatchQueue.main.async { [weak self] in // Timers don't do well on background queues
|
||||
guard let strongSelf = self else { return }
|
||||
strongSelf.hasStarted = true
|
||||
// Create timers
|
||||
strongSelf.pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: strongSelf.pollForNewMessagesInterval, repeats: true) { _ in self?.pollForNewMessages() }
|
||||
strongSelf.pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: strongSelf.pollForDeletedMessagesInterval, repeats: true) { _ in self?.pollForDeletedMessages() }
|
||||
strongSelf.pollForModeratorsTimer = Timer.scheduledTimer(withTimeInterval: strongSelf.pollForModeratorsInterval, repeats: true) { _ in self?.pollForModerators() }
|
||||
// Perform initial updates
|
||||
strongSelf.pollForNewMessages()
|
||||
strongSelf.pollForDeletedMessages()
|
||||
strongSelf.pollForModerators()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func stop() {
|
||||
pollForNewMessagesTimer?.invalidate()
|
||||
pollForDeletedMessagesTimer?.invalidate()
|
||||
pollForModeratorsTimer?.invalidate()
|
||||
hasStarted = false
|
||||
}
|
||||
|
||||
// MARK: Polling
|
||||
@discardableResult
|
||||
public func pollForNewMessages() -> Promise<Void> {
|
||||
guard isMainAppAndActive else { stop(); return Promise.value(()) }
|
||||
return pollForNewMessages(isBackgroundPoll: false)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func pollForNewMessages(isBackgroundPoll: Bool) -> Promise<Void> {
|
||||
guard !self.isPolling else { return Promise.value(()) }
|
||||
self.isPolling = true
|
||||
let openGroup = self.openGroup
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
promise.retainUntilComplete()
|
||||
OpenGroupAPIV2.getMessages(for: openGroup.room, on: openGroup.server).done(on: DispatchQueue.global(qos: .default)) { [weak self] messages in
|
||||
guard let self = self else { return }
|
||||
self.isPolling = false
|
||||
// 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 messages = messages.sorted { $0.serverID! < $1.serverID! } // Safe because messages with a nil serverID are filtered out
|
||||
messages.forEach { message in
|
||||
guard let data = Data(base64Encoded: message.base64EncodedData) else {
|
||||
return SNLog("Ignoring open group message with invalid encoding.")
|
||||
}
|
||||
let job = MessageReceiveJob(data: data, openGroupMessageServerID: UInt64(message.serverID!), openGroupID: self.openGroup.id, isBackgroundPoll: isBackgroundPoll)
|
||||
SNMessagingKitConfiguration.shared.storage.write { transaction in
|
||||
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
|
||||
}
|
||||
}
|
||||
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { _ in
|
||||
seal.fulfill(()) // The promise is just used to keep track of when we're done
|
||||
}.retainUntilComplete()
|
||||
return promise
|
||||
}
|
||||
|
||||
private func pollForDeletedMessages() {
|
||||
let openGroup = self.openGroup
|
||||
OpenGroupAPIV2.getDeletedMessages(for: openGroup.room, on: openGroup.server).done(on: DispatchQueue.global(qos: .default)) { serverIDs in
|
||||
let messageIDs = serverIDs.compactMap { Storage.shared.getIDForMessage(withServerID: UInt64($0)) }
|
||||
SNMessagingKitConfiguration.shared.storage.write { transaction in
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
messageIDs.forEach { messageID in
|
||||
TSMessage.fetch(uniqueId: messageID, transaction: transaction)?.remove(with: transaction)
|
||||
}
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
||||
private func pollForModerators() {
|
||||
OpenGroupAPIV2.getModerators(for: openGroup.room, on: openGroup.server).retainUntilComplete()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue