@objc(LKGroupChatPoller) public final class LokiGroupChatPoller : NSObject { private let group: LokiGroupChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil private var hasStarted = false private let pollForNewMessagesInterval: TimeInterval = 4 private let pollForDeletedMessagesInterval: TimeInterval = 20 private let storage = OWSPrimaryStorage.shared() private let ourHexEncodedPubKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey @objc(initForGroup:) public init(for group: LokiGroupChat) { self.group = group super.init() } @objc public func startIfNeeded() { if hasStarted { return } pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() } pollForNewMessages() // Perform initial update pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() } hasStarted = true } @objc public func stop() { pollForNewMessagesTimer?.invalidate() pollForDeletedMessagesTimer?.invalidate() hasStarted = false } private func pollForNewMessages() { let group = self.group let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done { [weak self] messages in guard let self = self else { return } messages.reversed().forEach { message in let senderHexEncodedPublicKey = message.hexEncodedPublicKey if (senderHexEncodedPublicKey != self.ourHexEncodedPubKey) { self.handleIncomingMessage(message, group: group); } else { self.handleOutgoingMessage(message, group: group) } } } } private func handleOutgoingMessage(_ message: LokiGroupMessage, group: LokiGroupChat) { // Any Outgoing message should have a message server id mapped to it guard let messageServerID = message.serverID else { return } var hasMessage = false storage.newDatabaseConnection().read { transaction in let id = self.storage.getIDForMessage(withServerID: UInt(messageServerID), in: transaction); hasMessage = id != nil } // Check if we already have a message for this server message guard !hasMessage, let groupID = group.id.data(using: .utf8) else { return } // Get the thread let groupThread = TSGroupThread.getOrCreateThread(withGroupId: groupID) // Save the message let message = TSOutgoingMessage(outgoingMessageWithTimestamp: message.timestamp, in: groupThread, messageBody: message.body, attachmentIds: [], expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, groupMetaMessage: .deliver, quotedMessage: nil, contactShare: nil, linkPreview: nil) storage.newDatabaseConnection().readWrite { transaction in message.update(withSentRecipient: group.server, wasSentByUD: false, transaction: transaction) message.savePublicChatMessageID(messageServerID, with: transaction) guard let messageID = message.uniqueId else { owsFailDebug("[Loki] Outgoing public chat message should have a unique id set") return } self.storage.setIDForMessageWithServerID(UInt(messageServerID), to: messageID, in: transaction) } } private func handleIncomingMessage(_ message: LokiGroupMessage, group: LokiGroupChat) { let senderHexEncodedPublicKey = message.hexEncodedPublicKey let endIndex = senderHexEncodedPublicKey.endIndex let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex..