mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +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.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";
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -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] = []
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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/ConfettiEffect:ConfettiEffect",
|
||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
"//submodules/GZip:GZip",
|
"//submodules/GZip:GZip",
|
||||||
|
"//submodules/InstantPageCache:InstantPageCache",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -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>()
|
||||||
|
@ -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)"
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user