Last active
November 22, 2018 10:15
-
-
Save darko55555/0330361dd6f68208f71a75d581cd4559 to your computer and use it in GitHub Desktop.
Chat mentions solution made with Rx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var mention:Observable<[FRep]>! | |
var rightSideTrimmedTextLastWord = String() | |
var lastTypedMention = String() | |
var lastTypedMentionRange:Range<String.Index>? | |
func observeMessageText(){ | |
mention = messageText | |
.flatMap{ [weak self] inputText -> Observable<[FRep]> in | |
if inputText.isEmpty { | |
return .just([]) | |
} | |
//get's text up to the textinput cursor | |
let textWithRemovedRightSide = inputText[inputText.utf16.startIndex..<inputText.utf16.index(inputText.utf16.startIndex, offsetBy: self!.textview.selectedRange.upperBound)] | |
//first @ from the left side to the last index - searching to the left | |
if let lastTypedMentionRange = String(textWithRemovedRightSide) | |
.range(of: "@", options: String.CompareOptions.backwards, range: nil, locale: nil){ | |
let range = lastTypedMentionRange.lowerBound..<textWithRemovedRightSide.endIndex | |
self?.lastTypedMentionRange = range | |
//Check if there is a character before @ and hide mention suggestion if there is | |
if range.lowerBound > inputText.utf16.startIndex{ | |
let characterBeforeTheMentionStart = inputText[inputText.utf16.index(range.lowerBound, offsetBy: -1)] | |
if characterBeforeTheMentionStart != " " { return .just([]) } | |
} | |
self?.lastTypedMention = String(inputText[range]) | |
if (self?.chat.members | |
.filter{ $0.hashedEmail != FirebaseConstants.myUserId } | |
.compactMap{$0.name} | |
.contains(self?.lastTypedMention.replacingOccurrences(of: "@", with: "")))!{ | |
self?.toggleMentionsView(toState: .closed) | |
self?.filteredUsers.removeAll() | |
return .just([]) | |
} | |
self?.currentMention = String(inputText[range]) | |
self?.filteredUsers = (self?.chatMembersValidForMentions | |
.compactMap{$0} | |
.filter{$0.name != nil} | |
.filter{($0.name!.lowercased() | |
.hasPrefix((self?.lastTypedMention.lowercased() | |
.replacingOccurrences(of: "@", with: ""))!))})! | |
}else{ | |
return Observable.just([]) | |
} | |
return Observable.just(self!.filteredUsers) | |
} | |
mention | |
.subscribe(onNext:{ [weak self] in | |
!$0.isEmpty ? self?.toggleMentionsView(toState: .open) : self?.toggleMentionsView(toState: .closed) | |
}) | |
.disposed(by: disposeBag) | |
} | |
enum MentionsViewState{ | |
case open, closed | |
} | |
func toggleMentionsView(toState state:MentionsViewState){ | |
switch state { | |
case .closed: | |
mentionsViewHeightContraint?.constant = CGFloat(0.0) | |
return | |
case .open: | |
DispatchQueue.main.async {[weak self] in | |
//4.5 because the agreement is to show 4.5 rows so the user knows they can scroll | |
guard let mentionsRowHeight = self?.mentionsRowHeight, | |
let numberOfUsers = self?.filteredUsers.count else { return } | |
let mentionsViewExpandedHeight = min(Double(numberOfUsers)*mentionsRowHeight,4.5*mentionsRowHeight) | |
UIView.animate(withDuration: 0.22, delay: 0.1, usingSpringWithDamping: 2.5, initialSpringVelocity: 7, options: .curveEaseInOut, animations: { | |
self?.mentionsViewHeightContraint?.constant = CGFloat(mentionsViewExpandedHeight) | |
self?.view.layoutIfNeeded() | |
self?.didShowMentionsView = true | |
}, completion: nil) | |
return | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Solution where you only need to provide two datasources
What is great about this solution is that your names can contain emojis, zalgo or any other imaginable combination, it'll work.
Any improvements suggestion is greatly appreciated :)