General UI bug fixes

This commit is contained in:
Ali 2022-11-24 23:55:40 +04:00
parent 9c09a61e47
commit 3b93037be9
20 changed files with 259 additions and 90 deletions

View File

@ -373,6 +373,17 @@ public enum ChatLocation: Equatable {
case feed(id: Int32)
}
public extension ChatLocation {
var normalized: ChatLocation {
switch self {
case .peer, .feed:
return self
case let .replyThread(message):
return .replyThread(message: message.normalized)
}
}
}
public enum ChatControllerActivateInput {
case text
case entityInput

View File

@ -1254,7 +1254,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
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, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> 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)
@ -1302,21 +1302,28 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return
}
let contextContentSource: ContextContentSource
if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode {
contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false))
if case let .channel(channel) = 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.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
} else {
var subject: ChatControllerSubject?
if case let .search(messageId) = source, let id = messageId {
subject = .message(id: .id(id), highlight: false, timecode: nil)
let contextContentSource: ContextContentSource
if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode {
contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false))
} else {
var subject: ChatControllerSubject?
if case let .search(messageId) = source, let id = messageId {
subject = .message(id: .id(id), highlight: false, timecode: nil)
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
self.tabContainerNode.tabSelected = { [weak self] id, isDisabled in
@ -2048,8 +2055,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0
transition.updateAlpha(node: self.tabContainerNode, alpha: self.chatListDisplayNode.inlineStackContainerTransitionFraction * 0.5 + (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * 1.0)
self.tabContainerNode.isUserInteractionEnabled = self.chatListDisplayNode.inlineStackContainerNode == nil
//transition.updateAlpha(node: self.tabContainerNode, alpha: self.chatListDisplayNode.inlineStackContainerTransitionFraction * 0.5 + (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * 1.0)
//self.tabContainerNode.isUserInteractionEnabled = self.chatListDisplayNode.inlineStackContainerNode == nil
transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
@ -4689,12 +4696,6 @@ private final class ChatListLocationContext {
longTapped: {
}
)
self.rightButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
content: .text(title: presentationData.strings.Common_Done, isBold: true),
pressed: { [weak self] _ in
self?.parentController?.donePressed()
}
)))
} else {
self.chatTitleComponent = ChatTitleComponent(
context: self.context,
@ -4724,7 +4725,16 @@ private final class ChatListLocationContext {
self.parentController?.activateSearch()
}
)
}
if stateAndFilterId.state.editing {
self.rightButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
content: .text(title: presentationData.strings.Common_Done, isBold: true),
pressed: { [weak self] _ in
self?.parentController?.donePressed()
}
)))
} else {
self.rightButton = AnyComponentWithIdentity(id: "more", component: AnyComponent(NavigationButtonComponent(
content: .more,
pressed: { [weak self] sourceView in

View File

@ -1510,7 +1510,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
})
let foundThreads: Signal<[EngineChatList.Item], NoError>
if case .forum = location {
if case .forum = location, (key == .topics || key == .chats) {
foundThreads = chatListViewForLocation(chatListLocation: location, location: .initial(count: 1000, filter: nil), account: context.account)
|> map { view -> [EngineChatList.Item] in
var filteredItems: [EngineChatList.Item] = []

View File

@ -1142,7 +1142,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
strongSelf.contextContainer.additionalActivationProgressLayer = nil
if item.interaction.inlineNavigationLocation != nil {
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
if case let .peer(peerId) = inlineNavigationLocation.location {
if case let .chatList(index) = item.index, index.messageIndex.id.peerId == peerId {
return false
}
}
strongSelf.contextContainer.targetNodeForActivationProgress = strongSelf.avatarContainerNode
} else if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view {
strongSelf.contextContainer.targetNodeForActivationProgress = strongSelf.compoundTextButtonNode
@ -1313,9 +1318,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
reallyHighlighted = true
}
}
if item.interaction.inlineNavigationLocation != nil {
reallyHighlighted = false
}
}
return reallyHighlighted
}
@ -1403,7 +1405,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
let dateFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
let badgeFont = Font.with(size: floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
let avatarBadgeFont = Font.with(size: 16.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
let avatarBadgeFont = Font.with(size: floor(item.presentationData.fontSize.itemListBaseFontSize * 16.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
let account = item.context.account
var messages: [EngineMessage]
@ -1579,7 +1581,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let badgeDiameter = floor(item.presentationData.fontSize.baseDisplaySize * 20.0 / 17.0)
let avatarBadgeDiameter: CGFloat = 22.0
let avatarBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 22.0 / 17.0))
let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0)
@ -2354,7 +2356,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
itemHeight += titleSpacing
itemHeight += authorSpacing
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + 8.0), size: CGSize(width: rawContentWidth, height: itemHeight - 12.0 - 9.0))
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + floor(item.presentationData.fontSize.itemListBaseFontSize * 8.0 / 17.0)), size: CGSize(width: rawContentWidth, height: itemHeight - 12.0 - 9.0))
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
var heightOffset: CGFloat = 0.0
@ -2407,7 +2409,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var mainContentAlpha: CGFloat = 1.0
if case .chatList = item.chatListLocation {
mainContentFrame = CGRect(origin: CGPoint(x: params.leftInset + 72.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
mainContentFrame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
mainContentBoundsOffset = mainContentFrame.origin.x
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
@ -2497,7 +2499,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var avatarScaleOffset: CGFloat = 0.0
var avatarScale: CGFloat = 1.0
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
let targetAvatarScale: CGFloat = 54.0 / avatarFrame.width
let targetAvatarScale: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 54.0 / 17.0) / avatarFrame.width
avatarScale = targetAvatarScale * inlineNavigationLocation.progress + 1.0 * (1.0 - inlineNavigationLocation.progress)
let targetAvatarScaleOffset: CGFloat = -(avatarFrame.width - avatarFrame.width * avatarScale) * 0.5
@ -2863,7 +2865,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
finalBottomRect
], color: theme.pinnedItemBackgroundColor.mixedWith(theme.unreadBadgeInactiveBackgroundColor, alpha: 0.1))
compoundTextButtonNode.frame = compoundHighlightingNode.frame
transition.updateFrame(node: compoundTextButtonNode, frame: compoundHighlightingNode.frame)
if let textArrowImage = textArrowImage {
let textArrowNode: ASImageNode
@ -2903,8 +2905,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let dustNode = strongSelf.dustNode {
compoundTextButtonNode.addSubnode(dustNode)
}
strongSelf.textNode.textNode.frame = textNodeFrame.offsetBy(dx: -compoundTextButtonNode.frame.minX, dy: -compoundTextButtonNode.frame.minY)
}
strongSelf.textNode.textNode.frame = textNodeFrame.offsetBy(dx: -compoundTextButtonNode.frame.minX, dy: -compoundTextButtonNode.frame.minY)
strongSelf.authorNode.assignParentNode(parentNode: compoundTextButtonNode)
} else {
@ -2913,8 +2915,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let dustNode = strongSelf.dustNode {
strongSelf.mainContentContainerNode.addSubnode(dustNode)
}
strongSelf.textNode.textNode.frame = textNodeFrame
}
strongSelf.textNode.textNode.frame = textNodeFrame
strongSelf.authorNode.assignParentNode(parentNode: nil)
}
@ -3043,9 +3045,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let titlePosition = strongSelf.titleNode.position
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y))
transition.animatePositionAdditive(node: strongSelf.textNode.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
if let dustNode = strongSelf.dustNode {
transition.animatePositionAdditive(node: dustNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
if strongSelf.textNode.textNode.supernode === strongSelf.mainContentContainerNode {
transition.animatePositionAdditive(node: strongSelf.textNode.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
if let dustNode = strongSelf.dustNode {
transition.animatePositionAdditive(node: dustNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
}
}
let authorPosition = strongSelf.authorNode.position

View File

@ -659,10 +659,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
hasTimer = true
}
var hasSchedule = true
if controller.chatLocation?.threadId != nil {
hasSchedule = false
}
let hasSchedule = true
self.openingMedia = true

View File

@ -1,23 +1,58 @@
final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView {
private let peerId: PeerId?
private let threadId: Int64?
private let namespace: MessageId.Namespace
private let tagMask: MessageTags
var entries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
init(postbox: PostboxImpl, tagMask: MessageTags, namespace: MessageId.Namespace) {
init(postbox: PostboxImpl, peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) {
self.peerId = peerId
self.threadId = threadId
self.tagMask = tagMask
self.namespace = namespace
for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) {
self.entries.insert(entry)
if let peerId = self.peerId {
if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) {
self.entries.insert(entry)
}
} else {
for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) {
self.entries.insert(entry)
}
}
}
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
var updated = false
for operation in transaction.currentInvalidateMessageTagSummaries {
switch operation {
if let peerId = self.peerId {
var maybeUpdated = false
loop: for operation in transaction.currentInvalidateMessageTagSummaries {
switch operation {
case let .add(entry):
if entry.key.peerId == peerId && entry.key.threadId == self.threadId {
maybeUpdated = true
break loop
}
case let .remove(key):
if key.peerId == peerId && key.threadId == self.threadId {
maybeUpdated = true
break loop
}
}
}
if maybeUpdated {
self.entries.removeAll()
if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) {
self.entries.insert(entry)
}
updated = true
}
} else {
for operation in transaction.currentInvalidateMessageTagSummaries {
switch operation {
case let .add(entry):
if entry.key.namespace == self.namespace && entry.key.tagMask == self.tagMask {
self.entries.insert(entry)
@ -33,8 +68,10 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView
}
updated = true
}
}
}
}
return updated
}

View File

@ -74,6 +74,10 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table {
return entries
}
func get(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> InvalidatedMessageHistoryTagsSummaryEntry? {
return self.get(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId))
}
private func get(_ key: InvalidatedMessageHistoryTagsSummaryKey) -> InvalidatedMessageHistoryTagsSummaryEntry? {
if let value = self.valueBox.get(self.table, key: self.key(key)) {
var version: Int32 = 0

View File

@ -10,7 +10,7 @@ public enum PostboxViewKey: Hashable {
case globalMessageTags(globalTag: GlobalMessageTags, position: MessageIndex, count: Int, groupingPredicate: ((Message, Message) -> Bool)?)
case peer(peerId: PeerId, components: PeerViewComponents)
case pendingMessageActions(type: PendingMessageActionType)
case invalidatedMessageHistoryTagSummaries(tagMask: MessageTags, namespace: MessageId.Namespace)
case invalidatedMessageHistoryTagSummaries(peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace)
case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace)
case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace)
case cachedPeerData(peerId: PeerId)
@ -61,7 +61,9 @@ public enum PostboxViewKey: Hashable {
hasher.combine(peerId)
case let .pendingMessageActions(type):
hasher.combine(type)
case let .invalidatedMessageHistoryTagSummaries(tagMask, namespace):
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
hasher.combine(peerId)
hasher.combine(threadId)
hasher.combine(tagMask)
hasher.combine(namespace)
case let .pendingMessageActionsSummary(type, peerId, namespace):
@ -191,8 +193,8 @@ public enum PostboxViewKey: Hashable {
} else {
return false
}
case .invalidatedMessageHistoryTagSummaries:
if case .invalidatedMessageHistoryTagSummaries = rhs {
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
if case .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace) = rhs {
return true
} else {
return false
@ -395,8 +397,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
return MutablePeerView(postbox: postbox, peerId: peerId, components: components)
case let .pendingMessageActions(type):
return MutablePendingMessageActionsView(postbox: postbox, type: type)
case let .invalidatedMessageHistoryTagSummaries(tagMask, namespace):
return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, tagMask: tagMask, namespace: namespace)
case let .invalidatedMessageHistoryTagSummaries(peerId, threadId, tagMask, namespace):
return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace)
case let .pendingMessageActionsSummary(type, peerId, namespace):
return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace)
case let .historyTagSummaryView(tag, peerId, threadId, namespace):

View File

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

View File

@ -93,7 +93,7 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
let actionsKey = PostboxViewKey.pendingMessageActions(type: .consumeUnseenPersonalMessage)
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud)
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: nil, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud)
let disposable = postbox.combinedView(keys: [actionsKey, invalidateKey]).start(next: { view in
var entries: [PendingMessageActionsEntry] = []
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
@ -156,7 +156,7 @@ func managedReadReactionActions(postbox: Postbox, network: Network, stateManager
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
let actionsKey = PostboxViewKey.pendingMessageActions(type: .readReaction)
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud)
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: nil, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud)
let disposable = postbox.combinedView(keys: [actionsKey, invalidateKey]).start(next: { view in
var entries: [PendingMessageActionsEntry] = []
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
@ -435,11 +435,11 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e
} |> switchToLatest
}
func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, threadId: Int64) -> 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 invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(peerId: peerId, threadId: threadId, 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 {
@ -480,7 +480,7 @@ private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Netw
guard let threadId = entry.key.threadId else {
return .complete()
}
if let peer = transaction.getPeer(entry.key.peerId), let inputPeer = apiInputPeer(peer) {
if let peer = transaction.getPeer(entry.key.peerId) as? TelegramChannel, peer.flags.contains(.isForum), 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

View File

@ -590,6 +590,14 @@ public struct ChatReplyThreadMessage: Equatable {
self.initialAnchor = initialAnchor
self.isNotAvailable = isNotAvailable
}
public var normalized: ChatReplyThreadMessage {
if self.isForumPost {
return ChatReplyThreadMessage(messageId: self.messageId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
} else {
return self
}
}
}
public enum FetchChannelReplyThreadMessageError {

View File

@ -494,5 +494,10 @@ public extension TelegramEngine {
}
|> ignoreValues
}
public func keepMessageCountersSyncrhonized(peerId: EnginePeer.Id, threadId: Int64) -> Signal<Never, NoError> {
return managedSynchronizeMessageHistoryTagSummaries(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, threadId: threadId)
|> ignoreValues
}
}
}

View File

@ -9,6 +9,8 @@ public func stringForFullAuthorName(message: EngineMessage, strings: Presentatio
var authorName = ""
if author.id == accountPeerId {
authorName = strings.DialogList_You
} else if author.isDeleted {
authorName = strings.User_DeletedAccount
} else {
authorName = author.compactDisplayTitle
}

View File

@ -317,7 +317,7 @@ final class AuthorizedApplicationContext {
if let _ = threadData, let threadId = firstMessage.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(
messageId: MessageId(peerId: firstMessage.id.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
))
).normalized)
} else {
guard let peer = firstMessage.peers[firstMessage.id.peerId] else {
return

View File

@ -295,6 +295,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var hasActiveGroupCallDisposable: Disposable?
private var sendAsPeersDisposable: Disposable?
private var preloadAttachBotIconsDisposables: DisposableSet?
private var keepMessageCountersSyncrhonizedDisposable: Disposable?
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
@ -5663,6 +5664,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.inviteRequestsDisposable.dispose()
self.sendAsPeersDisposable?.dispose()
self.preloadAttachBotIconsDisposables?.dispose()
self.keepMessageCountersSyncrhonizedDisposable?.dispose()
}
deallocate()
}
@ -9326,7 +9328,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
guard case let .peer(peerId) = strongSelf.chatLocation else {
guard let peerId = strongSelf.chatLocation.peerId else {
return
}
guard let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage else {
@ -9370,7 +9372,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})))
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
let chatLocation: ChatLocation
if let _ = strongSelf.chatLocation.threadId {
chatLocation = strongSelf.chatLocation
} else {
chatLocation = .peer(id: peerId)
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: chatLocation, subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
@ -9986,6 +9995,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
if case let .replyThread(message) = self.chatLocation, message.isForumPost {
if self.keepMessageCountersSyncrhonizedDisposable == nil {
self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: message.messageId.peerId, threadId: Int64(message.messageId.id)).start()
}
}
if let scheduledActivateInput = scheduledActivateInput, case .text = scheduledActivateInput {
self.scheduledActivateInput = nil
@ -14964,16 +14979,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if isPinnedMessages, let messageId = messageLocation.messageId {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in
let _ = (combineLatest(
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)),
self.context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .local)
)
|> deliverOnMainQueue).start(next: { [weak self] peer, messages in
guard let self, let peer = peer else {
return
}
if let navigationController = self.effectiveNavigationController {
self.dismiss()
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
guard let navigationController = self.effectiveNavigationController else {
return
}
self.dismiss()
let navigateToLocation: NavigateToChatControllerParams.Location
if let threadId = messages.first?.threadId {
navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, 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))
} else {
navigateToLocation = .peer(peer)
}
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: navigateToLocation, subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always))
})
} else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) {
let _ = (self.context.engine.data.get(

View File

@ -2368,24 +2368,57 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
})
if let loaded = displayedRange.visibleRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
if loaded.firstIndex < 5 && historyView.originalView.laterId != nil {
if !"".isEmpty {
print("load next")
return
var mathesFirst = false
if loaded.firstIndex <= 5 {
var firstHasGroups = false
for index in (max(0, historyView.filteredEntries.count - 5) ..< historyView.filteredEntries.count).reversed() {
switch historyView.filteredEntries[index] {
case .MessageEntry:
break
case .MessageGroupEntry:
firstHasGroups = true
default:
break
}
}
if firstHasGroups {
mathesFirst = loaded.firstIndex <= 1
} else {
mathesFirst = loaded.firstIndex <= 5
}
}
var mathesLast = false
if loaded.lastIndex >= historyView.filteredEntries.count - 5 {
var lastHasGroups = false
for index in 0 ..< min(5, historyView.filteredEntries.count) {
switch historyView.filteredEntries[index] {
case .MessageEntry:
break
case .MessageGroupEntry:
lastHasGroups = true
default:
break
}
}
if lastHasGroups {
mathesLast = loaded.lastIndex >= historyView.filteredEntries.count - 1
} else {
mathesLast = loaded.lastIndex >= historyView.filteredEntries.count - 5
}
}
if mathesFirst && historyView.originalView.laterId != nil {
let locationInput: ChatHistoryLocation = .Navigation(index: .message(lastEntry.index), anchorIndex: .message(lastEntry.index), count: historyMessageCount, highlight: false)
if self.chatHistoryLocationValue?.content != locationInput {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())
}
} else if loaded.firstIndex < 5, historyView.originalView.laterId == nil, !historyView.originalView.holeLater, let chatHistoryLocationValue = self.chatHistoryLocationValue, !chatHistoryLocationValue.isAtUpperBound, historyView.originalView.anchorIndex != .upperBound {
} else if mathesFirst, historyView.originalView.laterId == nil, !historyView.originalView.holeLater, let chatHistoryLocationValue = self.chatHistoryLocationValue, !chatHistoryLocationValue.isAtUpperBound, historyView.originalView.anchorIndex != .upperBound {
if self.chatHistoryLocationValue == historyView.locationInput {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .upperBound, anchorIndex: .upperBound, count: historyMessageCount, highlight: false), id: self.takeNextHistoryLocationId())
}
} else if loaded.lastIndex >= historyView.filteredEntries.count - 5 && historyView.originalView.earlierId != nil {
if !"".isEmpty {
print("load previous")
return
}
} else if mathesLast && historyView.originalView.earlierId != nil {
let locationInput: ChatHistoryLocation = .Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: historyMessageCount, highlight: false)
if self.chatHistoryLocationValue?.content != locationInput {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())

