Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deletion callback; replace edited mention; start mention without " " before #136

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
8 changes: 6 additions & 2 deletions Classes/Internal/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ internal extension String {
return (foundRange, string)
}

func isMentionEnabledAt(_ location: Int) -> (Bool, String) {
func isMentionEnabledAt(_ location: Int, considerTextBefore: Bool = true) -> (Bool, String) {
guard location != 0 else { return (true, "") }

let start = utf16.index(startIndex, offsetBy: location - 1)
let end = utf16.index(start, offsetBy: 1)
let textBeforeTrigger = String(utf16[start ..< end]) ?? ""

return (textBeforeTrigger == " " || textBeforeTrigger == "\n", textBeforeTrigger)
if considerTextBefore {
return (textBeforeTrigger == " " || textBeforeTrigger == "\n", textBeforeTrigger)
} else {
return (true, textBeforeTrigger)
}
}
}
46 changes: 41 additions & 5 deletions Classes/MentionListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ public class MentionListener: NSObject {
*/
private let searchSpaces: Bool

/**
@brief Tell listener that mentioning should start regardless characters before trigger
*/
private let considerTextBefore: Bool

/**
@brief Replace an edited mention with a newly created one
*/
private let replaceEditedMention: Bool

/**
@brief Triggers to start a mention. Default: @
*/
Expand Down Expand Up @@ -67,6 +77,11 @@ public class MentionListener: NSObject {
*/
private let didHandleMentionOnReturn: () -> Bool

/**
@brief Called when a user deletes a mention
*/
private let didDeleteMention: (Mention) -> Void

/**
@brief Called when the UITextView is editing a mention.

Expand Down Expand Up @@ -132,9 +147,12 @@ public class MentionListener: NSObject {
triggers: [String] = ["@"],
cooldownInterval: TimeInterval = 0.5,
searchSpaces: Bool = false,
considerTextBefore: Bool = true,
replaceEditedMention: Bool = false,
removeEntireMention: Bool = false,
hideMentions: @escaping () -> Void,
didHandleMentionOnReturn: @escaping () -> Bool,
didDeleteMention: @escaping (Mention) -> Void = { _ in },
showMentionsListWithString: @escaping (String, String) -> Void
) {
self.mentionTextAttributes = mentionTextAttributes ?? { _ in
Expand All @@ -146,6 +164,8 @@ public class MentionListener: NSObject {
mentionTextAttributes: self.mentionTextAttributes(nil))

self.searchSpaces = searchSpaces
self.considerTextBefore = considerTextBefore
self.replaceEditedMention = replaceEditedMention
self.mentionsTextView = mentionsTextView
self.delegate = delegate
self.spaceAfterMention = spaceAfterMention
Expand All @@ -155,6 +175,7 @@ public class MentionListener: NSObject {
self.hideMentions = hideMentions
self.didHandleMentionOnReturn = didHandleMentionOnReturn
self.showMentionsListWithString = showMentionsListWithString
self.didDeleteMention = didDeleteMention
self.mentionsTextView.typingAttributes = self.defaultTextAttributes.dictionary
super.init()
mentionsTextView.delegate = self
Expand Down Expand Up @@ -197,7 +218,9 @@ extension MentionListener /* Public */ {
*/
@discardableResult public func addMention(_ createMention: CreateMention) -> Bool {
guard currentMentionRange.location != NSNotFound else { return false }

if let mention = mentions |> mentionBeingEdited(at: currentMentionRange), replaceEditedMention {
mention |> clearMention()
}
mentions = mentions
|> add(createMention, spaceAfterMention: spaceAfterMention, at: currentMentionRange)

Expand Down Expand Up @@ -247,8 +270,18 @@ extension MentionListener /* Private */ {
*/
private func handleMentionsList(_ textView: UITextView, range: NSRange) {
let startIndex = mentionsTextView.text.startIndex
let endIndex = mentionsTextView.text.index(startIndex,
var endIndex = mentionsTextView.text.index(startIndex,
offsetBy: min(NSMaxRange(range), mentionsTextView.text.count))
// Need to convert NSRange and the offset to String.Index
// in order to handle emojis character count case
if let selectedRange = textView.selectedTextRange {
let cursorPosition = textView.offset(from: textView.beginningOfDocument, to: selectedRange.end)
let positionRange = NSRange(location: 0, length: cursorPosition)
if let stringOffset = Range(positionRange, in: textView.text) {
endIndex = stringOffset.upperBound
}
}

let stringToSelectedIndex = String(mentionsTextView.text[startIndex ..< endIndex])

var textBeforeTrigger = " "
Expand All @@ -258,7 +291,7 @@ extension MentionListener /* Private */ {
let trigger = searchResult.foundString

if location != NSNotFound {
(mentionEnabled, textBeforeTrigger) = mentionsTextView.text.isMentionEnabledAt(location)
(mentionEnabled, textBeforeTrigger) = mentionsTextView.text.isMentionEnabledAt(location, considerTextBefore: considerTextBefore)
} else {
mentionEnabled = false
}
Expand Down Expand Up @@ -293,7 +326,6 @@ extension MentionListener /* Private */ {
return
}
}

hideMentions()
}

Expand All @@ -311,6 +343,7 @@ extension MentionListener /* Private */ {
|> apply(self.defaultTextAttributes, range: mention.range)
self.mentionsTextView.attributedText = text
self.mentionsTextView.selectedRange = selectedRange
self.didDeleteMention(mention)
}
}

Expand All @@ -336,7 +369,10 @@ extension MentionListener /* Private */ {
extension MentionListener: UITextViewDelegate {
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange,
replacementText text: String) -> Bool {
_ = delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text)
let delegateResult = delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text)
guard delegateResult ?? true else {
return false
}

textView.typingAttributes = defaultTextAttributes.dictionary

Expand Down