diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 37cb896107..21c11250d4 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7144,4 +7144,4 @@ Sorry for the inconvenience."; "ChatList.Archive" = "Archive"; -"ChatList.Archive" = "Archive"; +"TextFormat.Spoiler" = "Spoiler"; diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 587e4be879..e011f5df7c 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -925,6 +925,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1090087980] = { return Api.MessageEntity.parse_messageEntityStrike($0) } dict[34469328] = { return Api.MessageEntity.parse_messageEntityBlockquote($0) } dict[1981704948] = { return Api.MessageEntity.parse_messageEntityBankCard($0) } + dict[852137487] = { return Api.MessageEntity.parse_messageEntitySpoiler($0) } dict[483901197] = { return Api.InputPhoto.parse_inputPhotoEmpty($0) } dict[1001634122] = { return Api.InputPhoto.parse_inputPhoto($0) } dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 7e5f5d77c2..6dafefb779 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -23219,6 +23219,7 @@ public extension Api { case messageEntityStrike(offset: Int32, length: Int32) case messageEntityBlockquote(offset: Int32, length: Int32) case messageEntityBankCard(offset: Int32, length: Int32) + case messageEntitySpoiler(offset: Int32, length: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -23359,6 +23360,13 @@ public extension Api { serializeInt32(offset, buffer: buffer, boxed: false) serializeInt32(length, buffer: buffer, boxed: false) break + case .messageEntitySpoiler(let offset, let length): + if boxed { + buffer.appendInt32(852137487) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break } } @@ -23402,6 +23410,8 @@ public extension Api { return ("messageEntityBlockquote", [("offset", offset), ("length", length)]) case .messageEntityBankCard(let offset, let length): return ("messageEntityBankCard", [("offset", offset), ("length", length)]) + case .messageEntitySpoiler(let offset, let length): + return ("messageEntitySpoiler", [("offset", offset), ("length", length)]) } } @@ -23685,6 +23695,20 @@ public extension Api { return nil } } + public static func parse_messageEntitySpoiler(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.MessageEntity.messageEntitySpoiler(offset: _1!, length: _2!) + } + else { + return nil + } + } } public enum InputPhoto: TypeConstructorDescription { diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index e26b60dd06..20d48266fb 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -366,6 +366,8 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BlockQuote)) case let .messageEntityBankCard(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard)) + case let .messageEntitySpoiler(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Spoiler)) } } return result diff --git a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift index 1879a9c4ef..d3e1a625ec 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift @@ -46,6 +46,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa apiEntities.append(.messageEntityUnderline(offset: offset, length: length)) case .BankCard: apiEntities.append(.messageEntityBankCard(offset: offset, length: length)) + case .Spoiler: + apiEntities.append(.messageEntitySpoiler(offset: offset, length: length)) case .Custom: break } diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index 2eb72db842..e962a163a9 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -691,6 +691,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi break case .BankCard: break + case .Spoiler: + break case .Custom: break } @@ -740,6 +742,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp result.append(.messageEntityUnderline(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) case .BankCard: break + case .Spoiler: + break case .Custom: break } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index f1503ed736..e9ab11ad0a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -20,6 +20,7 @@ public enum MessageTextEntityType: Equatable { case BlockQuote case Underline case BankCard + case Spoiler case Custom(type: CustomEntityType) } @@ -68,6 +69,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.type = .Underline case 16: self.type = .BankCard + case 17: + self.type = .Spoiler case Int32.max: self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0)) default: @@ -121,6 +124,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.type = .Underline case 16: self.type = .BankCard + case 17: + self.type = .Spoiler case Int32.max: let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 self.type = .Custom(type: customType) @@ -169,6 +174,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { encoder.encodeInt32(15, forKey: "_rawValue") case .BankCard: encoder.encodeInt32(16, forKey: "_rawValue") + case .Spoiler: + encoder.encodeInt32(17, forKey: "_rawValue") case let .Custom(type): encoder.encodeInt32(Int32.max, forKey: "_rawValue") encoder.encodeInt32(type, forKey: "type") @@ -217,6 +224,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { try container.encode(15 as Int32, forKey: "_rawValue") case .BankCard: try container.encode(16 as Int32, forKey: "_rawValue") + case .Spoiler: + try container.encode(17 as Int32, forKey: "_rawValue") case let .Custom(type): try container.encode(Int32.max as Int32, forKey: "_rawValue") try container.encode(type as Int32, forKey: "type") diff --git a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift index 75efd13155..4929e88372 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift @@ -15,6 +15,7 @@ final class ChatTextInputMenu { private var stringLink: String = "Link" private var stringStrikethrough: String = "Strikethrough" private var stringUnderline: String = "Underline" + private var stringSpoiler: String = "Spoiler" private(set) var state: ChatTextInputMenuState = .inactive { didSet { @@ -31,7 +32,8 @@ final class ChatTextInputMenu { UIMenuItem(title: self.stringMonospace, action: Selector(("formatAttributesMonospace:"))), UIMenuItem(title: self.stringLink, action: Selector(("formatAttributesLink:"))), UIMenuItem(title: self.stringStrikethrough, action: Selector(("formatAttributesStrikethrough:"))), - UIMenuItem(title: self.stringUnderline, action: Selector(("formatAttributesUnderline:"))) + UIMenuItem(title: self.stringUnderline, action: Selector(("formatAttributesUnderline:"))), + UIMenuItem(title: self.stringSpoiler, action: Selector(("formatAttributesSpoiler:"))) ] } @@ -60,6 +62,7 @@ final class ChatTextInputMenu { self.stringLink = strings.TextFormat_Link self.stringStrikethrough = strings.TextFormat_Strikethrough self.stringUnderline = strings.TextFormat_Underline + self.stringSpoiler = strings.TextFormat_Spoiler } func activate() { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index f1dae67d4e..ba2f818023 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2097,7 +2097,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } else { return ASEditableTextNodeTargetForAction(target: nil) } - } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) { + } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) { if case .format = self.inputMenu.state { return ASEditableTextNodeTargetForAction(target: self) } else { @@ -2172,6 +2172,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } + @objc func formatAttributesSpoiler(_ sender: Any) { + self.inputMenu.back() + self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in + return (chatTextInputAddFormattingAttribute(current, attribute: ChatTextInputAttributes.spoiler), inputMode) + } + } + @objc func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { self.updateActivity() var cleanText = text diff --git a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift index 8272112c34..d1f2131210 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift @@ -890,7 +890,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A } else { return ASEditableTextNodeTargetForAction(target: nil) } - } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) { + } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) { if case .format = self.inputMenu.state { return ASEditableTextNodeTargetForAction(target: self) } else { @@ -969,6 +969,13 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A } } + @objc func formatAttributesSpoiler(_ sender: Any) { + self.inputMenu.back() + self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in + return (chatTextInputAddFormattingAttribute(current, attribute: ChatTextInputAttributes.spoiler), inputMode) + } + } + @objc func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { var cleanText = text let removeSequences: [String] = ["\u{202d}", "\u{202c}"] diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index ae4ddfbff1..6cb4c144ce 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -15,8 +15,9 @@ public struct ChatTextInputAttributes { public static let underline = NSAttributedString.Key(rawValue: "Attribute__Underline") public static let textMention = NSAttributedString.Key(rawValue: "Attribute__TextMention") public static let textUrl = NSAttributedString.Key(rawValue: "Attribute__TextUrl") + public static let spoiler = NSAttributedString.Key(rawValue: "Attribute__Spoiler") - public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl] + public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.spoiler] } public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString { @@ -83,6 +84,9 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo } else if key == ChatTextInputAttributes.underline { result.addAttribute(key, value: value, range: range) result.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) + } else if key == ChatTextInputAttributes.spoiler { + result.addAttribute(key, value: value, range: range) + result.addAttribute(NSAttributedString.Key.backgroundColor, value: textColor.withAlphaComponent(0.15), range: fullRange) } } @@ -435,6 +439,7 @@ public func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange) + textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.font, value: Font.regular(baseFontSize), range: fullRange) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.primaryTextColor, range: fullRange) @@ -465,6 +470,9 @@ public func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme } else if key == ChatTextInputAttributes.underline { textNode.textView.textStorage.addAttribute(key, value: value, range: range) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) + } else if key == ChatTextInputAttributes.spoiler { + textNode.textView.textStorage.addAttribute(key, value: value, range: range) + textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: fullRange) } } @@ -523,6 +531,7 @@ public func refreshGenericTextInputAttributes(_ textNode: ASEditableTextNode, th textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange) textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange) + textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.font, value: Font.regular(baseFontSize), range: fullRange) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.primaryTextColor, range: fullRange) @@ -553,6 +562,9 @@ public func refreshGenericTextInputAttributes(_ textNode: ASEditableTextNode, th } else if key == ChatTextInputAttributes.underline { textNode.textView.textStorage.addAttribute(key, value: value, range: range) textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) + } else if key == ChatTextInputAttributes.spoiler { + textNode.textView.textStorage.addAttribute(key, value: value, range: range) + textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: fullRange) } } @@ -730,6 +742,8 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu textInputAttribute = ChatTextInputAttributes.italic case "~~": textInputAttribute = ChatTextInputAttributes.strikethrough + case "||": + textInputAttribute = ChatTextInputAttributes.spoiler default: textInputAttribute = nil } diff --git a/submodules/TextFormat/Sources/GenerateTextEntities.swift b/submodules/TextFormat/Sources/GenerateTextEntities.swift index 2ff847f04a..8f0114185c 100644 --- a/submodules/TextFormat/Sources/GenerateTextEntities.swift +++ b/submodules/TextFormat/Sources/GenerateTextEntities.swift @@ -148,6 +148,8 @@ public func generateChatInputTextEntities(_ text: NSAttributedString) -> [Messag entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextMention(peerId: value.peerId))) } else if key == ChatTextInputAttributes.textUrl, let value = value as? ChatTextInputTextUrlAttribute { entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .TextUrl(url: value.url))) + } else if key == ChatTextInputAttributes.spoiler { + entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Spoiler)) } } }) diff --git a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift index 622f252f7d..939be1acd5 100644 --- a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift +++ b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift @@ -38,6 +38,8 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M string.addAttribute(ChatTextInputAttributes.strikethrough, value: true as NSNumber, range: range) case .Underline: string.addAttribute(ChatTextInputAttributes.underline, value: true as NSNumber, range: range) + case .Spoiler: + string.addAttribute(ChatTextInputAttributes.spoiler, value: true as NSNumber, range: range) default: break } @@ -137,8 +139,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti } string.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention), value: nsString!.substring(with: range), range: range) case .Strikethrough: - string.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true as NSNumber, range: range) -// string.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) + string.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) case .Underline: string.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) case let .TextMention(peerId): @@ -224,6 +225,8 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti nsString = text as NSString } string.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.BankCard), value: nsString!.substring(with: range), range: range) + case .Spoiler: + string.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true as NSNumber, range: range) case let .Custom(type): if type == ApplicationSpecificEntityType.Timecode { string.addAttribute(NSAttributedString.Key.foregroundColor, value: linkColor, range: range)