Thread improvements

This commit is contained in:
Ali 2022-11-11 22:34:06 +04:00
parent b35ede7462
commit 29a3c18d07
42 changed files with 675 additions and 193 deletions

View File

@ -490,7 +490,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
}
}
func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool, chatListController: ChatListControllerImpl?, joined: Bool) -> Signal<[ContextMenuItem], NoError> {
func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool?, chatListController: ChatListControllerImpl?, joined: Bool) -> Signal<[ContextMenuItem], NoError> {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let strings = presentationData.strings
@ -512,7 +512,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
var items: [ContextMenuItem] = []
if channel.hasPermission(.manageTopics) {
if let isPinned = isPinned, channel.hasPermission(.manageTopics) {
items.append(.action(ContextMenuActionItem(text: isPinned ? presentationData.strings.ChatList_Context_Unpin : presentationData.strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
f(.default)

View File

@ -420,7 +420,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
/*self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
self.navigationBar?.allowsCustomTransition = { [weak self] in
guard let strongSelf = self else {
return false
@ -429,7 +429,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return false
}
return true
}*/
}
}
switch self.location {
@ -516,7 +516,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if stateAndFilterId.state.editing && stateAndFilterId.state.selectedThreadIds.count > 0 {
chatTitleView.titleContent = .custom(strongSelf.presentationData.strings.ChatList_SelectedTopics(Int32(stateAndFilterId.state.selectedThreadIds.count)), nil, false)
} else {
chatTitleView.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil)
chatTitleView.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil, customMessageCount: nil)
}
strongSelf.infoReady.set(.single(true))
@ -1639,7 +1639,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self?.toolbarActionSelected(action: action)
}
self.chatListDisplayNode.containerNode.activateChatPreview = { [weak self] item, node, gesture, location in
self.chatListDisplayNode.containerNode.activateChatPreview = { [weak self] item, threadId, node, gesture, location in
guard let strongSelf = self else {
gesture?.cancel()
return
@ -1664,10 +1664,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
switch item.index {
case .chatList:
if case let .channel(channel) = peer.peer, channel.flags.contains(.isForum) {
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
if let threadId = threadId {
let source: ContextContentSource
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
messageId: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
)), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
} else {
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
} else {
let source: ContextContentSource
if let location = location {

View File

@ -183,7 +183,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let timestamp1: Int32 = 100000
let peers: [EnginePeer.Id: EnginePeer] = [:]
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })
@ -575,8 +575,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
itemNode.listNode.contentScrollingEnded = { [weak self] listView in
return self?.contentScrollingEnded?(listView) ?? false
}
itemNode.listNode.activateChatPreview = { [weak self] item, sourceNode, gesture, location in
self?.activateChatPreview?(item, sourceNode, gesture, location)
itemNode.listNode.activateChatPreview = { [weak self] item, threadId, sourceNode, gesture, location in
self?.activateChatPreview?(item, threadId, sourceNode, gesture, location)
}
itemNode.listNode.addedVisibleChatsWithPeerIds = { [weak self] ids in
self?.addedVisibleChatsWithPeerIds?(ids)
@ -626,7 +626,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
var contentScrollingEnded: ((ListView) -> Bool)?
var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
var didBeginSelectingChats: (() -> Void)?
var displayFilterLimit: (() -> Void)?

View File

@ -1847,7 +1847,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, toggleArchivedFolderHiddenByDefault: {
}, toggleThreadsSelection: { _, _ in
}, hidePsa: { _ in
}, activateChatPreview: { item, node, gesture, location in
}, activateChatPreview: { item, _, node, gesture, location in
guard let peerContextAction = interaction.peerContextAction else {
gesture?.cancel()
return
@ -3067,7 +3067,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[peer1.id] = peer1
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })

View File

@ -1071,11 +1071,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
})
self.contextContainer.activated = { [weak self] gesture, _ in
self.contextContainer.activated = { [weak self] gesture, location in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.interaction.activateChatPreview(item, strongSelf.contextContainer, gesture, nil)
var threadId: Int64?
if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view {
if case let .peer(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, topForumTopicItems) = item.content, let topicItem = topForumTopicItems.first {
threadId = topicItem.id
}
}
item.interaction.activateChatPreview(item, threadId, strongSelf.contextContainer, gesture, nil)
}
}
@ -1088,7 +1094,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
guard let item = self.item else {
return
}
item.interaction.activateChatPreview(item, self.contextContainer, nil, point)
item.interaction.activateChatPreview(item, nil, self.contextContainer, nil, point)
}
func setupItem(item: ChatListItem, synchronousLoads: Bool) {
@ -1239,6 +1245,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.highlightedBackgroundNode.layer.removeAllAnimations()
transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: highlightProgress)
if let compoundHighlightingNode = self.compoundHighlightingNode {
transition.updateAlpha(layer: compoundHighlightingNode.layer, alpha: 0.0)
}
if let item = self.item, case .chatList = item.index {
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil, transition: transition)
}
@ -1253,6 +1263,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
})
}
if let compoundHighlightingNode = self.compoundHighlightingNode {
transition.updateAlpha(layer: compoundHighlightingNode.layer, alpha: self.authorNode.alpha)
}
if let item = self.item, case let .chatList(index) = item.index {
let onlineIcon: UIImage?
if index.pinningIndex != nil {
@ -1312,7 +1326,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let contentPeer: ContentPeer
let combinedReadState: EnginePeerReadCounters?
let unreadCount: (count: Int32, unread: Bool, muted: Bool, mutedCount: Int32?)
let unreadCount: (count: Int32, unread: Bool, muted: Bool, mutedCount: Int32?, isProvisonal: Bool)
let isRemovedFromTotalUnreadCount: Bool
let peerPresence: EnginePeer.Presence?
let draftState: ChatListItemContent.DraftState?
@ -1336,9 +1350,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue
if let combinedReadState = combinedReadState, promoInfoValue == nil && !ignoreUnreadBadge {
unreadCount = (combinedReadState.count, combinedReadState.isUnread, isRemovedFromTotalUnreadCountValue || combinedReadState.isMuted, nil)
unreadCount = (combinedReadState.count, combinedReadState.isUnread, isRemovedFromTotalUnreadCountValue || combinedReadState.isMuted, nil, !combinedReadState.hasEverRead)
} else {
unreadCount = (0, false, false, nil)
unreadCount = (0, false, false, nil, false)
}
if let _ = promoInfoValue {
isRemovedFromTotalUnreadCount = false
@ -1393,7 +1407,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
inputActivities = nil
isPeerGroup = true
groupHiddenByDefault = hiddenByDefault
unreadCount = (Int32(unreadCountValue), unreadCountValue != 0, true, nil)
unreadCount = (Int32(unreadCountValue), unreadCountValue != 0, true, nil, false)
peerPresence = nil
promoInfo = nil
displayAsMessage = false
@ -1449,6 +1463,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let sizeAndApply = reorderControlLayout(item.presentationData.theme)
reorderControlSizeAndApply = sizeAndApply
reorderInset = sizeAndApply.0
} else if case let .forum(pinnedIndex, _, _, _, _) = item.index, case .index = pinnedIndex {
if case let .chat(itemPeer) = contentPeer, case let .channel(channel) = itemPeer.peer {
let canPin = channel.flags.contains(.isCreator) || channel.hasPermission(.pinMessages)
if canPin {
let sizeAndApply = reorderControlLayout(item.presentationData.theme)
reorderControlSizeAndApply = sizeAndApply
reorderInset = sizeAndApply.0
}
}
}
} else {
editingOffset = 0.0
@ -1851,11 +1874,21 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
let badgeTextColor: UIColor
if unreadCount.muted {
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundInactive(item.presentationData.theme, diameter: badgeDiameter)
badgeTextColor = theme.unreadBadgeInactiveTextColor
if unreadCount.isProvisonal {
badgeTextColor = theme.unreadBadgeInactiveBackgroundColor
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundInactiveProvisional(item.presentationData.theme, diameter: badgeDiameter)
} else {
badgeTextColor = theme.unreadBadgeInactiveTextColor
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundInactive(item.presentationData.theme, diameter: badgeDiameter)
}
} else {
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundActive(item.presentationData.theme, diameter: badgeDiameter)
badgeTextColor = theme.unreadBadgeActiveTextColor
if unreadCount.isProvisonal {
badgeTextColor = theme.unreadBadgeActiveBackgroundColor
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundActiveProvisional(item.presentationData.theme, diameter: badgeDiameter)
} else {
badgeTextColor = theme.unreadBadgeActiveTextColor
currentBadgeBackgroundImage = PresentationResourcesChatList.badgeBackgroundActive(item.presentationData.theme, diameter: badgeDiameter)
}
}
let unreadCountText = compactNumericCountString(Int(unreadCount.count), decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
if unreadCount.count > 0 {
@ -2027,9 +2060,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let isSearching = item.interaction.searchTextHighightState != nil
var isFirstForumThreadSelectable = false
var forumThreads: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32)] = []
if forumThread != nil || !topForumTopicItems.isEmpty {
if let forumThread = forumThread {
isFirstForumThreadSelectable = forumThread.isUnread
forumThreads.append((id: forumThread.id, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor))
}
for item in topForumTopicItems {
@ -2063,7 +2098,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var textMaxWidth = rawContentWidth - badgeSize
var textArrowImage: UIImage?
if !forumThreads.isEmpty {
if isFirstForumThreadSelectable {
textArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
textMaxWidth -= 18.0
}
@ -2156,7 +2191,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let threadInfo {
isClosed = threadInfo.isClosed
}
peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canPin: channel.flags.contains(.isCreator) || channel.adminRights != nil, canOpenClose: canOpenClose, canDelete: canDelete)
peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canPin: channel.flags.contains(.isCreator) || channel.hasPermission(.pinMessages), canOpenClose: canOpenClose, canDelete: canDelete)
peerLeftRevealOptions = []
} else {
peerRevealOptions = []
@ -2395,7 +2430,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
attemptSynchronous: synchronousLoads
))
let topForumTopicRect = authorApply()
var topForumTopicRect = authorApply()
if !isFirstForumThreadSelectable {
topForumTopicRect = nil
}
let _ = titleApply()
let _ = badgeApply(animateBadges, !isMuted)
let _ = mentionBadgeApply(animateBadges, true)
@ -2501,24 +2540,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.height.isZero ? 0.0 : (authorLayout.height - 3.0))), size: textLayout.size)
strongSelf.textNode.textNode.frame = textNodeFrame
if let textArrowImage = textArrowImage, !isSearching {
let textArrowNode: ASImageNode
if let current = strongSelf.textArrowNode {
textArrowNode = current
} else {
textArrowNode = ASImageNode()
strongSelf.textArrowNode = textArrowNode
strongSelf.textNode.textNode.addSubnode(textArrowNode)
}
textArrowNode.image = textArrowImage
let arrowScale: CGFloat = 0.75
let textArrowSize = CGSize(width: floor(textArrowImage.size.width * arrowScale), height: floor(textArrowImage.size.height * arrowScale))
textArrowNode.frame = CGRect(origin: CGPoint(x: textNodeFrame.width - 3.0, y: floorToScreenPixels((textNodeFrame.height - textArrowSize.height) / 2.0)), size: textArrowSize)
} else if let textArrowNode = strongSelf.textArrowNode {
strongSelf.textArrowNode = nil
textArrowNode.removeFromSupernode()
}
if let topForumTopicRect, !isSearching {
let compoundHighlightingNode: LinkHighlightingNode
if let current = strongSelf.compoundHighlightingNode {
@ -2588,6 +2609,24 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
], color: theme.pinnedItemBackgroundColor.mixedWith(theme.unreadBadgeInactiveBackgroundColor, alpha: 0.1))
compoundTextButtonNode.frame = compoundHighlightingNode.frame
if let textArrowImage = textArrowImage {
let textArrowNode: ASImageNode
if let current = strongSelf.textArrowNode {
textArrowNode = current
} else {
textArrowNode = ASImageNode()
strongSelf.textArrowNode = textArrowNode
compoundTextButtonNode.addSubnode(textArrowNode)
}
textArrowNode.image = textArrowImage
let arrowScale: CGFloat = 0.75
let textArrowSize = CGSize(width: floor(textArrowImage.size.width * arrowScale), height: floor(textArrowImage.size.height * arrowScale))
textArrowNode.frame = CGRect(origin: CGPoint(x: finalBottomRect.maxX - 0.0 - textArrowSize.width, y: finalBottomRect.minY + floorToScreenPixels((finalBottomRect.height - textArrowSize.height) / 2.0)), size: textArrowSize)
} else if let textArrowNode = strongSelf.textArrowNode {
strongSelf.textArrowNode = nil
textArrowNode.removeFromSupernode()
}
} else {
if let compoundHighlightingNode = strongSelf.compoundHighlightingNode {
strongSelf.compoundHighlightingNode = nil
@ -2597,6 +2636,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.compoundTextButtonNode = nil
compoundTextButtonNode.removeFromSupernode()
}
if let textArrowNode = strongSelf.textArrowNode {
strongSelf.textArrowNode = nil
textArrowNode.removeFromSupernode()
}
}
if !textLayout.spoilers.isEmpty {

View File

@ -77,7 +77,7 @@ public final class ChatListNodeInteraction {
let toggleArchivedFolderHiddenByDefault: () -> Void
let toggleThreadsSelection: ([Int64], Bool) -> Void
let hidePsa: (EnginePeer.Id) -> Void
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
let activateChatPreview: (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
let present: (ViewController) -> Void
let openForumThread: (EnginePeer.Id, Int64) -> Void
@ -113,7 +113,7 @@ public final class ChatListNodeInteraction {
toggleArchivedFolderHiddenByDefault: @escaping () -> Void,
toggleThreadsSelection: @escaping ([Int64], Bool) -> Void,
hidePsa: @escaping (EnginePeer.Id) -> Void,
activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void,
activateChatPreview: @escaping (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void,
present: @escaping (ViewController) -> Void,
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void
) {
@ -783,7 +783,7 @@ public final class ChatListNode: ListView {
public var push: ((ViewController) -> Void)?
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
public var hidePsa: ((EnginePeer.Id) -> Void)?
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
public var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
private var theme: PresentationTheme
@ -1133,12 +1133,12 @@ public final class ChatListNode: ListView {
}
}, hidePsa: { [weak self] id in
self?.hidePsa?(id)
}, activateChatPreview: { [weak self] item, node, gesture, location in
}, activateChatPreview: { [weak self] item, threadId, node, gesture, location in
guard let strongSelf = self else {
return
}
if let activateChatPreview = strongSelf.activateChatPreview {
activateChatPreview(item, node, gesture, location)
activateChatPreview(item, threadId, node, gesture, location)
} else {
gesture?.cancel()
}

View File

@ -187,12 +187,25 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
]
)
)
let readStateKey: PostboxViewKey = .combinedReadState(peerId: peerId, handleThreads: false)
var isFirst = false
return account.postbox.combinedView(keys: [viewKey])
return account.postbox.combinedView(keys: [viewKey, readStateKey])
|> map { views -> ChatListNodeViewUpdate in
guard let view = views.views[viewKey] as? MessageHistoryThreadIndexView else {
preconditionFailure()
}
guard let readStateView = views.views[readStateKey] as? CombinedReadStateView else {
preconditionFailure()
}
var maxReadId: Int32 = 0
if let state = readStateView.state?.states.first(where: { $0.0 == Namespaces.Message.Cloud }) {
if case let .idBased(maxIncomingReadId, _, _, _, _) = state.1 {
maxReadId = maxIncomingReadId
}
}
var items: [EngineChatList.Item] = []
for item in view.items {
@ -243,7 +256,12 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
pinnedIndex = .none
}
let readCounters = EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: data.maxOutgoingReadId, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))]), isMuted: false)
var topicMaxIncomingReadId = data.maxIncomingReadId
if data.maxIncomingReadId == 0 && maxReadId != 0 && Int64(maxReadId) <= item.id {
topicMaxIncomingReadId = max(topicMaxIncomingReadId, maxReadId)
}
let readCounters = EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: topicMaxIncomingReadId, maxOutgoingReadId: data.maxOutgoingReadId, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))]), isMuted: false)
var draft: EngineChatList.Draft?
if let embeddedState = item.embeddedInterfaceState, let _ = embeddedState.overrideChatTimestamp {

View File

@ -483,7 +483,7 @@ open class NavigationBar: ASDisplayNode {
}
}
private let stripeNode: ASDisplayNode
public let stripeNode: ASDisplayNode
private let clippingNode: SparseNode
private let buttonsContainerNode: ASDisplayNode

