mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Thread improvements
This commit is contained in:
parent
b35ede7462
commit
29a3c18d07
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)?
|
||||
|
@ -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 })
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -483,7 +483,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private let stripeNode: ASDisplayNode
|
||||
public let stripeNode: ASDisplayNode
|
||||
private let clippingNode: SparseNode
|
||||
private let buttonsContainerNode: ASDisplayNode
|
||||
|
||||
|
@ -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? {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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 })
|
||||
|
||||
|
@ -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 })
|
||||
|
@ -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 })
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)] = []
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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! {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user