Various improvements

This commit is contained in:
Ilya Laktyushin 2022-05-19 21:15:31 +04:00
parent b0cb39b8cd
commit 3fb304bd6f
30 changed files with 319 additions and 136 deletions

View File

@ -7612,3 +7612,6 @@ Sorry for the inconvenience.";
"Conversation.CopyProtectionSavingDisabledSecret" = "Saving is restricted"; "Conversation.CopyProtectionSavingDisabledSecret" = "Saving is restricted";
"Conversation.CopyProtectionForwardingDisabledSecret" = "Forwards are restricted"; "Conversation.CopyProtectionForwardingDisabledSecret" = "Forwards are restricted";
"Settings.Terms_URL" = "https://telegram.org/tos";
"Settings.PrivacyPolicy_URL" = "https://telegram.org/privacy";

View File

@ -326,11 +326,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
switch result { switch result {
case .done: case .done:
f(.default) f(.default)
case .limitExceeded: case let .limitExceeded(count, _):
f(.default) f(.default)
var replaceImpl: ((ViewController) -> Void)? var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .pins, count: 0, action: { let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
let premiumScreen = PremiumIntroScreen(context: context) let premiumScreen = PremiumIntroScreen(context: context)
replaceImpl?(premiumScreen) replaceImpl?(premiumScreen)
}) })

View File

