diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 127d6d2eda..65019d171b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12160,6 +12160,7 @@ Sorry for the inconvenience."; "Chat.AdminActionSheet.BanFooterMultiple" = "Fully ban users"; "Chat.AdminActionSheet.RestrictFooterMultiple" = "Partially restrict users"; "Chat.AdminActionSheet.PermissionsSectionHeader" = "WHAT CAN THIS USER DO?"; +"Chat.AdminActionSheet.PermissionsSectionHeaderMultiple" = "WHAT CAN THESE USERS DO?"; "Chat.AdminActionSheet.ActionButton" = "Proceed"; "Chat.AdminAction.ToastMessagesDeletedTitleSingle" = "Message Deleted"; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index f8cacc148f..d0db28a125 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -550,18 +550,24 @@ public struct ChatTextInputStateText: Codable, Equatable { parsedAttributes.append(ChatTextInputStateTextAttribute(type: .underline, range: range.location ..< (range.location + range.length))) } else if key == ChatTextInputAttributes.spoiler { parsedAttributes.append(ChatTextInputStateTextAttribute(type: .spoiler, range: range.location ..< (range.location + range.length))) - } else if key == ChatTextInputAttributes.block, let value = value as? ChatTextInputTextQuoteAttribute { - switch value.kind { - case .quote: - parsedAttributes.append(ChatTextInputStateTextAttribute(type: .quote(isCollapsed: value.isCollapsed), range: range.location ..< (range.location + range.length))) - case let .code(language): - 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))) } } }) + attributedText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: attributedText.length), options: [], using: { value, range, _ in + if let value = value as? ChatTextInputTextQuoteAttribute { + switch value.kind { + case .quote: + parsedAttributes.append(ChatTextInputStateTextAttribute(type: .quote(isCollapsed: value.isCollapsed), range: range.location ..< (range.location + range.length))) + case let .code(language): + parsedAttributes.append(ChatTextInputStateTextAttribute(type: .codeBlock(language: language), range: range.location ..< (range.location + range.length))) + } + } + }) + attributedText.enumerateAttribute(ChatTextInputAttributes.collapsedBlock, in: NSRange(location: 0, length: attributedText.length), options: [], using: { value, range, _ in + if let value = value as? NSAttributedString { + parsedAttributes.append(ChatTextInputStateTextAttribute(type: .collapsedQuote(text: ChatTextInputStateText(attributedText: value)), range: range.location ..< (range.location + range.length))) + } + }) self.attributes = parsedAttributes } diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index 1c94d2519d..8e3ab429af 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -470,7 +470,8 @@ public final class ChatInterfaceState: Codable, Equatable { if self.composeInputState.inputText.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && self.replyMessageSubject == nil { return nil } else { - return SynchronizeableChatInputState(replySubject: self.replyMessageSubject?.subjectModel, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange) + let sourceText = expandedInputStateAttributedString(self.composeInputState.inputText) + return SynchronizeableChatInputState(replySubject: self.replyMessageSubject?.subjectModel, text: sourceText.string, entities: generateChatInputTextEntities(sourceText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index dbb04fca3d..be0a5d2c0f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -246,7 +246,7 @@ public final class StarsContext { public let description: String? public let photo: TelegramMediaWebFile? - init( + public init( id: String, count: Int64, date: Int32, diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift index 584b12f197..150c207776 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift @@ -1247,7 +1247,7 @@ private final class AdminUserActionsSheetComponent: Component { theme: environment.theme, header: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( - string: environment.strings.Chat_AdminActionSheet_PermissionsSectionHeader, + string: component.peers.count == 1 ? environment.strings.Chat_AdminActionSheet_PermissionsSectionHeader : environment.strings.Chat_AdminActionSheet_PermissionsSectionHeaderMultiple, font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor )), diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/BUILD index 5b069f65cd..9efb6c1ba2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/BUILD @@ -16,6 +16,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl", "//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView", "//submodules/AccountContext", + "//submodules/TelegramUI/Components/TextNodeWithEntities", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift index 2348bde696..f98a704e0d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift @@ -7,6 +7,7 @@ import ChatInputTextViewImpl import MessageInlineBlockBackgroundView import TextFormat import AccountContext +import TextNodeWithEntities public protocol ChatInputTextNodeDelegate: AnyObject { func chatInputTextNodeDidUpdateText() @@ -540,7 +541,19 @@ private final class ChatInputTextLegacyInternal: NSObject, ChatInputTextInternal var startIndex = glyphRange.lowerBound while startIndex < glyphRange.upperBound { var effectiveRange = NSRange(location: NSNotFound, length: 0) - let rect = self.customLayoutManager.lineFragmentUsedRect(forGlyphAt: startIndex, effectiveRange: &effectiveRange) + var rect = self.customLayoutManager.lineFragmentUsedRect(forGlyphAt: startIndex, effectiveRange: &effectiveRange) + + let characterRange = self.customLayoutManager.characterRange(forGlyphRange: NSRange(location: startIndex, length: 1), actualGlyphRange: nil) + if characterRange.location != NSNotFound { + if let attribute = self.customTextStorage.attribute(NSAttributedString.Key("Attribute__Blockquote"), at: characterRange.location, effectiveRange: nil) { + let _ = attribute + rect.size.width += 13.0 + } else if let attribute = self.customTextStorage.attribute(.attachment, at: characterRange.location, effectiveRange: nil) as? ChatInputTextCollapsedQuoteAttachment { + let _ = attribute + rect.size.width += 8.0 + } + } + if boundingRect.isEmpty { boundingRect = rect } else { @@ -552,6 +565,7 @@ private final class ChatInputTextLegacyInternal: NSObject, ChatInputTextInternal break } } + return boundingRect } @@ -804,11 +818,12 @@ private let registeredViewProvider: Void = { public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, ChatInputTextCollapsedQuoteAttachment { final class View: UIView { let attachment: ChatInputTextCollapsedQuoteAttachmentImpl - let textNode: ImmediateTextNode + let textNode: ImmediateTextNodeWithEntities init(attachment: ChatInputTextCollapsedQuoteAttachmentImpl) { self.attachment = attachment - self.textNode = ImmediateTextNode() + self.textNode = ImmediateTextNodeWithEntities() + self.textNode.displaysAsynchronously = false self.textNode.maximumNumberOfLines = 3 super.init(frame: CGRect()) @@ -825,10 +840,6 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, 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(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length))*/ - let renderingText = textAttributedStringForStateText( context: context, stateText: attachment.text, @@ -846,11 +857,11 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, textNode.maximumNumberOfLines = 3 textNode.attributedText = renderingText - textNode.cutout = TextNodeCutout(topRight: CGSize(width: 40.0, height: 10.0)) + textNode.cutout = TextNodeCutout(topRight: CGSize(width: 30.0, height: 10.0)) - let layoutInfo = textNode.updateLayoutFullInfo(CGSize(width: constrainedSize.width - 9.0, height: constrainedSize.height)) + let layoutSize = textNode.updateLayout(CGSize(width: constrainedSize.width - 9.0, height: constrainedSize.height)) - return CGSize(width: constrainedSize.width, height: 8.0 + layoutInfo.size.height + 8.0) + return CGSize(width: constrainedSize.width, height: 8.0 + layoutSize.height + 8.0) } override func layoutSubviews() { @@ -876,13 +887,24 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, 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))*/ + self.textNode.arguments = TextNodeWithEntities.Arguments( + context: context, + cache: context.animationCache, + renderer: context.animationRenderer, + placeholderColor: .gray, + attemptSynchronous: true + ) + self.textNode.attributedText = renderingText - self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 10.0, height: 8.0)) + self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 30.0, height: 10.0)) + + self.textNode.displaySpoilerEffect = true + self.textNode.visibility = true let maxTextSize = CGSize(width: self.bounds.size.width - 9.0, height: self.bounds.size.height) - let layoutInfo = self.textNode.updateLayoutFullInfo(maxTextSize) + let layoutSize = self.textNode.updateLayout(maxTextSize) - self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 8.0), size: layoutInfo.size) + self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 8.0), size: layoutSize) } } @@ -1450,16 +1472,17 @@ private final class QuoteBackgroundView: UIView { self.iconView.frame = CGRect(origin: CGPoint(x: size.width - 4.0 - quoteIcon.size.width, y: 4.0), size: quoteIcon.size) let collapseButtonSize = CGSize(width: 18.0, height: 18.0) - self.collapseButton.frame = CGRect(origin: CGPoint(x: size.width - 2.0 - collapseButtonSize.width, y: 2.0), size: collapseButtonSize) if isCollapsed { self.collapseButtonIconView.image = quoteExpandImage + self.collapseButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size) } else { self.collapseButtonIconView.image = quoteCollapseImage + self.collapseButton.frame = CGRect(origin: CGPoint(x: size.width - 2.0 - collapseButtonSize.width, y: 2.0), size: collapseButtonSize) } if let image = self.collapseButtonIconView.image { let iconSize = image.size.aspectFitted(collapseButtonSize) - self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize) + self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: self.collapseButton.bounds.width - 2.0 - collapseButtonSize.width + floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: 2.0 + floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize) } var primaryColor: UIColor @@ -1469,7 +1492,7 @@ private final class QuoteBackgroundView: UIView { switch kind { case .quote: - if size.height >= 100.0 || isCollapsed { + if size.height >= 60.0 || isCollapsed { self.iconView.isHidden = true self.collapseButton.isHidden = false } else { diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift index 7340cfbc37..ffc6c7d3a7 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift @@ -45,7 +45,7 @@ private func generateBlockMaskImage() -> UIImage { gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! context.setBlendMode(.destinationIn) - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: 0.0, y: size.height - 7.0), options: CGGradientDrawingOptions()) + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: 0.0, y: size.height - 18.0), options: CGGradientDrawingOptions()) })!.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: size.height - 1.0, right: size.width - 1.0), resizingMode: .stretch) } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index a11c432d40..7d3da90cdd 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1212,13 +1212,23 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in let result = NSMutableAttributedString(attributedString: current.inputText) + var selectionRange = current.selectionRange if let _ = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: nil) as? ChatTextInputTextQuoteAttribute { - let blockString = result.attributedSubstring(from: range) + let blockString = NSMutableAttributedString(attributedString: result.attributedSubstring(from: range)) + blockString.removeAttribute(ChatTextInputAttributes.block, range: NSRange(location: 0, length: blockString.length)) + result.replaceCharacters(in: range, with: "") result.insert(NSAttributedString(string: " ", attributes: [ ChatTextInputAttributes.collapsedBlock: blockString ]), at: range.lowerBound) + + if selectionRange.lowerBound >= range.lowerBound && selectionRange.upperBound < range.upperBound { + selectionRange = range.lowerBound ..< range.lowerBound + } else if selectionRange.lowerBound >= range.upperBound { + let deltaLength = 1 - range.length + selectionRange = (selectionRange.lowerBound + deltaLength) ..< (selectionRange.lowerBound + deltaLength) + } } else if let current = result.attribute(ChatTextInputAttributes.collapsedBlock, at: range.lowerBound, effectiveRange: nil) as? NSAttributedString { result.replaceCharacters(in: range, with: "") @@ -1226,13 +1236,24 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch updatedBlockString.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: false), range: NSRange(location: 0, length: updatedBlockString.length)) result.insert(updatedBlockString, at: range.lowerBound) + + if selectionRange.lowerBound >= range.upperBound { + let deltaLength = updatedBlockString.length - 1 + selectionRange = (selectionRange.lowerBound + deltaLength) ..< (selectionRange.lowerBound + deltaLength) + } } let stateResult = stateAttributedStringForText(result) + if selectionRange.lowerBound < 0 { + selectionRange = 0 ..< selectionRange.upperBound + } + if selectionRange.upperBound > stateResult.length { + selectionRange = selectionRange.lowerBound ..< stateResult.length + } return (ChatTextInputState( inputText: stateResult, - selectionRange: current.selectionRange + selectionRange: selectionRange ), inputMode) } } diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index f15e284ace..52dc6e7292 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -179,7 +179,7 @@ public struct ChatTextFontAttributes: OptionSet, Hashable, Sequence { public func textAttributedStringForStateText(context: AnyObject, stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor, writingDirection: NSWritingDirection?, spoilersRevealed: Bool, availableEmojis: Set, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) -> NSAttributedString { let quoteAttributes = ChatInputTextCollapsedQuoteAttributes( context: context, - fontSize: fontSize, + fontSize: round(fontSize * 0.8235294117647058), textColor: textColor, accentTextColor: accentTextColor )