mirror of https://github.com/oxen-io/session-ios
Replace PublicChatManager
parent
b8d9334d19
commit
e38dae5a05
@ -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<Void> {
|
||||||
|
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<String> = []
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,8 @@
|
|||||||
|
|
||||||
extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol { }
|
extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol {
|
||||||
|
|
||||||
|
public func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set<String>, using transaction: Any) {
|
||||||
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||||
|
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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<Void> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<OpenGroup> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue