Quote improvements

This commit is contained in:
Isaac
2024-05-24 15:52:01 +04:00
parent 5fe5812328
commit 3f7113797e
12 changed files with 199 additions and 94 deletions

View File

@@ -20,8 +20,9 @@ public struct ChatTextInputAttributes {
public static let spoiler = NSAttributedString.Key(rawValue: "Attribute__Spoiler")
public static let customEmoji = NSAttributedString.Key(rawValue: "Attribute__CustomEmoji")
public static let block = NSAttributedString.Key(rawValue: "Attribute__Blockquote")
public static let collapsedBlock = NSAttributedString.Key(rawValue: "Attribute__CollapsedBlockquote")
public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.spoiler, ChatTextInputAttributes.customEmoji, ChatTextInputAttributes.block]
public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.spoiler, ChatTextInputAttributes.customEmoji, ChatTextInputAttributes.block, ChatTextInputAttributes.collapsedBlock]
}
public let originalTextAttributeKey = NSAttributedString.Key(rawValue: "Attribute__OriginalText")
@@ -36,27 +37,36 @@ public final class OriginalTextAttribute: NSObject {
}
public final class ChatInputTextCollapsedQuoteAttributes: Equatable {
public let font: UIFont
public let context: AnyObject
public let fontSize: CGFloat
public let textColor: UIColor
public let accentTextColor: UIColor
public init(
font: UIFont,
textColor: UIColor
context: AnyObject,
fontSize: CGFloat,
textColor: UIColor,
accentTextColor: UIColor
) {
self.font = font
self.context = context
self.fontSize = fontSize
self.textColor = textColor
self.accentTextColor = accentTextColor
}
public static func ==(lhs: ChatInputTextCollapsedQuoteAttributes, rhs: ChatInputTextCollapsedQuoteAttributes) -> Bool {
if lhs === rhs {
return true
}
if !lhs.font.isEqual(rhs.font) {
if lhs.fontSize != rhs.fontSize {
return false
}
if !lhs.textColor.isEqual(rhs.textColor) {
return false
}
if !lhs.accentTextColor.isEqual(rhs.accentTextColor) {
return false
}
return true
}
@@ -66,6 +76,27 @@ public protocol ChatInputTextCollapsedQuoteAttachment: NSTextAttachment {
var text: NSAttributedString { get }
}
public func expandedInputStateAttributedString(_ text: NSAttributedString) -> NSAttributedString {
let sourceString = NSMutableAttributedString(attributedString: text)
while true {
var found = false
let fullRange = NSRange(sourceString.string.startIndex ..< sourceString.string.endIndex, in: sourceString.string)
sourceString.enumerateAttribute(ChatTextInputAttributes.collapsedBlock, in: fullRange, options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in
if let value = value as? NSAttributedString {
let updatedBlockString = NSMutableAttributedString(attributedString: value)
updatedBlockString.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: true), range: NSRange(location: 0, length: updatedBlockString.length))
sourceString.replaceCharacters(in: range, with: updatedBlockString)
stop.pointee = true
found = true
}
})
if !found {
break
}
}
return sourceString
}
public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
let sourceString = NSMutableAttributedString(attributedString: text)
while true {
@@ -77,7 +108,7 @@ public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttrib
stop.pointee = true
found = true
} else if let value = value as? ChatInputTextCollapsedQuoteAttachment {
sourceString.replaceCharacters(in: range, with: value.text)
sourceString.replaceCharacters(in: range, with: NSAttributedString(string: " ", attributes: [ChatTextInputAttributes.collapsedBlock: value.text]))
stop.pointee = true
found = true
}
@@ -92,7 +123,15 @@ public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttrib
sourceString.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
for (key, value) in attributes {
if ChatTextInputAttributes.allAttributes.contains(key) || key == NSAttributedString.Key.attachment {
var matchAttribute = false
if ChatTextInputAttributes.allAttributes.contains(key) {
matchAttribute = true
} else if key == NSAttributedString.Key.attachment {
if value is EmojiTextAttachment {
matchAttribute = true
}
}
if matchAttribute {
result.addAttribute(key, value: value, range: range)
}
}
@@ -137,15 +176,17 @@ public struct ChatTextFontAttributes: OptionSet, Hashable, Sequence {
}
}
public func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor, writingDirection: NSWritingDirection?, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) -> NSAttributedString {
public func textAttributedStringForStateText(context: AnyObject, stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor, writingDirection: NSWritingDirection?, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) -> NSAttributedString {
let quoteAttributes = ChatInputTextCollapsedQuoteAttributes(
font: Font.regular(round(fontSize * 0.8235294117647058)),
textColor: textColor
context: context,
fontSize: fontSize,
textColor: textColor,
accentTextColor: accentTextColor
)
let stateText = NSMutableAttributedString(attributedString: stateText)
while true {
/*while true {
var found = false
stateText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: stateText.length), options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in
if let value = value as? ChatTextInputTextQuoteAttribute {
@@ -162,6 +203,23 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
if !found {
break
}
}*/
while true {
var found = false
stateText.enumerateAttribute(ChatTextInputAttributes.collapsedBlock, in: NSRange(location: 0, length: stateText.length), options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in
if let value = value as? NSAttributedString {
if let makeCollapsedQuoteAttachment {
found = true
stop.pointee = true
stateText.replaceCharacters(in: range, with: "")
stateText.insert(NSAttributedString(attachment: makeCollapsedQuoteAttachment(value, quoteAttributes)), at: range.lowerBound)
}
}
})
if !found {
break
}
}
let result = NSMutableAttributedString(string: stateText.string)
@@ -719,11 +777,11 @@ private func refreshBlockQuotes(text: NSString, initialAttributedText: NSAttribu
}
}
public func refreshChatTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
refreshChatTextInputAttributes(textView: textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
public func refreshChatTextInputAttributes(context: AnyObject, textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
refreshChatTextInputAttributes(context: context, textView: textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
}
public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColor: UIColor, accentTextColor: UIColor, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
public func refreshChatTextInputAttributes(context: AnyObject, textView: UITextView, primaryTextColor: UIColor, accentTextColor: UIColor, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
return
}
@@ -740,21 +798,21 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
refreshTextMentions(text: text, initialAttributedText: initialAttributedText, attributedText: attributedText, fullRange: fullRange)
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
var resultAttributedText = textAttributedStringForStateText(context: context, stateText: attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: text.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
resultAttributedText = textAttributedStringForStateText(context: context, stateText: attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: text.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshBlockQuotes(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
resultAttributedText = textAttributedStringForStateText(context: context, stateText: attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
if !resultAttributedText.isEqual(to: initialAttributedText) {
fullRange = NSRange(location: 0, length: textView.textStorage.length)
@@ -864,7 +922,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
textView.textStorage.endEditing()
}
public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?, spoilersRevealed: Bool = false) {
public func refreshGenericTextInputAttributes(context: AnyObject, textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?, spoilersRevealed: Bool = false) {
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
return
}
@@ -877,14 +935,14 @@ public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: Pre
var text: NSString = initialAttributedText.string as NSString
var fullRange = NSRange(location: 0, length: initialAttributedText.length)
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
var resultAttributedText = textAttributedStringForStateText(context: context, stateText: attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: initialAttributedText.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
resultAttributedText = textAttributedStringForStateText(context: context, stateText: attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
if !resultAttributedText.isEqual(to: initialAttributedText) {
textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)

View File

@@ -56,6 +56,25 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M
break
}
}
while true {
var found = false
string.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: string.length), using: { value, range, stop in
if let value = value as? ChatTextInputTextQuoteAttribute, value.isCollapsed {
found = true
let blockString = string.attributedSubstring(from: range)
string.replaceCharacters(in: range, with: "")
string.insert(NSAttributedString(string: " ", attributes: [
ChatTextInputAttributes.collapsedBlock: blockString
]), at: range.lowerBound)
stop.pointee = true
}
})
if !found {
break
}
}
return string
}