mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various improvements
This commit is contained in:
parent
b0cb39b8cd
commit
3fb304bd6f
@ -7612,3 +7612,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Conversation.CopyProtectionSavingDisabledSecret" = "Saving is restricted";
|
||||
"Conversation.CopyProtectionForwardingDisabledSecret" = "Forwards are restricted";
|
||||
|
||||
"Settings.Terms_URL" = "https://telegram.org/tos";
|
||||
"Settings.PrivacyPolicy_URL" = "https://telegram.org/privacy";
|
||||
|
@ -326,11 +326,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
||||
switch result {
|
||||
case .done:
|
||||
f(.default)
|
||||
case .limitExceeded:
|
||||
case let .limitExceeded(count, _):
|
||||
f(.default)
|
||||
|
||||
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)
|
||||
replaceImpl?(premiumScreen)
|
||||
})
|
||||
|
@ -160,6 +160,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
private var activeDownloadsDisposable: Disposable?
|
||||
private var clearUnseenDownloadsTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private var isPremium: Bool = false
|
||||
|
||||
private var didSetupTabs = false
|
||||
|
||||
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -762,7 +764,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.tabContainerNode.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)
|
||||
}
|
||||
self.reloadFilters()
|
||||
@ -833,7 +835,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1810,7 +1812,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
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)))
|
||||
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 {
|
||||
self.chatListDisplayNode.inlineTabContainerNode.isHidden = !tabContainerData.1 || tabContainerData.0.count <= 1
|
||||
} else {
|
||||
@ -1947,14 +1949,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
]),
|
||||
filterItems,
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
let isPremium = accountPeer?.isPremium ?? false
|
||||
let isPremium = peerView.peers[peerView.peerId]?.isPremium ?? false
|
||||
strongSelf.isPremium = isPremium
|
||||
|
||||
let (_, items) = countAndFilterItems
|
||||
var filterItems: [ChatListFilterTabEntry] = []
|
||||
|
@ -842,9 +842,9 @@ public final class ChatListNode: ListView {
|
||||
switch result {
|
||||
case .done:
|
||||
break
|
||||
case .limitExceeded:
|
||||
case let .limitExceeded(count, _):
|
||||
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)
|
||||
replaceImpl?(premiumScreen)
|
||||
})
|
||||
|
@ -20,6 +20,10 @@ public final class MultilineTextComponent: Component {
|
||||
public let insets: UIEdgeInsets
|
||||
public let textShadowColor: UIColor?
|
||||
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(
|
||||
text: TextContent,
|
||||
@ -31,7 +35,11 @@ public final class MultilineTextComponent: Component {
|
||||
cutout: TextNodeCutout? = nil,
|
||||
insets: UIEdgeInsets = UIEdgeInsets(),
|
||||
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.horizontalAlignment = horizontalAlignment
|
||||
@ -43,6 +51,10 @@ public final class MultilineTextComponent: Component {
|
||||
self.insets = insets
|
||||
self.textShadowColor = textShadowColor
|
||||
self.textStroke = textStroke
|
||||
self.highlightColor = highlightColor
|
||||
self.highlightAction = highlightAction
|
||||
self.tapAction = tapAction
|
||||
self.longTapAction = longTapAction
|
||||
}
|
||||
|
||||
public static func ==(lhs: MultilineTextComponent, rhs: MultilineTextComponent) -> Bool {
|
||||
@ -90,10 +102,18 @@ public final class MultilineTextComponent: Component {
|
||||
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
|
||||
}
|
||||
|
||||
public final class View: TextView {
|
||||
public final class View: ImmediateTextView {
|
||||
public func update(component: MultilineTextComponent, availableSize: CGSize) -> CGSize {
|
||||
let attributedString: NSAttributedString
|
||||
switch component.text {
|
||||
@ -102,26 +122,25 @@ public final class MultilineTextComponent: Component {
|
||||
case let .markdown(text, 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 (layout, apply) = makeLayout(TextNodeLayoutArguments(
|
||||
attributedString: attributedString,
|
||||
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
|
||||
let size = self.updateLayout(availableSize)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,12 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
case `default`
|
||||
}
|
||||
|
||||
public enum StatusBarStyle {
|
||||
case none
|
||||
case ignore
|
||||
case `default`
|
||||
}
|
||||
|
||||
public final class Environment: Equatable {
|
||||
public let statusBarHeight: CGFloat
|
||||
public let navigationHeight: CGFloat
|
||||
@ -127,11 +133,11 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
}
|
||||
|
||||
public final class Node: ViewControllerTracingNode {
|
||||
private var presentationData: PresentationData
|
||||
fileprivate var presentationData: PresentationData
|
||||
private weak var controller: ViewControllerComponentContainer?
|
||||
|
||||
private var component: AnyComponent<ViewControllerComponentContainer.Environment>
|
||||
private let theme: PresentationTheme?
|
||||
var theme: PresentationTheme?
|
||||
public let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
|
||||
|
||||
private var currentIsVisible: Bool = false
|
||||
@ -204,30 +210,68 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let theme: PresentationTheme?
|
||||
private var theme: PresentationTheme?
|
||||
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.component = AnyComponent(component)
|
||||
self.theme = theme
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let navigationBarPresentationData: NavigationBarPresentationData?
|
||||
switch navigationBarAppearance {
|
||||
case .none:
|
||||
navigationBarPresentationData = nil
|
||||
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:
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 })
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData)
|
||||
}
|
||||
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) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
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
|
||||
|
||||
self.validLayout = layout
|
||||
self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition))
|
||||
}
|
||||
|
||||
|
@ -30,5 +30,7 @@ public protocol PeekControllerContentNode {
|
||||
}
|
||||
|
||||
public protocol PeekControllerAccessoryNode {
|
||||
var dismiss: () -> Void { get set }
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
@ -115,6 +115,9 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.addSubnode(self.actionsContainerNode)
|
||||
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
self.fullScreenAccessoryNode?.dismiss = { [weak self] in
|
||||
self?.requestDismiss()
|
||||
}
|
||||
self.addSubnode(fullScreenAccessoryNode)
|
||||
}
|
||||
|
||||
@ -194,6 +197,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
fullScreenAccessoryNode.updateLayout(size: layout.size, transition: transition)
|
||||
transition.updateFrame(node: fullScreenAccessoryNode, frame: CGRect(origin: .zero, size: layout.size))
|
||||
}
|
||||
|
||||
self.contentNodeHasValidLayout = true
|
||||
|
@ -226,7 +226,7 @@ public class ASTextNode: ImmediateTextNode {
|
||||
}
|
||||
}
|
||||
|
||||
public class ImmediateTextView: TextView {
|
||||
open class ImmediateTextView: TextView {
|
||||
public var attributedText: NSAttributedString?
|
||||
public var textAlignment: NSTextAlignment = .natural
|
||||
public var verticalAlignment: TextVerticalAlignment = .top
|
||||
|
@ -4,7 +4,7 @@ import SwiftSignalKit
|
||||
|
||||
private var backArrowImageCache: [Int32: UIImage] = [:]
|
||||
|
||||
public final class SparseNode: ASDisplayNode {
|
||||
open class SparseNode: ASDisplayNode {
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero {
|
||||
return nil
|
||||
|
@ -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 {
|
||||
if let attributedString = attributedString {
|
||||
|
||||
let stringLength = attributedString.length
|
||||
|
||||
let font: CTFont
|
||||
|
@ -15,6 +15,8 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/UrlHandling:UrlHandling",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,7 @@ swift_library(
|
||||
"//submodules/ConfettiEffect:ConfettiEffect",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/InstantPageCache:InstantPageCache",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -15,6 +15,7 @@ import Markdown
|
||||
import InAppPurchaseManager
|
||||
import ConfettiEffect
|
||||
import TextFormat
|
||||
import InstantPageCache
|
||||
|
||||
private final class SectionGroupComponent: Component {
|
||||
public final class Item: Equatable {
|
||||
@ -884,7 +885,39 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
),
|
||||
horizontalAlignment: .natural,
|
||||
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: {},
|
||||
availableSize: CGSize(width: availableWidth - sideInsets - textSideInset * 2.0, height: .greatestFiniteMagnitude),
|
||||
@ -1222,7 +1255,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
.opacity(bottomPanelAlpha)
|
||||
)
|
||||
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)
|
||||
)
|
||||
context.add(button
|
||||
@ -1235,7 +1268,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
fileprivate let context: AccountContext
|
||||
|
||||
private var didSetReady = false
|
||||
private let _ready = Promise<Bool>()
|
||||
|
@ -599,7 +599,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
let limit = state.limits.maxFoldersCount
|
||||
let premiumLimit = state.premiumLimits.maxFoldersCount
|
||||
iconName = "Premium/Folder"
|
||||
badgeText = "\(limit)"
|
||||
badgeText = "\(component.count)"
|
||||
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
|
||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||
premiumValue = "\(premiumLimit)"
|
||||
@ -608,7 +608,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
let limit = state.limits.maxFolderChatsCount
|
||||
let premiumLimit = state.premiumLimits.maxFolderChatsCount
|
||||
iconName = "Premium/Chat"
|
||||
badgeText = "\(limit)"
|
||||
badgeText = "\(component.count)"
|
||||
string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string
|
||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||
premiumValue = "\(premiumLimit)"
|
||||
@ -617,7 +617,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
let limit = state.limits.maxPinnedChatCount
|
||||
let premiumLimit = state.premiumLimits.maxPinnedChatCount
|
||||
iconName = "Premium/Pin"
|
||||
badgeText = "\(limit)"
|
||||
badgeText = "\(component.count)"
|
||||
string = strings.Premium_MaxPinsText("\(limit)", "\(premiumLimit)").string
|
||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||
premiumValue = "\(premiumLimit)"
|
||||
|
@ -11,7 +11,7 @@ import PresentationDataUtils
|
||||
import SolidRoundedButtonNode
|
||||
import AppBundle
|
||||
|
||||
public final class PremiumStickersScreen: ViewController {
|
||||
public final class PremiumReactionsScreen: ViewController {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
@ -21,7 +21,7 @@ public final class PremiumStickersScreen: ViewController {
|
||||
public var proceed: (() -> Void)?
|
||||
|
||||
private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||
private weak var controller: PremiumStickersScreen?
|
||||
private weak var controller: PremiumReactionsScreen?
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let blurView: UIVisualEffectView
|
||||
@ -38,7 +38,7 @@ public final class PremiumStickersScreen: ViewController {
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(controller: PremiumStickersScreen) {
|
||||
init(controller: PremiumReactionsScreen) {
|
||||
self.controller = controller
|
||||
self.presentationData = controller.presentationData
|
||||
|
||||
@ -70,7 +70,14 @@ public final class PremiumStickersScreen: ViewController {
|
||||
self.overlayTextNode.maximumNumberOfLines = 0
|
||||
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.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)
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.darkDimNode)
|
||||
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.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)
|
||||
@ -209,6 +219,8 @@ public final class PremiumStickersScreen: ViewController {
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
@ -7,69 +7,6 @@ import InstantPageUI
|
||||
import InstantPageCache
|
||||
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> {
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
return resolvedUrl
|
||||
|
@ -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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -35,8 +35,9 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
|
||||
public let item: StickerPreviewPeekItem
|
||||
let isLocked: Bool
|
||||
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.theme = theme
|
||||
self.strings = strings
|
||||
@ -47,6 +48,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
|
||||
} else {
|
||||
self.menu = menu
|
||||
}
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
}
|
||||
|
||||
public func presentation() -> PeekControllerContentPresentation {
|
||||
@ -71,7 +73,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
|
||||
|
||||
public func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)? {
|
||||
if self.isLocked {
|
||||
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings)
|
||||
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings, proceed: self.openPremiumIntro)
|
||||
} else {
|
||||
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 proceedButton: SolidRoundedButtonNode
|
||||
let cancelButton: HighlightableButtonNode
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, proceed: @escaping () -> Void) {
|
||||
self.proceed = proceed
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
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.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.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.cancelButton)
|
||||
|
||||
self.proceedButton.pressed = {
|
||||
|
||||
self.proceedButton.pressed = { [weak self] in
|
||||
self?.proceed()
|
||||
}
|
||||
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc func cancelPressed() {
|
||||
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -52,7 +52,7 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio
|
||||
|
||||
let count = sameKind.count + additionalCount
|
||||
if count > limitCount, itemIds.firstIndex(of: itemId) == nil {
|
||||
return .limitExceeded(count: count, limit: limitCount)
|
||||
return .limitExceeded(count: sameKind.count, limit: limitCount)
|
||||
} else {
|
||||
if let index = itemIds.firstIndex(of: itemId) {
|
||||
itemIds.remove(at: index)
|
||||
|
@ -1095,8 +1095,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if case .premium = value {
|
||||
controller?.dismiss()
|
||||
|
||||
let controller = PremiumStickersScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, reactions: premiumReactions)
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
let controller = PremiumReactionsScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, reactions: premiumReactions)
|
||||
strongSelf.push(controller)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -621,6 +621,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
self.strings = strings
|
||||
|
||||
let titleContent = self.titleContent
|
||||
self.titleCredibilityIcon = .none
|
||||
self.titleContent = titleContent
|
||||
let _ = self.updateStatus()
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -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 {
|
||||
var menuItems: [ContextMenuItem] = []
|
||||
if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isAnimated {
|
||||
|
@ -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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -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 {
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user