mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			90 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			90 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Swift
		
	
| 
								 
											5 years ago
										 
									 | 
							
								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
							 | 
						||
| 
								 | 
							
								    private static var userIDScanLimit: UInt = 4096
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // 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
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        let openGroup = Storage.shared.getOpenGroup(for: threadID)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        storage.dbReadConnection.read { transaction in
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								            candidates = cache.compactMap { publicKey in
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                let displayNameOrNil: String?
							 | 
						||
| 
								 | 
							
								                if let openGroup = openGroup {
							 | 
						||
| 
								 | 
							
								                    displayNameOrNil = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: openGroup.channel, on: openGroup.server)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                } else {
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                    displayNameOrNil = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                }
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                guard let displayName = displayNameOrNil else { return nil }
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                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
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |