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

@ -402,6 +402,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
case spoiler case spoiler
case quote(isCollapsed: Bool) case quote(isCollapsed: Bool)
case codeBlock(language: String?) case codeBlock(language: String?)
case collapsedQuote(text: ChatTextInputStateText)
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self) let container = try decoder.container(keyedBy: StringCodingKey.self)
@ -433,6 +434,8 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
self = .quote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "isCollapsed") ?? false) self = .quote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "isCollapsed") ?? false)
case 10: case 10:
self = .codeBlock(language: try container.decodeIfPresent(String.self, forKey: "l")) self = .codeBlock(language: try container.decodeIfPresent(String.self, forKey: "l"))
case 11:
self = .collapsedQuote(text: try container.decode(ChatTextInputStateText.self, forKey: "text"))
default: default:
assertionFailure() assertionFailure()
self = .bold self = .bold
@ -470,6 +473,9 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
case let .codeBlock(language): case let .codeBlock(language):
try container.encode(10 as Int32, forKey: "t") try container.encode(10 as Int32, forKey: "t")
try container.encodeIfPresent(language, forKey: "l") try container.encodeIfPresent(language, forKey: "l")
case let .collapsedQuote(text):
try container.encode(11 as Int32, forKey: "t")
try container.encode(text, forKey: "text")
} }
} }
} }
@ -522,6 +528,7 @@ public struct ChatTextInputStateText: Codable, Equatable {
public init(attributedText: NSAttributedString) { public init(attributedText: NSAttributedString) {
self.text = attributedText.string self.text = attributedText.string
var parsedAttributes: [ChatTextInputStateTextAttribute] = [] var parsedAttributes: [ChatTextInputStateTextAttribute] = []
attributedText.enumerateAttributes(in: NSRange(location: 0, length: attributedText.length), options: [], using: { attributes, range, _ in attributedText.enumerateAttributes(in: NSRange(location: 0, length: attributedText.length), options: [], using: { attributes, range, _ in
for (key, value) in attributes { for (key, value) in attributes {
@ -550,6 +557,8 @@ public struct ChatTextInputStateText: Codable, Equatable {
case let .code(language): case let .code(language):
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .codeBlock(language: language), range: range.location ..< (range.location + range.length))) parsedAttributes.append(ChatTextInputStateTextAttribute(type: .codeBlock(language: language), range: range.location ..< (range.location + range.length)))
} }
} else if key == ChatTextInputAttributes.collapsedBlock, let value = value as? NSAttributedString {
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .collapsedQuote(text: ChatTextInputStateText(attributedText: value)), range: range.location ..< (range.location + range.length)))
} }
} }
}) })
@ -598,6 +607,8 @@ public struct ChatTextInputStateText: Codable, Equatable {
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: isCollapsed), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: isCollapsed), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
case let .codeBlock(language): case let .codeBlock(language):
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .code(language: language), isCollapsed: false), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .code(language: language), isCollapsed: false), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
case let .collapsedQuote(text):
result.addAttribute(ChatTextInputAttributes.collapsedBlock, value: text.attributedText(), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
} }
} }
return result return result

View File