View File

@ -93,7 +93,10 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), let moreInfoNavigationButton = moreInfoNavigationButton {
if case .replyThread = presentationInterfaceState.chatLocation {
} else {
return moreInfoNavigationButton
if case .pinnedMessages = presentationInterfaceState.subject {
} else {
return moreInfoNavigationButton
}
}
}

View File

@ -59,7 +59,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
var isFirst = true
if params.useExisting {
for controller in params.navigationController.viewControllers.reversed() {
if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
guard let controller = controller as? ChatControllerImpl else {
isFirst = false
continue
}
if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
if let updateTextInputState = params.updateTextInputState {
controller.updateTextInputState(updateTextInputState)
}

View File

@ -2180,7 +2180,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.usernameNode.displaysAsynchronously = false
self.buttonsContainerNode = SparseNode()
self.buttonsContainerNode.clipsToBounds = false
self.buttonsContainerNode.clipsToBounds = true
self.regularContentNode = PeerInfoHeaderRegularContentNode()
var requestUpdateLayoutImpl: (() -> Void)?
@ -2930,10 +2930,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
}
if subtitleIsButton {
subtitleFrame.origin.y += 11.0
}
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
@ -3028,6 +3024,19 @@ final class PeerInfoHeaderNode: ASDisplayNode {
avatarOffset = apparentTitleLockOffset + 0.0 * (1.0 - titleCollapseFraction) + 10.0 * titleCollapseFraction
}
if subtitleIsButton {
subtitleFrame.origin.y += 11.0 * (1.0 - titleCollapseFraction)
if let subtitleBackgroundButton = self.subtitleBackgroundButton {
transition.updateAlpha(node: subtitleBackgroundButton, alpha: (1.0 - titleCollapseFraction))
}
if let subtitleBackgroundNode = self.subtitleBackgroundNode {
transition.updateAlpha(node: subtitleBackgroundNode, alpha: (1.0 - titleCollapseFraction))
}
if let subtitleArrowNode = self.subtitleArrowNode {
transition.updateAlpha(node: subtitleArrowNode, alpha: (1.0 - titleCollapseFraction))
}
}
let avatarCornerRadius: CGFloat
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
avatarCornerRadius = floor(avatarSize * 0.25)

View File

@ -678,12 +678,27 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
}
}
} else {
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(messageId.id))
|> map { info -> ResolvedUrl? in
if let _ = info {
return .replyThread(messageId: messageId)
return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false))
|> take(1)
|> mapToSignal { messages -> Signal<ResolvedUrl?, NoError> in
if let threadId = messages.first?.threadId {
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|> map { info -> ResolvedUrl? in
if let _ = info {
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, 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), messageId: messageId)
} else {
return .peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
}
}
} else {
return .peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(messageId.id))
|> map { info -> ResolvedUrl? in
if let _ = info {
return .replyThread(messageId: messageId)
} else {
return .peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
}
}
}
}
}