View File

@ -467,33 +467,148 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
if layer is AVSampleBufferDisplayLayer {
return nil
}
let view = CALayer()
view.isHidden = layer.isHidden
view.opacity = layer.opacity
view.contents = layer.contents
view.contentsRect = layer.contentsRect
view.contentsScale = layer.contentsScale
view.contentsCenter = layer.contentsCenter
view.contentsGravity = layer.contentsGravity
view.masksToBounds = layer.masksToBounds
view.cornerRadius = layer.cornerRadius
view.backgroundColor = layer.backgroundColor
view.layerTintColor = layer.layerTintColor
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.transform = sublayer.transform
subtree.position = sublayer.position
subtree.bounds = sublayer.bounds
subtree.anchorPoint = sublayer.anchorPoint
view.addSublayer(subtree)
} else {
return nil
if let layer = layer as? CAShapeLayer {
let view = CAShapeLayer()
view.isHidden = layer.isHidden
view.opacity = layer.opacity
view.contents = layer.contents
view.contentsRect = layer.contentsRect
view.contentsScale = layer.contentsScale
view.contentsCenter = layer.contentsCenter
view.contentsGravity = layer.contentsGravity
view.masksToBounds = layer.masksToBounds
view.cornerRadius = layer.cornerRadius
view.backgroundColor = layer.backgroundColor
view.layerTintColor = layer.layerTintColor
/*
open var path: CGPath?
/* The color to fill the path, or nil for no fill. Defaults to opaque
* black. Animatable. */
open var fillColor: CGColor?
/* The fill rule used when filling the path. Options are `non-zero' and
* `even-odd'. Defaults to `non-zero'. */
open var fillRule: CAShapeLayerFillRule
/* The color to fill the path's stroked outline, or nil for no stroking.
* Defaults to nil. Animatable. */
open var strokeColor: CGColor?
/* These values define the subregion of the path used to draw the
* stroked outline. The values must be in the range [0,1] with zero
* representing the start of the path and one the end. Values in
* between zero and one are interpolated linearly along the path
* length. strokeStart defaults to zero and strokeEnd to one. Both are
* animatable. */
open var strokeStart: CGFloat
open var strokeEnd: CGFloat
/* The line width used when stroking the path. Defaults to one.
* Animatable. */
open var lineWidth: CGFloat
/* The miter limit used when stroking the path. Defaults to ten.
* Animatable. */
open var miterLimit: CGFloat
/* The cap style used when stroking the path. Options are `butt', `round'
* and `square'. Defaults to `butt'. */
open var lineCap: CAShapeLayerLineCap
/* The join style used when stroking the path. Options are `miter', `round'
* and `bevel'. Defaults to `miter'. */
open var lineJoin: CAShapeLayerLineJoin
/* The phase of the dashing pattern applied when creating the stroke.
* Defaults to zero. Animatable. */
open var lineDashPhase: CGFloat
/* The dash pattern (an array of NSNumbers) applied when creating the
* stroked version of the path. Defaults to nil. */
open var lineDashPattern: [NSNumber]?
*/
view.path = layer.path
view.fillColor = layer.fillColor
view.fillRule = layer.fillRule
view.strokeColor = layer.strokeColor
view.strokeStart = layer.strokeStart
view.strokeEnd = layer.strokeEnd
view.lineWidth = layer.lineWidth
view.miterLimit = layer.miterLimit
view.lineCap = layer.lineCap
view.lineJoin = layer.lineJoin
view.lineDashPhase = layer.lineDashPhase
view.lineDashPattern = layer.lineDashPattern
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.transform = sublayer.transform
subtree.position = sublayer.position
subtree.bounds = sublayer.bounds
subtree.anchorPoint = sublayer.anchorPoint
view.addSublayer(subtree)
} else {
return nil
}
}
}
return view
} else {
let view = CALayer()
view.isHidden = layer.isHidden
view.opacity = layer.opacity
view.contents = layer.contents
view.contentsRect = layer.contentsRect
view.contentsScale = layer.contentsScale
view.contentsCenter = layer.contentsCenter
view.contentsGravity = layer.contentsGravity
view.masksToBounds = layer.masksToBounds
view.cornerRadius = layer.cornerRadius
view.backgroundColor = layer.backgroundColor
view.layerTintColor = layer.layerTintColor
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.transform = sublayer.transform
subtree.position = sublayer.position
subtree.bounds = sublayer.bounds
subtree.anchorPoint = sublayer.anchorPoint
view.addSublayer(subtree)
} else {
return nil
}
}
}
return view
}
return view
}
private func makeLayerSubtreeSnapshotAsView(layer: CALayer) -> UIView? {

View File

@ -86,7 +86,7 @@ public final class HashtagSearchController: TelegramBaseController {
}, toggleArchivedFolderHiddenByDefault: {
}, toggleThreadsSelection: { _, _ in
}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in

View File

@ -376,7 +376,7 @@ final class ChatListIndexTable: Table {
} else {
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
}
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: previousCount, markedUnread: false))])
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: previousCount, markedUnread: false))])
}
} else {
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
@ -386,7 +386,7 @@ final class ChatListIndexTable: Table {
} else {
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
}
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: previousCount, markedUnread: false))])
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: previousCount, markedUnread: false))])
} else {
initialReadState = alteredInitialPeerCombinedReadStates[peerId] ?? postbox.readStateTable.getCombinedState(peerId)
}
@ -395,7 +395,7 @@ final class ChatListIndexTable: Table {
let currentReadState: CombinedPeerReadState?
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
let count = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
currentReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
currentReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
} else {
currentReadState = postbox.readStateTable.getCombinedState(peerId)
}
@ -628,7 +628,7 @@ final class ChatListIndexTable: Table {
let combinedState: CombinedPeerReadState?
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
} else {
combinedState = postbox.readStateTable.getCombinedState(peerId)
}
@ -720,7 +720,7 @@ final class ChatListIndexTable: Table {
let combinedState: CombinedPeerReadState?
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 1, maxKnownId: 0, count: count, markedUnread: false))])
} else {
combinedState = postbox.readStateTable.getCombinedState(peerId)
}

View File

@ -706,7 +706,7 @@ final class MutableChatListView {
isMuted = !summary.hasUnmutedUnread
}
}
readState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
readState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
} else {
readState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId).flatMap { state -> ChatListViewReadState in
return ChatListViewReadState(state: state, isMuted: false)

View File

@ -927,7 +927,7 @@ private final class ChatListViewSpaceState {
}
}
updatedReadState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
updatedReadState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
} else {
updatedReadState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId).flatMap { state in
return ChatListViewReadState(state: state, isMuted: false)
@ -1551,7 +1551,7 @@ struct ChatListViewState {
}
}
readState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
readState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted)
} else {
readState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId).flatMap { state in
return ChatListViewReadState(state: state, isMuted: false)

View File

@ -4,11 +4,13 @@ public struct InvalidatedMessageHistoryTagsSummaryKey: Equatable, Hashable {
public let peerId: PeerId
public let namespace: MessageId.Namespace
public let tagMask: MessageTags
public let threadId: Int64?
public init(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
public init(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags, threadId: Int64?) {
self.peerId = peerId
self.namespace = namespace
self.tagMask = tagMask
self.threadId = threadId
}
}
@ -28,11 +30,20 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table {
}
private func key(_ key: InvalidatedMessageHistoryTagsSummaryKey) -> ValueBoxKey {
let result = ValueBoxKey(length: 4 + 4 + 8)
result.setUInt32(0, value: key.tagMask.rawValue)
result.setInt32(4, value: key.namespace)
result.setInt64(4 + 4, value: key.peerId.toInt64())
return result
if let threadId = key.threadId {
let result = ValueBoxKey(length: 4 + 4 + 8 + 8)
result.setUInt32(0, value: key.tagMask.rawValue)
result.setInt32(4, value: key.namespace)
result.setInt64(4 + 4, value: key.peerId.toInt64())
result.setInt64(4 + 4 + 8, value: threadId)
return result
} else {
let result = ValueBoxKey(length: 4 + 4 + 8)
result.setUInt32(0, value: key.tagMask.rawValue)
result.setInt32(4, value: key.namespace)
result.setInt64(4 + 4, value: key.peerId.toInt64())
return result
}
}
private func lowerBound(tagMask: MessageTags, namespace: MessageId.Namespace) -> ValueBoxKey {
@ -51,7 +62,13 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table {
self.valueBox.range(self.table, start: self.lowerBound(tagMask: tagMask, namespace: namespace), end: self.upperBound(tagMask: tagMask, namespace: namespace), values: { key, value in
var version: Int32 = 0
value.read(&version, offset: 0, length: 4)
entries.append(InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(4), tagMask: MessageTags(rawValue: key.getUInt32(0))), version: version))
var threadId: Int64?
if key.length >= 4 + 4 + 8 + 8 {
threadId = key.getInt64(4 + 4 + 8)
}
entries.append(InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(4), tagMask: MessageTags(rawValue: key.getUInt32(0)), threadId: threadId), version: version))
return true
}, limit: 0)
return entries