@ -338,7 +338,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
} }
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in textInputNode.attributedText = textAttributedStringForStateText(context: self.context, stateText: state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count) textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
@ -1022,7 +1022,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
public func chatInputTextNodeDidUpdateText() { public func chatInputTextNodeDidUpdateText() {
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: self.context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
@ -1196,11 +1196,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
textInputNode.textView.isScrollEnabled = false textInputNode.textView.isScrollEnabled = false
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: self.context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in textInputNode.attributedText = textAttributedStringForStateText(context: self.context, stateText: self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
@ -1357,7 +1357,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
let textFont = Font.regular(baseFontSize) let textFont = Font.regular(baseFontSize)
let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
let attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in let attributedText = textAttributedStringForStateText(context: self.context, stateText: self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
@ -1764,7 +1764,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
} }
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in let cleanReplacementString = textAttributedStringForStateText(context: self.context, stateText: NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
string.replaceCharacters(in: range, with: cleanReplacementString) string.replaceCharacters(in: range, with: cleanReplacementString)

View File

@ -147,6 +147,7 @@ private func processPollText(_ text: String) -> String {
} }
private final class CreatePollControllerArguments { private final class CreatePollControllerArguments {
let context: AccountContext
let updatePollText: (String) -> Void let updatePollText: (String) -> Void
let updateOptionText: (Int, String, Bool) -> Void let updateOptionText: (Int, String, Bool) -> Void
let moveToNextOption: (Int) -> Void let moveToNextOption: (Int) -> Void
@ -163,7 +164,8 @@ private final class CreatePollControllerArguments {
let solutionTextFocused: (Bool) -> Void let solutionTextFocused: (Bool) -> Void
let questionTextFocused: (Bool) -> Void let questionTextFocused: (Bool) -> Void
init(updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String, Bool) -> Void, moveToNextOption: @escaping (Int) -> Void, moveToPreviousOption: @escaping (Int) -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int, Bool) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, toggleOptionSelected: @escaping (Int) -> Void, updateAnonymous: @escaping (Bool) -> Void, updateMultipleChoice: @escaping (Bool) -> Void, displayMultipleChoiceDisabled: @escaping () -> Void, updateQuiz: @escaping (Bool) -> Void, updateSolutionText: @escaping (NSAttributedString) -> Void, solutionTextFocused: @escaping (Bool) -> Void, questionTextFocused: @escaping (Bool) -> Void) { init(context: AccountContext, updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String, Bool) -> Void, moveToNextOption: @escaping (Int) -> Void, moveToPreviousOption: @escaping (Int) -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int, Bool) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, toggleOptionSelected: @escaping (Int) -> Void, updateAnonymous: @escaping (Bool) -> Void, updateMultipleChoice: @escaping (Bool) -> Void, displayMultipleChoiceDisabled: @escaping () -> Void, updateQuiz: @escaping (Bool) -> Void, updateSolutionText: @escaping (NSAttributedString) -> Void, solutionTextFocused: @escaping (Bool) -> Void, questionTextFocused: @escaping (Bool) -> Void) {
self.context = context
self.updatePollText = updatePollText self.updatePollText = updatePollText
self.updateOptionText = updateOptionText self.updateOptionText = updateOptionText
self.moveToNextOption = moveToNextOption self.moveToNextOption = moveToNextOption
@ -397,7 +399,7 @@ private enum CreatePollEntry: ItemListNodeEntry {
case let .quizSolutionHeader(text): case let .quizSolutionHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .quizSolutionText(placeholder, text): case let .quizSolutionText(placeholder, text):
return CreatePollTextInputItem(presentationData: presentationData, text: text.value, placeholder: placeholder, maxLength: CreatePollTextInputItemTextLimit(value: 200, display: true), sectionId: self.section, style: .blocks, textUpdated: { text in return CreatePollTextInputItem(context: arguments.context, presentationData: presentationData, text: text.value, placeholder: placeholder, maxLength: CreatePollTextInputItemTextLimit(value: 200, display: true), sectionId: self.section, style: .blocks, textUpdated: { text in
arguments.updateSolutionText(text) arguments.updateSolutionText(text)
}, updatedFocus: { value in }, updatedFocus: { value in
arguments.solutionTextFocused(value) arguments.solutionTextFocused(value)
@ -664,7 +666,7 @@ private func legacyCreatePollController(context: AccountContext, updatedPresenta
let updateAddressNameDisposable = MetaDisposable() let updateAddressNameDisposable = MetaDisposable()
actionsDisposable.add(updateAddressNameDisposable) actionsDisposable.add(updateAddressNameDisposable)
let arguments = CreatePollControllerArguments(updatePollText: { value in let arguments = CreatePollControllerArguments(context: context, updatePollText: { value in
updateState { state in updateState { state in
var state = state var state = state
state.focusOptionId = nil state.focusOptionId = nil

View File

@ -8,6 +8,7 @@ import ItemListUI
import TextFormat import TextFormat
import ObjCRuntimeUtils import ObjCRuntimeUtils
import TextInputMenu import TextInputMenu
import AccountContext
public enum CreatePollTextInputItemTextLimitMode { public enum CreatePollTextInputItemTextLimitMode {
case characters case characters
@ -37,6 +38,7 @@ public struct ItemListMultilineInputInlineAction {
} }
public class CreatePollTextInputItem: ListViewItem, ItemListItem { public class CreatePollTextInputItem: ListViewItem, ItemListItem {
let context: AccountContext
let presentationData: ItemListPresentationData let presentationData: ItemListPresentationData
let text: NSAttributedString let text: NSAttributedString
let placeholder: String let placeholder: String
@ -55,7 +57,8 @@ public class CreatePollTextInputItem: ListViewItem, ItemListItem {
let inlineAction: ItemListMultilineInputInlineAction? let inlineAction: ItemListMultilineInputInlineAction?
public let tag: ItemListItemTag? public let tag: ItemListItemTag?
public init(presentationData: ItemListPresentationData, text: NSAttributedString, placeholder: String, maxLength: CreatePollTextInputItemTextLimit?, sectionId: ItemListSectionId, style: ItemListStyle, capitalization: Bool = true, autocorrection: Bool = true, returnKeyType: UIReturnKeyType = .default, minimalHeight: CGFloat? = nil, textUpdated: @escaping (NSAttributedString) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> Void)? = nil, updatedFocus: ((Bool) -> Void)? = nil, tag: ItemListItemTag? = nil, action: (() -> Void)? = nil, inlineAction: ItemListMultilineInputInlineAction? = nil) { public init(context: AccountContext, presentationData: ItemListPresentationData, text: NSAttributedString, placeholder: String, maxLength: CreatePollTextInputItemTextLimit?, sectionId: ItemListSectionId, style: ItemListStyle, capitalization: Bool = true, autocorrection: Bool = true, returnKeyType: UIReturnKeyType = .default, minimalHeight: CGFloat? = nil, textUpdated: @escaping (NSAttributedString) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> Void)? = nil, updatedFocus: ((Bool) -> Void)? = nil, tag: ItemListItemTag? = nil, action: (() -> Void)? = nil, inlineAction: ItemListMultilineInputInlineAction? = nil) {
self.context = context
self.presentationData = presentationData self.presentationData = presentationData
self.text = text self.text = text
self.placeholder = placeholder self.placeholder = placeholder
@ -228,7 +231,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
rightInset += inlineAction.icon.size.width + 8.0 rightInset += inlineAction.icon.size.width + 8.0
} }
let itemText = textAttributedStringForStateText(item.text, fontSize: 17.0, textColor: item.presentationData.theme.chat.inputPanel.primaryTextColor, accentTextColor: item.presentationData.theme.chat.inputPanel.panelControlAccentColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) let itemText = textAttributedStringForStateText(context: item.context, stateText: item.text, fontSize: 17.0, textColor: item.presentationData.theme.chat.inputPanel.primaryTextColor, accentTextColor: item.presentationData.theme.chat.inputPanel.panelControlAccentColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
let measureText = NSMutableAttributedString(attributedString: itemText) let measureText = NSMutableAttributedString(attributedString: itemText)
let measureRawString = measureText.string let measureRawString = measureText.string
if measureRawString.hasSuffix("\n") || measureRawString.isEmpty { if measureRawString.hasSuffix("\n") || measureRawString.isEmpty {
@ -294,11 +297,11 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
if let currentText = strongSelf.textNode.attributedText { if let currentText = strongSelf.textNode.attributedText {
if currentText.string != attributedText.string || updatedTheme != nil { if currentText.string != attributedText.string || updatedTheme != nil {
strongSelf.textNode.attributedText = attributedText strongSelf.textNode.attributedText = attributedText
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) refreshGenericTextInputAttributes(context: item.context, textView: strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
} }
} else { } else {
strongSelf.textNode.attributedText = attributedText strongSelf.textNode.attributedText = attributedText
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) refreshGenericTextInputAttributes(context: item.context, textView: strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
} }
if strongSelf.backgroundNode.supernode == nil { if strongSelf.backgroundNode.supernode == nil {
@ -591,7 +594,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
if let item = self.item { if let item = self.item {
if let _ = self.textNode.attributedText { if let _ = self.textNode.attributedText {
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) refreshGenericTextInputAttributes(context: item.context, textView: editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
let updatedText = stateAttributedStringForText(self.textNode.attributedText!) let updatedText = stateAttributedStringForText(self.textNode.attributedText!)
item.textUpdated(updatedText) item.textUpdated(updatedText)
} else { } else {
@ -621,7 +624,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
} }
refreshChatTextInputTypingAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0) refreshChatTextInputTypingAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0)
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) refreshGenericTextInputAttributes(context: item.context, textView: editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
} }
} }
@ -672,7 +675,7 @@ private func chatTextInputAddFormattingAttribute(item: CreatePollTextInputItem,
textNode.selectedRange = nsRange textNode.selectedRange = nsRange
refreshChatTextInputTypingAttributes(textNode.textView, theme: theme, baseFontSize: 17.0) refreshChatTextInputTypingAttributes(textNode.textView, theme: theme, baseFontSize: 17.0)
refreshGenericTextInputAttributes(textNode.textView, theme: theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil) refreshGenericTextInputAttributes(context: item.context, textView: textNode.textView, theme: theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
let updatedText = stateAttributedStringForText(textNode.attributedText!) let updatedText = stateAttributedStringForText(textNode.attributedText!)
item.textUpdated(updatedText) item.textUpdated(updatedText)

View File

@ -15,6 +15,7 @@ swift_library(
"//submodules/AppBundle", "//submodules/AppBundle",
"//submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl", "//submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl",
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView", "//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
"//submodules/AccountContext",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -6,6 +6,7 @@ import AppBundle
import ChatInputTextViewImpl import ChatInputTextViewImpl
import MessageInlineBlockBackgroundView import MessageInlineBlockBackgroundView
import TextFormat import TextFormat
import AccountContext
public protocol ChatInputTextNodeDelegate: AnyObject { public protocol ChatInputTextNodeDelegate: AnyObject {
func chatInputTextNodeDidUpdateText() func chatInputTextNodeDidUpdateText()
@ -820,9 +821,26 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
} }
static func calculateSize(attachment: ChatInputTextCollapsedQuoteAttachmentImpl, constrainedSize: CGSize) -> CGSize { static func calculateSize(attachment: ChatInputTextCollapsedQuoteAttachmentImpl, constrainedSize: CGSize) -> CGSize {
let renderingText = NSMutableAttributedString(attributedString: attachment.text) guard let context = attachment.attributes.context as? AccountContext else {
return CGSize(width: 10.0, height: 10.0)
}
/*let renderingText = NSMutableAttributedString(attributedString: attachment.text)
renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length)) renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length))
renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length)) renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length))*/
let renderingText = textAttributedStringForStateText(
context: context,
stateText: attachment.text,
fontSize: attachment.attributes.fontSize,
textColor: attachment.attributes.textColor,
accentTextColor: attachment.attributes.accentTextColor,
writingDirection: nil,
spoilersRevealed: false,
availableEmojis: Set(context.animatedEmojiStickersValue.keys),
emojiViewProvider: nil,
makeCollapsedQuoteAttachment: nil
)
let textNode = ImmediateTextNode() let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 3 textNode.maximumNumberOfLines = 3
@ -838,9 +856,25 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
let renderingText = NSMutableAttributedString(attributedString: attachment.text) guard let context = self.attachment.attributes.context as? AccountContext else {
return
}
let renderingText = textAttributedStringForStateText(
context: context, stateText: self.attachment.text,
fontSize: self.attachment.attributes.fontSize,
textColor: self.attachment.attributes.textColor,
accentTextColor: self.attachment.attributes.accentTextColor,
writingDirection: nil,
spoilersRevealed: false,
availableEmojis: Set(context.animatedEmojiStickersValue.keys),
emojiViewProvider: nil,
makeCollapsedQuoteAttachment: nil
)
/*let renderingText = NSMutableAttributedString(attributedString: attachment.text)
renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length)) renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length))
renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length)) renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length))*/
self.textNode.attributedText = renderingText self.textNode.attributedText = renderingText
self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 10.0, height: 8.0)) self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 10.0, height: 8.0))
@ -888,13 +922,6 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
private func renderingText() -> NSAttributedString {
let result = NSMutableAttributedString(attributedString: self.text)
result.addAttribute(.font, value: self.attributes.font, range: NSRange(location: 0, length: result.length))
result.addAttribute(.foregroundColor, value: self.attributes.textColor, range: NSRange(location: 0, length: result.length))
return result
}
override public func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect { override public func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
return CGRect(origin: CGPoint(), size: View.calculateSize(attachment: self, constrainedSize: CGSize(width: lineFrag.width, height: 10000.0))) return CGRect(origin: CGPoint(), size: View.calculateSize(attachment: self, constrainedSize: CGSize(width: lineFrag.width, height: 10000.0)))
} }

View File

@ -314,7 +314,7 @@ public final class TextFieldComponent: Component {
let inputState = f(self.inputState) let inputState = f(self.inputState)
let currentAttributedText = self.textView.attributedText let currentAttributedText = self.textView.attributedText
let updatedAttributedText = textAttributedStringForStateText(inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in let updatedAttributedText = textAttributedStringForStateText(context: component.context, stateText: inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
if currentAttributedText != updatedAttributedText { if currentAttributedText != updatedAttributedText {
@ -322,7 +322,7 @@ public final class TextFieldComponent: Component {
} }
self.textView.selectedRange = NSMakeRange(inputState.selectionRange.lowerBound, inputState.selectionRange.count) self.textView.selectedRange = NSMakeRange(inputState.selectionRange.lowerBound, inputState.selectionRange.count)
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: component.context, textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
@ -456,7 +456,7 @@ public final class TextFieldComponent: Component {
guard let component = self.component else { guard let component = self.component else {
return return
} }
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: component.context, textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize) refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)
@ -963,7 +963,7 @@ public final class TextFieldComponent: Component {
self.textView.isScrollEnabled = false self.textView.isScrollEnabled = false
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: component.context, textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize) refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)

View File

@ -2170,7 +2170,7 @@ extension ChatControllerImpl {
invertedMediaAttribute = attribute as? InvertMediaMessageAttribute invertedMediaAttribute = attribute as? InvertMediaMessageAttribute
} }
let text = trimChatInputText(convertMarkdownToAttributes(editMessage.inputState.inputText)) let text = trimChatInputText(convertMarkdownToAttributes(expandedInputStateAttributedString(editMessage.inputState.inputText)))
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text)) let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
var entitiesAttribute: TextEntitiesMessageAttribute? var entitiesAttribute: TextEntitiesMessageAttribute?

View File

@ -3890,7 +3890,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
var messages: [EnqueueMessage] = [] var messages: [EnqueueMessage] = []
let effectiveInputText = effectivePresentationInterfaceState.interfaceState.composeInputState.inputText let effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText)
let peerSpecificEmojiPack = (self.controller?.peerView?.cachedData as? CachedChannelData)?.emojiPack let peerSpecificEmojiPack = (self.controller?.peerView?.cachedData as? CachedChannelData)?.emojiPack

View File

@ -720,7 +720,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.loadTextInputNode() self.loadTextInputNode()
} }
if let textInputNode = self.textInputNode, let _ = self.presentationInterfaceState { if let textInputNode = self.textInputNode, let _ = self.presentationInterfaceState, let context = self.context {
self.updatingInputState = true self.updatingInputState = true
var textColor: UIColor = .black var textColor: UIColor = .black
@ -731,13 +731,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
} }
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in textInputNode.attributedText = textAttributedStringForStateText(context: context, stateText: state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count) textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
if let presentationInterfaceState = self.presentationInterfaceState { if let presentationInterfaceState = self.presentationInterfaceState {
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
} }
@ -1209,39 +1209,23 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
guard let self else { guard let self else {
return return
} }
guard let presentationInterfaceState = self.presentationInterfaceState else {
return
}
let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor
let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
let textAttributedString = textAttributedStringForStateText( let result = NSMutableAttributedString(attributedString: current.inputText)
current.inputText,
fontSize: baseFontSize,
textColor: textColor,
accentTextColor: accentTextColor,
writingDirection: nil,
spoilersRevealed: self.spoilersRevealed,
availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(),
emojiViewProvider: self.emojiViewProvider,
makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}
)
let result = NSMutableAttributedString(attributedString: textAttributedString)
var effectiveRange: NSRange = NSRange(location: NSNotFound, length: 0) if let _ = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: nil) as? ChatTextInputTextQuoteAttribute {
if let current = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: &effectiveRange) as? ChatTextInputTextQuoteAttribute { let blockString = result.attributedSubstring(from: range)
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: current.kind, isCollapsed: !current.isCollapsed), range: effectiveRange) result.replaceCharacters(in: range, with: "")
} else if let current = result.attribute(.attachment, at: range.lowerBound, effectiveRange: &effectiveRange) as? ChatInputTextCollapsedQuoteAttachment { result.insert(NSAttributedString(string: " ", attributes: [
result.replaceCharacters(in: effectiveRange, with: "") ChatTextInputAttributes.collapsedBlock: blockString
let updatedQuote = NSMutableAttributedString(attributedString: current.text) ]), at: range.lowerBound)
updatedQuote.removeAttribute(ChatTextInputAttributes.block, range: NSRange(location: 0, length: updatedQuote.length)) } else if let current = result.attribute(ChatTextInputAttributes.collapsedBlock, at: range.lowerBound, effectiveRange: nil) as? NSAttributedString {
updatedQuote.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: false), range: NSRange(location: 0, length: updatedQuote.length)) result.replaceCharacters(in: range, with: "")
result.insert(updatedQuote, at: effectiveRange.lowerBound)
let updatedBlockString = NSMutableAttributedString(attributedString: current)
updatedBlockString.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: false), range: NSRange(location: 0, length: updatedBlockString.length))
result.insert(updatedBlockString, at: range.lowerBound)
} }
let stateResult = stateAttributedStringForText(result) let stateResult = stateAttributedStringForText(result)
@ -2944,9 +2928,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
func chatInputTextNodeDidUpdateText() { func chatInputTextNodeDidUpdateText() {
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState, let context = self.context {
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
@ -3108,7 +3092,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
private func updateInternalSpoilersRevealed(_ revealed: Bool, animated: Bool) { private func updateInternalSpoilersRevealed(_ revealed: Bool, animated: Bool) {
guard self.spoilersRevealed == revealed, let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState else { guard self.spoilersRevealed == revealed, let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState, let context = self.context else {
return return
} }
@ -3118,11 +3102,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
textInputNode.textView.isScrollEnabled = false textInputNode.textView.isScrollEnabled = false
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in refreshChatTextInputAttributes(context: context, textView: textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in textInputNode.attributedText = textAttributedStringForStateText(context: context, stateText: self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
@ -4353,7 +4337,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
} }
func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let editableTextNode = self.textInputNode else { guard let editableTextNode = self.textInputNode, let context = self.context else {
return false return false
} }
@ -4380,7 +4364,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
} }
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in let cleanReplacementString = textAttributedStringForStateText(context: context, stateText: NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
}) })
string.replaceCharacters(in: range, with: cleanReplacementString) string.replaceCharacters(in: range, with: cleanReplacementString)

View File

@ -20,8 +20,9 @@ public struct ChatTextInputAttributes {
public static let spoiler = NSAttributedString.Key(rawValue: "Attribute__Spoiler") public static let spoiler = NSAttributedString.Key(rawValue: "Attribute__Spoiler")
public static let customEmoji = NSAttributedString.Key(rawValue: "Attribute__CustomEmoji") public static let customEmoji = NSAttributedString.Key(rawValue: "Attribute__CustomEmoji")
public static let block = NSAttributedString.Key(rawValue: "Attribute__Blockquote") 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") public let originalTextAttributeKey = NSAttributedString.Key(rawValue: "Attribute__OriginalText")
@ -36,27 +37,36 @@ public final class OriginalTextAttribute: NSObject {
} }
public final class ChatInputTextCollapsedQuoteAttributes: Equatable { public final class ChatInputTextCollapsedQuoteAttributes: Equatable {
public let font: UIFont public let context: AnyObject
public let fontSize: CGFloat
public let textColor: UIColor public let textColor: UIColor
public let accentTextColor: UIColor
public init( public init(
font: UIFont, context: AnyObject,
textColor: UIColor fontSize: CGFloat,
textColor: UIColor,
accentTextColor: UIColor
) { ) {
self.font = font self.context = context
self.fontSize = fontSize
self.textColor = textColor self.textColor = textColor
self.accentTextColor = accentTextColor
} }
public static func ==(lhs: ChatInputTextCollapsedQuoteAttributes, rhs: ChatInputTextCollapsedQuoteAttributes) -> Bool { public static func ==(lhs: ChatInputTextCollapsedQuoteAttributes, rhs: ChatInputTextCollapsedQuoteAttributes) -> Bool {
if lhs === rhs { if lhs === rhs {
return true return true
} }
if !lhs.font.isEqual(rhs.font) { if lhs.fontSize != rhs.fontSize {
return false return false
} }
if !lhs.textColor.isEqual(rhs.textColor) { if !lhs.textColor.isEqual(rhs.textColor) {
return false return false
} }
if !lhs.accentTextColor.isEqual(rhs.accentTextColor) {
return false
}
return true return true
} }
@ -66,6 +76,27 @@ public protocol ChatInputTextCollapsedQuoteAttachment: NSTextAttachment {
var text: NSAttributedString { get } 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 { public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
let sourceString = NSMutableAttributedString(attributedString: text) let sourceString = NSMutableAttributedString(attributedString: text)
while true { while true {
@ -77,7 +108,7 @@ public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttrib
stop.pointee = true stop.pointee = true
found = true found = true
} else if let value = value as? ChatInputTextCollapsedQuoteAttachment { } 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 stop.pointee = true
found = true found = true
} }
@ -92,7 +123,15 @@ public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttrib
sourceString.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in sourceString.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
for (key, value) in attributes { 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) 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( let quoteAttributes = ChatInputTextCollapsedQuoteAttributes(
font: Font.regular(round(fontSize * 0.8235294117647058)), context: context,
textColor: textColor fontSize: fontSize,
textColor: textColor,
accentTextColor: accentTextColor
) )
let stateText = NSMutableAttributedString(attributedString: stateText) let stateText = NSMutableAttributedString(attributedString: stateText)
while true { /*while true {
var found = false var found = false
stateText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: stateText.length), options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in stateText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: stateText.length), options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in
if let value = value as? ChatTextInputTextQuoteAttribute { if let value = value as? ChatTextInputTextQuoteAttribute {
@ -162,6 +203,23 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
if !found { if !found {
break 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) 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)?) { public func refreshChatTextInputAttributes(context: AnyObject, 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) 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 { guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
return return
} }
@ -740,21 +798,21 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText)) var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
refreshTextMentions(text: text, initialAttributedText: initialAttributedText, attributedText: attributedText, fullRange: fullRange) 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 text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: text.length) fullRange = NSRange(location: 0, length: text.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText)) attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange) 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 text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: text.length) fullRange = NSRange(location: 0, length: text.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText)) attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshBlockQuotes(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange) 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) { if !resultAttributedText.isEqual(to: initialAttributedText) {
fullRange = NSRange(location: 0, length: textView.textStorage.length) fullRange = NSRange(location: 0, length: textView.textStorage.length)
@ -864,7 +922,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
textView.textStorage.endEditing() 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 { guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
return return
} }
@ -877,14 +935,14 @@ public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: Pre
var text: NSString = initialAttributedText.string as NSString var text: NSString = initialAttributedText.string as NSString
var fullRange = NSRange(location: 0, length: initialAttributedText.length) var fullRange = NSRange(location: 0, length: initialAttributedText.length)
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText)) 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 text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: initialAttributedText.length) fullRange = NSRange(location: 0, length: initialAttributedText.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText)) attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange) 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) { if !resultAttributedText.isEqual(to: initialAttributedText) {
textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange) textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)

View File

@ -56,6 +56,25 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M
break 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 return string
} }