@ -160,6 +160,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private var activeDownloadsDisposable: Disposable? private var activeDownloadsDisposable: Disposable?
private var clearUnseenDownloadsTimer: SwiftSignalKit.Timer? private var clearUnseenDownloadsTimer: SwiftSignalKit.Timer?
private var isPremium: Bool = false
private var didSetupTabs = false private var didSetupTabs = false
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
@ -762,7 +764,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
strongSelf.tabContainerNode.cancelAnimations() strongSelf.tabContainerNode.cancelAnimations()
strongSelf.chatListDisplayNode.inlineTabContainerNode.cancelAnimations() strongSelf.chatListDisplayNode.inlineTabContainerNode.cancelAnimations()
} }
strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: false, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
strongSelf.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) strongSelf.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
} }
self.reloadFilters() self.reloadFilters()
@ -833,7 +835,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
if let layout = self.validLayout { if let layout = self.validLayout {
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: true, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate) self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate)
self.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate) self.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate)
} }
@ -1810,7 +1812,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0 let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0
transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0))) transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: true, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
if let tabContainerData = self.tabContainerData { if let tabContainerData = self.tabContainerData {
self.chatListDisplayNode.inlineTabContainerNode.isHidden = !tabContainerData.1 || tabContainerData.0.count <= 1 self.chatListDisplayNode.inlineTabContainerNode.isHidden = !tabContainerData.1 || tabContainerData.0.count <= 1
} else { } else {
@ -1947,14 +1949,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
]), ]),
filterItems, filterItems,
displayTabsAtBottom, displayTabsAtBottom,
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) self.context.account.postbox.peerView(id: self.context.account.peerId)
) )
|> deliverOnMainQueue).start(next: { [weak self] _, countAndFilterItems, displayTabsAtBottom, accountPeer in |> deliverOnMainQueue).start(next: { [weak self] _, countAndFilterItems, displayTabsAtBottom, peerView in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let isPremium = accountPeer?.isPremium ?? false let isPremium = peerView.peers[peerView.peerId]?.isPremium ?? false
strongSelf.isPremium = isPremium
let (_, items) = countAndFilterItems let (_, items) = countAndFilterItems
var filterItems: [ChatListFilterTabEntry] = [] var filterItems: [ChatListFilterTabEntry] = []

View File

@ -842,9 +842,9 @@ public final class ChatListNode: ListView {
switch result { switch result {
case .done: case .done:
break break
case .limitExceeded: case let .limitExceeded(count, _):
var replaceImpl: ((ViewController) -> Void)? var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .pins, count: 0, action: { let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
let premiumScreen = PremiumIntroScreen(context: context) let premiumScreen = PremiumIntroScreen(context: context)
replaceImpl?(premiumScreen) replaceImpl?(premiumScreen)
}) })

View File

@ -20,6 +20,10 @@ public final class MultilineTextComponent: Component {
public let insets: UIEdgeInsets public let insets: UIEdgeInsets
public let textShadowColor: UIColor? public let textShadowColor: UIColor?
public let textStroke: (UIColor, CGFloat)? public let textStroke: (UIColor, CGFloat)?
public let highlightColor: UIColor?
public let highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)?
public let tapAction: (([NSAttributedString.Key: Any], Int) -> Void)?
public let longTapAction: (([NSAttributedString.Key: Any], Int) -> Void)?
public init( public init(
text: TextContent, text: TextContent,
@ -31,7 +35,11 @@ public final class MultilineTextComponent: Component {
cutout: TextNodeCutout? = nil, cutout: TextNodeCutout? = nil,
insets: UIEdgeInsets = UIEdgeInsets(), insets: UIEdgeInsets = UIEdgeInsets(),
textShadowColor: UIColor? = nil, textShadowColor: UIColor? = nil,
textStroke: (UIColor, CGFloat)? = nil textStroke: (UIColor, CGFloat)? = nil,
highlightColor: UIColor? = nil,
highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? = nil,
tapAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil,
longTapAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil
) { ) {
self.text = text self.text = text
self.horizontalAlignment = horizontalAlignment self.horizontalAlignment = horizontalAlignment
@ -43,6 +51,10 @@ public final class MultilineTextComponent: Component {
self.insets = insets self.insets = insets
self.textShadowColor = textShadowColor self.textShadowColor = textShadowColor
self.textStroke = textStroke self.textStroke = textStroke
self.highlightColor = highlightColor
self.highlightAction = highlightAction
self.tapAction = tapAction
self.longTapAction = longTapAction
} }
public static func ==(lhs: MultilineTextComponent, rhs: MultilineTextComponent) -> Bool { public static func ==(lhs: MultilineTextComponent, rhs: MultilineTextComponent) -> Bool {
@ -90,10 +102,18 @@ public final class MultilineTextComponent: Component {
return false return false
} }
if let lhsHighlightColor = lhs.highlightColor, let rhsHighlightColor = rhs.highlightColor {
if !lhsHighlightColor.isEqual(rhsHighlightColor) {
return false
}
} else if (lhs.highlightColor != nil) != (rhs.highlightColor != nil) {
return false
}
return true return true
} }
public final class View: TextView { public final class View: ImmediateTextView {
public func update(component: MultilineTextComponent, availableSize: CGSize) -> CGSize { public func update(component: MultilineTextComponent, availableSize: CGSize) -> CGSize {
let attributedString: NSAttributedString let attributedString: NSAttributedString
switch component.text { switch component.text {
@ -102,26 +122,25 @@ public final class MultilineTextComponent: Component {
case let .markdown(text, attributes): case let .markdown(text, attributes):
attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes) attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes)
} }
self.attributedText = attributedString
self.maximumNumberOfLines = component.maximumNumberOfLines
self.truncationType = component.truncationType
self.textAlignment = component.horizontalAlignment
self.verticalAlignment = component.verticalAlignment
self.lineSpacing = component.lineSpacing
self.cutout = component.cutout
self.insets = component.insets
self.textShadowColor = component.textShadowColor
self.textStroke = component.textStroke
self.linkHighlightColor = component.highlightColor
self.highlightAttributeAction = component.highlightAction
self.tapAttributeAction = component.tapAction
self.longTapAttributeAction = component.longTapAction
let makeLayout = TextView.asyncLayout(self) let size = self.updateLayout(availableSize)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(
attributedString: attributedString, return size
backgroundColor: nil,
maximumNumberOfLines: component.maximumNumberOfLines,
truncationType: component.truncationType,
constrainedSize: availableSize,
alignment: component.horizontalAlignment,
verticalAlignment: component.verticalAlignment,
lineSpacing: component.lineSpacing,
cutout: component.cutout,
insets: component.insets,
textShadowColor: component.textShadowColor,
textStroke: component.textStroke,
displaySpoilers: false
))
let _ = apply()
return layout.size
} }
} }

View File

@ -59,6 +59,12 @@ open class ViewControllerComponentContainer: ViewController {
case `default` case `default`
} }
public enum StatusBarStyle {
case none
case ignore
case `default`
}
public final class Environment: Equatable { public final class Environment: Equatable {
public let statusBarHeight: CGFloat public let statusBarHeight: CGFloat
public let navigationHeight: CGFloat public let navigationHeight: CGFloat
@ -127,11 +133,11 @@ open class ViewControllerComponentContainer: ViewController {
} }
public final class Node: ViewControllerTracingNode { public final class Node: ViewControllerTracingNode {
private var presentationData: PresentationData fileprivate var presentationData: PresentationData
private weak var controller: ViewControllerComponentContainer? private weak var controller: ViewControllerComponentContainer?
private var component: AnyComponent<ViewControllerComponentContainer.Environment> private var component: AnyComponent<ViewControllerComponentContainer.Environment>
private let theme: PresentationTheme? var theme: PresentationTheme?
public let hostView: ComponentHostView<ViewControllerComponentContainer.Environment> public let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
private var currentIsVisible: Bool = false private var currentIsVisible: Bool = false
@ -204,30 +210,68 @@ open class ViewControllerComponentContainer: ViewController {
} }
private let context: AccountContext private let context: AccountContext
private let theme: PresentationTheme? private var theme: PresentationTheme?
private let component: AnyComponent<ViewControllerComponentContainer.Environment> private let component: AnyComponent<ViewControllerComponentContainer.Environment>
public init<C: Component>(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment { private var presentationDataDisposable: Disposable?
private var validLayout: ContainerViewLayout?
public init<C: Component>(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle = .default, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
self.context = context self.context = context
self.component = AnyComponent(component) self.component = AnyComponent(component)
self.theme = theme self.theme = theme
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let navigationBarPresentationData: NavigationBarPresentationData? let navigationBarPresentationData: NavigationBarPresentationData?
switch navigationBarAppearance { switch navigationBarAppearance {
case .none: case .none:
navigationBarPresentationData = nil navigationBarPresentationData = nil
case .transparent: case .transparent:
navigationBarPresentationData = NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 }, hideBackground: true, hideBadge: false, hideSeparator: true) navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true)
case .default: case .default:
navigationBarPresentationData = NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 }) navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData)
} }
super.init(navigationBarPresentationData: navigationBarPresentationData) super.init(navigationBarPresentationData: navigationBarPresentationData)
self.presentationDataDisposable = (self.context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.node.presentationData = presentationData
switch statusBarStyle {
case .none:
strongSelf.statusBar.statusBarStyle = .Hide
case .ignore:
strongSelf.statusBar.statusBarStyle = .Ignore
case .default:
strongSelf.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
}
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
}
})
switch statusBarStyle {
case .none:
self.statusBar.statusBarStyle = .Hide
case .ignore:
self.statusBar.statusBarStyle = .Ignore
case .default:
self.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
}
} }
required public init(coder aDecoder: NSCoder) { required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit {
self.presentationDataDisposable?.dispose()
}
override open func loadDisplayNode() { override open func loadDisplayNode() {
self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme) self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme)
@ -255,6 +299,7 @@ open class ViewControllerComponentContainer: ViewController {
let navigationHeight = self.navigationLayout(layout: layout).navigationFrame.maxY let navigationHeight = self.navigationLayout(layout: layout).navigationFrame.maxY
self.validLayout = layout
self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition))
} }