View File

@ -288,6 +288,8 @@ final class MessageHistoryTable: Table {
}
if let threadId = message.threadId {
self.threadsTable.add(threadId: threadId, index: message.index)
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
let globalTags = message.globalTags.rawValue
if globalTags != 0 {
@ -1352,6 +1354,8 @@ final class MessageHistoryTable: Table {
}
if let threadId = message.threadId {
self.threadsTable.remove(threadId: threadId, index: index)
self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
for tag in message.globalTags {
self.globalTagsTable.remove(tag, index: index)
@ -1569,9 +1573,14 @@ final class MessageHistoryTable: Table {
if previousMessage.threadId != message.threadId || index != message.index {
if let threadId = previousMessage.threadId {
self.threadsTable.remove(threadId: threadId, index: index)
self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
if let threadId = message.threadId {
self.threadsTable.add(threadId: threadId, index: message.index)
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
}

View File

@ -134,12 +134,12 @@ class MessageHistoryTagsSummaryTable: Table {
if !isNewlyAdded || !current.range.contains(id) {
self.set(key, summary: current.withAddedCount(1), updatedSummaries: &updatedSummaries)
if current.range.maxId == 0 {
self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag), operations: &invalidateSummaries)
self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId), operations: &invalidateSummaries)
}
}
} else {
self.set(key, summary: MessageHistoryTagNamespaceSummary(version: 0, count: 1, range: MessageHistoryTagNamespaceCountValidityRange(maxId: 0)), updatedSummaries: &updatedSummaries)
self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag), operations: &invalidateSummaries)
self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId), operations: &invalidateSummaries)
}
}

View File

@ -66,13 +66,11 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView {
if !self.isLoading {
let pinnedThreadIds = postbox.messageHistoryThreadPinnedTable.get(peerId: self.peerId)
var nextPinnedIndex = 0
for item in postbox.messageHistoryThreadIndexTable.getAll(peerId: self.peerId) {
var pinnedIndex: Int?
if pinnedThreadIds.contains(item.threadId) {
pinnedIndex = nextPinnedIndex
nextPinnedIndex += 1
if let index = pinnedThreadIds.firstIndex(of: item.threadId) {
pinnedIndex = index
}
var tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = [:]
@ -131,7 +129,7 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView {
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
var updated = false
if transaction.updatedMessageThreadPeerIds.contains(self.peerId) || transaction.updatedPinnedThreads.contains(self.peerId) || transaction.updatedPeerThreadCombinedStates.contains(self.peerId) || transaction.currentUpdatedMessageTagSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedMessageActionsSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedPeerChatListEmbeddedStates.contains(self.peerId) || transaction.currentUpdatedPeerNotificationSettings[self.peerId] != nil {
if transaction.updatedMessageThreadPeerIds.contains(self.peerId) || transaction.updatedPinnedThreads.contains(self.peerId) || transaction.updatedPeerThreadCombinedStates.contains(self.peerId) || transaction.currentUpdatedMessageTagSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedMessageActionsSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedPeerChatListEmbeddedStates.contains(self.peerId) || transaction.currentUpdatedPeerNotificationSettings[self.peerId] != nil || transaction.updatedPinnedThreads.contains(self.peerId) {
self.reload(postbox: postbox)
updated = true
}

View File

@ -920,6 +920,9 @@ final class MutableMessageHistoryView {
if let hole = loadedSample.hole {
let direction: MessageHistoryViewRelativeHoleDirection
if let endId = hole.endId {
if self.tag == nil, hole.namespace == 0 {
assert(true)
}
direction = .range(start: MessageId(peerId: hole.peerId, namespace: hole.namespace, id: hole.startId), end: MessageId(peerId: hole.peerId, namespace: hole.namespace, id: endId))
} else {
direction = .aroundId(MessageId(peerId: hole.peerId, namespace: hole.namespace, id: hole.startId))

View File

@ -533,7 +533,13 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace:
case let .index(index):
if index.id.peerId == space.peerId && index.id.namespace == space.namespace {
if indices.contains(Int(index.id.id)) {
if tag == nil && space.namespace == 0 {
assert(true)
}
return ([MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound()], SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, threadId: threadId, indices: indices, startId: index.id.id, endId: nil))
} else {
//print("\(indices.rangeView.map({ $0 })) do not contain \(index.id.id)")
}
}
}
@ -545,6 +551,11 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace:
case .upperBound, .index:
holeBounds = (Int32.max - 1, 1)
}
if tag == nil && space.namespace == 0 {
assert(true)
}
if case let .index(index) = anchor, index.id.peerId == space.peerId {
return ([MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound()], SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, threadId: threadId, indices: indices, startId: holeBounds.startId, endId: holeBounds.endId))
} else {
@ -585,6 +596,11 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace:
} else {
holeStartIndex = indices[indices.endIndex]
}
if tag == nil && space.namespace == 0 {
assert(true)
}
lowerOrAtAnchorHole = (items.lowerOrAtAnchor.count - i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, threadId: threadId, indices: indices, startId: Int32(holeStartIndex), endId: 1))
if i == -1 {
@ -653,6 +669,11 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace:
} else {
holeStartIndex = indices[indices.startIndex]
}
if tag == nil && space.namespace == 0 {
assert(true)
}
higherThanAnchorHole = (i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, threadId: threadId, indices: indices, startId: Int32(holeStartIndex), endId: Int32.max - 1))
if i == items.higherThanAnchor.count {

View File

@ -1066,9 +1066,9 @@ public final class Transaction {
return view!
}
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) {
assert(!self.disposed)
self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, namespace: namespace, tagMask: tagMask)
self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, threadId: threadId, namespace: namespace, tagMask: tagMask)
}
public func removeInvalidatedMessageHistoryTagsSummaryEntry(_ entry: InvalidatedMessageHistoryTagsSummaryEntry) {
@ -1560,7 +1560,7 @@ final class PostboxImpl {
self.pendingMessageActionsTable = PendingMessageActionsTable(valueBox: self.valueBox, table: PendingMessageActionsTable.tableSpec(46), useCaches: useCaches, metadataTable: self.pendingMessageActionsMetadataTable)
self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, table: MessageHistoryTagsTable.tableSpec(12), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
self.messageHistoryThreadsTable = MessageHistoryThreadsTable(valueBox: self.valueBox, table: MessageHistoryThreadsTable.tableSpec(62), useCaches: useCaches)
self.peerThreadCombinedStateTable = PeerThreadCombinedStateTable(valueBox: self.valueBox, table: PeerThreadCombinedStateTable.tableSpec(74), useCaches: useCaches)
self.peerThreadCombinedStateTable = PeerThreadCombinedStateTable(valueBox: self.valueBox, table: PeerThreadCombinedStateTable.tableSpec(77), useCaches: useCaches)
self.peerThreadsSummaryTable = PeerThreadsSummaryTable(valueBox: self.valueBox, table: PeerThreadsSummaryTable.tableSpec(75), useCaches: useCaches, seedConfiguration: self.seedConfiguration)
self.messageHistoryThreadTagsTable = MessageHistoryThreadTagsTable(valueBox: self.valueBox, table: MessageHistoryThreadTagsTable.tableSpec(71), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
self.messageHistoryThreadHoleIndexTable = MessageHistoryThreadHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadHoleIndexTable.tableSpec(63), useCaches: useCaches, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration)
@ -3634,8 +3634,8 @@ final class PostboxImpl {
}
}
fileprivate func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
self.invalidatedMessageHistoryTagsSummaryTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask), operations: &self.currentInvalidateMessageTagSummaries)
fileprivate func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) {
self.invalidatedMessageHistoryTagsSummaryTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId), operations: &self.currentInvalidateMessageTagSummaries)
}
fileprivate func removeInvalidatedMessageHistoryTagsSummaryEntry(_ entry: InvalidatedMessageHistoryTagsSummaryEntry) {

View File

@ -220,7 +220,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })

View File

@ -840,7 +840,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in })

View File

@ -364,7 +364,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in })

View File

@ -1091,6 +1091,7 @@ public class Account {
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedReadReactionActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedSynchronizeMessageHistoryTagSummaries(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenReactionsOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())

View File

@ -459,6 +459,10 @@ extension StoreMessage {
attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId))
}
}
if threadId == nil && peerId.namespace == Namespaces.Peer.CloudChannel {
threadId = 1
}
var forwardInfo: StoreMessageForwardInfo?
if let fwdFrom = fwdFrom {
@ -708,6 +712,10 @@ extension StoreMessage {
break
}
}
if threadId == nil && peerId.namespace == Namespaces.Peer.CloudChannel {
threadId = 1
}
if (flags & (1 << 17)) != 0 {
attributes.append(ContentRequiresValidationMessageAttribute())

View File

@ -569,6 +569,10 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
}
}
}
if threadId == nil, let channel = transaction.getPeer(peerId) as? TelegramChannel, channel.flags.contains(.isForum) {
threadId = 1
}
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList))
case let .forward(source, threadId, grouping, requestedAttributes, _):
@ -798,6 +802,10 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
if peerId.namespace == Namespaces.Peer.SecretChat {
augmentedMediaList = augmentedMediaList.map(convertForwardedMediaForSecretChat)
}
if threadId == nil, let channel = transaction.getPeer(peerId) as? TelegramChannel, channel.flags.contains(.isForum) {
threadId = 1
}
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList))
}

View File

@ -3063,27 +3063,59 @@ func replayFinalState(
var wasOperationScheduledMessageIds: [MessageId] = []
var readInboxCloudMessageIds: [PeerId: Int32] = [:]
var addedOperationIncomingMessageIds: [MessageId] = []
for operation in finalState.state.operations {
switch operation {
case let .AddMessages(messages, location):
if case .UpperHistoryBlock = location {
for message in messages {
if case let .Id(id) = message.id {
if message.flags.contains(.Incoming) {
addedOperationIncomingMessageIds.append(id)
if let authorId = message.authorId {
recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps)
}
}
if message.flags.contains(.WasScheduled) {
wasOperationScheduledMessageIds.append(id)
case let .AddMessages(messages, location):
if case .UpperHistoryBlock = location {
for message in messages {
if case let .Id(id) = message.id {
if message.flags.contains(.Incoming) {
addedOperationIncomingMessageIds.append(id)
if let authorId = message.authorId {
recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps)
}
}
if message.flags.contains(.WasScheduled) {
wasOperationScheduledMessageIds.append(id)
}
}
}
default:
break
}
case let .ReadInbox(messageId):
if messageId.namespace == Namespaces.Message.Cloud {
if let current = readInboxCloudMessageIds[messageId.peerId] {
if current < messageId.id {
readInboxCloudMessageIds[messageId.peerId] = messageId.id
}
} else {
readInboxCloudMessageIds[messageId.peerId] = messageId.id
}
}
case let .ResetIncomingReadState(_, peerId, namespace, maxIncomingReadId, _, _):
if namespace == Namespaces.Message.Cloud {
if let current = readInboxCloudMessageIds[peerId] {
if current < maxIncomingReadId {
readInboxCloudMessageIds[peerId] = maxIncomingReadId
}
} else {
readInboxCloudMessageIds[peerId] = maxIncomingReadId
}
}
case let .ResetReadState(peerId, namespace, maxIncomingReadId, _, _, _, _):
if namespace == Namespaces.Message.Cloud {
if let current = readInboxCloudMessageIds[peerId] {
if current < maxIncomingReadId {
readInboxCloudMessageIds[peerId] = maxIncomingReadId
}
} else {
readInboxCloudMessageIds[peerId] = maxIncomingReadId
}
}
default:
break
}
}
var wasScheduledMessageIds:[MessageId] = []
@ -3187,7 +3219,22 @@ func replayFinalState(
if message.flags.contains(.Incoming) {
if var data = transaction.getMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) {
if data.maxIncomingReadId != 0 && id.id >= data.maxKnownMessageId {
var combinedMaxIncomingReadId = data.maxIncomingReadId
if combinedMaxIncomingReadId == 0 {
assert(true)
}
if let maxId = readInboxCloudMessageIds[id.peerId] {
combinedMaxIncomingReadId = max(combinedMaxIncomingReadId, maxId)
} else if let groupReadState = transaction.getCombinedPeerReadState(id.peerId), let state = groupReadState.states.first(where: { $0.0 == Namespaces.Message.Cloud })?.1, case let .idBased(maxIncomingReadId, _, _, _, _) = state {
combinedMaxIncomingReadId = max(combinedMaxIncomingReadId, maxIncomingReadId)
}
if combinedMaxIncomingReadId != data.maxIncomingReadId {
assert(true)
}
if combinedMaxIncomingReadId != 0 && id.id >= data.maxKnownMessageId {
data.maxKnownMessageId = id.id
data.incomingUnreadCount += 1

View File

@ -321,12 +321,6 @@ final class ChatHistoryPreloadManager {
return
}
#if DEBUG
if "".isEmpty {
return
}
#endif
strongSelf.canPreloadHistoryValue = value
if value {
for i in 0 ..< min(3, strongSelf.entries.count) {
@ -382,7 +376,9 @@ final class ChatHistoryPreloadManager {
return
}
#if DEBUG
//return
if "".isEmpty {
return
}
#endif
var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = []

View File

@ -162,7 +162,7 @@ final class HistoryViewStateValidationContexts {
}
}
if let location = location, let peerId = location.peerId, let threadId = location.threadId {
if let location = location, let peerId = location.peerId, let threadId = location.threadId, let historyState = historyState, historyState.hasInvalidationIndex {
var rangesToInvalidate: [[MessageId]] = []
let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in
if ranges.isEmpty {
@ -179,8 +179,17 @@ final class HistoryViewStateValidationContexts {
}
for entry in view.entries {
if entry.message.id.peerId == peerId && entry.message.id.namespace == Namespaces.Message.Cloud {
addToRange(entry.message.id, &rangesToInvalidate)
if historyState.matchesPeerId(entry.message.id.peerId) && entry.message.id.namespace == Namespaces.Message.Cloud {
if let tag = view.tagMask {
if !entry.message.tags.contains(tag) {
continue
}
}
if !historyState.isMessageValid(entry.message) {
addToRange(entry.message.id, &rangesToInvalidate)
} else {
addRangeBreak(&rangesToInvalidate)
}
}
}

View File

@ -434,3 +434,75 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e
}
} |> switchToLatest
}
func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
return Signal { _ in
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: MessageTags(rawValue: 0), namespace: Namespaces.Message.Cloud)
let disposable = postbox.combinedView(keys: [invalidateKey]).start(next: { view in
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
if let v = view.views[invalidateKey] as? InvalidatedMessageHistoryTagSummariesView {
invalidateEntries = v.entries
}
let (disposeOperations, _, beginValidateOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)], beginValidateOperations: [(InvalidatedMessageHistoryTagsSummaryEntry, MetaDisposable)]) in
return helper.update(entries: [], invalidateEntries: invalidateEntries)
}
for disposable in disposeOperations {
disposable.dispose()
}
for (entry, disposable) in beginValidateOperations {
let signal = synchronizeMessageHistoryTagSummary(postbox: postbox, network: network, entry: entry)
|> then(postbox.transaction { transaction -> Void in
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
})
disposable.set(signal.start())
}
})
return ActionDisposable {
let disposables = helper.with { helper -> [Disposable] in
return helper.reset()
}
for disposable in disposables {
disposable.dispose()
}
disposable.dispose()
}
}
}
private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Network, entry: InvalidatedMessageHistoryTagsSummaryEntry) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in
guard let threadId = entry.key.threadId else {
return .complete()
}
if let peer = transaction.getPeer(entry.key.peerId), let inputPeer = apiInputPeer(peer) {
return network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: threadId), offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Void, NoError> in
guard let result = result else {
return .complete()
}
return postbox.transaction { transaction -> Void in
switch result {
case let .channelMessages(_, _, count, _, messages, _, _, _):
let topId: Int32 = messages.first?.id(namespace: Namespaces.Message.Cloud)?.id ?? 1
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: threadId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: count, maxId: topId)
default:
break
}
}
}
} else {
return .complete()
}
}
|> switchToLatest
}

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 149
return 150
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -58,6 +58,23 @@ public struct EnginePeerReadCounters: Equatable {
}
return state.isUnread
}
public var hasEverRead: Bool {
guard let state = self.state else {
return false
}
for (_, state) in state.states {
switch state {
case let .idBased(maxIncomingReadId, _, _, _, _):
if maxIncomingReadId != 0 {
return true
}
case .indexBased:
return true
}
}
return false
}
public func isOutgoingMessageIndexRead(_ index: EngineMessage.Index) -> Bool {
guard let state = self.state else {

View File

@ -297,7 +297,9 @@ public enum PresentationResourceParameterKey: Hashable {
case chatFreePartialCheck(CGFloat, Bool)
case chatListBadgeBackgroundActive(CGFloat)
case chatListBadgeBackgroundActiveProvisional(CGFloat)
case chatListBadgeBackgroundInactive(CGFloat)
case chatListBadgeBackgroundInactiveProvisional(CGFloat)
case chatListBadgeBackgroundMention(CGFloat)
case badgeBackgroundReactions(CGFloat)
case badgeBackgroundInactiveReactions(CGFloat)

View File

@ -20,17 +20,23 @@ private func generateStatusCheckImage(theme: PresentationTheme, single: Bool) ->
})
}
private func generateBadgeBackgroundImage(theme: PresentationTheme, diameter: CGFloat, active: Bool, reaction: Bool, icon: UIImage? = nil) -> UIImage? {
private func generateBadgeBackgroundImage(theme: PresentationTheme, diameter: CGFloat, active: Bool, reaction: Bool, provisional: Bool, icon: UIImage? = nil) -> UIImage? {
return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if active {
if reaction {
context.setFillColor(theme.chatList.reactionBadgeActiveBackgroundColor.cgColor)
} else if provisional {
context.setFillColor(theme.chatList.unreadBadgeActiveBackgroundColor.withMultipliedAlpha(0.1).cgColor)
} else {
context.setFillColor(theme.chatList.unreadBadgeActiveBackgroundColor.cgColor)
}
} else {
context.setFillColor(theme.chatList.unreadBadgeInactiveBackgroundColor.cgColor)
if provisional {
context.setFillColor(theme.chatList.unreadBadgeInactiveBackgroundColor.withMultipliedAlpha(0.2).cgColor)
} else {
context.setFillColor(theme.chatList.unreadBadgeInactiveBackgroundColor.cgColor)
}
}
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
if let icon = icon, let cgImage = icon.cgImage {
@ -177,37 +183,49 @@ public struct PresentationResourcesChatList {
public static func badgeBackgroundActive(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundActive(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: false)
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: false, provisional: false)
})
}
public static func badgeBackgroundActiveProvisional(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundActiveProvisional(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: false, provisional: true)
})
}
public static func badgeBackgroundInactive(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundInactive(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false)
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, provisional: false)
})
}
public static func badgeBackgroundInactiveProvisional(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundInactiveProvisional(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, provisional: true)
})
}
public static func badgeBackgroundMention(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundMention(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/MentionBadgeIcon"), color: theme.chatList.unreadBadgeActiveTextColor))
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: false, provisional: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/MentionBadgeIcon"), color: theme.chatList.unreadBadgeActiveTextColor))
})
}
public static func badgeBackgroundInactiveMention(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.chatListBadgeBackgroundInactiveMention(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/MentionBadgeIcon"), color: theme.chatList.unreadBadgeInactiveTextColor))
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, provisional: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/MentionBadgeIcon"), color: theme.chatList.unreadBadgeInactiveTextColor))
})
}
public static func badgeBackgroundReactions(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.badgeBackgroundReactions(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: true, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/ReactionsBadgeIcon"), color: theme.chatList.unreadBadgeActiveTextColor))
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: true, reaction: true, provisional: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/ReactionsBadgeIcon"), color: theme.chatList.unreadBadgeActiveTextColor))
})
}
public static func badgeBackgroundInactiveReactions(_ theme: PresentationTheme, diameter: CGFloat) -> UIImage? {
return theme.image(PresentationResourceParameterKey.badgeBackgroundInactiveReactions(diameter), { theme in
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/ReactionsBadgeIcon"), color: theme.chatList.unreadBadgeInactiveTextColor))
return generateBadgeBackgroundImage(theme: theme, diameter: diameter, active: false, reaction: false, provisional: false, icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/ReactionsBadgeIcon"), color: theme.chatList.unreadBadgeInactiveTextColor))
})
}

View File

@ -31,7 +31,7 @@ public enum ChatTitleContent {
case replies
}
case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool, isMuted: Bool?)
case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool, isMuted: Bool?, customMessageCount: Int?)
case replyThread(type: ReplyThreadType, count: Int)
case custom(String, String?, Bool)
}
@ -123,7 +123,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
var isEnabled = true
switch titleContent {
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted):
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _):
if peerView.peerId.isReplies {
let typeText: String = self.strings.DialogList_Replies
segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
@ -319,12 +319,15 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var inputActivitiesAllowed = true
if let titleContent = self.titleContent {
switch titleContent {
case let .peer(peerView, _, _, isScheduledMessages, _):
case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount):
if let peer = peerViewMainPeer(peerView) {
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
inputActivitiesAllowed = false
}
}
if customMessageCount != nil {
inputActivitiesAllowed = false
}
case .replyThread:
inputActivitiesAllowed = true
default:
@ -420,8 +423,11 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
} else {
if let titleContent = self.titleContent {
switch titleContent {
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, _):
if let peer = peerViewMainPeer(peerView) {
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, _, customMessageCount):
if let customMessageCount = customMessageCount, customMessageCount != 0 {
let string = NSAttributedString(string: self.strings.Conversation_ForwardOptions_Messages(Int32(customMessageCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
state = .info(string, .generic)
} else if let peer = peerViewMainPeer(peerView) {
let servicePeer = isServicePeer(peer)
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)

View File

@ -28,7 +28,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
let avatarNode: AvatarNode
private var videoNode: UniversalVideoNode?
private let statusView: ComponentView<Empty>
let statusView: ComponentView<Empty>
private var videoContent: NativeVideoContent?
private let playbackStartDisposable = MetaDisposable()

View File

@ -4084,7 +4084,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.controllerInteraction = controllerInteraction
if chatLocation.threadId == nil {
//if chatLocation.threadId == nil {
if let peerId = chatLocation.peerId, peerId != context.account.peerId {
switch subject {
case .pinnedMessages, .scheduledMessages, .forwardedMessages:
@ -4105,7 +4105,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
return true
}
}
//}
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer)
self.navigationItem.titleView = self.chatTitleView
@ -4448,7 +4448,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .pinnedMessages = presentationInterfaceState.subject {
strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
} else {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil)
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil)
let imageOverride: AvatarNodeImageOverride?
if strongSelf.context.account.peerId == peer.id {
imageOverride = .savedMessagesIcon
@ -4826,17 +4826,30 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let peerView = context.account.viewTracker.peerView(peerId)
let messageAndTopic = messagePromise.get()
|> mapToSignal { message -> Signal<(message: Message?, threadData: MessageHistoryThreadData?), NoError> in
|> mapToSignal { message -> Signal<(message: Message?, threadData: MessageHistoryThreadData?, messageCount: Int), NoError> in
guard let replyThreadId = replyThreadId else {
return .single((message, nil))
return .single((message, nil, 0))
}
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: replyThreadId)
return context.account.postbox.combinedView(keys: [viewKey])
|> map { views -> (message: Message?, threadData: MessageHistoryThreadData?) in
let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Cloud)
let localCountViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Local)
return context.account.postbox.combinedView(keys: [viewKey, countViewKey, localCountViewKey])
|> map { views -> (message: Message?, threadData: MessageHistoryThreadData?, messageCount: Int) in
guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else {
return (message, nil)
return (message, nil, 0)
}
return (message, view.info?.data.get(MessageHistoryThreadData.self))
var messageCount = 0
if let summaryView = views.views[countViewKey] as? MessageHistoryTagSummaryView, let count = summaryView.count {
if replyThreadId == 1 {
messageCount += Int(count)
} else {
messageCount += max(Int(count) - 1, 0)
}
}
if let summaryView = views.views[localCountViewKey] as? MessageHistoryTagSummaryView, let count = summaryView.count {
messageCount += Int(count)
}
return (message, view.info?.data.get(MessageHistoryThreadData.self), messageCount)
}
}
@ -4933,7 +4946,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if let threadInfo = messageAndTopic.threadData?.info {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted)
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount)
let avatarContent: EmojiStatusComponent.Content
if let fileId = threadInfo.icon {

View File

@ -239,7 +239,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
}
if channel.flags.contains(.isForum) {
if let _ = chatPresentationInterfaceState.threadData {
/*if let _ = chatPresentationInterfaceState.threadData {
} else {
if chatPresentationInterfaceState.interfaceState.replyMessageId == nil {
if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) {
@ -251,7 +251,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
return (panel, nil)
}
}
}
}*/
}
} else if let group = peer as? TelegramGroup {
switch group.membership {

View File

@ -240,7 +240,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, toggleArchivedFolderHiddenByDefault: {
}, toggleThreadsSelection: { _, _ in
}, hidePsa: { _ in
}, activateChatPreview: { [weak self] item, node, gesture, _ in
}, activateChatPreview: { [weak self] item, _, node, gesture, _ in
guard let strongSelf = self else {
gesture?.cancel()
return

View File

@ -926,12 +926,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
}
}
func canEditPeerInfo(context: AccountContext, peer: Peer?, threadData: MessageHistoryThreadData?) -> Bool {
func canEditPeerInfo(context: AccountContext, peer: Peer?, chatLocation: ChatLocation, threadData: MessageHistoryThreadData?) -> Bool {
if context.account.peerId == peer?.id {
return true
}
if let channel = peer as? TelegramChannel {
if let threadData = threadData {
if chatLocation.threadId == 1 {
return false
}
if channel.hasPermission(.manageTopics) {
return true
}
@ -1201,7 +1204,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
return result
}
func peerInfoCanEdit(peer: Peer?, threadData: MessageHistoryThreadData?, cachedData: CachedPeerData?, isContact: Bool?) -> Bool {
func peerInfoCanEdit(peer: Peer?, chatLocation: ChatLocation, threadData: MessageHistoryThreadData?, cachedData: CachedPeerData?, isContact: Bool?) -> Bool {
if let user = peer as? TelegramUser {
if user.isDeleted {
return false
@ -1212,7 +1215,9 @@ func peerInfoCanEdit(peer: Peer?, threadData: MessageHistoryThreadData?, cachedD
return true
} else if let peer = peer as? TelegramChannel {
if peer.flags.contains(.isForum), let threadData = threadData {
if peer.flags.contains(.isCreator) {
if chatLocation.threadId == 1 {
return false
} else if peer.flags.contains(.isCreator) {
return true
} else if threadData.isOwnedByMe {
return true

View File

@ -638,7 +638,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
transition.updateAlpha(node: self, alpha: 1.0 - fraction)
}
func update(peer: Peer?, threadData: MessageHistoryThreadData?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
guard let peer = peer else {
return
}
@ -655,7 +655,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
clipStyle = .round
}
if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) {
var overlayHidden = true
if let updatingAvatar = updatingAvatar {
overlayHidden = false
@ -749,12 +749,12 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
}
var removedPhotoResourceIds = Set<String>()
func update(peer: Peer?, threadData: MessageHistoryThreadData?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
func update(peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
guard let peer = peer else {
return
}
let canEdit = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
let canEdit = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData)
let previousItem = self.item
var item = item
@ -1929,14 +1929,14 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
self.itemNodes[key]?.layer.addShakeAnimation()
}
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, chatLocation: ChatLocation, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize()))
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) {
if self.avatarButtonNode.supernode == nil {
self.addSubnode(self.avatarButtonNode)
}
@ -1959,12 +1959,12 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
}
} else if let _ = peer as? TelegramGroup {
fieldKeys.append(.title)
if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) {
fieldKeys.append(.description)
}
} else if let _ = peer as? TelegramChannel {
fieldKeys.append(.title)
if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
if canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData) {
fieldKeys.append(.description)
}
}
@ -2018,10 +2018,10 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
} else {
placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder
}
isEnabled = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData)
case .description:
placeholder = presentationData.strings.Channel_Edit_AboutItem
isEnabled = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
isEnabled = canEditPeerInfo(context: self.context, peer: peer, chatLocation: chatLocation, threadData: threadData)
}
let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText)
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight)))
@ -2059,6 +2059,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private let isSettings: Bool
private let videoCallsEnabled: Bool
private let forumTopicThreadId: Int64?
private let chatLocation: ChatLocation
private(set) var isAvatarExpanded: Bool
var skipCollapseCompletion = false
@ -2119,13 +2120,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var emojiStatusPackDisposable = MetaDisposable()
var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>()
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?) {
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) {
self.context = context
self.isAvatarExpanded = avatarInitiallyExpanded
self.isOpenedFromChat = isOpenedFromChat
self.isSettings = isSettings
self.videoCallsEnabled = true
self.forumTopicThreadId = forumTopicThreadId
self.chatLocation = chatLocation
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings)
@ -2249,7 +2251,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else {
return
}
strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, chatLocation: chatLocation, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
}
self.avatarListNode.animateOverlaysFadeIn = { [weak self] in
@ -2528,7 +2530,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.buttonsContainerNode.alpha = self.regularContentNode.alpha
self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition)
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition)
transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight)))
let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self)
@ -2540,6 +2542,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var transitionSourceTitleFrame = CGRect()
var transitionSourceSubtitleFrame = CGRect()
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor
@ -2548,8 +2552,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height
transitionFraction = navigationTransition.fraction
if let sourceAvatarNode = (navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode {
transitionSourceAvatarFrame = sourceAvatarNode.view.convert(sourceAvatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view)
if let avatarNavigationNode = navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode {
if let statusView = avatarNavigationNode.statusView.view {
transitionSourceAvatarFrame = statusView.convert(statusView.bounds, to: navigationTransition.sourceNavigationBar.view)
} else {
transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view)
}
} else {
transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4)
}
transitionSourceTitleFrame = navigationTransition.sourceTitleFrame
transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame
@ -2763,7 +2773,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
], mainState: TitleNodeStateRegular)
self.usernameNode.accessibilityLabel = usernameString.string
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
let avatarCenter: CGPoint
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
@ -2945,8 +2954,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition)
self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
self.avatarOverlayNode.update(peer: peer, threadData: threadData, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
if additive {
transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.avatarContainerNode, scale: avatarScale)
transition.updateSublayerTransformScaleAdditive(node: self.avatarOverlayNode, scale: avatarScale)

View File

@ -1320,7 +1320,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
return result
}
private func editingItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction) -> [(AnyHashable, [PeerInfoScreenItem])] {
private func editingItems(data: PeerInfoScreenData?, chatLocation: ChatLocation, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction) -> [(AnyHashable, [PeerInfoScreenItem])] {
enum Section: Int, CaseIterable {
case notifications
case groupLocation
@ -1619,7 +1619,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
}
}
if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel, threadData: data.threadData) {
if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel, chatLocation: chatLocation, threadData: data.threadData) {
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/Menu/Stickers"), action: {
interaction.editingOpenStickerPackSetup()
}))
@ -1950,7 +1950,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if case let .replyThread(message) = chatLocation {
forumTopicThreadId = Int64(message.messageId.id)
}
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, forumTopicThreadId: forumTopicThreadId)
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly)
super.init()
@ -3079,7 +3079,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} else {
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil)
}
} else if let group = data.peer as? TelegramGroup, canEditPeerInfo(context: strongSelf.context, peer: group, threadData: data.threadData) {
} else if let group = data.peer as? TelegramGroup, canEditPeerInfo(context: strongSelf.context, peer: group, chatLocation: chatLocation, threadData: data.threadData) {
let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? ""
let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? ""
@ -3138,7 +3138,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil)
}))
}
} else if let channel = data.peer as? TelegramChannel, canEditPeerInfo(context: strongSelf.context, peer: channel, threadData: data.threadData) {
} else if let channel = data.peer as? TelegramChannel, canEditPeerInfo(context: strongSelf.context, peer: channel, chatLocation: strongSelf.chatLocation, threadData: data.threadData) {
let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? ""
let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? ""
@ -6737,7 +6737,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
private func openAvatarForEditing(fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
guard let peer = self.data?.peer, canEditPeerInfo(context: self.context, peer: peer, threadData: self.data?.threadData) else {
guard let peer = self.data?.peer, canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
return
}
@ -7984,7 +7984,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
var validEditingSections: [AnyHashable] = []
let editItems = self.isSettings ? settingsEditingItems(data: self.data, state: self.state, context: self.context, presentationData: self.presentationData, interaction: self.interaction) : editingItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction)
let editItems = self.isSettings ? settingsEditingItems(data: self.data, state: self.state, context: self.context, presentationData: self.presentationData, interaction: self.interaction) : editingItems(data: self.data, chatLocation: self.chatLocation, context: self.context, presentationData: self.presentationData, interaction: self.interaction)
for (sectionId, sectionItems) in editItems {
var insets = UIEdgeInsets()
@ -8296,7 +8296,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
} else if peerInfoCanEdit(peer: self.data?.peer, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
} else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
}
if self.state.selectedMessageIds == nil {
@ -9206,6 +9206,10 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
private var previousBackButtonBadge: ASDisplayNode?
private var currentBackButton: ASDisplayNode?
private var previousRightButton: CALayer?
private var previousContentNode: ASDisplayNode?
private var previousContentNodeFrame: CGRect?
private var previousTitleNode: (ASDisplayNode, PortalView)?
private var previousStatusNode: (ASDisplayNode, ASDisplayNode)?
@ -9257,6 +9261,19 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
self.currentBackButton = currentBackButton
self.addSubnode(currentBackButton)
}
if let _ = bottomNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode {
} else if let previousRightButton = bottomNavigationBar.rightButtonNode.view.layer.snapshotContentTree() {
self.previousRightButton = previousRightButton
self.view.layer.addSublayer(previousRightButton)
}
if let contentNode = bottomNavigationBar.contentNode {
self.previousContentNode = contentNode
self.previousContentNodeFrame = contentNode.view.convert(contentNode.view.bounds, to: bottomNavigationBar.view)
self.addSubnode(contentNode)
}
if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let previousTitleNode = PortalView(matchPosition: false) {
previousTitleNode.view.frame = previousTitleView.titleContainerView.frame
previousTitleView.titleContainerView.addPortal(view: previousTitleNode)
@ -9294,6 +9311,12 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
transition.updateAlpha(node: previousBackButton, alpha: fraction)
}
if let previousRightButton = self.previousRightButton {
let previousRightButtonFrame = bottomNavigationBar.rightButtonNode.view.convert(bottomNavigationBar.rightButtonNode.view.bounds, to: bottomNavigationBar.view)
previousRightButton.frame = previousRightButtonFrame
transition.updateAlpha(layer: previousRightButton, alpha: fraction)
}
if let currentBackButtonArrow = self.currentBackButtonArrow {
let currentBackButtonArrowFrame = topNavigationBar.backButtonArrow.view.convert(topNavigationBar.backButtonArrow.view.bounds, to: topNavigationBar.view)
currentBackButtonArrow.frame = currentBackButtonArrowFrame
@ -9370,6 +9393,13 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
let bottomHeight = bottomNavigationBar.backgroundNode.bounds.height
transition.updateSublayerTransformOffset(layer: bottomNavigationBar.additionalContentNode.layer, offset: CGPoint(x: 0.0, y: (1.0 - fraction) * (topHeight - bottomHeight)))
if let previousContentNode = self.previousContentNode, let previousContentNodeFrame = self.previousContentNodeFrame {
var updatedPreviousContentNodeFrame = bottomNavigationBar.view.convert(previousContentNodeFrame, to: bottomNavigationBar.view)
updatedPreviousContentNodeFrame.origin.y += (1.0 - fraction) * (topHeight - bottomHeight)
transition.updateFrame(node: previousContentNode, frame: updatedPreviousContentNodeFrame)
transition.updateAlpha(node: previousContentNode, alpha: fraction)
}
}
}
@ -9390,6 +9420,11 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
bottomNavigationBar.isHidden = false
self.headerNode.navigationTransition = nil
self.screenNode.insertSubnode(self.headerNode, aboveSubnode: self.screenNode.scrollNode)
if let previousContentNode = self.previousContentNode, let previousContentNodeFrame = self.previousContentNodeFrame {
previousContentNode.frame = previousContentNodeFrame
bottomNavigationBar.insertSubnode(previousContentNode, belowSubnode: bottomNavigationBar.stripeNode)
}
}
}