diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 860bdf6375..725eaeb617 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2672,16 +2672,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ) |> deliverOnMainQueue).start(next: { [weak self] peerView, message, onlineMemberCount in if let strongSelf = self { - - var replyCount = 0 - if let message = message { - for attribute in message.attributes { - if let attribute = attribute as? ReplyThreadMessageAttribute { - replyCount = Int(attribute.count) - } - } - } - strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: replyCount) + strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, text: message?.text ?? "") let firstTime = strongSelf.peerView == nil strongSelf.peerView = peerView @@ -3257,12 +3248,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if let _ = combinedInitialData.cachedData as? CachedSecretChatData { } - if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation { - if isChannelPost { - pinnedMessageId = messageId - } else { - pinnedMessageId = nil - } + if case .replyThread = strongSelf.chatLocation { + pinnedMessageId = nil } var pinnedMessage: Message? @@ -3404,12 +3391,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if let _ = cachedData as? CachedSecretChatData { } - if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation { - if isChannelPost { - pinnedMessageId = messageId - } else { - pinnedMessageId = nil - } + if case .replyThread = strongSelf.chatLocation { + pinnedMessageId = nil } var pinnedMessage: Message? @@ -6118,6 +6101,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func navigationButtonAction(_ action: ChatNavigationButtonAction) { switch action { + case .spacer: + break case .cancelMessageSelection: self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) case .clearHistory: diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 7f80093aa6..e2ee6a6a68 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -113,7 +113,8 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } } - if case let .replyThread(messageId, isChannelPost, _) = location, !isChannelPost { + var addedThreadHead = false + if case let .replyThread(messageId, isChannelPost, _) = location, view.earlierId == nil, !view.isLoading { loop: for entry in view.additionalData { switch entry { case let .message(id, message) where id == messageId: @@ -139,8 +140,18 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } } + var replyCount = 0 + for attribute in message.attributes { + if let attribute = attribute as? ReplyThreadMessageAttribute { + replyCount = Int(attribute.count) + } + } + addedThreadHead = true entries.insert(.MessageEntry(message, presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])), at: 0) + if view.entries.count > 0 { + entries.insert(.ReplyCountEntry(message.index, isChannelPost, replyCount, presentationData), at: 1) + } } break loop default: @@ -190,6 +201,9 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } else { isEmpty = false } + if addedThreadHead { + isEmpty = false + } if isEmpty { entries.removeAll() } diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntry.swift b/submodules/TelegramUI/Sources/ChatHistoryEntry.swift index 61adbd48ca..02c589c44a 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntry.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntry.swift @@ -37,6 +37,7 @@ enum ChatHistoryEntry: Identifiable, Comparable { case MessageEntry(Message, ChatPresentationData, Bool, MessageHistoryEntryMonthLocation?, ChatHistoryMessageSelection, ChatMessageEntryAttributes) case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)], ChatPresentationData) case UnreadEntry(MessageIndex, ChatPresentationData) + case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData) case ChatInfoEntry(String, ChatPresentationData) case SearchEntry(PresentationTheme, PresentationStrings) @@ -57,10 +58,12 @@ enum ChatHistoryEntry: Identifiable, Comparable { return UInt64(groupInfo.stableId) | ((UInt64(2) << 40)) case .UnreadEntry: return UInt64(4) << 40 - case .ChatInfoEntry: + case .ReplyCountEntry: return UInt64(5) << 40 - case .SearchEntry: + case .ChatInfoEntry: return UInt64(6) << 40 + case .SearchEntry: + return UInt64(7) << 40 } } @@ -72,6 +75,8 @@ enum ChatHistoryEntry: Identifiable, Comparable { return messages[messages.count - 1].0.index case let .UnreadEntry(index, _): return index + case let .ReplyCountEntry(index, _, _, _): + return index case .ChatInfoEntry: return MessageIndex.absoluteLowerBound() case .SearchEntry: @@ -182,6 +187,12 @@ enum ChatHistoryEntry: Identifiable, Comparable { } else { return false } + case let .ReplyCountEntry(lhsIndex, lhsIsComments, lhsCount, lhsPresentationData): + if case let .ReplyCountEntry(rhsIndex, rhsIsComments, rhsCount, rhsPresentationData) = rhs, lhsIndex == rhsIndex, lhsIsComments == rhsIsComments, lhsCount == rhsCount, lhsPresentationData === rhsPresentationData { + return true + } else { + return false + } case let .ChatInfoEntry(lhsText, lhsPresentationData): if case let .ChatInfoEntry(rhsText, rhsPresentationData) = rhs, lhsText == rhsText, lhsPresentationData === rhsPresentationData { return true diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 41fba048d2..1dea8b502d 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -275,6 +275,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .UnreadEntry(_, presentationData): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) + case let .ReplyCountEntry(_, isComments, count, presentationData): + return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint) case let .ChatInfoEntry(text, presentationData): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint) case let .SearchEntry(theme, strings): @@ -318,6 +320,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .UnreadEntry(_, presentationData): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) + case let .ReplyCountEntry(_, isComments, count, presentationData): + return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint) case let .ChatInfoEntry(text, presentationData): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint) case let .SearchEntry(theme, strings): @@ -1560,6 +1564,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { itemNode.layer.animateScale(from: 0.94, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring) } else if let itemNode = itemNode as? ChatUnreadItemNode { itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay) + } else if let itemNode = itemNode as? ChatReplyCountItemNode { + itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay) } } strongSelf.forEachItemHeaderNode { itemNode in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index e14442ec14..559b5454a1 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -464,7 +464,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } } - if data.canReply { + var isReplyThreadHead = false + if case let .replyThread(messageId, _, _) = chatPresentationInterfaceState.chatLocation { + isReplyThreadHead = messages[0].id == messageId + } + + if !isReplyThreadHead, data.canReply { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in @@ -741,7 +746,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } } - if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction) { + if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction), !isReplyThreadHead { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in @@ -806,7 +811,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } } } - if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction { + if !isReplyThreadHead, !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) }, action: { controller, f in @@ -844,7 +849,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if let _ = message.peers[message.id.peerId] as? TelegramChannel { clearCacheAsDelete = true } - if (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction { + if !isReplyThreadHead, (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction { let title: String var isSending = false var isEditing = false diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 91915964bb..0dc0a99395 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -14,6 +14,7 @@ enum ChatNavigationButtonAction: Equatable { case search case dismiss case toggleInfoPanel + case spacer } struct ChatNavigationButton: Equatable { @@ -92,7 +93,11 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch return ChatNavigationButton(action: .search, buttonItem: buttonItem) } } else { - return nil + if case .spacer = currentButton?.action { + return currentButton + } else { + return ChatNavigationButton(action: .spacer, buttonItem: UIBarButtonItem(title: "", style: .plain, target: target, action: selector)) + } } } if case let .peer(peerId) = presentationInterfaceState.chatLocation { @@ -106,7 +111,11 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch return ChatNavigationButton(action: .search, buttonItem: buttonItem) } } else { - return nil + if case .spacer = currentButton?.action { + return currentButton + } else { + return ChatNavigationButton(action: .spacer, buttonItem: UIBarButtonItem(title: "", style: .plain, target: target, action: selector)) + } } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index e898e0198b..91e88121cb 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -742,7 +742,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude)) } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index d9299f9338..6827a33cd1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1044,7 +1044,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode inlineBotNameString = attribute.title } } else if let attribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == attribute.messageId { + if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == attribute.messageId { } else { replyMessage = firstMessage.associatedMessages[attribute.messageId] } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index a3c216e374..f6889fe4c6 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -337,7 +337,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index 37efd84ef6..bb3ffe9407 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -180,6 +180,8 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) - lhsHeader = nil } else if let lhs = lhs as? ChatUnreadItem { lhsHeader = lhs.header + } else if let lhs = lhs as? ChatReplyCountItem { + lhsHeader = lhs.header } else { lhsHeader = nil } @@ -191,6 +193,8 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) - rhsHeader = nil } else if let rhs = rhs as? ChatUnreadItem { rhsHeader = rhs.header + } else if let rhs = rhs as? ChatReplyCountItem { + rhsHeader = rhs.header } else { rhsHeader = nil } @@ -443,6 +447,10 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { if bottom.header.id != self.header.id { dateAtBottom = true } + } else if let bottom = bottom as? ChatReplyCountItem { + if bottom.header.id != self.header.id { + dateAtBottom = true + } } else if let _ = bottom as? ChatHoleItem { dateAtBottom = true } else { diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 9e98a7489c..06979ee0ad 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -411,7 +411,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } diff --git a/submodules/TelegramUI/Sources/ChatReplyCountItem.swift b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift new file mode 100644 index 0000000000..c6208f1e50 --- /dev/null +++ b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift @@ -0,0 +1,176 @@ +import Foundation +import UIKit +import Postbox +import AsyncDisplayKit +import Display +import SwiftSignalKit +import TelegramPresentationData +import AccountContext + +private let titleFont = UIFont.systemFont(ofSize: 13.0) + +class ChatReplyCountItem: ListViewItem { + let index: MessageIndex + let isComments: Bool + let count: Int + let presentationData: ChatPresentationData + let header: ChatMessageDateHeader + + init(index: MessageIndex, isComments: Bool, count: Int, presentationData: ChatPresentationData, context: AccountContext) { + self.index = index + self.isComments = isComments + self.count = count + self.presentationData = presentationData + self.header = ChatMessageDateHeader(timestamp: index.timestamp, scheduled: false, presentationData: presentationData, context: context) + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = ChatReplyCountItemNode() + node.layoutForParams(params, item: self, previousItem: previousItem, nextItem: nextItem) + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in }) + }) + } + } + } + + public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? ChatReplyCountItemNode { + let nodeLayout = nodeValue.asyncLayout() + + async { + let dateAtBottom = !chatItemsHaveCommonDateHeader(self, nextItem) + + let (layout, apply) = nodeLayout(self, params, dateAtBottom) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } else { + assertionFailure() + } + } + } +} + +class ChatReplyCountItemNode: ListViewItemNode { + var item: ChatReplyCountItem? + let backgroundNode: ASImageNode + let labelNode: TextNode + + private var theme: ChatPresentationThemeData? + + private let layoutConstants = ChatMessageItemLayoutConstants.default + + init() { + self.backgroundNode = ASImageNode() + self.backgroundNode.isLayerBacked = true + self.backgroundNode.displayWithoutProcessing = true + + self.labelNode = TextNode() + self.labelNode.isUserInteractionEnabled = false + + super.init(layerBacked: false, dynamicBounce: true, rotated: true) + + self.addSubnode(self.backgroundNode) + + self.addSubnode(self.labelNode) + + self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) + + self.scrollPositioningInsets = UIEdgeInsets(top: 5.0, left: 0.0, bottom: 6.0, right: 0.0) + self.canBeUsedAsScrollToItemAnchor = false + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + super.animateInsertion(currentTimestamp, duration: duration, short: short) + + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + override func animateAdded(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { + if let item = item as? ChatReplyCountItem { + let dateAtBottom = !chatItemsHaveCommonDateHeader(item, nextItem) + let (layout, apply) = self.asyncLayout()(item, params, dateAtBottom) + apply() + self.contentSize = layout.contentSize + self.insets = layout.insets + } + } + + func asyncLayout() -> (_ item: ChatReplyCountItem, _ params: ListViewItemLayoutParams, _ dateAtBottom: Bool) -> (ListViewItemNodeLayout, () -> Void) { + let labelLayout = TextNode.asyncLayout(self.labelNode) + let layoutConstants = self.layoutConstants + let currentTheme = self.theme + + return { item, params, dateAtBottom in + var updatedBackgroundImage: UIImage? + if currentTheme != item.presentationData.theme { + updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.theme) + } + + let text: String + //TODO:localize + if item.isComments { + if item.count == 0 { + text = "Comments" + } else if item.count == 1 { + text = "1 Comment" + } else { + text = "\(item.count) Comments" + } + } else { + if item.count == 0 { + text = "Replies" + } else if item.count == 1 { + text = "1 Reply" + } else { + text = "\(item.count) Replies" + } + } + + let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let backgroundSize = CGSize(width: params.width, height: 25.0) + + return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { [weak self] in + if let strongSelf = self { + strongSelf.item = item + strongSelf.theme = item.presentationData.theme + + if let updatedBackgroundImage = updatedBackgroundImage { + strongSelf.backgroundNode.image = updatedBackgroundImage + } + + let _ = apply() + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize) + strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - size.size.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - size.size.height) / 2.0)), size: size.size) + } + }) + } + } + + override public func header() -> ListViewItemHeader? { + if let item = self.item { + return item.header + } else { + return nil + } + } + + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { + super.animateRemoved(currentTimestamp, duration: duration) + + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + } +} diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 99d6435721..8988126ecb 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -792,9 +792,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) { placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder - } else if case .replyThread = interfaceState.chatLocation { + } else if case let .replyThread(_, isChannelPost, _) = interfaceState.chatLocation { //TODO:localize - placeholder = "Reply" + if isChannelPost { + placeholder = "Comment" + } else { + placeholder = "Reply" + } } else { placeholder = interfaceState.strings.Conversation_InputTextPlaceholder } diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 4dafec51f7..dd226e10fe 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -24,7 +24,7 @@ enum ChatTitleContent { } case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool) - case replyThread(type: ReplyThreadType, count: Int) + case replyThread(type: ReplyThreadType, text: String) case group([Peer]) case custom(String) } @@ -141,27 +141,20 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } } - case let .replyThread(type, count): + case let .replyThread(type, text): //TODO:localize let typeText: String - switch type { - case .comments: - if count == 0 { + if !text.isEmpty { + typeText = text + } else { + switch type { + case .comments: typeText = "Comments" - } else if count == 1 { - typeText = "1 Comment" - } else { - typeText = "\(count) Comments" - } - case .replies: - if count == 0 { + case .replies: typeText = "Replies" - } else if count == 1 { - typeText = "1 Reply" - } else { - typeText = "\(count) Replies" } } + string = NSAttributedString(string: typeText, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor) isEnabled = false case .group: