From 86d0d9e9b0b6e2082c69d6f67cee59f03c62032b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 7 Sep 2021 22:25:51 +0400 Subject: [PATCH] UI fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 11 +- submodules/AdUI/BUILD | 25 ++ submodules/AdUI/Sources/AdInfoScreen.swift | 223 ++++++++++++++++++ .../Sources/BotCheckoutControllerNode.swift | 8 +- .../ChatListUI/Sources/ChatContextMenus.swift | 4 +- .../ContextUI/Sources/ContextController.swift | 62 +++-- .../ContextUI/Sources/PeekController.swift | 7 +- submodules/Display/Source/ListView.swift | 2 +- submodules/Display/Source/NavigationBar.swift | 12 +- .../Items/UniversalVideoGalleryItem.swift | 4 +- .../Sources/PeerReportController.swift | 2 +- .../Sources/VoiceChatController.swift | 12 +- .../TelegramEngine/Messages/AdMessages.swift | 2 +- submodules/TelegramUI/BUILD | 1 + .../TelegramUI/Sources/ChatController.swift | 16 +- .../Sources/ChatControllerNode.swift | 43 +++- .../Sources/ChatHistoryListNode.swift | 12 +- .../ChatInterfaceStateContextMenus.swift | 101 ++++---- .../Sources/PeerInfo/PeerInfoScreen.swift | 10 +- submodules/TgVoipWebrtc/tgcalls | 2 +- 20 files changed, 460 insertions(+), 99 deletions(-) create mode 100644 submodules/AdUI/BUILD create mode 100644 submodules/AdUI/Sources/AdInfoScreen.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 799d68451e..7edfa28d3c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -6766,9 +6766,16 @@ Sorry for the inconvenience."; "Channel.AdminLog.MessageChangedThemeRemove" = "%1$@ disabled chat theme"; "SponsoredMessageMenu.Info" = "What are sponsored\nmessages?"; -"SponsoredMessageInfo.Text" = "See https://telegram.org"; +"SponsoredMessageInfoScreen.Title" = "What are sponsored messages?"; +"SponsoredMessageInfoScreen.Text" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message. + +Unline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that. + +Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at: +[url] +Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together."; "SponsoredMessageInfo.Action" = "Learn More"; -"SponsoredMessageInfo.ActionUrl" = "https://telegram.org"; +"SponsoredMessageInfo.ActionUrl" = "https://telegram.org/ads"; "Chat.NavigationNoChannels" = "You have no unread channels"; diff --git a/submodules/AdUI/BUILD b/submodules/AdUI/BUILD new file mode 100644 index 0000000000..c9ec77d57d --- /dev/null +++ b/submodules/AdUI/BUILD @@ -0,0 +1,25 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AdUI", + module_name = "AdUI", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/Postbox:Postbox", + "//submodules/TelegramCore:TelegramCore", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/TelegramUIPreferences:TelegramUIPreferences", + "//submodules/AccountContext:AccountContext", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/AdUI/Sources/AdInfoScreen.swift b/submodules/AdUI/Sources/AdInfoScreen.swift new file mode 100644 index 0000000000..61cf7923de --- /dev/null +++ b/submodules/AdUI/Sources/AdInfoScreen.swift @@ -0,0 +1,223 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext + +public final class AdInfoScreen: ViewController { + private final class Node: ViewControllerTracingNode { + private weak var controller: AdInfoScreen? + private let context: AccountContext + private var presentationData: PresentationData + + private let titleNode: ImmediateTextNode + + private final class LinkNode: HighlightableButtonNode { + private let backgroundNode: ASImageNode + private let textNode: ImmediateTextNode + + private let action: () -> Void + + init(text: String, color: UIColor, action: @escaping () -> Void) { + self.action = action + + self.backgroundNode = ASImageNode() + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 10.0, color: nil, strokeColor: color, strokeWidth: 1.0, backgroundColor: nil) + + self.textNode = ImmediateTextNode() + self.textNode.maximumNumberOfLines = 1 + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: color) + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.textNode) + + self.addTarget(self, action:#selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc private func pressed() { + self.action() + } + + func update(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { + let size = CGSize(width: width, height: 44.0) + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) + + let textSize = self.textNode.updateLayout(CGSize(width: width - 8.0 * 2.0, height: 44.0)) + transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize)) + + return size.height + } + } + + private enum Item { + case text(ImmediateTextNode) + case link(LinkNode) + } + private let items: [Item] + + private let scrollNode: ASScrollNode + + init(controller: AdInfoScreen, context: AccountContext) { + self.controller = controller + self.context = context + + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + + self.titleNode = ImmediateTextNode() + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor) + + self.scrollNode = ASScrollNode() + self.scrollNode.view.showsVerticalScrollIndicator = true + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.scrollsToTop = true + self.scrollNode.view.delaysContentTouches = false + self.scrollNode.view.canCancelContentTouches = true + if #available(iOS 11.0, *) { + self.scrollNode.view.contentInsetAdjustmentBehavior = .never + } + + var openUrl: (() -> Void)? + + let rawText = self.presentationData.strings.SponsoredMessageInfoScreen_Text + var items: [Item] = [] + var didAddUrl = false + for component in rawText.components(separatedBy: "[url]") { + var itemText = component + if itemText.hasPrefix("\n") { + itemText = String(itemText[itemText.index(itemText.startIndex, offsetBy: 1)...]) + } + if itemText.hasSuffix("\n") { + itemText = String(itemText[..) + + func getActionsMinHeight() -> CGFloat? + func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) func dismiss(completion: (() -> Void)?) } @@ -130,6 +131,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi let contentReady = Promise() private var currentItems: [ContextMenuItem]? + private var currentActionsMinHeight: CGFloat? private var validLayout: ContainerViewLayout? @@ -448,7 +450,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi self.itemsDisposable.set((items |> deliverOnMainQueue).start(next: { [weak self] items in - self?.setItems(items: items) + self?.setItems(items: items, minHeight: nil) })) switch source { @@ -1167,24 +1169,33 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi intermediateCompletion() }) } + + func getActionsMinHeight() -> CGFloat? { + if !self.actionsContainerNode.bounds.height.isZero { + return self.actionsContainerNode.bounds.height + } else { + return nil + } + } - func setItemsSignal(items: Signal<[ContextMenuItem], NoError>) { + func setItemsSignal(items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) { self.items = items self.itemsDisposable.set((items |> deliverOnMainQueue).start(next: { [weak self] items in guard let strongSelf = self else { return } - strongSelf.setItems(items: items) + strongSelf.setItems(items: items, minHeight: minHeight) })) } - private func setItems(items: [ContextMenuItem]) { + private func setItems(items: [ContextMenuItem], minHeight: CGFloat?) { if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true { return } self.currentItems = items + self.currentActionsMinHeight = minHeight let previousActionsContainerNode = self.actionsContainerNode self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in @@ -1282,17 +1293,19 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view) - let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) - self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize) + let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) + let adjustedActionsSize = CGSize(width: realActionsSize.width, height: max(realActionsSize.height, self.currentActionsMinHeight ?? 0.0)) + + self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize) let contentSize = originalProjectedContentViewFrame.1.size self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition) - let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height) + let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - adjustedActionsSize.height) let originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) let preferredActionsX = originalProjectedContentViewFrame.1.minX - var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize) + var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - adjustedActionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: realActionsSize) let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX let originalContentY = originalProjectedContentViewFrame.1.minY @@ -1307,7 +1320,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } var contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset) - contentHeight = max(contentHeight, actionsSize.height + originalActionsFrame.minY + actionsBottomInset) + contentHeight = max(contentHeight, adjustedActionsSize.height + originalActionsFrame.minY + actionsBottomInset) var overflowOffset: CGFloat var contentContainerFrame: CGRect @@ -1360,26 +1373,28 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view) - let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) - self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize) + let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) + let adjustedActionsSize = CGSize(width: realActionsSize.width, height: max(realActionsSize.height, self.currentActionsMinHeight ?? 0.0)) + + self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize) let contentSize = originalProjectedContentViewFrame.1.size self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition) - let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height) + let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - adjustedActionsSize.height) let preferredActionsX: CGFloat let originalActionsY: CGFloat if centerVertically { originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) - preferredActionsX = originalProjectedContentViewFrame.1.maxX - actionsSize.width + preferredActionsX = originalProjectedContentViewFrame.1.maxX - adjustedActionsSize.width } else if keepInPlace { - originalActionsY = originalProjectedContentViewFrame.1.minY - contentActionsSpacing - actionsSize.height - preferredActionsX = max(actionsSideInset, originalProjectedContentViewFrame.1.maxX - actionsSize.width) + originalActionsY = originalProjectedContentViewFrame.1.minY - contentActionsSpacing - adjustedActionsSize.height + preferredActionsX = max(actionsSideInset, originalProjectedContentViewFrame.1.maxX - adjustedActionsSize.width) } else { originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) preferredActionsX = originalProjectedContentViewFrame.1.minX } - var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize) + var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - adjustedActionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: realActionsSize) let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX let originalContentY: CGFloat if keepInPlace { @@ -1957,11 +1972,18 @@ public final class ContextController: ViewController, StandalonePresentableContr self.controllerNode.animateIn() } } + + public func getActionsMinHeight() -> CGFloat? { + if self.isNodeLoaded { + return self.controllerNode.getActionsMinHeight() + } + return nil + } - public func setItems(_ items: Signal<[ContextMenuItem], NoError>) { + public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat? = nil) { self.items = items if self.isNodeLoaded { - self.controllerNode.setItemsSignal(items: items) + self.controllerNode.setItemsSignal(items: items, minHeight: minHeight) } } diff --git a/submodules/ContextUI/Sources/PeekController.swift b/submodules/ContextUI/Sources/PeekController.swift index 5e4176b9ae..7f68dd1682 100644 --- a/submodules/ContextUI/Sources/PeekController.swift +++ b/submodules/ContextUI/Sources/PeekController.swift @@ -33,9 +33,12 @@ extension PeekControllerTheme { public final class PeekController: ViewController, ContextControllerProtocol { public var useComplexItemsTransitionAnimation: Bool = false public var immediateItemsTransitionAnimation = false + + public func getActionsMinHeight() -> CGFloat? { + return nil + } - public func setItems(_ items: Signal<[ContextMenuItem], NoError>) { - + public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) { } private var controllerNode: PeekControllerNode { diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 0750056871..c462c472ba 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -151,7 +151,7 @@ private func cancelContextGestures(view: UIView) { open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate { public final let scroller: ListViewScroller - private final var visibleSize: CGSize = CGSize() + public private(set) final var visibleSize: CGSize = CGSize() public private(set) final var insets = UIEdgeInsets() public final var visualInsets: UIEdgeInsets? public private(set) final var headerInsets = UIEdgeInsets() diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index bd41443373..94fa74f893 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -1,8 +1,6 @@ import UIKit import AsyncDisplayKit -private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) - private var backArrowImageCache: [Int32: UIImage] = [:] public final class SparseNode: ASDisplayNode { @@ -236,6 +234,8 @@ open class NavigationBar: ASDisplayNode { public static var defaultSecondaryContentHeight: CGFloat { return 38.0 } + + public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) var presentationData: NavigationBarPresentationData @@ -388,7 +388,7 @@ open class NavigationBar: ASDisplayNode { private var title: String? { didSet { if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.accessibilityLabel = title if self.titleNode.supernode == nil { self.buttonsContainerNode.addSubnode(self.titleNode) @@ -837,7 +837,7 @@ open class NavigationBar: ASDisplayNode { self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.accessibilityLabel = title } self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor @@ -919,7 +919,7 @@ open class NavigationBar: ASDisplayNode { self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.accessibilityLabel = title } self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor @@ -1191,7 +1191,7 @@ open class NavigationBar: ASDisplayNode { } } else if let title = self.title { let node = ImmediateTextNode() - node.attributedText = NSAttributedString(string: title, font: titleFont, textColor: foregroundColor) + node.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: foregroundColor) return node } else { return nil diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index d66f332707..95e12d23b3 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -2087,7 +2087,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { return } - c.setItems(strongSelf.contextMenuSpeedItems()) + c.setItems(strongSelf.contextMenuSpeedItems(), minHeight: nil) }))) if let (message, _, _) = strongSelf.contentInfo() { @@ -2206,7 +2206,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { c.dismiss(completion: nil) return } - c.setItems(strongSelf.contextMenuMainItems()) + c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil) }))) return items diff --git a/submodules/PeerInfoUI/Sources/PeerReportController.swift b/submodules/PeerInfoUI/Sources/PeerReportController.swift index 9b628f73fd..11606cc045 100644 --- a/submodules/PeerInfoUI/Sources/PeerReportController.swift +++ b/submodules/PeerInfoUI/Sources/PeerReportController.swift @@ -158,7 +158,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro backAction(c) }))) } - contextController.setItems(.single(items)) + contextController.setItems(.single(items), minHeight: nil) } else { contextController?.dismiss(completion: nil) parent.view.endEditing(true) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 1252e78f81..d58939ab38 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -2473,7 +2473,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuDisplayAsItems()) + c.setItems(strongSelf.contextMenuDisplayAsItems(), minHeight: nil) }))) items.append(.separator) break @@ -2506,7 +2506,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuAudioItems()) + c.setItems(strongSelf.contextMenuAudioItems(), minHeight: nil) }))) } @@ -2543,7 +2543,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuPermissionItems()) + c.setItems(strongSelf.contextMenuPermissionItems(), minHeight: nil) }))) } } @@ -2795,7 +2795,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems()) + c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil) }))) return .single(items) } @@ -2890,7 +2890,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems()) + c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil) }))) return items } @@ -2936,7 +2936,7 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems()) + c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil) }))) } return .single(items) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index e6603c5c21..10519e69eb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -260,7 +260,7 @@ private class AdMessagesHistoryContextImpl { self.state.set(CachedState.getCached(postbox: account.postbox, peerId: peerId) |> mapToSignal { cachedState -> Signal in - if let cachedState = cachedState, cachedState.timestamp >= Int32(Date().timeIntervalSince1970) - 5 * 60 { + if false, let cachedState = cachedState, cachedState.timestamp >= Int32(Date().timeIntervalSince1970) - 5 * 60 { return account.postbox.transaction { transaction -> State in return State(messages: cachedState.messages.map { message in return message.toMessage(peerId: peerId, transaction: transaction) diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 7e22f4728c..1c767bcd77 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -239,6 +239,7 @@ swift_library( "//submodules/GradientBackground:GradientBackground", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", "//submodules/ComponentFlow:ComponentFlow", + "//submodules/AdUI:AdUI", ] + select({ "@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6ddd9fa173..216ca639e1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3438,7 +3438,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) if case .standard(previewing: false) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { - if strongSelf.nextChannelToReadDisposable == nil { + var isRegularChat = false + if let subject = subject { + if case .message = subject { + isRegularChat = true + } + } else { + isRegularChat = true + } + if isRegularChat, strongSelf.nextChannelToReadDisposable == nil { strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, chatListFilterId: strongSelf.currentChatListFilter, getFilterPredicate: chatListFilterPredicate), ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) @@ -6487,7 +6495,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }))) - contextController.setItems(.single(contextItems)) + contextController.setItems(.single(contextItems), minHeight: nil) } return } else { @@ -6506,7 +6514,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }))) - contextController.setItems(.single(contextItems)) + contextController.setItems(.single(contextItems), minHeight: nil) return } else { @@ -12618,7 +12626,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if canDisplayContextMenu, let contextController = contextController { - contextController.setItems(.single(contextItems)) + contextController.setItems(.single(contextItems), minHeight: nil) } else { actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 7c0af628ab..aac002769d 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2611,6 +2611,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState let titleAccessoryPanelSnapshot: UIView? let navigationBarHeight: CGFloat + let inputPanelNodeSnapshot: UIView? + let inputPanelOverscrollNodeSnapshot: UIView? fileprivate init( historySnapshotState: ChatHistoryListNode.SnapshotState, @@ -2618,7 +2620,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?, navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState, titleAccessoryPanelSnapshot: UIView?, - navigationBarHeight: CGFloat + navigationBarHeight: CGFloat, + inputPanelNodeSnapshot: UIView?, + inputPanelOverscrollNodeSnapshot: UIView? ) { self.historySnapshotState = historySnapshotState self.titleViewSnapshotState = titleViewSnapshotState @@ -2626,6 +2630,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationButtonsSnapshotState = navigationButtonsSnapshotState self.titleAccessoryPanelSnapshot = titleAccessoryPanelSnapshot self.navigationBarHeight = navigationBarHeight + self.inputPanelNodeSnapshot = inputPanelNodeSnapshot + self.inputPanelOverscrollNodeSnapshot = inputPanelOverscrollNodeSnapshot } } @@ -2638,13 +2644,25 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { snapshot.frame = titleAccessoryPanelNode.frame titleAccessoryPanelSnapshot = snapshot } + var inputPanelNodeSnapshot: UIView? + if let inputPanelNode = self.inputPanelNode, let snapshot = inputPanelNode.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = inputPanelNode.frame + inputPanelNodeSnapshot = snapshot + } + var inputPanelOverscrollNodeSnapshot: UIView? + if let inputPanelOverscrollNode = self.inputPanelOverscrollNode, let snapshot = inputPanelOverscrollNode.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = inputPanelOverscrollNode.frame + inputPanelOverscrollNodeSnapshot = snapshot + } return SnapshotState( historySnapshotState: self.historyNode.prepareSnapshotState(), titleViewSnapshotState: titleViewSnapshotState, avatarSnapshotState: avatarSnapshotState, navigationButtonsSnapshotState: self.navigateButtons.prepareSnapshotState(), titleAccessoryPanelSnapshot: titleAccessoryPanelSnapshot, - navigationBarHeight: self.navigationBar?.backgroundNode.bounds.height ?? 0.0 + navigationBarHeight: self.navigationBar?.backgroundNode.bounds.height ?? 0.0, + inputPanelNodeSnapshot: inputPanelNodeSnapshot, + inputPanelOverscrollNodeSnapshot: inputPanelOverscrollNodeSnapshot ) } @@ -2684,6 +2702,27 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { navigationBar.backgroundNode.update(size: currentFrame.size, transition: .animated(duration: 0.5, curve: .spring)) } } + + if let inputPanelNode = self.inputPanelNode, let inputPanelNodeSnapshot = snapshotState.inputPanelNodeSnapshot { + inputPanelNode.view.superview?.insertSubview(inputPanelNodeSnapshot, belowSubview: inputPanelNode.view) + + inputPanelNodeSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputPanelNodeSnapshot] _ in + inputPanelNodeSnapshot?.removeFromSuperview() + }) + inputPanelNodeSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -5.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + + if let inputPanelOverscrollNodeSnapshot = snapshotState.inputPanelOverscrollNodeSnapshot { + inputPanelNode.view.superview?.insertSubview(inputPanelOverscrollNodeSnapshot, belowSubview: inputPanelNode.view) + + inputPanelOverscrollNodeSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputPanelOverscrollNodeSnapshot] _ in + inputPanelOverscrollNodeSnapshot?.removeFromSuperview() + }) + inputPanelOverscrollNodeSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -5.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } + + inputPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + inputPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 5.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } } private var preivousChatInputPanelOverscrollNodeTimestamp: Double = 0.0 diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 31209b19ff..8e92d1f2a6 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1039,6 +1039,16 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { var disableAnimations = false if let strongSelf = self, updatedScrollPosition == nil, case .InteractiveChanges = reason, case let .known(offset) = strongSelf.visibleContentOffset(), abs(offset) <= 0.9, let previous = previous { + var fillsScreen = true + switch strongSelf.visibleBottomContentOffset() { + case let .known(bottomOffset): + if bottomOffset <= strongSelf.visibleSize.height - strongSelf.insets.bottom { + fillsScreen = false + } + default: + break + } + var previousNumAds = 0 for entry in previous.filteredEntries { if case let .MessageEntry(message, _, _, _, _, _) = entry { @@ -1062,7 +1072,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } - if let firstNonAdIndex = firstNonAdIndex, previousNumAds == 0, updatedNumAds != 0 { + if fillsScreen, let firstNonAdIndex = firstNonAdIndex, previousNumAds == 0, updatedNumAds != 0 { updatedScrollPosition = .index(index: .message(firstNonAdIndex), position: .top(0.0), directionHint: .Up, animated: false, highlight: false) disableAnimations = true } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index e584dd7f96..04b2756f19 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -21,6 +21,7 @@ import UndoUI import ShimmerEffect import AnimatedAvatarSetNode import AvatarNode +import AdUI private struct MessageContextMenuData { let starStatus: Bool? @@ -139,7 +140,10 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo return false } -private func canViewReadStats(message: Message, appConfig: AppConfiguration) -> Bool { +private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool { + if !isMessageRead { + return false + } if message.flags.contains(.Incoming) { return false } @@ -149,6 +153,21 @@ private func canViewReadStats(message: Message, appConfig: AppConfiguration) -> for media in message.media { if let _ = media as? TelegramMediaAction { return false + } else if let file = media as? TelegramMediaFile { + if file.isVoice || file.isInstantVideo { + var hasRead = false + for attribute in message.attributes { + if let attribute = attribute as? ConsumableContentMessageAttribute { + if attribute.consumed { + hasRead = true + break + } + } + } + if !hasRead { + return false + } + } } } @@ -345,18 +364,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState var actions: [ContextMenuItem] = [] actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in - f(.default) - - controllerInteraction.presentController(textAlertController(context: context, title: nil, text: presentationData.strings.SponsoredMessageInfo_Text, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.SponsoredMessageInfo_Action, action: { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.SponsoredMessageInfo_ActionUrl, forceExternal: true, presentationData: presentationData, navigationController: controllerInteraction.navigationController(), dismissInput: { - controllerInteraction.navigationController()?.view.endEditing(true) - }) - }), - ]), nil) + }, iconSource: nil, action: { c, _ in + c.dismiss(completion: { + controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context)) + }) }))) actions.append(.separator) @@ -518,30 +529,40 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState let cachedData = context.account.postbox.transaction { transaction -> CachedPeerData? in return transaction.getPeerCachedData(peerId: messages[0].id.peerId) } + + let readState = context.account.postbox.transaction { transaction -> CombinedPeerReadState? in + return transaction.getCombinedPeerReadState(messages[0].id.peerId) + } - let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration), NoError> = combineLatest( + let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool), NoError> = combineLatest( loadLimits, loadStickerSaveStatusSignal, loadResourceStatusSignal, context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })), context.account.pendingUpdateMessageManager.updatingMessageMedia |> take(1), - cachedData + cachedData, + readState ) - |> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration) in + |> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool) in let (limitsConfiguration, appConfig) = limitsAndAppConfig var canEdit = false if !isAction { let message = messages[0] canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message) } + + var isMessageRead = false + if let readState = readState { + isMessageRead = readState.isOutgoingMessageIndexRead(message.index) + } - return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig) + return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead) } return dataSignal |> deliverOnMainQueue - |> map { data, updatingMessageMedia, cachedData, appConfig -> [ContextMenuItem] in + |> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead -> [ContextMenuItem] in var actions: [ContextMenuItem] = [] var isPinnedMessages = false @@ -1103,8 +1124,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } if !isPinnedMessages, !isReplyThreadHead, data.canSelect { + var didAddSeparator = false if !selectAll || messages.count == 1 { - if !actions.isEmpty { + if !actions.isEmpty && !didAddSeparator { + didAddSeparator = true actions.append(.separator) } @@ -1118,6 +1141,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } if messages.count > 1 { + if !actions.isEmpty && !didAddSeparator { + didAddSeparator = true + actions.append(.separator) + } + actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSelectAll(Int32(messages.count)), icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SelectAll"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in @@ -1128,7 +1156,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } } - if let peer = message.peers[message.id.peerId], canViewReadStats(message: message, appConfig: appConfig) { + if let peer = message.peers[message.id.peerId], canViewReadStats(message: message, isMessageRead: isMessageRead, appConfig: appConfig) { var hasReadReports = false if let channel = peer as? TelegramChannel { if case .group = channel.info { @@ -1160,29 +1188,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState subActions.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, textColor: .primary, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, action: { controller, _ in - controller.setItems(contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: chatPresentationInterfaceState, context: context, messages: messages, controllerInteraction: controllerInteraction, selectAll: selectAll, interfaceInteraction: interfaceInteraction, readStats: stats)) + controller.setItems(contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: chatPresentationInterfaceState, context: context, messages: messages, controllerInteraction: controllerInteraction, selectAll: selectAll, interfaceInteraction: interfaceInteraction, readStats: stats), minHeight: nil) }))) - let debugRepeatCount: Int - #if DEBUG - debugRepeatCount = stats.peers.count == 1 ? 1 : 50 - #else - debugRepeatCount = 1 - #endif + for peer in stats.peers { + let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0)) - for _ in 0 ..< debugRepeatCount { - for peer in stats.peers { - let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0)) - - subActions.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 30.0, height: 30.0), signal: avatarSignal), action: { _, f in - c.dismiss(completion: { - controllerInteraction.openPeer(peer.id, .default, nil) - }) - }))) - } + subActions.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 30.0, height: 30.0), signal: avatarSignal), action: { _, f in + c.dismiss(completion: { + controllerInteraction.openPeer(peer.id, .default, nil) + }) + }))) } - c.setItems(.single(subActions)) + let minHeight = c.getActionsMinHeight() + c.setItems(.single(subActions), minHeight: minHeight) } else { f(.default) } @@ -1845,14 +1865,13 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus let rightTextInset: CGFloat = sideInset + 36.0 - let calculatedWidth = min(constrainedWidth, 260.0) + let calculatedWidth = min(constrainedWidth, 250.0) let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize) if let currentStats = self.currentStats { if currentStats.peers.isEmpty { - //TODO:localize - self.textNode.attributedText = NSAttributedString(string: "No Views", font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor) + self.textNode.attributedText = NSAttributedString(string: " ", font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor) } else if currentStats.peers.count == 1 { self.textNode.attributedText = NSAttributedString(string: currentStats.peers[0].displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } else { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index e6cdee785c..d6248b1a19 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1848,7 +1848,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } return items - }) + }, minHeight: nil) }))) } if strongSelf.searchDisplayController == nil { @@ -1982,7 +1982,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } return items - }) + }, minHeight: nil) }))) } @@ -3697,7 +3697,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD }, action: { [weak self] c, f in self?.openReport(user: false, contextController: c, backAction: { c in if let mainItemsImpl = mainItemsImpl { - c.setItems(mainItemsImpl()) + c.setItems(mainItemsImpl(), minHeight: nil) } }) }))) @@ -4439,7 +4439,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD }))) if let contextController = contextController { - contextController.setItems(.single(items)) + contextController.setItems(.single(items), minHeight: nil) } else { strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) if let (layout, navigationHeight) = strongSelf.validLayout { @@ -4535,7 +4535,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } if let contextController = contextController { - contextController.setItems(.single(items)) + contextController.setItems(.single(items), minHeight: nil) } else { strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) if let (layout, navigationHeight) = strongSelf.validLayout { diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index aaede253be..f76a9290fa 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit aaede253be4dbfdfcb2258cdb6faa843785192e6 +Subproject commit f76a9290fa502a8df473dd872aedf9a553b089cc