View File

@ -30,5 +30,7 @@ public protocol PeekControllerContentNode {
} }
public protocol PeekControllerAccessoryNode { public protocol PeekControllerAccessoryNode {
var dismiss: () -> Void { get set }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
} }

View File

@ -115,6 +115,9 @@ final class PeekControllerNode: ViewControllerTracingNode {
self.addSubnode(self.actionsContainerNode) self.addSubnode(self.actionsContainerNode)
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode { if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
self.fullScreenAccessoryNode?.dismiss = { [weak self] in
self?.requestDismiss()
}
self.addSubnode(fullScreenAccessoryNode) self.addSubnode(fullScreenAccessoryNode)
} }
@ -194,6 +197,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode { if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
fullScreenAccessoryNode.updateLayout(size: layout.size, transition: transition) fullScreenAccessoryNode.updateLayout(size: layout.size, transition: transition)
transition.updateFrame(node: fullScreenAccessoryNode, frame: CGRect(origin: .zero, size: layout.size))
} }
self.contentNodeHasValidLayout = true self.contentNodeHasValidLayout = true

View File

@ -226,7 +226,7 @@ public class ASTextNode: ImmediateTextNode {
} }
} }
public class ImmediateTextView: TextView { open class ImmediateTextView: TextView {
public var attributedText: NSAttributedString? public var attributedText: NSAttributedString?
public var textAlignment: NSTextAlignment = .natural public var textAlignment: NSTextAlignment = .natural
public var verticalAlignment: TextVerticalAlignment = .top public var verticalAlignment: TextVerticalAlignment = .top

View File

@ -4,7 +4,7 @@ import SwiftSignalKit
private var backArrowImageCache: [Int32: UIImage] = [:] private var backArrowImageCache: [Int32: UIImage] = [:]
public final class SparseNode: ASDisplayNode { open class SparseNode: ASDisplayNode {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.alpha.isZero { if self.alpha.isZero {
return nil return nil

View File

@ -1621,7 +1621,6 @@ open class TextView: UIView {
private class func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool) -> TextNodeLayout { private class func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool) -> TextNodeLayout {
if let attributedString = attributedString { if let attributedString = attributedString {
let stringLength = attributedString.length let stringLength = attributedString.length
let font: CTFont let font: CTFont

View File

@ -15,6 +15,8 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/PersistentStringHash:PersistentStringHash", "//submodules/PersistentStringHash:PersistentStringHash",
"//submodules/AccountContext:AccountContext",
"//submodules/UrlHandling:UrlHandling",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -0,0 +1,88 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import AccountContext
//import InstantPageUI
import UrlHandling
public func extractAnchor(string: String) -> (String, String?) {
var anchorValue: String?
if let anchorRange = string.range(of: "#") {
let anchor = string[anchorRange.upperBound...]
if !anchor.isEmpty {
anchorValue = String(anchor)
}
}
var trimmedUrl = string
if let anchor = anchorValue, let anchorRange = string.range(of: "#\(anchor)") {
let url = string[..<anchorRange.lowerBound]
if !url.isEmpty {
trimmedUrl = String(url)
}
}
return (trimmedUrl, anchorValue)
}
private let refreshTimeout: Int32 = 60 * 60 * 12
public func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var faqUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
faqUrl = "https://telegram.org/faq#general-questions"
}
return cachedInternalInstantPage(context: context, url: faqUrl)
}
public func cachedTermsPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var termsUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_Terms_URL
if termsUrl == "Settings.Terms_URL" || termsUrl.isEmpty {
termsUrl = "https://telegram.org/tos"
}
return cachedInternalInstantPage(context: context, url: termsUrl)
}
public func cachedPrivacyPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var privacyUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_PrivacyPolicy_URL
if privacyUrl == "Settings.PrivacyPolicy_URL" || privacyUrl.isEmpty {
privacyUrl = "https://telegram.org/privacy"
}
return cachedInternalInstantPage(context: context, url: privacyUrl)
}
private func cachedInternalInstantPage(context: AccountContext, url: String) -> Signal<ResolvedUrl, NoError> {
let (cachedUrl, anchor) = extractAnchor(string: url)
return cachedInstantPage(postbox: context.account.postbox, url: cachedUrl)
|> mapToSignal { cachedInstantPage -> Signal<ResolvedUrl, NoError> in
let updated = resolveInstantViewUrl(account: context.account, url: url)
|> afterNext { result in
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
if instantPage.isComplete {
let _ = updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage).start()
} else {
let _ = (actualizedWebpage(postbox: context.account.postbox, network: context.account.network, webpage: webPage)
|> mapToSignal { webPage -> Signal<Void, NoError> in
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
return updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage)
} else {
return .complete()
}
}).start()
}
}
}
let now = Int32(CFAbsoluteTimeGetCurrent())
if let cachedInstantPage = cachedInstantPage, case let .Loaded(content) = cachedInstantPage.webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
let current: Signal<ResolvedUrl, NoError> = .single(.instantView(cachedInstantPage.webPage, anchor))
if now > cachedInstantPage.timestamp + refreshTimeout {
return current
|> then(updated)
} else {
return current
}
} else {
return updated
}
}
}

