diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index b274ca7099..269bc6efcb 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1067,6 +1067,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) { openAsInlineForum = false + } else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) { + openAsInlineForum = false } else { if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { openAsInlineForum = false @@ -1519,7 +1521,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let threadId = threadId { let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: !channel.isMonoForum, isMonoforumPost: channel.isMonoForum, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: channel.isForumOrMonoForum, isMonoforumPost: channel.isMonoForum, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 3f5693a2b0..4f5a1f5393 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -463,6 +463,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { public static let toggleUnread = Actions(rawValue: 1 << 0) public static let delete = Actions(rawValue: 1 << 1) + public static let togglePinned = Actions(rawValue: 1 << 2) } case custom(Actions) @@ -994,6 +995,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { final class TopicItemNode: ASDisplayNode { let topicTitleNode: TextNode let titleTopicIconView: ComponentHostView? + var titleTopicAvatarNode: AvatarNode? var titleTopicIconComponent: EmojiStatusComponent? var visibilityStatus: Bool = false { @@ -1011,30 +1013,34 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } - private init(topicTitleNode: TextNode, titleTopicIconView: ComponentHostView?, titleTopicIconComponent: EmojiStatusComponent?) { + private init(topicTitleNode: TextNode, titleTopicIconView: ComponentHostView?, titleTopicAvatarNode: AvatarNode?, titleTopicIconComponent: EmojiStatusComponent?) { self.topicTitleNode = topicTitleNode self.titleTopicIconView = titleTopicIconView + self.titleTopicAvatarNode = titleTopicAvatarNode self.titleTopicIconComponent = titleTopicIconComponent super.init() self.addSubnode(self.topicTitleNode) + if let titleTopicAvatarNode = self.titleTopicAvatarNode { + self.view.addSubview(titleTopicAvatarNode.view) + } if let titleTopicIconView = self.titleTopicIconView { self.view.addSubview(titleTopicIconView) } } - static func asyncLayout(_ currentNode: TopicItemNode?) -> (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode) { + static func asyncLayout(_ currentNode: TopicItemNode?) -> (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ threadPeer: EnginePeer?, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode) { let makeTopicTitleLayout = TextNode.asyncLayout(currentNode?.topicTitleNode) - return { constrainedWidth, context, theme, threadId, title, iconId, iconColor in - let remainingWidth = max(1.0, constrainedWidth - (((iconId == nil && iconColor == nil) ? 1.0 : 18.0) + 2.0)) + return { constrainedWidth, context, theme, threadId, threadPeer, title, iconId, iconColor in + let remainingWidth = max(1.0, constrainedWidth - (((iconId == nil && iconColor == nil && threadPeer == nil) ? 1.0 : 18.0) + 2.0)) let topicTitleArguments = TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: remainingWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)) let topicTitleLayout = makeTopicTitleLayout(topicTitleArguments) - return (CGSize(width: ((iconId == nil && iconColor == nil) ? 1.0 : 18.0) + 2.0 + topicTitleLayout.0.size.width, height: topicTitleLayout.0.size.height), { + return (CGSize(width: ((iconId == nil && iconColor == nil && threadPeer == nil) ? 1.0 : 18.0) + 2.0 + topicTitleLayout.0.size.width, height: topicTitleLayout.0.size.height), { let topicTitleNode = topicTitleLayout.1() let titleTopicIconContent: EmojiStatusComponent.Content? @@ -1068,7 +1074,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } - let targetNode = currentNode ?? TopicItemNode(topicTitleNode: topicTitleNode, titleTopicIconView: titleTopicIconView, titleTopicIconComponent: titleTopicIconComponent) + var titleTopicAvatarNode: AvatarNode? + if let _ = threadPeer { + if let current = currentNode?.titleTopicAvatarNode { + titleTopicAvatarNode = current + } else { + titleTopicAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0)) + } + } + + let targetNode = currentNode ?? TopicItemNode(topicTitleNode: topicTitleNode, titleTopicIconView: titleTopicIconView, titleTopicAvatarNode: titleTopicAvatarNode, titleTopicIconComponent: titleTopicIconComponent) targetNode.titleTopicIconComponent = titleTopicIconComponent @@ -1081,6 +1096,18 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { ) titleTopicIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: iconSize) + topicTitleNode.frame = CGRect(origin: CGPoint(x: 18.0 + 2.0, y: 0.0), size: topicTitleLayout.0.size) + } else if let titleTopicAvatarNode, let threadPeer { + let iconSize = CGSize(width: 18.0, height: 18.0) + + titleTopicAvatarNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: iconSize) + titleTopicAvatarNode.updateSize(size: iconSize) + if threadPeer.smallProfileImage != nil { + titleTopicAvatarNode.setPeerV2(context: context, theme: theme, peer: threadPeer, overrideImage: nil, emptyColor: theme.list.mediaPlaceholderColor, clipStyle: .round, synchronousLoad: false, displayDimensions: iconSize) + } else { + titleTopicAvatarNode.setPeer(context: context, theme: theme, peer: threadPeer, overrideImage: nil, emptyColor: theme.list.mediaPlaceholderColor, clipStyle: .round, synchronousLoad: false, displayDimensions: iconSize) + } + topicTitleNode.frame = CGRect(origin: CGPoint(x: 18.0 + 2.0, y: 0.0), size: topicTitleLayout.0.size) } else { topicTitleNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 0.0), size: topicTitleLayout.0.size) @@ -1144,9 +1171,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } - func asyncLayout() -> (_ context: AccountContext, _ constrainedWidth: CGFloat, _ theme: PresentationTheme, _ authorTitle: NSAttributedString?, _ topics: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)]) -> (CGSize, () -> CGRect?) { + func asyncLayout() -> (_ context: AccountContext, _ constrainedWidth: CGFloat, _ theme: PresentationTheme, _ authorTitle: NSAttributedString?, _ topics: [(id: Int64, threadPeer: EnginePeer?, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)]) -> (CGSize, () -> CGRect?) { let makeAuthorLayout = TextNode.asyncLayout(self.authorNode) - var makeExistingTopicLayouts: [Int64: (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode)] = [:] + var makeExistingTopicLayouts: [Int64: (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ threadPeer: EnginePeer?, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode)] = [:] for (topicId, topicNode) in self.topicNodes { makeExistingTopicLayouts[topicId] = TopicItemNode.asyncLayout(topicNode) } @@ -1178,7 +1205,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } let makeTopicLayout = makeExistingTopicLayouts[topic.id] ?? TopicItemNode.asyncLayout(nil) - let (topicSize, topicApply) = makeTopicLayout(remainingWidth, context, theme, topic.id, topic.title, topic.iconId, topic.iconColor) + let (topicSize, topicApply) = makeTopicLayout(remainingWidth, context, theme, topic.id, topic.threadPeer, topic.title, topic.iconId, topic.iconColor) topicsSizeAndApply.append((topic.id, topicSize, topicApply)) remainingWidth -= topicSize.width + 4.0 @@ -3337,17 +3364,17 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let isSearching = item.interaction.searchTextHighightState != nil var isFirstForumThreadSelectable = false - var forumThreads: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)] = [] + var forumThreads: [(id: Int64, threadPeer: EnginePeer?, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)] = [] if case .savedMessagesChats = item.chatListLocation { } else if case let .peer(peer) = item.content, case let .channel(channel) = peer.peer.peer, channel.flags.contains(.isMonoforum) { if forumThread != nil || !topForumTopicItems.isEmpty { if let forumThread { isFirstForumThreadSelectable = forumThread.isUnread - forumThreads.append((id: forumThread.id, title: NSAttributedString(string: forumThread.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil)) + forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil)) } for topicItem in topForumTopicItems { if forumThread?.id != topicItem.id { - forumThreads.append((id: topicItem.id, title: NSAttributedString(string: topicItem.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil)) + forumThreads.append((id: topicItem.id, threadPeer: topicItem.threadPeer, title: NSAttributedString(string: topicItem.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil)) } } @@ -3364,13 +3391,13 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else 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)) + forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor)) } for topicItem in topForumTopicItems { if case let .peer(peer) = item.content, peer.peer.peerId.id._internalGetInt64Value() == topicItem.id { } else if forumThread?.id != topicItem.id { - forumThreads.append((id: topicItem.id, title: NSAttributedString(string: topicItem.title, font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: topicItem.iconFileId, iconColor: topicItem.iconColor)) + forumThreads.append((id: topicItem.id, threadPeer: topicItem.threadPeer, title: NSAttributedString(string: topicItem.title, font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: topicItem.iconFileId, iconColor: topicItem.iconColor)) } } diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 143f6d3439..66d1765b66 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -96,7 +96,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case enableDebugDataDisplay(Bool) case rippleEffect(Bool) case browserExperiment(Bool) - case localTranscription(Bool) + case allForumsHaveTabs(Bool) case enableReactionOverrides(Bool) case compressedEmojiCache(Bool) case storiesJpegExperiment(Bool) @@ -133,7 +133,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.web.rawValue case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure: return DebugControllerSection.experiments.rawValue - case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation: + case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation: return DebugControllerSection.experiments.rawValue case .logTranslationRecognition, .resetTranslationStates: return DebugControllerSection.translation.rawValue @@ -226,7 +226,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 39 case .browserExperiment: return 40 - case .localTranscription: + case .allForumsHaveTabs: return 41 case .enableReactionOverrides: return 42 @@ -1264,12 +1264,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .localTranscription(value): - return ItemListSwitchItem(presentationData: presentationData, title: "Local Transcription", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .allForumsHaveTabs(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Forum Tabs Debug", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings - settings.localTranscription = value + settings.allForumsHaveTabs = value return PreferencesEntry(settings) }) }).start() @@ -1526,7 +1526,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.browserExperiment(experimentalSettings.browserExperiment)) } #endif - entries.append(.localTranscription(experimentalSettings.localTranscription)) + entries.append(.allForumsHaveTabs(experimentalSettings.allForumsHaveTabs)) if case .internal = sharedContext.applicationBindings.appBuildType { entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides)) } diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index f88e3ded10..9443d324d1 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -862,6 +862,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel } } + public func resetScrolledToItem() { + self.scrolledToItem = nil + } + public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { if let shouldStopScrolling = self.shouldStopScrolling, shouldStopScrolling(velocity.y) { targetContentOffset.pointee.y = scrollView.contentOffset.y diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 4743642867..29dc942b51 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -65,7 +65,11 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } - if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value && !displayAsRegularChat { + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value + if !isThreadBased { + displayAsRegularChat = true + } + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false @@ -435,13 +439,17 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value + if !isThreadBased { + displayAsRegularChat = true + } let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary) var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value && !displayAsRegularChat { + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false @@ -569,9 +577,13 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value + if !isThreadBased { + displayAsRegularChat = true + } var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value && !displayAsRegularChat { + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false @@ -618,9 +630,13 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value + if !isThreadBased { + displayAsRegularChat = true + } var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value && !displayAsRegularChat { + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false @@ -800,9 +816,13 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value + if !isThreadBased { + displayAsRegularChat = true + } var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value && !displayAsRegularChat { + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false @@ -858,9 +878,13 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value + if !isThreadBased { + displayAsRegularChat = true + } var isUnread: Bool - if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value && !displayAsRegularChat { + if isThreadBased && !displayAsRegularChat { isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0 } else { isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false @@ -952,10 +976,19 @@ private final class ChatListViewSpaceState { if let cachedPeerData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) { displayAsRegularChat = true } + let isThreadBased: Bool + if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId) { + isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value + } else { + isThreadBased = false + } + if !isThreadBased { + displayAsRegularChat = true + } var updatedReadState = entryData.readState - if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value, !displayAsRegularChat { - let summary = postbox.peerThreadsSummaryTable.get(peerId: peer.id) + if isThreadBased, !displayAsRegularChat { + let summary = postbox.peerThreadsSummaryTable.get(peerId: entryData.index.messageIndex.id.peerId) var count: Int32 = 0 var isMuted: Bool = false @@ -1605,6 +1638,9 @@ struct ChatListViewState { autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData) } + if !isThreadBased { + displayAsRegularChat = true + } var topForumTopics: [ChatListForumTopicData] = [] let readState: ChatListViewReadState? diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 96ca46a17a..6febb45e68 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -247,7 +247,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: } |> castError(CreateForumChannelTopicError.self) |> mapToSignal { _ -> Signal in - return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)]) + return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)]) |> castError(CreateForumChannelTopicError.self) |> map { _ -> Int64 in return topicId @@ -277,7 +277,7 @@ func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId if let info = info { return .single(.result(info)) } else { - return .single(.progress) |> then(resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: threadId)]) + return .single(.progress) |> then(resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: threadId)]) |> mapToSignal { _ -> Signal in return account.postbox.transaction { transaction -> FetchForumChannelTopicResult in if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index cf5a42468c..ef9f61a669 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2019,7 +2019,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM } if peer.flags.contains(.isMonoforum) { - let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) })) + let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in + let threadPeerId = PeerId(threadId) + if let threadPeer = state.peers[threadPeerId] { + return apiInputPeer(threadPeer) + } else if let threadPeer = transaction.getPeer(threadPeerId) { + return apiInputPeer(threadPeer) + } else { + return nil + } + })) |> map { result -> (Peer, FetchedForumThreads)? in let result = FetchedForumThreads(savedDialogs: result) return (peer, result) @@ -2146,7 +2155,7 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM } } -func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchMessageHistoryHoleSource, ids: [PeerAndBoundThreadId]) -> Signal { +func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchMessageHistoryHoleSource, additionalPeers: AccumulatedPeers, ids: [PeerAndBoundThreadId]) -> Signal { let forumThreadIds = Set(ids) if forumThreadIds.isEmpty { @@ -2172,7 +2181,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM } if peer.flags.contains(.isMonoforum) { - let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) })) + let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in + let threadPeerId = PeerId(threadId) + if let threadPeer = additionalPeers.get(threadPeerId) { + return apiInputPeer(threadPeer) + } else if let threadPeer = transaction.getPeer(threadPeerId) { + return apiInputPeer(threadPeer) + } else { + return nil + } + })) |> map { result -> (Peer, FetchedForumThreads)? in let result = FetchedForumThreads(savedDialogs: result) return (peer, result) @@ -2329,7 +2347,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM } if peer.flags.contains(.isMonoforum) { - let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) })) + let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in + let threadPeerId = PeerId(threadId) + if let threadPeer = fetchedChatList.peers.get(threadPeerId) { + return apiInputPeer(threadPeer) + } else if let threadPeer = transaction.getPeer(threadPeerId) { + return apiInputPeer(threadPeer) + } else { + return nil + } + })) |> map { result -> (Peer, FetchedForumThreads)? in let result = FetchedForumThreads(savedDialogs: result) return (peer, result) diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index a9e5e8e27d..c73d259282 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -225,7 +225,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages, additionalPeers: parsedPeers, result: Void()) |> mapToSignal { _ -> Signal in if resolveThreads && !threadIds.isEmpty { - return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, ids: Array(threadIds)) + return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, additionalPeers: parsedPeers, ids: Array(threadIds)) |> mapToSignal { _ -> Signal in return postbox.transaction { transaction -> T in return f(transaction, parsedPeers, []) @@ -325,7 +325,8 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis let combinedMessages = storeMessages + additionalMessages return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: combinedMessages, reactions: [], result: Void()) |> mapToSignal { _ -> Signal in - return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: parsedPeers.union(with: additionalPeers), result: Void()) + let additionalPeers = parsedPeers.union(with: additionalPeers) + return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: additionalPeers, result: Void()) |> mapToSignal { _ -> Signal in var threadIds = Set() for message in combinedMessages { @@ -335,7 +336,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis } if resolveThreads && !threadIds.isEmpty { - return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, ids: Array(threadIds)) + return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, additionalPeers: additionalPeers, ids: Array(threadIds)) |> mapToSignal { _ -> Signal in return postbox.transaction { transaction -> T in return f(transaction, parsedPeers, []) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index e9bd0743d6..5bc38f77ae 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -76,7 +76,11 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { peerSummaryIsThreadBased: { peer in if let channel = peer as? TelegramChannel { if channel.flags.contains(.isForum) { - return (true, false) + if channel.flags.contains(.displayForumAsTabs) { + return (false, false) + } else { + return (true, false) + } } else if channel.flags.contains(.isMonoforum) { return (true, true) } else { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index 596614cfe2..0610057116 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -208,7 +208,9 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo } var displayAsRegularChat: Bool = false - if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { + if let channel = peer as? TelegramChannel, channel.flags.contains(.displayForumAsTabs) { + displayAsRegularChat = true + } else if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData { displayAsRegularChat = cachedData.viewForumAsMessages.knownValue ?? false } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index ce98d2a1a5..e9a4798453 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -125,8 +125,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let compactAuthorName = message.author?.compactDisplayTitle ?? "" var isChannel = false - if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { - isChannel = true + var isMonoforum = false + if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel { + if case .broadcast = peer.info { + isChannel = true + } + isMonoforum = peer.isMonoForum } switch action.action { @@ -135,7 +139,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = NSAttributedString(string: strings.Notification_CreatedChannel, font: titleFont, textColor: primaryTextColor) } else { if forChatList { - attributedString = NSAttributedString(string: strings.Notification_CreatedGroup, font: titleFont, textColor: primaryTextColor) + if isMonoforum { + //TODO:Localize + attributedString = NSAttributedString(string: "No messages here yet...", font: titleFont, textColor: primaryTextColor) + } else { + attributedString = NSAttributedString(string: strings.Notification_CreatedGroup, font: titleFont, textColor: primaryTextColor) + } } else { attributedString = addAttributesToStringWithRanges(strings.Notification_CreatedChatWithTitle(authorName, title)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) } diff --git a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift index c26c8e5ce1..535c0b7a65 100644 --- a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift @@ -26,7 +26,7 @@ import LottieComponent import BundleIconComponent private protocol ChatEmptyNodeContent { - func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize + func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize } private let titleFont = Font.semibold(15.0) @@ -46,7 +46,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod self.addSubnode(self.textNode) } - func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings @@ -155,7 +155,7 @@ public final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNod let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, []) } - public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let isFirstTime = self.currentTheme == nil if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { @@ -363,7 +363,7 @@ public final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeS let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, []) } - public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings @@ -496,7 +496,7 @@ private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNode self.addSubnode(self.subtitleNode) } - func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings @@ -630,7 +630,7 @@ private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeC self.addSubnode(self.subtitleNode) } - func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings @@ -759,7 +759,7 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC self.shareBusinessLink?(businessLink.url) } - func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { var maxWidth: CGFloat = size.width var centerText = false @@ -1125,7 +1125,7 @@ public final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeCo self.addSubnode(self.textNode) } - public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper) if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { self.currentTheme = interfaceState.theme @@ -1266,7 +1266,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE } } - public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper) let maxWidth = min(270.0, size.width) @@ -1778,7 +1778,7 @@ public final class ChatEmptyNode: ASDisplayNode { } } - public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: Subject, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) { + public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: Subject, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { self.wallpaperBackgroundNode = backgroundNode if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { @@ -1816,7 +1816,7 @@ public final class ChatEmptyNode: ASDisplayNode { contentType = .secret } else if let group = peer as? TelegramGroup, case .creator = group.role { contentType = .group - } else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) { + } else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) && !channel.isMonoForum { contentType = .group } else if let _ = interfaceState.peerNearbyData { contentType = .peerNearby @@ -1835,6 +1835,8 @@ public final class ChatEmptyNode: ASDisplayNode { } } } + } else if let channel = peer as? TelegramChannel, channel.isMonoForum, let sendPaidMessageStars = interfaceState.sendPaidMessageStars { + contentType = .starsRequired(sendPaidMessageStars.value) } else { contentType = .regular } @@ -1910,14 +1912,14 @@ public final class ChatEmptyNode: ASDisplayNode { var contentSize = CGSize() if let contentNode = self.content?.1 { - contentSize = contentNode.updateLayout(interfaceState: interfaceState, subject: subject, size: displayRect.size, transition: contentTransition) + contentSize = contentNode.updateLayout(interfaceState: interfaceState, subject: subject, size: displayRect.size, leftInset: leftInset, rightInset: rightInset, transition: contentTransition) if updateGreetingSticker { self.context.prefetchManager?.prepareNextGreetingSticker() } } - let contentFrame = CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - contentSize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - contentSize.height) / 2.0)), size: contentSize) + let contentFrame = CGRect(origin: CGPoint(x: displayRect.minX + leftInset + floor((displayRect.width - leftInset - rightInset - contentSize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - contentSize.height) / 2.0)), size: contentSize) if let contentNode = self.content?.1 { contentTransition.updateFrame(node: contentNode, frame: contentFrame) } @@ -1978,7 +1980,7 @@ public final class ChatEmptyNode: ASDisplayNode { wallpaperBackgroundNode: backgroundNode, constrainedSize: CGSize(width: size.width - insets.left - insets.right, height: 200.0) ) - let attachedDescriptionFrame = CGRect(origin: CGPoint(x: floor((size.width - attachedDescriptionSize.width) * 0.5), y: contentFrame.maxY + 4.0), size: attachedDescriptionSize) + let attachedDescriptionFrame = CGRect(origin: CGPoint(x: leftInset + floor((size.width - leftInset - rightInset - attachedDescriptionSize.width) * 0.5), y: contentFrame.maxY + 4.0), size: attachedDescriptionSize) transition.updateFrame(node: attachedDescriptionNode, frame: attachedDescriptionFrame) if let (rect, containerSize) = self.absolutePosition { diff --git a/submodules/TelegramUI/Components/Chat/ChatLoadingNode/Sources/ChatLoadingNode.swift b/submodules/TelegramUI/Components/Chat/ChatLoadingNode/Sources/ChatLoadingNode.swift index 760609e3c5..79537cbef6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatLoadingNode/Sources/ChatLoadingNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatLoadingNode/Sources/ChatLoadingNode.swift @@ -126,7 +126,7 @@ public final class ChatLoadingPlaceholderMessageContainer { } if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode { - let avatarFrame = CGRect(origin: CGPoint(x: 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize) + let avatarFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize) transition.updateFrame(node: avatarNode, frame: avatarFrame) transition.updateFrame(node: avatarBorderNode, frame: avatarFrame) @@ -134,7 +134,7 @@ public final class ChatLoadingPlaceholderMessageContainer { avatarOffset += avatarSize.width - 1.0 } - let bubbleFrame = CGRect(origin: CGPoint(x: 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height)) + let bubbleFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height)) transition.updateFrame(node: self.bubbleNode, frame: bubbleFrame) transition.updateFrame(node: self.bubbleBorderNode, frame: bubbleFrame) } @@ -484,7 +484,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode { for messageContainer in self.messageContainers { let messageSize = dimensions[index % 14] - messageContainer.update(size: bounds.size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: 0.0, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition) + messageContainer.update(size: bounds.size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: insets.left, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition) offset += messageSize.height index += 1 } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 1b059bc3c9..5b65447c23 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1423,10 +1423,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.contextSourceNode.contentNode.insertSubnode(strongSelf.textNode.textNode, aboveSubnode: strongSelf.imageNode) } - strongSelf.textNode.textNode.frame = imageFrame + animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: imageFrame, completion: nil) } - strongSelf.imageNode.frame = updatedContentFrame + animation.animator.updateFrame(layer: strongSelf.imageNode.layer, frame: updatedContentFrame, completion: nil) strongSelf.contextSourceNode.contentRect = contextContentFrame strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect @@ -1458,11 +1458,13 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let foregroundColor: UIColor = .clear// = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper) let shimmeringColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderShimmerColor, wallpaper: item.presentationData.theme.wallpaper) strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: animationNodeFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)) - strongSelf.placeholderNode.frame = animationNodeFrame + animation.animator.updateFrame(layer: strongSelf.placeholderNode.layer, frame: animationNodeFrame, completion: nil) } if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode { - strongSelf.animationNode?.frame = animationNodeFrame + if let animationNode = strongSelf.animationNode { + animation.animator.updateFrame(layer: animationNode.layer, frame: animationNodeFrame, completion: nil) + } if let animationNode = strongSelf.animationNode as? AnimatedStickerNode { animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size) @@ -1494,7 +1496,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account) - updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: !incoming ? updatedImageFrame.minX - buttonSize.width - 6.0 : updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0 + imageBottomPadding), size: buttonSize) + animation.animator.updateFrame(layer: updatedShareButtonNode.layer, frame: CGRect(origin: CGPoint(x: !incoming ? updatedImageFrame.minX - buttonSize.width - 6.0 : updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0 + imageBottomPadding), size: buttonSize), completion: nil) } else if let shareButtonNode = strongSelf.shareButtonNode { shareButtonNode.removeFromSupernode() strongSelf.shareButtonNode = nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 97f6229ea4..635239cca1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1926,7 +1926,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI inlineBotNameString = attribute.title } } else if let attribute = attribute as? ReplyMessageAttribute { - if let threadId = firstMessage.threadId, Int32(clamping: threadId) == attribute.messageId.id { + if let threadId = firstMessage.threadId, Int32(clamping: threadId) == attribute.messageId.id, let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum { } else { replyMessage = firstMessage.associatedMessages[attribute.messageId] } @@ -2429,6 +2429,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var mediaInfoSizeApply: (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode?) = (CGSize(), { _ in nil }) var hasTitleAvatar = false + var hasTitleTopicNavigation = false if displayHeader { let bubbleWidthInsets: CGFloat = mosaicRange == nil ? layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right : 0.0 @@ -2439,6 +2440,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if isSidePanelOpen && incoming { hasTitleAvatar = true + hasTitleTopicNavigation = item.chatLocation.threadId == nil } let inlineBotNameColor = messageTheme.accentTextColor @@ -2544,7 +2546,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var nameAvatarSpaceWidth: CGFloat = 0.0 if hasTitleAvatar { headerSize.height += 12.0 - nameAvatarSpaceWidth += 26.0 + 5.0 + 4.0 + 26.0 + nameAvatarSpaceWidth += 26.0 + 5.0 + if hasTitleTopicNavigation { + nameAvatarSpaceWidth += 4.0 + 26.0 + } nameNodeOriginY += 5.0 } @@ -3285,6 +3290,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI contentOrigin: contentOrigin, nameNodeOriginY: nameNodeOriginY + detachedContentNodesHeight + additionalTopHeight, hasTitleAvatar: hasTitleAvatar, + hasTitleTopicNavigation: hasTitleTopicNavigation, authorNameColor: authorNameColor, layoutConstants: layoutConstants, currentCredibilityIcon: currentCredibilityIcon, @@ -3348,6 +3354,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI contentOrigin: CGPoint, nameNodeOriginY: CGFloat, hasTitleAvatar: Bool, + hasTitleTopicNavigation: Bool, authorNameColor: UIColor?, layoutConstants: ChatMessageItemLayoutConstants, currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)?, @@ -3538,21 +3545,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.clippingNode.addSubnode(nameAvatarNode) } - let nameNavigateButton: NameNavigateButton - if let current = strongSelf.nameNavigateButton { - nameNavigateButton = current - } else { - nameNavigateButton = NameNavigateButton(frame: CGRect()) - strongSelf.nameNavigateButton = nameNavigateButton - strongSelf.clippingNode.view.addSubview(nameNavigateButton) - nameNavigateButton.action = { [weak strongSelf] in - guard let strongSelf, let item = strongSelf.item else { - return - } - item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId, nil) - } - } - let nameAvatarFrame = CGRect(origin: CGPoint(x: nameNodeFrame.minX, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)) let nameNavigateFrame = CGRect(origin: CGPoint(x: nameNodeFrame.maxX + 4.0 + nameNavigateButtonOffset, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)) @@ -3563,11 +3555,38 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } nameAvatarNode.updateSize(size: nameAvatarFrame.size) - nameNavigateButton.update(size: nameNavigateFrame.size, color: authorNameColor ?? item.presentationData.theme.theme.chat.message.incoming.accentTextColor) + if hasTitleTopicNavigation { + let nameNavigateButton: NameNavigateButton + if let current = strongSelf.nameNavigateButton { + nameNavigateButton = current + } else { + nameNavigateButton = NameNavigateButton(frame: CGRect()) + strongSelf.nameNavigateButton = nameNavigateButton + strongSelf.clippingNode.view.addSubview(nameNavigateButton) + nameNavigateButton.action = { [weak strongSelf] in + guard let strongSelf, let item = strongSelf.item else { + return + } + item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId, nil) + } + } + nameNavigateButton.update(size: nameNavigateFrame.size, color: authorNameColor ?? item.presentationData.theme.theme.chat.message.incoming.accentTextColor) + } else { + if let nameNavigateButton = strongSelf.nameNavigateButton { + strongSelf.nameNavigateButton = nil + nameNavigateButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak nameNavigateButton] _ in + nameNavigateButton?.removeFromSuperview() + }) + animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: CGRect(origin: CGPoint(x: nameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)), completion: nil) + animation.transition.updateTransformScale(layer: nameNavigateButton.layer, scale: CGPoint(x: 0.001, y: 0.001)) + } + } if animateNameAvatar { animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil) - animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil) + if let nameNavigateButton = strongSelf.nameNavigateButton { + animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil) + } } else { nameAvatarNode.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.minX - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)) animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil) @@ -3576,11 +3595,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI nameAvatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } - nameNavigateButton.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)) - animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil) - if animation.isAnimated { - animation.transition.animateTransformScale(view: nameNavigateButton, from: 0.001) - nameNavigateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + if let nameNavigateButton = strongSelf.nameNavigateButton { + nameNavigateButton.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)) + animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil) + if animation.isAnimated { + animation.transition.animateTransformScale(view: nameNavigateButton, from: 0.001) + nameNavigateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index 454da26df0..83ac51b1f9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -141,7 +141,14 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs } } - if abs(lhsEffectiveTimestamp - rhsEffectiveTimestamp) < Int32(10 * 60) && sameChat && sameAuthor && sameThread && !isPaid { + var isNonMergeablePaid = isPaid + if isNonMergeablePaid { + if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, channel.flags.contains(.isMonoforum) { + isNonMergeablePaid = false + } + } + + if abs(lhsEffectiveTimestamp - rhsEffectiveTimestamp) < Int32(10 * 60) && sameChat && sameAuthor && sameThread && !isNonMergeablePaid { if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, case .group = channel.info, lhsEffectiveAuthor?.id == channel.id, !lhs.effectivelyIncoming(accountPeerId) { return .none } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift index 772f6cbde2..386b50a569 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift @@ -178,6 +178,9 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { if firstMessage.id.peerId.isRepliesOrVerificationCodes, let author = firstMessage.forwardInfo?.author { avatarPeer = EnginePeer(author) } + if case let .channel(channel) = avatarPeer, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = firstMessage.peers[linkedMonoforumId] as? TelegramChannel { + avatarPeer = .channel(mainChannel) + } self.avatarNode.setPeer(context: item.context, theme: presentationData.theme, peer: avatarPeer, overrideImage: peer.id == item.context.account.peerId ? .savedMessagesIcon : nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index d3f6727a94..cb1cce2204 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -995,7 +995,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { let placeholderFrame = updatedImageFrame.insetBy(dx: innerImageInset, dy: innerImageInset) strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: placeholderFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency) - strongSelf.placeholderNode.frame = placeholderFrame + animation.animator.updateFrame(layer: strongSelf.placeholderNode.layer, frame: placeholderFrame, completion: nil) } strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize) @@ -1067,7 +1067,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.contextSourceNode.contentNode.addSubnode(threadInfoNode) } let threadInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 6.0) : (params.width - params.rightInset - threadInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0), size: threadInfoSize) - threadInfoNode.frame = threadInfoFrame + animation.animator.updateFrame(layer: threadInfoNode.layer, frame: threadInfoFrame, completion: nil) headersOffset += threadInfoSize.height + 10.0 } else if let replyInfoNode = strongSelf.replyInfoNode { @@ -1120,7 +1120,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { } } let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0 + 5.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0 - 5.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: forwardInfoSize) - forwardInfoNode.frame = forwardInfoFrame + animation.animator.updateFrame(layer: forwardInfoNode.layer, frame: forwardInfoFrame, completion: nil) messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height + 8.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift index 06dcda5e87..b5b43da910 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift @@ -590,6 +590,8 @@ public final class ChatSideTopicsPanel: Component { public final class View: UIView { private let scrollView: ScrollView + private let scrollContainerView: UIView + private let scrollViewMask: UIImageView private let background = ComponentView() private let separatorLayer: SimpleLayer @@ -612,13 +614,20 @@ public final class ChatSideTopicsPanel: Component { self.selectedLineView = UIImageView() self.scrollView = ScrollView(frame: CGRect()) + self.scrollContainerView = UIView() + self.scrollViewMask = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [ + UIColor(white: 1.0, alpha: 0.0), + UIColor(white: 1.0, alpha: 1.0) + ], locations: [0.0, 1.0], direction: .vertical)?.stretchableImage(withLeftCapWidth: 0, topCapHeight: 8)) + self.scrollContainerView.mask = self.scrollViewMask + self.separatorLayer = SimpleLayer() super.init(frame: frame) self.scrollView.delaysContentTouches = false self.scrollView.canCancelContentTouches = true - self.scrollView.clipsToBounds = false + self.scrollView.clipsToBounds = true self.scrollView.contentInsetAdjustmentBehavior = .never if #available(iOS 13.0, *) { self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false @@ -629,7 +638,8 @@ public final class ChatSideTopicsPanel: Component { self.scrollView.alwaysBounceVertical = false self.scrollView.scrollsToTop = false - self.addSubview(self.scrollView) + self.addSubview(self.scrollContainerView) + self.scrollContainerView.addSubview(self.scrollView) self.scrollView.addSubview(self.selectedLineView) } @@ -709,7 +719,7 @@ public final class ChatSideTopicsPanel: Component { if let backgroundView = self.background.view { if backgroundView.superview == nil { - self.insertSubview(backgroundView, belowSubview: self.scrollView) + self.insertSubview(backgroundView, at: 0) } transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize)) } @@ -735,20 +745,9 @@ public final class ChatSideTopicsPanel: Component { let itemSpacing: CGFloat = 24.0 - var contentSize = CGSize(width: panelWidth, height: 0.0) - contentSize.height += containerInsets.top - - var validIds: [Item.Id] = [] - var isFirst = true - var selectedItemFrame: CGRect? + var topContainerInset: CGFloat = containerInsets.top do { - if isFirst { - isFirst = false - } else { - contentSize.height += itemSpacing - } - var itemTransition = transition var animateIn = false let itemView: TabItemView @@ -764,11 +763,11 @@ public final class ChatSideTopicsPanel: Component { component.togglePanel() }) self.tabItemView = itemView - self.scrollView.addSubview(itemView) + self.addSubview(itemView) } let itemSize = itemView.update(context: component.context, theme: component.theme, width: panelWidth, transition: .immediate) - let itemFrame = CGRect(origin: CGPoint(x: containerInsets.left, y: contentSize.height), size: itemSize) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: itemSize) itemTransition.setPosition(layer: itemView.layer, position: itemFrame.center) itemTransition.setBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) @@ -778,10 +777,17 @@ public final class ChatSideTopicsPanel: Component { transition.containedViewLayoutTransition.animateTransformScale(view: itemView, from: 0.001) } - contentSize.height += itemSize.height - contentSize.height -= 20.0 + topContainerInset += itemSize.height + topContainerInset -= 24.0 } + var contentSize = CGSize(width: panelWidth, height: 0.0) + contentSize.height += 36.0 + + var validIds: [Item.Id] = [] + var isFirst = true + var selectedItemFrame: CGRect? + do { if isFirst { isFirst = false @@ -931,7 +937,11 @@ public final class ChatSideTopicsPanel: Component { contentSize.height += containerInsets.bottom - let scrollSize = CGSize(width: availableSize.width, height: availableSize.height) + let scrollSize = CGSize(width: availableSize.width, height: availableSize.height - topContainerInset) + + self.scrollContainerView.frame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: scrollSize) + self.scrollViewMask.frame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: scrollSize) + if self.scrollView.bounds.size != scrollSize { self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize) } diff --git a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift index f84b0ddd68..a6811b04db 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift @@ -145,6 +145,8 @@ final class ChatIntroItemComponent: Component { backgroundNode: backgroundNode, size: size, insets: UIEdgeInsets(), + leftInset: 0.0, + rightInset: 0.0, transition: .immediate ) diff --git a/submodules/TelegramUI/Sources/ChatControllerContentData.swift b/submodules/TelegramUI/Sources/ChatControllerContentData.swift index 09d0604f0a..3614bdb994 100644 --- a/submodules/TelegramUI/Sources/ChatControllerContentData.swift +++ b/submodules/TelegramUI/Sources/ChatControllerContentData.swift @@ -810,9 +810,16 @@ extension ChatControllerImpl { contactStatus = ChatContactStatus(canAddContact: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) if let channel = peerView.peers[peerView.peerId] as? TelegramChannel { - if channel.flags.contains(.isCreator) || channel.adminRights != nil { + if channel.isMonoForum { + if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = peerView.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething) { + } else { + sendPaidMessageStars = channel.sendPaidMessageStars + } } else { - sendPaidMessageStars = channel.sendPaidMessageStars + if channel.flags.contains(.isCreator) || channel.adminRights != nil { + } else { + sendPaidMessageStars = channel.sendPaidMessageStars + } } } } @@ -1857,8 +1864,6 @@ extension ChatControllerImpl { } if globalRemainingUnreadChatCount > 0 { strongSelf.initialNavigationBadge = "\(globalRemainingUnreadChatCount)" - } else { - strongSelf.initialNavigationBadge = "" } } } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index ea0cd685b3..0d3d298a14 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -195,7 +195,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { private var emptyNode: ChatEmptyNode? private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType? private var didDisplayEmptyGreeting = false - private var validEmptyNodeLayout: (CGSize, UIEdgeInsets)? + private var validEmptyNodeLayout: (CGSize, UIEdgeInsets, CGFloat, CGFloat)? var restrictedNode: ChatRecentActionsEmptyNode? private(set) var validLayout: (ContainerViewLayout, CGFloat)? @@ -1019,7 +1019,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { self.contentContainerNode.contentNode.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer) } - if let (size, insets) = self.validEmptyNodeLayout { + if let (size, insets, leftInset, rightInset) = self.validEmptyNodeLayout { let mappedType: ChatEmptyNode.Subject.EmptyType switch emptyType { case .generic: @@ -1033,7 +1033,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { case .botInfo: mappedType = .botInfo } - emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, transition: .immediate) + emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, leftInset: leftInset, rightInset: rightInset, transition: .immediate) emptyNode.frame = CGRect(origin: CGPoint(), size: size) } if animated { @@ -2046,6 +2046,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) } + if immediatelyLayoutLeftPanelNodeAndAnimateAppearance || dismissedLeftPanel != nil || immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || dismissedTitleTopicsAccessoryPanelNode != nil { + self.historyNode.resetScrolledToItem() + } + if let blurredHistoryNode = self.blurredHistoryNode { transition.updateFrame(node: blurredHistoryNode, frame: contentBounds) } @@ -2199,28 +2203,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } - var emptyNodeInsets = insets - emptyNodeInsets.bottom += inputPanelsHeight - self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets) - if let emptyNode = self.emptyNode, let emptyType = self.emptyType { - let mappedType: ChatEmptyNode.Subject.EmptyType - switch emptyType { - case .generic: - mappedType = .generic - case .joined: - mappedType = .joined - case .clearedHistory: - mappedType = .clearedHistory - case .topic: - mappedType = .topic - case .botInfo: - mappedType = .botInfo - } - emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, transition: transition) - transition.updateFrame(node: emptyNode, frame: contentBounds) - emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) - } - var contentBottomInset: CGFloat = inputPanelsHeight + 4.0 if let scrollContainerNode = self.scrollContainerNode { @@ -2239,10 +2221,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { let visibleAreaInset = UIEdgeInsets(top: containerInsets.top, left: 0.0, bottom: containerInsets.bottom + inputPanelsHeight, right: 0.0) self.visibleAreaInset = visibleAreaInset - self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition) + + var loadingNodeInsets = visibleAreaInset + loadingNodeInsets.left = layout.safeInsets.left + loadingNodeInsets.right = layout.safeInsets.right + if let leftPanelSize { + loadingNodeInsets.left += leftPanelSize.width + } + self.loadingNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, transition: transition) if let loadingPlaceholderNode = self.loadingPlaceholderNode { - loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, metrics: layout.metrics, transition: transition) + loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, metrics: layout.metrics, transition: transition) loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) } @@ -2293,6 +2282,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { listInsets.left += leftPanelSize.width } + var emptyNodeInsets = insets + emptyNodeInsets.bottom += inputPanelsHeight + self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets, listInsets.left, listInsets.right) + if let emptyNode = self.emptyNode, let emptyType = self.emptyType { + let mappedType: ChatEmptyNode.Subject.EmptyType + switch emptyType { + case .generic: + mappedType = .generic + case .joined: + mappedType = .joined + case .clearedHistory: + mappedType = .clearedHistory + case .topic: + mappedType = .topic + case .botInfo: + mappedType = .botInfo + } + emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, leftInset: listInsets.left, rightInset: listInsets.right, transition: transition) + transition.updateFrame(node: emptyNode, frame: contentBounds) + emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) + } + var displayTopDimNode = false let ensureTopInsetForOverlayHighlightedItems: CGFloat? = nil var expandTopDimNode = false @@ -3283,71 +3294,16 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { guard let self else { return nil } - guard let peerId = self.chatPresentationInterfaceState.chatLocation.peerId else { + guard let peer = self.chatPresentationInterfaceState.renderedPeer?.peer else { + return nil + } + if !peer.isForumOrMonoForum { return nil } - let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId) + let threadListSignal: Signal = context.sharedContext.subscribeChatListData(context: self.context, location: peer.isMonoForum ? .savedMessagesChats(peerId: peer.id) : .forum(peerId: peer.id)) - let threadListSignal: Signal = self.context.account.postbox.combinedView(keys: [viewKey]) - |> map { views -> EngineChatList? in - guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { - preconditionFailure() - } - - var items: [EngineChatList.Item] = [] - for item in view.items { - guard let sourcePeer = item.peer else { - continue - } - - let sourceId = PeerId(item.id) - - var messages: [EngineMessage] = [] - if let topMessage = item.topMessage { - messages.append(EngineMessage(topMessage)) - } - - let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) - - items.append(EngineChatList.Item( - id: .chatList(sourceId), - index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), - messages: messages, - readCounters: EnginePeerReadCounters( - incomingReadId: 0, outgoingReadId: 0, count: Int32(item.unreadCount), markedUnread: item.markedUnread), - isMuted: false, - draft: nil, - threadData: nil, - renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), - presence: nil, - hasUnseenMentions: false, - hasUnseenReactions: false, - forumTopicData: nil, - topForumTopicItems: [], - hasFailed: false, - isContact: false, - autoremoveTimeout: nil, - storyStats: nil, - displayAsTopicList: false, - isPremiumRequiredToMessage: false, - mediaDraftContentType: nil - )) - } - - let list = EngineChatList( - items: items.reversed(), - groupItems: [], - additionalItems: [], - hasEarlier: false, - hasLater: false, - isLoading: view.isLoading - ) - - return list - } - - return threadListSignal + return threadListSignal |> map(Optional.init) }, loadMoreSearchResults: { [weak self] in guard let self, let controller = self.controller else { diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 2c5c8dd5cf..9add155f1d 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -135,6 +135,20 @@ func chatHistoryEntriesForView( continue loop } } + } else if case .peer = location { + for media in message.media { + if let action = media as? TelegramMediaAction, case .groupCreated = action.action { + var chatPeer: Peer? + for entry in view.additionalData { + if case let .peer(_, peer) = entry { + chatPeer = peer + } + } + if let channel = chatPeer as? TelegramChannel, channel.isMonoForum { + continue loop + } + } + } } count += 1 diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index a6c73537f1..1513f1b4fd 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -241,7 +241,7 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa return panel } } - } else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil { + } else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, (channel.flags.contains(.displayForumAsTabs) || context.sharedContext.immediateExperimentalUISettings.allForumsHaveTabs), chatPresentationInterfaceState.search == nil { let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId { if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { diff --git a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift index a5fb16ae2d..ed821174ce 100644 --- a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift @@ -182,7 +182,7 @@ private final class CustomBadgeComponent: Component { } } -final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode, ASScrollViewDelegate { +final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode { private struct Params: Equatable { var width: CGFloat var leftInset: CGFloat @@ -736,6 +736,8 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C private let isMonoforum: Bool private let scrollView: ScrollView + private let scrollViewContainer: UIView + private let scrollViewMask: UIImageView private var params: Params? @@ -756,12 +758,18 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C self.selectedLineView = UIImageView() self.scrollView = ScrollView(frame: CGRect()) + self.scrollViewMask = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [ + UIColor(white: 1.0, alpha: 0.0), + UIColor(white: 1.0, alpha: 1.0) + ], locations: [0.0, 1.0], direction: .horizontal)?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 0)) + + self.scrollViewContainer = UIView() super.init() self.scrollView.delaysContentTouches = false self.scrollView.canCancelContentTouches = true - self.scrollView.clipsToBounds = false + self.scrollView.clipsToBounds = true self.scrollView.contentInsetAdjustmentBehavior = .never if #available(iOS 13.0, *) { self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false @@ -771,9 +779,11 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C self.scrollView.alwaysBounceHorizontal = false self.scrollView.alwaysBounceVertical = false self.scrollView.scrollsToTop = false - self.scrollView.delegate = self.wrappedScrollViewDelegate - self.view.addSubview(self.scrollView) + self.scrollViewContainer.addSubview(self.scrollView) + self.scrollViewContainer.mask = self.scrollViewMask + + self.view.addSubview(self.scrollViewContainer) self.scrollView.addSubview(self.selectedLineView) @@ -843,20 +853,9 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 16.0, bottom: 0.0, right: params.rightInset + 16.0) let itemSpacing: CGFloat = 24.0 - var contentSize = CGSize(width: 0.0, height: panelHeight) - contentSize.width += containerInsets.left + 8.0 - - var validIds: [Item.Id] = [] - var isFirst = true - var selectedItemFrame: CGRect? + var leftContentInset: CGFloat = containerInsets.left + 8.0 do { - if isFirst { - isFirst = false - } else { - contentSize.width += itemSpacing - } - var itemTransition = transition var animateIn = false let itemView: TabItemView @@ -872,11 +871,11 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C self.interfaceInteraction?.toggleChatSidebarMode() }) self.tabItemView = itemView - self.scrollView.addSubview(itemView) + self.view.addSubview(itemView) } let itemSize = itemView.update(context: self.context, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) - let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize) + let itemFrame = CGRect(origin: CGPoint(x: leftContentInset, y: -5.0), size: itemSize) itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center) itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) @@ -886,9 +885,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C transition.animateTransformScale(view: itemView, from: 0.001) } - contentSize.width += itemSize.width + leftContentInset += itemSize.width + 8.0 } + var contentSize = CGSize(width: itemSpacing - 8.0, height: panelHeight) + + var validIds: [Item.Id] = [] + var isFirst = true + var selectedItemFrame: CGRect? + do { if isFirst { isFirst = false @@ -1034,9 +1039,12 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C contentSize.width += containerInsets.right - let scrollSize = CGSize(width: params.width, height: contentSize.height) + let scrollSize = CGSize(width: params.width - leftContentInset, height: contentSize.height) + self.scrollViewContainer.frame = CGRect(origin: CGPoint(x: leftContentInset, y: 0.0), size: scrollSize) + self.scrollViewMask.frame = CGRect(origin: CGPoint(), size: scrollSize) + if self.scrollView.bounds.size != scrollSize { - self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize) + self.scrollView.frame = CGRect(origin: CGPoint(), size: scrollSize) } if self.scrollView.contentSize != contentSize { self.scrollView.contentSize = contentSize diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index add15899cf..5a76c30635 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -167,9 +167,13 @@ final class ChatTranslationPanelNode: ASDisplayNode { let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight)) if let icon = self.buttonIconNode.image { let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing + buttonPadding * 2.0, height: panelHeight) - self.button.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - buttonSize.width) / 2.0), y: 0.0), size: buttonSize) - self.buttonIconNode.frame = CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size) - self.buttonTextNode.frame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize) + transition.updateFrame(node: self.button, frame: CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - buttonSize.width) / 2.0), y: 0.0), size: buttonSize)) + + transition.updateFrame(node: self.buttonIconNode, frame: CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size)) + + let buttonTextFrame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize) + transition.updatePosition(node: self.buttonTextNode, position: buttonTextFrame.center) + self.buttonTextNode.bounds = CGRect(origin: CGPoint(), size: buttonTextFrame.size) } transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index 23a121900f..478a10afa0 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -42,16 +42,20 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam viewForumAsMessages = .single(false) } } else if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) { - viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) - |> take(1) - |> map { combinedView in - guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else { - return false - } - if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { - return true - } else { - return false + if channel.flags.contains(.displayForumAsTabs) { + viewForumAsMessages = .single(true) + } else { + viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) + |> take(1) + |> map { combinedView in + guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else { + return false + } + if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { + return true + } else { + return false + } } } } else if case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId { diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 40951c029f..a887ae8853 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -64,7 +64,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh self.wallpaperBackgroundNode.updateLayout(size: size, displayMode: needsTiling ? .aspectFit : .aspectFill, transition: transition) transition.updateFrame(node: self.wallpaperBackgroundNode, frame: contentBounds) - self.emptyNode.updateLayout(interfaceState: self.presentationInterfaceState, subject: .detailsPlaceholder, loadingNode: nil, backgroundNode: self.wallpaperBackgroundNode, size: contentBounds.size, insets: .zero, transition: transition) + self.emptyNode.updateLayout(interfaceState: self.presentationInterfaceState, subject: .detailsPlaceholder, loadingNode: nil, backgroundNode: self.wallpaperBackgroundNode, size: contentBounds.size, insets: .zero, leftInset: 0.0, rightInset: 0.0, transition: transition) transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: .zero, size: size)) self.emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) } diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index f98202cd0b..b453452d14 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -66,6 +66,7 @@ public struct ExperimentalUISettings: Codable, Equatable { public var fakeAds: Bool public var conferenceDebug: Bool public var checkSerializedData: Bool + public var allForumsHaveTabs: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -109,7 +110,8 @@ public struct ExperimentalUISettings: Codable, Equatable { devRequests: false, fakeAds: false, conferenceDebug: false, - checkSerializedData: false + checkSerializedData: false, + allForumsHaveTabs: false ) } @@ -154,7 +156,8 @@ public struct ExperimentalUISettings: Codable, Equatable { devRequests: Bool, fakeAds: Bool, conferenceDebug: Bool, - checkSerializedData: Bool + checkSerializedData: Bool, + allForumsHaveTabs: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -197,6 +200,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.fakeAds = fakeAds self.conferenceDebug = conferenceDebug self.checkSerializedData = checkSerializedData + self.allForumsHaveTabs = allForumsHaveTabs } public init(from decoder: Decoder) throws { @@ -243,6 +247,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? false self.conferenceDebug = try container.decodeIfPresent(Bool.self, forKey: "conferenceDebug") ?? false self.checkSerializedData = try container.decodeIfPresent(Bool.self, forKey: "checkSerializedData") ?? false + self.allForumsHaveTabs = try container.decodeIfPresent(Bool.self, forKey: "allForumsHaveTabs") ?? false } public func encode(to encoder: Encoder) throws { @@ -289,6 +294,7 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds") try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug") try container.encodeIfPresent(self.checkSerializedData, forKey: "checkSerializedData") + try container.encodeIfPresent(self.allForumsHaveTabs, forKey: "allForumsHaveTabs") } }