session-ios/SessionMessagingKit/Sending & Receiving/Mentions/MentionsManager.swift

86 lines
3.9 KiB
Swift
Raw Permalink Normal View History

2020-11-11 00:58:56 +01:00
import PromiseKit
@objc(LKMentionsManager)
public final class MentionsManager : NSObject {
/// A mapping from thread ID to set of user hex encoded public keys.
///
/// - Note: Should only be accessed from the main queue to avoid race conditions.
@objc public static var userPublicKeyCache: [String:Set<String>] = [:]
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: Settings
2020-12-03 23:16:40 +01:00
private static var userIDScanLimit: UInt = 512
2020-11-11 00:58:56 +01:00
// MARK: Initialization
private override init() { }
// MARK: Implementation
@objc public static func cache(_ publicKey: String, for threadID: String) {
if let cache = userPublicKeyCache[threadID] {
userPublicKeyCache[threadID] = cache.union([ publicKey ])
} else {
userPublicKeyCache[threadID] = [ publicKey ]
}
}
@objc public static func getMentionCandidates(for query: String, in threadID: String) -> [Mention] {
// Prepare
guard let cache = userPublicKeyCache[threadID] else { return [] }
var candidates: [Mention] = []
// Gather candidates
2021-03-24 04:36:26 +01:00
let openGroupV2 = Storage.shared.getV2OpenGroup(for: threadID)
2020-11-11 00:58:56 +01:00
storage.dbReadConnection.read { transaction in
2020-11-16 00:34:47 +01:00
candidates = cache.compactMap { publicKey in
2021-05-04 07:46:48 +02:00
let context: Contact.Context = (openGroupV2 != nil) ? .openGroup : .regular
2021-02-26 05:56:41 +01:00
let displayNameOrNil = Storage.shared.getContact(with: publicKey)?.displayName(for: context)
2020-11-19 05:24:09 +01:00
guard let displayName = displayNameOrNil else { return nil }
2020-11-11 00:58:56 +01:00
guard !displayName.hasPrefix("Anonymous") else { return nil }
return Mention(publicKey: publicKey, displayName: displayName)
}
}
candidates = candidates.filter { $0.publicKey != getUserHexEncodedPublicKey() }
// Sort alphabetically first
candidates.sort { $0.displayName < $1.displayName }
if query.count >= 2 {
// Filter out any non-matching candidates
candidates = candidates.filter { $0.displayName.lowercased().contains(query.lowercased()) }
// Sort based on where in the candidate the query occurs
candidates.sort {
$0.displayName.lowercased().range(of: query.lowercased())!.lowerBound < $1.displayName.lowercased().range(of: query.lowercased())!.lowerBound
}
}
// Return
return candidates
}
@objc public static func populateUserPublicKeyCacheIfNeeded(for threadID: String, in transaction: YapDatabaseReadTransaction? = nil) {
var result: Set<String> = []
func populate(in transaction: YapDatabaseReadTransaction) {
guard let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
if let groupThread = thread as? TSGroupThread, groupThread.groupModel.groupType == .closedGroup {
result = result.union(groupThread.groupModel.groupMemberIds).subtracting([ getUserHexEncodedPublicKey() ])
} else {
guard userPublicKeyCache[threadID] == nil else { return }
let interactions = transaction.ext(TSMessageDatabaseViewExtensionName) as! YapDatabaseViewTransaction
interactions.enumerateKeysAndObjects(inGroup: threadID) { _, _, object, index, _ in
guard let message = object as? TSIncomingMessage, index < userIDScanLimit else { return }
result.insert(message.authorId)
}
}
result.insert(getUserHexEncodedPublicKey())
}
if let transaction = transaction {
populate(in: transaction)
} else {
storage.dbReadConnection.read { transaction in
populate(in: transaction)
}
}
if !result.isEmpty {
userPublicKeyCache[threadID] = result
}
}
}