mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
201 lines
9.7 KiB
Swift
201 lines
9.7 KiB
Swift
import Foundation
|
|
import TextFormat
|
|
import TelegramCore
|
|
import AccountContext
|
|
|
|
public func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute: NSAttributedString.Key, value: Any?) -> ChatTextInputState {
|
|
if !state.selectionRange.isEmpty {
|
|
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
|
var addAttribute = true
|
|
var attributesToRemove: [NSAttributedString.Key] = []
|
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
|
for (key, _) in attributes {
|
|
if key == attribute {
|
|
addAttribute = false
|
|
attributesToRemove.append(key)
|
|
}
|
|
}
|
|
}
|
|
|
|
var selectionRange = state.selectionRange
|
|
|
|
let result = NSMutableAttributedString(attributedString: state.inputText)
|
|
for attribute in attributesToRemove {
|
|
if attribute == ChatTextInputAttributes.block {
|
|
var removeRange = nsRange
|
|
|
|
var selectionIndex = nsRange.upperBound
|
|
if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a {
|
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound)
|
|
selectionIndex += 1
|
|
removeRange.length += 1
|
|
}
|
|
if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a {
|
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound)
|
|
selectionIndex += 1
|
|
removeRange.location += 1
|
|
} else if nsRange.lowerBound != 0 {
|
|
removeRange.location -= 1
|
|
removeRange.length += 1
|
|
}
|
|
|
|
if removeRange.lowerBound > result.length {
|
|
removeRange = NSRange(location: result.length, length: 0)
|
|
} else if removeRange.upperBound > result.length {
|
|
removeRange = NSRange(location: removeRange.lowerBound, length: result.length - removeRange.lowerBound)
|
|
}
|
|
result.removeAttribute(attribute, range: removeRange)
|
|
|
|
if selectionRange.lowerBound > result.length {
|
|
selectionRange = result.length ..< result.length
|
|
} else if selectionRange.upperBound > result.length {
|
|
selectionRange = selectionRange.lowerBound ..< result.length
|
|
}
|
|
|
|
// Prevent merge back
|
|
result.enumerateAttributes(in: NSRange(location: selectionIndex, length: result.length - selectionIndex), options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
|
for (key, value) in attributes {
|
|
if let value = value as? ChatTextInputTextQuoteAttribute {
|
|
result.removeAttribute(key, range: range)
|
|
result.addAttribute(key, value: ChatTextInputTextQuoteAttribute(kind: value.kind), range: range)
|
|
}
|
|
}
|
|
}
|
|
|
|
selectionRange = selectionIndex ..< selectionIndex
|
|
} else {
|
|
result.removeAttribute(attribute, range: nsRange)
|
|
}
|
|
}
|
|
|
|
if addAttribute {
|
|
if attribute == ChatTextInputAttributes.block {
|
|
result.addAttribute(attribute, value: value ?? ChatTextInputTextQuoteAttribute(kind: .quote), range: nsRange)
|
|
var selectionIndex = nsRange.upperBound
|
|
if nsRange.upperBound != result.length && (result.string as NSString).character(at: nsRange.upperBound) != 0x0a {
|
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.upperBound)
|
|
selectionIndex += 1
|
|
}
|
|
if nsRange.lowerBound != 0 && (result.string as NSString).character(at: nsRange.lowerBound - 1) != 0x0a {
|
|
result.insert(NSAttributedString(string: "\n"), at: nsRange.lowerBound)
|
|
selectionIndex += 1
|
|
}
|
|
selectionRange = selectionIndex ..< selectionIndex
|
|
} else {
|
|
result.addAttribute(attribute, value: true as Bool, range: nsRange)
|
|
}
|
|
}
|
|
if selectionRange.lowerBound > result.length {
|
|
selectionRange = result.length ..< result.length
|
|
} else if selectionRange.upperBound > result.length {
|
|
selectionRange = selectionRange.lowerBound ..< result.length
|
|
}
|
|
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
|
|
public func chatTextInputClearFormattingAttributes(_ state: ChatTextInputState) -> ChatTextInputState {
|
|
if !state.selectionRange.isEmpty {
|
|
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
|
var attributesToRemove: [NSAttributedString.Key] = []
|
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
|
for (key, _) in attributes {
|
|
attributesToRemove.append(key)
|
|
}
|
|
}
|
|
|
|
let result = NSMutableAttributedString(attributedString: state.inputText)
|
|
for attribute in attributesToRemove {
|
|
result.removeAttribute(attribute, range: nsRange)
|
|
}
|
|
return ChatTextInputState(inputText: result, selectionRange: state.selectionRange)
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
|
|
public func chatTextInputAddLinkAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>, url: String) -> ChatTextInputState {
|
|
if !selectionRange.isEmpty {
|
|
let nsRange = NSRange(location: selectionRange.lowerBound, length: selectionRange.count)
|
|
var linkRange = nsRange
|
|
var attributesToRemove: [(NSAttributedString.Key, NSRange)] = []
|
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
|
for (key, _) in attributes {
|
|
if key == ChatTextInputAttributes.textUrl {
|
|
attributesToRemove.append((key, range))
|
|
linkRange = linkRange.union(range)
|
|
} else {
|
|
attributesToRemove.append((key, nsRange))
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = NSMutableAttributedString(attributedString: state.inputText)
|
|
for (attribute, range) in attributesToRemove {
|
|
result.removeAttribute(attribute, range: range)
|
|
}
|
|
result.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: nsRange)
|
|
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
|
|
public func chatTextInputAddMentionAttribute(_ state: ChatTextInputState, peer: EnginePeer) -> ChatTextInputState {
|
|
let inputText = NSMutableAttributedString(attributedString: state.inputText)
|
|
|
|
let range = NSMakeRange(state.selectionRange.startIndex, state.selectionRange.endIndex - state.selectionRange.startIndex)
|
|
|
|
if let addressName = peer.addressName, !addressName.isEmpty {
|
|
let replacementText = "@\(addressName) "
|
|
|
|
inputText.replaceCharacters(in: range, with: replacementText)
|
|
|
|
let selectionPosition = range.lowerBound + (replacementText as NSString).length
|
|
|
|
return ChatTextInputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition)
|
|
} else if !peer.compactDisplayTitle.isEmpty {
|
|
let replacementText = NSMutableAttributedString()
|
|
replacementText.append(NSAttributedString(string: peer.compactDisplayTitle, attributes: [ChatTextInputAttributes.textMention: ChatTextInputTextMentionAttribute(peerId: peer.id)]))
|
|
replacementText.append(NSAttributedString(string: " "))
|
|
|
|
let updatedRange = NSRange(location: range.location , length: range.length)
|
|
|
|
inputText.replaceCharacters(in: updatedRange, with: replacementText)
|
|
|
|
let selectionPosition = updatedRange.lowerBound + replacementText.length
|
|
|
|
return ChatTextInputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition)
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
|
|
public func chatTextInputAddQuoteAttribute(_ state: ChatTextInputState, selectionRange: Range<Int>, kind: ChatTextInputTextQuoteAttribute.Kind) -> ChatTextInputState {
|
|
if selectionRange.isEmpty {
|
|
return state
|
|
}
|
|
let nsRange = NSRange(location: selectionRange.lowerBound, length: selectionRange.count)
|
|
var quoteRange = nsRange
|
|
var attributesToRemove: [(NSAttributedString.Key, NSRange)] = []
|
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, stop in
|
|
for (key, _) in attributes {
|
|
if key == ChatTextInputAttributes.block {
|
|
attributesToRemove.append((key, range))
|
|
quoteRange = quoteRange.union(range)
|
|
} else {
|
|
attributesToRemove.append((key, nsRange))
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = NSMutableAttributedString(attributedString: state.inputText)
|
|
for (attribute, range) in attributesToRemove {
|
|
result.removeAttribute(attribute, range: range)
|
|
}
|
|
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: kind), range: nsRange)
|
|
return ChatTextInputState(inputText: result, selectionRange: selectionRange)
|
|
}
|