View File

@ -44,6 +44,7 @@ swift_library(
"//submodules/ConfettiEffect:ConfettiEffect", "//submodules/ConfettiEffect:ConfettiEffect",
"//submodules/TextFormat:TextFormat", "//submodules/TextFormat:TextFormat",
"//submodules/GZip:GZip", "//submodules/GZip:GZip",
"//submodules/InstantPageCache:InstantPageCache",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -15,6 +15,7 @@ import Markdown
import InAppPurchaseManager import InAppPurchaseManager
import ConfettiEffect import ConfettiEffect
import TextFormat import TextFormat
import InstantPageCache
private final class SectionGroupComponent: Component { private final class SectionGroupComponent: Component {
public final class Item: Equatable { public final class Item: Equatable {
@ -884,7 +885,39 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
), ),
horizontalAlignment: .natural, horizontalAlignment: .natural,
maximumNumberOfLines: 0, maximumNumberOfLines: 0,
lineSpacing: 0.0 lineSpacing: 0.0,
highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.3),
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
},
tapAction: { attributes, _ in
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String,
let controller = environment.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
let context = controller.context
let signal: Signal<ResolvedUrl, NoError>?
switch url {
case "terms":
signal = cachedTermsPage(context: context)
case "privacy":
signal = cachedPrivacyPage(context: context)
default:
signal = nil
}
if let signal = signal {
let _ = (signal
|> deliverOnMainQueue).start(next: { resolvedUrl in
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak controller] c, arguments in
controller?.push(c)
}, dismissInput: {}, contentContext: nil)
})
}
}
}
), ),
environment: {}, environment: {},
availableSize: CGSize(width: availableWidth - sideInsets - textSideInset * 2.0, height: .greatestFiniteMagnitude), availableSize: CGSize(width: availableWidth - sideInsets - textSideInset * 2.0, height: .greatestFiniteMagnitude),
@ -1222,7 +1255,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
.opacity(bottomPanelAlpha) .opacity(bottomPanelAlpha)
) )
context.add(bottomSeparator context.add(bottomSeparator
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomPanel.size.height - bottomSeparator.size.height)) .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomPanel.size.height))
.opacity(bottomPanelAlpha) .opacity(bottomPanelAlpha)
) )
context.add(button context.add(button
@ -1235,7 +1268,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
} }
public final class PremiumIntroScreen: ViewControllerComponentContainer { public final class PremiumIntroScreen: ViewControllerComponentContainer {
private let context: AccountContext fileprivate let context: AccountContext
private var didSetReady = false private var didSetReady = false
private let _ready = Promise<Bool>() private let _ready = Promise<Bool>()

View File

@ -599,7 +599,7 @@ private final class LimitSheetContent: CombinedComponent {
let limit = state.limits.maxFoldersCount let limit = state.limits.maxFoldersCount
let premiumLimit = state.premiumLimits.maxFoldersCount let premiumLimit = state.premiumLimits.maxFoldersCount
iconName = "Premium/Folder" iconName = "Premium/Folder"
badgeText = "\(limit)" badgeText = "\(component.count)"
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
defaultValue = component.count > limit ? "\(limit)" : "" defaultValue = component.count > limit ? "\(limit)" : ""
premiumValue = "\(premiumLimit)" premiumValue = "\(premiumLimit)"
@ -608,7 +608,7 @@ private final class LimitSheetContent: CombinedComponent {
let limit = state.limits.maxFolderChatsCount let limit = state.limits.maxFolderChatsCount
let premiumLimit = state.premiumLimits.maxFolderChatsCount let premiumLimit = state.premiumLimits.maxFolderChatsCount
iconName = "Premium/Chat" iconName = "Premium/Chat"
badgeText = "\(limit)" badgeText = "\(component.count)"
string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string
defaultValue = component.count > limit ? "\(limit)" : "" defaultValue = component.count > limit ? "\(limit)" : ""
premiumValue = "\(premiumLimit)" premiumValue = "\(premiumLimit)"
@ -617,7 +617,7 @@ private final class LimitSheetContent: CombinedComponent {
let limit = state.limits.maxPinnedChatCount let limit = state.limits.maxPinnedChatCount
let premiumLimit = state.premiumLimits.maxPinnedChatCount let premiumLimit = state.premiumLimits.maxPinnedChatCount
iconName = "Premium/Pin" iconName = "Premium/Pin"
badgeText = "\(limit)" badgeText = "\(component.count)"
string = strings.Premium_MaxPinsText("\(limit)", "\(premiumLimit)").string string = strings.Premium_MaxPinsText("\(limit)", "\(premiumLimit)").string
defaultValue = component.count > limit ? "\(limit)" : "" defaultValue = component.count > limit ? "\(limit)" : ""
premiumValue = "\(premiumLimit)" premiumValue = "\(premiumLimit)"

View File

@ -11,7 +11,7 @@ import PresentationDataUtils
import SolidRoundedButtonNode import SolidRoundedButtonNode
import AppBundle import AppBundle
public final class PremiumStickersScreen: ViewController { public final class PremiumReactionsScreen: ViewController {
private let context: AccountContext private let context: AccountContext
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
@ -21,7 +21,7 @@ public final class PremiumStickersScreen: ViewController {
public var proceed: (() -> Void)? public var proceed: (() -> Void)?
private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate { private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
private weak var controller: PremiumStickersScreen? private weak var controller: PremiumReactionsScreen?
private var presentationData: PresentationData private var presentationData: PresentationData
private let blurView: UIVisualEffectView private let blurView: UIVisualEffectView
@ -38,7 +38,7 @@ public final class PremiumStickersScreen: ViewController {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
init(controller: PremiumStickersScreen) { init(controller: PremiumReactionsScreen) {
self.controller = controller self.controller = controller
self.presentationData = controller.presentationData self.presentationData = controller.presentationData
@ -70,7 +70,14 @@ public final class PremiumStickersScreen: ViewController {
self.overlayTextNode.maximumNumberOfLines = 0 self.overlayTextNode.maximumNumberOfLines = 0
self.overlayTextNode.lineSpacing = 0.1 self.overlayTextNode.lineSpacing = 0.1
self.proceedButton = SolidRoundedButtonNode(title: self.presentationData.strings.Premium_Reactions_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 11.0, gloss: true) self.proceedButton = SolidRoundedButtonNode(title: self.presentationData.strings.Premium_Reactions_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(
backgroundColor: .white,
backgroundColors: [
UIColor(rgb: 0x0077ff),
UIColor(rgb: 0x6b93ff),
UIColor(rgb: 0x8878ff),
UIColor(rgb: 0xe46ace)
], foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true)
self.cancelButton = HighlightableButtonNode() self.cancelButton = HighlightableButtonNode()
self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal) self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
@ -78,7 +85,7 @@ public final class PremiumStickersScreen: ViewController {
self.carouselNode = ReactionCarouselNode(context: controller.context, theme: controller.presentationData.theme, reactions: controller.reactions) self.carouselNode = ReactionCarouselNode(context: controller.context, theme: controller.presentationData.theme, reactions: controller.reactions)
super.init() super.init()
self.addSubnode(self.dimNode) self.addSubnode(self.dimNode)
self.addSubnode(self.darkDimNode) self.addSubnode(self.darkDimNode)
self.addSubnode(self.containerNode) self.addSubnode(self.containerNode)
@ -99,7 +106,10 @@ public final class PremiumStickersScreen: ViewController {
self.overlayTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.Premium_Reactions_Description, font: Font.regular(17.0), textColor: textColor) self.overlayTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.Premium_Reactions_Description, font: Font.regular(17.0), textColor: textColor)
self.proceedButton.pressed = { [weak self] in self.proceedButton.pressed = { [weak self] in
self?.animateOut() if let strongSelf = self, let controller = strongSelf.controller, let navigationController = controller.navigationController {
strongSelf.animateOut()
navigationController.pushViewController(PremiumIntroScreen(context: controller.context), animated: true)
}
} }
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
@ -209,6 +219,8 @@ public final class PremiumStickersScreen: ViewController {
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)
self.navigationPresentation = .flatModal
self.statusBar.statusBarStyle = .Ignore self.statusBar.statusBarStyle = .Ignore
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)

View File

@ -7,69 +7,6 @@ import InstantPageUI
import InstantPageCache import InstantPageCache
import UrlHandling import UrlHandling
private func extractAnchor(string: String) -> (String, String?) {
var anchorValue: String?
if let anchorRange = string.range(of: "#") {
let anchor = string[anchorRange.upperBound...]
if !anchor.isEmpty {
anchorValue = String(anchor)
}
}
var trimmedUrl = string
if let anchor = anchorValue, let anchorRange = string.range(of: "#\(anchor)") {
let url = string[..<anchorRange.lowerBound]
if !url.isEmpty {
trimmedUrl = String(url)
}
}
return (trimmedUrl, anchorValue)
}
private let refreshTimeout: Int32 = 60 * 60 * 12
public func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
var faqUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
faqUrl = "https://telegram.org/faq#general-questions"
}
let (cachedUrl, anchor) = extractAnchor(string: faqUrl)
return cachedInstantPage(postbox: context.account.postbox, url: cachedUrl)
|> mapToSignal { cachedInstantPage -> Signal<ResolvedUrl, NoError> in
let updated = resolveInstantViewUrl(account: context.account, url: faqUrl)
|> afterNext { result in
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
if instantPage.isComplete {
let _ = updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage).start()
} else {
let _ = (actualizedWebpage(postbox: context.account.postbox, network: context.account.network, webpage: webPage)
|> mapToSignal { webPage -> Signal<Void, NoError> in
if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
return updateCachedInstantPage(postbox: context.account.postbox, url: cachedUrl, webPage: webPage)
} else {
return .complete()
}
}).start()
}
}
}
let now = Int32(CFAbsoluteTimeGetCurrent())
if let cachedInstantPage = cachedInstantPage, case let .Loaded(content) = cachedInstantPage.webPage.content, let instantPage = content.instantPage, instantPage.isComplete {
let current: Signal<ResolvedUrl, NoError> = .single(.instantView(cachedInstantPage.webPage, anchor))
if now > cachedInstantPage.timestamp + refreshTimeout {
return current
|> then(updated)
} else {
return current
}
} else {
return updated
}
}
}
func faqSearchableItems(context: AccountContext, resolvedUrl: Signal<ResolvedUrl?, NoError>, suggestAccountDeletion: Bool) -> Signal<[SettingsSearchableItem], NoError> { func faqSearchableItems(context: AccountContext, resolvedUrl: Signal<ResolvedUrl?, NoError>, suggestAccountDeletion: Bool) -> Signal<[SettingsSearchableItem], NoError> {
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
return resolvedUrl return resolvedUrl

View File

@ -236,7 +236,9 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
} }
}))) })))
} }
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -369,7 +369,9 @@ private final class StickerPackContainer: ASDisplayNode {
} }
}))) })))
} }
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -35,8 +35,9 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
public let item: StickerPreviewPeekItem public let item: StickerPreviewPeekItem
let isLocked: Bool let isLocked: Bool
let menu: [ContextMenuItem] let menu: [ContextMenuItem]
let openPremiumIntro: () -> Void
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem]) { public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem], openPremiumIntro: @escaping () -> Void) {
self.account = account self.account = account
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -47,6 +48,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
} else { } else {
self.menu = menu self.menu = menu
} }
self.openPremiumIntro = openPremiumIntro
} }
public func presentation() -> PeekControllerContentPresentation { public func presentation() -> PeekControllerContentPresentation {
@ -71,7 +73,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
public func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)? { public func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)? {
if self.isLocked { if self.isLocked {
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings) return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings, proceed: self.openPremiumIntro)
} else { } else {
return nil return nil
} }
@ -211,12 +213,17 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
} }
} }
final class PremiumStickerPackAccessoryNode: ASDisplayNode, PeekControllerAccessoryNode { final class PremiumStickerPackAccessoryNode: SparseNode, PeekControllerAccessoryNode {
var dismiss: () -> Void = {}
let proceed: () -> Void
let textNode: ImmediateTextNode let textNode: ImmediateTextNode
let proceedButton: SolidRoundedButtonNode let proceedButton: SolidRoundedButtonNode
let cancelButton: HighlightableButtonNode let cancelButton: HighlightableButtonNode
init(theme: PresentationTheme, strings: PresentationStrings) { init(theme: PresentationTheme, strings: PresentationStrings, proceed: @escaping () -> Void) {
self.proceed = proceed
self.textNode = ImmediateTextNode() self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
self.textNode.textAlignment = .center self.textNode.textAlignment = .center
@ -224,7 +231,14 @@ final class PremiumStickerPackAccessoryNode: ASDisplayNode, PeekControllerAccess
self.textNode.attributedText = NSAttributedString(string: strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: theme.actionSheet.secondaryTextColor) self.textNode.attributedText = NSAttributedString(string: strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: theme.actionSheet.secondaryTextColor)
self.textNode.lineSpacing = 0.1 self.textNode.lineSpacing = 0.1
self.proceedButton = SolidRoundedButtonNode(title: strings.Premium_Stickers_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0, gloss: true) self.proceedButton = SolidRoundedButtonNode(title: strings.Premium_Stickers_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(
backgroundColor: .white,
backgroundColors: [
UIColor(rgb: 0x0077ff),
UIColor(rgb: 0x6b93ff),
UIColor(rgb: 0x8878ff),
UIColor(rgb: 0xe46ace)
], foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true)
self.cancelButton = HighlightableButtonNode() self.cancelButton = HighlightableButtonNode()
self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal) self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal)
@ -235,14 +249,14 @@ final class PremiumStickerPackAccessoryNode: ASDisplayNode, PeekControllerAccess
self.addSubnode(self.proceedButton) self.addSubnode(self.proceedButton)
self.addSubnode(self.cancelButton) self.addSubnode(self.cancelButton)
self.proceedButton.pressed = { self.proceedButton.pressed = { [weak self] in
self?.proceed()
} }
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
} }
@objc func cancelPressed() { @objc func cancelPressed() {
self.dismiss()
} }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {

View File

@ -52,7 +52,7 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio
let count = sameKind.count + additionalCount let count = sameKind.count + additionalCount
if count > limitCount, itemIds.firstIndex(of: itemId) == nil { if count > limitCount, itemIds.firstIndex(of: itemId) == nil {
return .limitExceeded(count: count, limit: limitCount) return .limitExceeded(count: sameKind.count, limit: limitCount)
} else { } else {
if let index = itemIds.firstIndex(of: itemId) { if let index = itemIds.firstIndex(of: itemId) {
itemIds.remove(at: index) itemIds.remove(at: index)

View File

@ -1095,8 +1095,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .premium = value { if case .premium = value {
controller?.dismiss() controller?.dismiss()
let controller = PremiumStickersScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, reactions: premiumReactions) let controller = PremiumReactionsScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, reactions: premiumReactions)
strongSelf.present(controller, in: .window(.root)) strongSelf.push(controller)
return return
} }

View File

@ -1617,7 +1617,9 @@ final class ChatMediaInputNode: ChatInputNode {
} }
} }
}))) })))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: item, menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: item, menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }
@ -1743,7 +1745,9 @@ final class ChatMediaInputNode: ChatInputNode {
} }
})) }))
) )
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -621,6 +621,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.strings = strings self.strings = strings
let titleContent = self.titleContent let titleContent = self.titleContent
self.titleCredibilityIcon = .none
self.titleContent = titleContent self.titleContent = titleContent
let _ = self.updateStatus() let _ = self.updateStatus()

View File

@ -531,7 +531,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
} }
})) }))
] ]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: item, menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: item, menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }
@ -595,7 +597,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
} }
})) }))
] ]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -174,7 +174,9 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
} }
}))) })))
} }
selectedItemNodeAndContent = (itemNode, StickerPreviewPeekContent(account: item.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems)) selectedItemNodeAndContent = (itemNode, StickerPreviewPeekContent(account: item.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems, openPremiumIntro: {
}))
} else { } else {
var menuItems: [ContextMenuItem] = [] var menuItems: [ContextMenuItem] = []
if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isAnimated { if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isAnimated {

View File

@ -220,7 +220,9 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
} }
})) }))
] ]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -175,7 +175,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
} }
})) }))
) )
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }

View File

@ -176,7 +176,9 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
} }
})) }))
] ]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems)) return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems, openPremiumIntro: {
}))
} else { } else {
return nil return nil
} }