mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'dd20006e40bc9aa72bac52c14c83c5380474a3f4'
This commit is contained in:
commit
b849968275
@ -7705,3 +7705,5 @@ Sorry for the inconvenience.";
|
|||||||
"Premium.Purchase.ErrorUnknown" = "An error occurred. Please try again.";
|
"Premium.Purchase.ErrorUnknown" = "An error occurred. Please try again.";
|
||||||
"Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again.";
|
"Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again.";
|
||||||
"Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment.";
|
"Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment.";
|
||||||
|
|
||||||
|
"Settings.Premium" = "Telegram Premium";
|
||||||
|
@ -51,6 +51,7 @@ public final class TelegramApplicationBindings {
|
|||||||
public let pushIdleTimerExtension: () -> Disposable
|
public let pushIdleTimerExtension: () -> Disposable
|
||||||
public let openSettings: () -> Void
|
public let openSettings: () -> Void
|
||||||
public let openAppStorePage: () -> Void
|
public let openAppStorePage: () -> Void
|
||||||
|
public let openSubscriptions: () -> Void
|
||||||
public let registerForNotifications: (@escaping (Bool) -> Void) -> Void
|
public let registerForNotifications: (@escaping (Bool) -> Void) -> Void
|
||||||
public let requestSiriAuthorization: (@escaping (Bool) -> Void) -> Void
|
public let requestSiriAuthorization: (@escaping (Bool) -> Void) -> Void
|
||||||
public let siriAuthorization: () -> AccessType
|
public let siriAuthorization: () -> AccessType
|
||||||
@ -62,7 +63,7 @@ public final class TelegramApplicationBindings {
|
|||||||
public let requestSetAlternateIconName: (String?, @escaping (Bool) -> Void) -> Void
|
public let requestSetAlternateIconName: (String?, @escaping (Bool) -> Void) -> Void
|
||||||
public let forceOrientation: (UIInterfaceOrientation) -> Void
|
public let forceOrientation: (UIInterfaceOrientation) -> Void
|
||||||
|
|
||||||
public init(isMainApp: Bool, appBundleId: String, appBuildType: TelegramAppBuildType, containerPath: String, appSpecificScheme: String, openUrl: @escaping (String) -> Void, openUniversalUrl: @escaping (String, TelegramApplicationOpenUrlCompletion) -> Void, canOpenUrl: @escaping (String) -> Bool, getTopWindow: @escaping () -> UIWindow?, displayNotification: @escaping (String) -> Void, applicationInForeground: Signal<Bool, NoError>, applicationIsActive: Signal<Bool, NoError>, clearMessageNotifications: @escaping ([MessageId]) -> Void, pushIdleTimerExtension: @escaping () -> Disposable, openSettings: @escaping () -> Void, openAppStorePage: @escaping () -> Void, registerForNotifications: @escaping (@escaping (Bool) -> Void) -> Void, requestSiriAuthorization: @escaping (@escaping (Bool) -> Void) -> Void, siriAuthorization: @escaping () -> AccessType, getWindowHost: @escaping () -> WindowHost?, presentNativeController: @escaping (UIViewController) -> Void, dismissNativeController: @escaping () -> Void, getAvailableAlternateIcons: @escaping () -> [PresentationAppIcon], getAlternateIconName: @escaping () -> String?, requestSetAlternateIconName: @escaping (String?, @escaping (Bool) -> Void) -> Void, forceOrientation: @escaping (UIInterfaceOrientation) -> Void) {
|
public init(isMainApp: Bool, appBundleId: String, appBuildType: TelegramAppBuildType, containerPath: String, appSpecificScheme: String, openUrl: @escaping (String) -> Void, openUniversalUrl: @escaping (String, TelegramApplicationOpenUrlCompletion) -> Void, canOpenUrl: @escaping (String) -> Bool, getTopWindow: @escaping () -> UIWindow?, displayNotification: @escaping (String) -> Void, applicationInForeground: Signal<Bool, NoError>, applicationIsActive: Signal<Bool, NoError>, clearMessageNotifications: @escaping ([MessageId]) -> Void, pushIdleTimerExtension: @escaping () -> Disposable, openSettings: @escaping () -> Void, openAppStorePage: @escaping () -> Void, openSubscriptions: @escaping () -> Void, registerForNotifications: @escaping (@escaping (Bool) -> Void) -> Void, requestSiriAuthorization: @escaping (@escaping (Bool) -> Void) -> Void, siriAuthorization: @escaping () -> AccessType, getWindowHost: @escaping () -> WindowHost?, presentNativeController: @escaping (UIViewController) -> Void, dismissNativeController: @escaping () -> Void, getAvailableAlternateIcons: @escaping () -> [PresentationAppIcon], getAlternateIconName: @escaping () -> String?, requestSetAlternateIconName: @escaping (String?, @escaping (Bool) -> Void) -> Void, forceOrientation: @escaping (UIInterfaceOrientation) -> Void) {
|
||||||
self.isMainApp = isMainApp
|
self.isMainApp = isMainApp
|
||||||
self.appBundleId = appBundleId
|
self.appBundleId = appBundleId
|
||||||
self.appBuildType = appBuildType
|
self.appBuildType = appBuildType
|
||||||
@ -79,6 +80,7 @@ public final class TelegramApplicationBindings {
|
|||||||
self.pushIdleTimerExtension = pushIdleTimerExtension
|
self.pushIdleTimerExtension = pushIdleTimerExtension
|
||||||
self.openSettings = openSettings
|
self.openSettings = openSettings
|
||||||
self.openAppStorePage = openAppStorePage
|
self.openAppStorePage = openAppStorePage
|
||||||
|
self.openSubscriptions = openSubscriptions
|
||||||
self.registerForNotifications = registerForNotifications
|
self.registerForNotifications = registerForNotifications
|
||||||
self.requestSiriAuthorization = requestSiriAuthorization
|
self.requestSiriAuthorization = requestSiriAuthorization
|
||||||
self.siriAuthorization = siriAuthorization
|
self.siriAuthorization = siriAuthorization
|
||||||
@ -885,3 +887,23 @@ public protocol AccountContext: AnyObject {
|
|||||||
func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription)
|
func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription)
|
||||||
func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void)
|
func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct PremiumConfiguration {
|
||||||
|
public static var defaultValue: PremiumConfiguration {
|
||||||
|
return PremiumConfiguration(isPremiumDisabled: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public let isPremiumDisabled: Bool
|
||||||
|
|
||||||
|
fileprivate init(isPremiumDisabled: Bool) {
|
||||||
|
self.isPremiumDisabled = isPremiumDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func with(appConfiguration: AppConfiguration) -> PremiumConfiguration {
|
||||||
|
if let data = appConfiguration.data, let value = data["premium_purchase_blocked"] as? Bool {
|
||||||
|
return PremiumConfiguration(isPremiumDisabled: value)
|
||||||
|
} else {
|
||||||
|
return .defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1259,8 +1259,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
@ -1453,8 +1454,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
|
@ -127,6 +127,8 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
|||||||
return self.item?.tag
|
return self.item?.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var exceededLimit = false
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.backgroundNode.isLayerBacked = true
|
self.backgroundNode.isLayerBacked = true
|
||||||
@ -197,6 +199,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
|||||||
var limitTextString: NSAttributedString?
|
var limitTextString: NSAttributedString?
|
||||||
var rightInset: CGFloat = params.rightInset
|
var rightInset: CGFloat = params.rightInset
|
||||||
|
|
||||||
|
var exceededLimit = false
|
||||||
if let maxLength = item.maxLength, maxLength.display {
|
if let maxLength = item.maxLength, maxLength.display {
|
||||||
let textLength: Int
|
let textLength: Int
|
||||||
switch maxLength.mode {
|
switch maxLength.mode {
|
||||||
@ -210,6 +213,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
|||||||
if displayTextLimit {
|
if displayTextLimit {
|
||||||
limitTextString = NSAttributedString(string: "\(remainingCount)", font: Font.regular(13.0), textColor: remainingCount < 0 ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemSecondaryTextColor)
|
limitTextString = NSAttributedString(string: "\(remainingCount)", font: Font.regular(13.0), textColor: remainingCount < 0 ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemSecondaryTextColor)
|
||||||
}
|
}
|
||||||
|
exceededLimit = remainingCount < 0
|
||||||
|
|
||||||
rightInset += 30.0 + 4.0
|
rightInset += 30.0 + 4.0
|
||||||
}
|
}
|
||||||
@ -254,6 +258,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.layoutParams = params
|
strongSelf.layoutParams = params
|
||||||
|
strongSelf.exceededLimit = exceededLimit
|
||||||
|
|
||||||
if let _ = updatedTheme {
|
if let _ = updatedTheme {
|
||||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||||
@ -470,6 +475,12 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
|||||||
self.textNode.layer.addShakeAnimation()
|
self.textNode.layer.addShakeAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func animateErrorIfNeeded() {
|
||||||
|
if self.exceededLimit {
|
||||||
|
self.animateError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func inlineActionPressed() {
|
@objc private func inlineActionPressed() {
|
||||||
if let action = self.item?.inlineAction?.action {
|
if let action = self.item?.inlineAction?.action {
|
||||||
action()
|
action()
|
||||||
|
@ -160,7 +160,8 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
|||||||
activeTitleColor: .white,
|
activeTitleColor: .white,
|
||||||
badgeIconName: badgeIconName,
|
badgeIconName: badgeIconName,
|
||||||
badgeText: "\(item.count)",
|
badgeText: "\(item.count)",
|
||||||
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount)
|
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount),
|
||||||
|
isPremiumDisabled: false
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0)
|
containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0)
|
||||||
|
@ -64,7 +64,18 @@ final class AppIconsDemoComponent: Component {
|
|||||||
|
|
||||||
if self.imageViews.isEmpty {
|
if self.imageViews.isEmpty {
|
||||||
for icon in component.appIcons {
|
for icon in component.appIcons {
|
||||||
if let image = UIImage(named: icon.imageName, in: getAppBundle(), compatibleWith: nil) {
|
let image: UIImage?
|
||||||
|
switch icon.imageName {
|
||||||
|
case "Premium":
|
||||||
|
image = UIImage(bundleImageName: "Premium/Icons/Premium")
|
||||||
|
case "PremiumBlack":
|
||||||
|
image = UIImage(bundleImageName: "Premium/Icons/Black")
|
||||||
|
case "PremiumTurbo":
|
||||||
|
image = UIImage(bundleImageName: "Premium/Icons/Turbo")
|
||||||
|
default:
|
||||||
|
image = nil
|
||||||
|
}
|
||||||
|
if let image = image {
|
||||||
let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: 90.0, height: 90.0)))
|
let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: 90.0, height: 90.0)))
|
||||||
imageView.clipsToBounds = true
|
imageView.clipsToBounds = true
|
||||||
imageView.layer.cornerRadius = 24.0
|
imageView.layer.cornerRadius = 24.0
|
||||||
|
@ -1320,35 +1320,40 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
let availableProducts: Signal<[InAppPurchaseManager.Product], NoError>
|
||||||
if let inAppPurchaseManager = context.inAppPurchaseManager {
|
if let inAppPurchaseManager = context.inAppPurchaseManager {
|
||||||
let otherPeerName: Signal<String?, NoError>
|
availableProducts = inAppPurchaseManager.availableProducts
|
||||||
if case let .profile(peerId) = source {
|
} else {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
availableProducts = .single([])
|
||||||
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
|
||||||
|> map { peer -> String? in
|
|
||||||
return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
otherPeerName = .single(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.disposable = combineLatest(
|
|
||||||
queue: Queue.mainQueue(),
|
|
||||||
inAppPurchaseManager.availableProducts,
|
|
||||||
context.account.postbox.peerView(id: context.account.peerId)
|
|
||||||
|> map { view -> Bool in
|
|
||||||
return view.peers[view.peerId]?.isPremium ?? false
|
|
||||||
},
|
|
||||||
otherPeerName
|
|
||||||
).start(next: { [weak self] products, isPremium, otherPeerName in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.premiumProduct = products.first
|
|
||||||
strongSelf.isPremium = isPremium
|
|
||||||
strongSelf.otherPeerName = otherPeerName
|
|
||||||
strongSelf.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let otherPeerName: Signal<String?, NoError>
|
||||||
|
if case let .profile(peerId) = source {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|
|> map { peer -> String? in
|
||||||
|
return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
otherPeerName = .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disposable = combineLatest(
|
||||||
|
queue: Queue.mainQueue(),
|
||||||
|
availableProducts,
|
||||||
|
context.account.postbox.peerView(id: context.account.peerId)
|
||||||
|
|> map { view -> Bool in
|
||||||
|
return view.peers[view.peerId]?.isPremium ?? false
|
||||||
|
},
|
||||||
|
otherPeerName
|
||||||
|
).start(next: { [weak self] products, isPremium, otherPeerName in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.premiumProduct = products.first
|
||||||
|
strongSelf.isPremium = isPremium
|
||||||
|
strongSelf.otherPeerName = otherPeerName
|
||||||
|
strongSelf.updated(transition: .immediate)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -44,6 +44,7 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
private let textColor: UIColor
|
private let textColor: UIColor
|
||||||
private let badgeText: String?
|
private let badgeText: String?
|
||||||
private let badgePosition: CGFloat
|
private let badgePosition: CGFloat
|
||||||
|
private let isPremiumDisabled: Bool
|
||||||
|
|
||||||
init(
|
init(
|
||||||
iconName: String?,
|
iconName: String?,
|
||||||
@ -51,7 +52,8 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
activeColors: [UIColor],
|
activeColors: [UIColor],
|
||||||
textColor: UIColor,
|
textColor: UIColor,
|
||||||
badgeText: String?,
|
badgeText: String?,
|
||||||
badgePosition: CGFloat
|
badgePosition: CGFloat,
|
||||||
|
isPremiumDisabled: Bool
|
||||||
) {
|
) {
|
||||||
self.iconName = iconName
|
self.iconName = iconName
|
||||||
self.inactiveColor = inactiveColor
|
self.inactiveColor = inactiveColor
|
||||||
@ -59,6 +61,7 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.badgeText = badgeText
|
self.badgeText = badgeText
|
||||||
self.badgePosition = badgePosition
|
self.badgePosition = badgePosition
|
||||||
|
self.isPremiumDisabled = isPremiumDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: PremiumLimitAnimationComponent, rhs: PremiumLimitAnimationComponent) -> Bool {
|
static func ==(lhs: PremiumLimitAnimationComponent, rhs: PremiumLimitAnimationComponent) -> Bool {
|
||||||
@ -80,6 +83,9 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
if lhs.badgePosition != rhs.badgePosition {
|
if lhs.badgePosition != rhs.badgePosition {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isPremiumDisabled != rhs.isPremiumDisabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,43 +189,40 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
func playAppearanceAnimation(component: PremiumLimitAnimationComponent, availableSize: CGSize) {
|
func playAppearanceAnimation(component: PremiumLimitAnimationComponent, availableSize: CGSize) {
|
||||||
self.badgeView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.4, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
self.badgeView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.4, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||||
|
|
||||||
let now = self.badgeView.layer.convertTime(CACurrentMediaTime(), from: nil)
|
|
||||||
|
|
||||||
let positionAnimation = CABasicAnimation(keyPath: "position.x")
|
let positionAnimation = CABasicAnimation(keyPath: "position.x")
|
||||||
positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: 0.0, y: 0.0))
|
positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: 0.0, y: 0.0))
|
||||||
positionAnimation.toValue = NSValue(cgPoint: self.badgeView.center)
|
positionAnimation.toValue = NSValue(cgPoint: self.badgeView.center)
|
||||||
positionAnimation.duration = 0.5
|
positionAnimation.duration = 0.5
|
||||||
positionAnimation.fillMode = .forwards
|
positionAnimation.fillMode = .forwards
|
||||||
positionAnimation.beginTime = now
|
self.badgeView.layer.add(positionAnimation, forKey: "appearance1")
|
||||||
|
|
||||||
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
|
||||||
rotateAnimation.fromValue = 0.0 as NSNumber
|
|
||||||
rotateAnimation.toValue = 0.2 as NSNumber
|
|
||||||
rotateAnimation.isAdditive = true
|
|
||||||
rotateAnimation.duration = 0.2
|
|
||||||
rotateAnimation.beginTime = now + 0.5
|
|
||||||
rotateAnimation.fillMode = .forwards
|
|
||||||
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
||||||
|
|
||||||
Queue.mainQueue().after(0.5, {
|
Queue.mainQueue().after(0.5, {
|
||||||
|
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||||
|
rotateAnimation.fromValue = 0.0 as NSNumber
|
||||||
|
rotateAnimation.toValue = 0.2 as NSNumber
|
||||||
|
rotateAnimation.duration = 0.2
|
||||||
|
rotateAnimation.fillMode = .forwards
|
||||||
|
rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||||
|
rotateAnimation.isRemovedOnCompletion = false
|
||||||
|
self.badgeView.layer.add(rotateAnimation, forKey: "appearance2")
|
||||||
|
|
||||||
if !self.badgeView.isHidden {
|
if !self.badgeView.isHidden {
|
||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.2) {
|
||||||
|
let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||||
|
returnAnimation.fromValue = 0.2 as NSNumber
|
||||||
|
returnAnimation.toValue = 0.0 as NSNumber
|
||||||
|
returnAnimation.duration = 0.18
|
||||||
|
returnAnimation.fillMode = .forwards
|
||||||
|
returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
||||||
|
self.badgeView.layer.add(returnAnimation, forKey: "appearance3")
|
||||||
|
self.badgeView.layer.removeAnimation(forKey: "appearance2")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
|
||||||
returnAnimation.fromValue = 0.2 as NSNumber
|
|
||||||
returnAnimation.toValue = 0.0 as NSNumber
|
|
||||||
returnAnimation.isAdditive = true
|
|
||||||
returnAnimation.duration = 0.18
|
|
||||||
returnAnimation.beginTime = now + 0.5 + 0.2
|
|
||||||
returnAnimation.fillMode = .forwards
|
|
||||||
returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
|
||||||
|
|
||||||
self.badgeView.layer.add(positionAnimation, forKey: "appearance1")
|
|
||||||
self.badgeView.layer.add(rotateAnimation, forKey: "appearance2")
|
|
||||||
self.badgeView.layer.add(returnAnimation, forKey: "appearance3")
|
|
||||||
|
|
||||||
self.badgeView.alpha = 1.0
|
self.badgeView.alpha = 1.0
|
||||||
self.badgeView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
self.badgeView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||||
|
|
||||||
@ -241,12 +244,14 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - lineHeight), size: CGSize(width: availableSize.width, height: lineHeight))
|
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - lineHeight), size: CGSize(width: availableSize.width, height: lineHeight))
|
||||||
self.container.frame = containerFrame
|
self.container.frame = containerFrame
|
||||||
|
|
||||||
self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
if !component.isPremiumDisabled {
|
||||||
self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
||||||
|
self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
||||||
|
|
||||||
self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight))
|
self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight))
|
||||||
if self.activeBackground.animation(forKey: "movement") == nil {
|
if self.activeBackground.animation(forKey: "movement") == nil {
|
||||||
self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0)
|
self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let countWidth: CGFloat
|
let countWidth: CGFloat
|
||||||
@ -276,24 +281,41 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
self.badgeMaskTailView.frame = CGRect(origin: CGPoint(x: badgeSize.width - 44.0, y: badgeSize.height - 36.0), size: CGSize(width: 44.0, height: 36.0))
|
self.badgeMaskTailView.frame = CGRect(origin: CGPoint(x: badgeSize.width - 44.0, y: badgeSize.height - 36.0), size: CGSize(width: 44.0, height: 36.0))
|
||||||
|
|
||||||
self.badgeView.bounds = CGRect(origin: .zero, size: badgeSize)
|
self.badgeView.bounds = CGRect(origin: .zero, size: badgeSize)
|
||||||
if component.badgePosition > 1.0 - .ulpOfOne {
|
|
||||||
|
var badgePosition = component.badgePosition
|
||||||
|
if component.isPremiumDisabled {
|
||||||
|
badgePosition = 0.5
|
||||||
|
}
|
||||||
|
if badgePosition > 1.0 - .ulpOfOne {
|
||||||
self.badgeView.layer.anchorPoint = CGPoint(x: 1.0, y: 1.0)
|
self.badgeView.layer.anchorPoint = CGPoint(x: 1.0, y: 1.0)
|
||||||
|
|
||||||
self.badgeMaskTailView.isHidden = false
|
self.badgeMaskTailView.isHidden = false
|
||||||
self.badgeMaskArrowView.isHidden = true
|
self.badgeMaskArrowView.isHidden = true
|
||||||
|
|
||||||
self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * component.badgePosition + 3.0, y: 82.0)
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * badgePosition + 3.0, y: 82.0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.badgeView.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
|
self.badgeView.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
|
||||||
|
|
||||||
self.badgeMaskTailView.isHidden = true
|
self.badgeMaskTailView.isHidden = true
|
||||||
self.badgeMaskArrowView.isHidden = false
|
self.badgeMaskArrowView.isHidden = component.isPremiumDisabled
|
||||||
|
|
||||||
self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * component.badgePosition, y: 82.0)
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * badgePosition, y: 82.0)
|
||||||
|
}
|
||||||
|
|
||||||
if self.badgeView.frame.maxX > availableSize.width {
|
if self.badgeView.frame.maxX > availableSize.width {
|
||||||
let delta = self.badgeView.frame.maxX - availableSize.width - 6.0
|
let delta = self.badgeView.frame.maxX - availableSize.width - 6.0
|
||||||
self.badgeView.center = self.badgeView.center.offsetBy(dx: -delta, dy: 0.0)
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.badgeView.center = self.badgeView.center.offsetBy(dx: -delta, dy: 0.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: badgeSize.width * 3.0, height: badgeSize.height))
|
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: badgeSize.width * 3.0, height: badgeSize.height))
|
||||||
@ -304,7 +326,16 @@ private class PremiumLimitAnimationComponent: Component {
|
|||||||
self.badgeIcon.frame = CGRect(x: 15.0, y: 9.0, width: 30.0, height: 30.0)
|
self.badgeIcon.frame = CGRect(x: 15.0, y: 9.0, width: 30.0, height: 30.0)
|
||||||
self.badgeCountLabel.frame = CGRect(x: badgeSize.width - countWidth - 11.0, y: 10.0, width: countWidth, height: 48.0)
|
self.badgeCountLabel.frame = CGRect(x: badgeSize.width - countWidth - 11.0, y: 10.0, width: countWidth, height: 48.0)
|
||||||
|
|
||||||
if !self.didPlayAppearanceAnimation {
|
if component.isPremiumDisabled {
|
||||||
|
if !self.didPlayAppearanceAnimation {
|
||||||
|
self.didPlayAppearanceAnimation = true
|
||||||
|
|
||||||
|
self.badgeView.alpha = 1.0
|
||||||
|
if let badgeText = component.badgeText {
|
||||||
|
self.badgeCountLabel.configure(with: badgeText, duration: 0.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !self.didPlayAppearanceAnimation {
|
||||||
self.didPlayAppearanceAnimation = true
|
self.didPlayAppearanceAnimation = true
|
||||||
self.playAppearanceAnimation(component: component, availableSize: availableSize)
|
self.playAppearanceAnimation(component: component, availableSize: availableSize)
|
||||||
}
|
}
|
||||||
@ -396,6 +427,7 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
let badgeIconName: String?
|
let badgeIconName: String?
|
||||||
let badgeText: String?
|
let badgeText: String?
|
||||||
let badgePosition: CGFloat
|
let badgePosition: CGFloat
|
||||||
|
let isPremiumDisabled: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
inactiveColor: UIColor,
|
inactiveColor: UIColor,
|
||||||
@ -408,7 +440,8 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
activeTitleColor: UIColor,
|
activeTitleColor: UIColor,
|
||||||
badgeIconName: String?,
|
badgeIconName: String?,
|
||||||
badgeText: String?,
|
badgeText: String?,
|
||||||
badgePosition: CGFloat
|
badgePosition: CGFloat,
|
||||||
|
isPremiumDisabled: Bool
|
||||||
) {
|
) {
|
||||||
self.inactiveColor = inactiveColor
|
self.inactiveColor = inactiveColor
|
||||||
self.activeColors = activeColors
|
self.activeColors = activeColors
|
||||||
@ -421,6 +454,7 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
self.badgeIconName = badgeIconName
|
self.badgeIconName = badgeIconName
|
||||||
self.badgeText = badgeText
|
self.badgeText = badgeText
|
||||||
self.badgePosition = badgePosition
|
self.badgePosition = badgePosition
|
||||||
|
self.isPremiumDisabled = isPremiumDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: PremiumLimitDisplayComponent, rhs: PremiumLimitDisplayComponent) -> Bool {
|
public static func ==(lhs: PremiumLimitDisplayComponent, rhs: PremiumLimitDisplayComponent) -> Bool {
|
||||||
@ -457,6 +491,9 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
if lhs.badgePosition != rhs.badgePosition {
|
if lhs.badgePosition != rhs.badgePosition {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isPremiumDisabled != rhs.isPremiumDisabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,62 +510,6 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
let height: CGFloat = 120.0
|
let height: CGFloat = 120.0
|
||||||
let lineHeight: CGFloat = 30.0
|
let lineHeight: CGFloat = 30.0
|
||||||
|
|
||||||
let inactiveTitle = inactiveTitle.update(
|
|
||||||
component: MultilineTextComponent(
|
|
||||||
text: .plain(
|
|
||||||
NSAttributedString(
|
|
||||||
string: component.inactiveTitle,
|
|
||||||
font: Font.semibold(15.0),
|
|
||||||
textColor: component.inactiveTitleColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
availableSize: context.availableSize,
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
|
|
||||||
let inactiveValue = inactiveValue.update(
|
|
||||||
component: MultilineTextComponent(
|
|
||||||
text: .plain(
|
|
||||||
NSAttributedString(
|
|
||||||
string: component.inactiveValue,
|
|
||||||
font: Font.semibold(15.0),
|
|
||||||
textColor: component.inactiveTitleColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
availableSize: context.availableSize,
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
|
|
||||||
let activeTitle = activeTitle.update(
|
|
||||||
component: MultilineTextComponent(
|
|
||||||
text: .plain(
|
|
||||||
NSAttributedString(
|
|
||||||
string: component.activeTitle,
|
|
||||||
font: Font.semibold(15.0),
|
|
||||||
textColor: component.activeTitleColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
availableSize: context.availableSize,
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
|
|
||||||
let activeValue = activeValue.update(
|
|
||||||
component: MultilineTextComponent(
|
|
||||||
text: .plain(
|
|
||||||
NSAttributedString(
|
|
||||||
string: component.activeValue,
|
|
||||||
font: Font.semibold(15.0),
|
|
||||||
textColor: component.activeTitleColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
availableSize: context.availableSize,
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
|
|
||||||
let animation = animation.update(
|
let animation = animation.update(
|
||||||
component: PremiumLimitAnimationComponent(
|
component: PremiumLimitAnimationComponent(
|
||||||
iconName: component.badgeIconName,
|
iconName: component.badgeIconName,
|
||||||
@ -536,7 +517,8 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
activeColors: component.activeColors,
|
activeColors: component.activeColors,
|
||||||
textColor: component.activeTitleColor,
|
textColor: component.activeTitleColor,
|
||||||
badgeText: component.badgeText,
|
badgeText: component.badgeText,
|
||||||
badgePosition: component.badgePosition
|
badgePosition: component.badgePosition,
|
||||||
|
isPremiumDisabled: component.isPremiumDisabled
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width, height: height),
|
availableSize: CGSize(width: context.availableSize.width, height: height),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
@ -546,21 +528,79 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
|||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: height / 2.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
context.add(inactiveTitle
|
if !component.isPremiumDisabled {
|
||||||
.position(CGPoint(x: inactiveTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
let inactiveTitle = inactiveTitle.update(
|
||||||
)
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(
|
||||||
|
NSAttributedString(
|
||||||
|
string: component.inactiveTitle,
|
||||||
|
font: Font.semibold(15.0),
|
||||||
|
textColor: component.inactiveTitleColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
context.add(inactiveValue
|
let inactiveValue = inactiveValue.update(
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0 - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
component: MultilineTextComponent(
|
||||||
)
|
text: .plain(
|
||||||
|
NSAttributedString(
|
||||||
|
string: component.inactiveValue,
|
||||||
|
font: Font.semibold(15.0),
|
||||||
|
textColor: component.inactiveTitleColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
context.add(activeTitle
|
let activeTitle = activeTitle.update(
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0 + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
component: MultilineTextComponent(
|
||||||
)
|
text: .plain(
|
||||||
|
NSAttributedString(
|
||||||
|
string: component.activeTitle,
|
||||||
|
font: Font.semibold(15.0),
|
||||||
|
textColor: component.activeTitleColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
context.add(activeValue
|
let activeValue = activeValue.update(
|
||||||
.position(CGPoint(x: context.availableSize.width - activeValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
component: MultilineTextComponent(
|
||||||
)
|
text: .plain(
|
||||||
|
NSAttributedString(
|
||||||
|
string: component.activeValue,
|
||||||
|
font: Font.semibold(15.0),
|
||||||
|
textColor: component.activeTitleColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(inactiveTitle
|
||||||
|
.position(CGPoint(x: inactiveTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(inactiveValue
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0 - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(activeTitle
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0 + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(activeValue
|
||||||
|
.position(CGPoint(x: context.availableSize.width - activeValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return CGSize(width: context.availableSize.width, height: height)
|
return CGSize(width: context.availableSize.width, height: height)
|
||||||
}
|
}
|
||||||
@ -656,6 +696,9 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
let state = context.state
|
let state = context.state
|
||||||
let subject = component.subject
|
let subject = component.subject
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||||
|
let isPremiumDisabled = premiumConfiguration.isPremiumDisabled
|
||||||
|
|
||||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||||
let textSideInset: CGFloat = 24.0 + environment.safeInsets.left
|
let textSideInset: CGFloat = 24.0 + environment.safeInsets.left
|
||||||
|
|
||||||
@ -684,7 +727,7 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
var titleText = strings.Premium_LimitReached
|
var titleText = strings.Premium_LimitReached
|
||||||
var buttonAnimationName = "premium_x2"
|
var buttonAnimationName = "premium_x2"
|
||||||
let iconName: String
|
let iconName: String
|
||||||
let badgeText: String
|
var badgeText: String
|
||||||
var string: String
|
var string: String
|
||||||
let defaultValue: String
|
let defaultValue: String
|
||||||
let premiumValue: String
|
let premiumValue: String
|
||||||
@ -703,6 +746,11 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
if !state.isPremium && badgePosition > 0.5 {
|
if !state.isPremium && badgePosition > 0.5 {
|
||||||
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
|
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isPremiumDisabled {
|
||||||
|
badgeText = "\(limit)"
|
||||||
|
string = strings.Premium_MaxFoldersCountNoPremiumText("\(limit)").string
|
||||||
|
}
|
||||||
case .chatsPerFolder:
|
case .chatsPerFolder:
|
||||||
let limit = state.limits.maxFolderChatsCount
|
let limit = state.limits.maxFolderChatsCount
|
||||||
let premiumLimit = state.premiumLimits.maxFolderChatsCount
|
let premiumLimit = state.premiumLimits.maxFolderChatsCount
|
||||||
@ -712,6 +760,11 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||||
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
||||||
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
||||||
|
|
||||||
|
if isPremiumDisabled {
|
||||||
|
badgeText = "\(limit)"
|
||||||
|
string = strings.Premium_MaxChatsInFolderNoPremiumText("\(limit)").string
|
||||||
|
}
|
||||||
case .pins:
|
case .pins:
|
||||||
let limit = state.limits.maxPinnedChatCount
|
let limit = state.limits.maxPinnedChatCount
|
||||||
let premiumLimit = state.premiumLimits.maxPinnedChatCount
|
let premiumLimit = state.premiumLimits.maxPinnedChatCount
|
||||||
@ -721,6 +774,11 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||||
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
||||||
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
||||||
|
|
||||||
|
if isPremiumDisabled {
|
||||||
|
badgeText = "\(limit)"
|
||||||
|
string = strings.Premium_MaxPinsNoPremiumText("\(limit)").string
|
||||||
|
}
|
||||||
case .files:
|
case .files:
|
||||||
let limit = Int64(state.limits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100
|
let limit = Int64(state.limits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100
|
||||||
let premiumLimit = Int64(state.premiumLimits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100
|
let premiumLimit = Int64(state.premiumLimits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100
|
||||||
@ -731,6 +789,11 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
premiumValue = component.count != 4 ? dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator)) : ""
|
premiumValue = component.count != 4 ? dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator)) : ""
|
||||||
badgePosition = component.count == 4 ? 1.0 : 0.5
|
badgePosition = component.count == 4 ? 1.0 : 0.5
|
||||||
titleText = strings.Premium_FileTooLarge
|
titleText = strings.Premium_FileTooLarge
|
||||||
|
|
||||||
|
if isPremiumDisabled {
|
||||||
|
badgeText = dataSizeString(limit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))
|
||||||
|
string = strings.Premium_MaxFileSizeNoPremiumText(dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))).string
|
||||||
|
}
|
||||||
case .accounts:
|
case .accounts:
|
||||||
let limit = 3
|
let limit = 3
|
||||||
let premiumLimit = component.count + 1
|
let premiumLimit = component.count + 1
|
||||||
@ -745,6 +808,11 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
||||||
}
|
}
|
||||||
buttonAnimationName = "premium_addone"
|
buttonAnimationName = "premium_addone"
|
||||||
|
|
||||||
|
if isPremiumDisabled {
|
||||||
|
badgeText = "\(limit)"
|
||||||
|
string = strings.Premium_MaxAccountsNoPremiumText("\(limit)").string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var reachedMaximumLimit = badgePosition >= 1.0
|
var reachedMaximumLimit = badgePosition >= 1.0
|
||||||
if case .folders = subject, !state.isPremium {
|
if case .folders = subject, !state.isPremium {
|
||||||
@ -784,16 +852,26 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let gradientColors: [UIColor]
|
||||||
|
if isPremiumDisabled {
|
||||||
|
gradientColors = [
|
||||||
|
UIColor(rgb: 0x007afe),
|
||||||
|
UIColor(rgb: 0x5494ff)
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
gradientColors = [
|
||||||
|
UIColor(rgb: 0x0077ff),
|
||||||
|
UIColor(rgb: 0x6b93ff),
|
||||||
|
UIColor(rgb: 0x8878ff),
|
||||||
|
UIColor(rgb: 0xe46ace)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
if state.initialized {
|
if state.initialized {
|
||||||
let limit = limit.update(
|
let limit = limit.update(
|
||||||
component: PremiumLimitDisplayComponent(
|
component: PremiumLimitDisplayComponent(
|
||||||
inactiveColor: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
|
inactiveColor: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
|
||||||
activeColors: [
|
activeColors: gradientColors,
|
||||||
UIColor(rgb: 0x0077ff),
|
|
||||||
UIColor(rgb: 0x6b93ff),
|
|
||||||
UIColor(rgb: 0x8878ff),
|
|
||||||
UIColor(rgb: 0xe46ace)
|
|
||||||
],
|
|
||||||
inactiveTitle: strings.Premium_Free,
|
inactiveTitle: strings.Premium_Free,
|
||||||
inactiveValue: defaultValue,
|
inactiveValue: defaultValue,
|
||||||
inactiveTitleColor: theme.list.itemPrimaryTextColor,
|
inactiveTitleColor: theme.list.itemPrimaryTextColor,
|
||||||
@ -802,7 +880,8 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
activeTitleColor: .white,
|
activeTitleColor: .white,
|
||||||
badgeIconName: iconName,
|
badgeIconName: iconName,
|
||||||
badgeText: badgeText,
|
badgeText: badgeText,
|
||||||
badgePosition: badgePosition
|
badgePosition: badgePosition,
|
||||||
|
isPremiumDisabled: isPremiumDisabled
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
@ -812,47 +891,50 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isIncreaseButton = !reachedMaximumLimit && !isPremiumDisabled
|
||||||
let button = button.update(
|
let button = button.update(
|
||||||
component: SolidRoundedButtonComponent(
|
component: SolidRoundedButtonComponent(
|
||||||
title: !reachedMaximumLimit ? strings.Premium_IncreaseLimit : strings.Common_OK,
|
title: isIncreaseButton ? strings.Premium_IncreaseLimit : strings.Common_OK,
|
||||||
|
|
||||||
theme: SolidRoundedButtonComponent.Theme(
|
theme: SolidRoundedButtonComponent.Theme(
|
||||||
backgroundColor: .black,
|
backgroundColor: .black,
|
||||||
backgroundColors: [
|
backgroundColors: gradientColors,
|
||||||
UIColor(rgb: 0x0077ff),
|
|
||||||
UIColor(rgb: 0x6b93ff),
|
|
||||||
UIColor(rgb: 0x8878ff),
|
|
||||||
UIColor(rgb: 0xe46ace)
|
|
||||||
],
|
|
||||||
foregroundColor: .white
|
foregroundColor: .white
|
||||||
),
|
),
|
||||||
font: .bold,
|
font: .bold,
|
||||||
fontSize: 17.0,
|
fontSize: 17.0,
|
||||||
height: 50.0,
|
height: 50.0,
|
||||||
cornerRadius: 10.0,
|
cornerRadius: 10.0,
|
||||||
gloss: !reachedMaximumLimit,
|
gloss: isIncreaseButton,
|
||||||
animationName: !reachedMaximumLimit ? buttonAnimationName : nil,
|
animationName: isIncreaseButton ? buttonAnimationName : nil,
|
||||||
iconPosition: .right,
|
iconPosition: .right,
|
||||||
action: { [weak component] in
|
action: { [weak component] in
|
||||||
guard let component = component else {
|
guard let component = component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
component.dismiss()
|
component.dismiss()
|
||||||
component.action()
|
if isIncreaseButton {
|
||||||
|
component.action()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var textOffset: CGFloat = 228.0
|
||||||
|
if isPremiumDisabled {
|
||||||
|
textOffset -= 68.0
|
||||||
|
}
|
||||||
|
|
||||||
context.add(title
|
context.add(title
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0))
|
||||||
)
|
)
|
||||||
context.add(text
|
context.add(text
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 228.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset))
|
||||||
)
|
)
|
||||||
|
|
||||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: 228.0 + ceil(text.size.height / 2.0) + 38.0), size: button.size)
|
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + ceil(text.size.height / 2.0) + 38.0), size: button.size)
|
||||||
context.add(button
|
context.add(button
|
||||||
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
||||||
)
|
)
|
||||||
|
@ -167,7 +167,8 @@ private final class LimitComponent: CombinedComponent {
|
|||||||
activeTitleColor: component.activeTextColor,
|
activeTitleColor: component.activeTextColor,
|
||||||
badgeIconName: "",
|
badgeIconName: "",
|
||||||
badgeText: nil,
|
badgeText: nil,
|
||||||
badgePosition: 0.0
|
badgePosition: 0.0,
|
||||||
|
isPremiumDisabled: false
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
|
@ -30,7 +30,6 @@ open class RollingLabel: UILabel {
|
|||||||
open var showSymbol = false
|
open var showSymbol = false
|
||||||
private var scrollLayers: [CAScrollLayer] = []
|
private var scrollLayers: [CAScrollLayer] = []
|
||||||
private var scrollLabels: [UILabel] = []
|
private var scrollLabels: [UILabel] = []
|
||||||
private let duration = 1.12
|
|
||||||
private let durationOffset = 0.2
|
private let durationOffset = 0.2
|
||||||
private let textsNotAnimated = [","]
|
private let textsNotAnimated = [","]
|
||||||
|
|
||||||
@ -38,26 +37,26 @@ open class RollingLabel: UILabel {
|
|||||||
self.suffix = suffix
|
self.suffix = suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
func configure(with string: String) {
|
func configure(with string: String, duration: Double = 0.9) {
|
||||||
fullText = string
|
self.fullText = string
|
||||||
|
|
||||||
clean()
|
self.clean()
|
||||||
setupSubviews()
|
self.setupSubviews()
|
||||||
|
|
||||||
self.text = " "
|
self.text = " "
|
||||||
self.animate()
|
self.animate(duration: duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func animate(ascending: Bool = true) {
|
private func animate(ascending: Bool = true, duration: Double) {
|
||||||
createAnimations(ascending: ascending)
|
self.createAnimations(ascending: ascending, duration: duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clean() {
|
private func clean() {
|
||||||
self.text = nil
|
self.text = nil
|
||||||
self.subviews.forEach { $0.removeFromSuperview() }
|
self.subviews.forEach { $0.removeFromSuperview() }
|
||||||
self.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
|
self.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
|
||||||
scrollLayers.removeAll()
|
self.scrollLayers.removeAll()
|
||||||
scrollLabels.removeAll()
|
self.scrollLabels.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSubviews() {
|
private func setupSubviews() {
|
||||||
@ -168,7 +167,7 @@ open class RollingLabel: UILabel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createAnimations(ascending: Bool) {
|
private func createAnimations(ascending: Bool, duration: Double) {
|
||||||
var offset: CFTimeInterval = 0.0
|
var offset: CFTimeInterval = 0.0
|
||||||
|
|
||||||
for scrollLayer in scrollLayers {
|
for scrollLayer in scrollLayers {
|
||||||
|
@ -257,7 +257,7 @@ private class StickerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let placeholderFrame = CGRect(origin: .zero, size: imageSize)
|
let placeholderFrame = CGRect(origin: CGPoint(x: -10.0, y: 0.0), size: imageSize)
|
||||||
let thumbnailDimensions = PixelDimensions(width: 512, height: 512)
|
let thumbnailDimensions = PixelDimensions(width: 512, height: 512)
|
||||||
self.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: self.file.immediateThumbnailData, size: placeholderFrame.size, imageSize: thumbnailDimensions.cgSize)
|
self.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: self.file.immediateThumbnailData, size: placeholderFrame.size, imageSize: thumbnailDimensions.cgSize)
|
||||||
self.placeholderNode.frame = placeholderFrame
|
self.placeholderNode.frame = placeholderFrame
|
||||||
@ -297,7 +297,7 @@ private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.clipsToBounds = true
|
// self.clipsToBounds = true
|
||||||
|
|
||||||
self.addSubnode(self.scrollNode)
|
self.addSubnode(self.scrollNode)
|
||||||
self.scrollNode.addSubnode(self.tapNode)
|
self.scrollNode.addSubnode(self.tapNode)
|
||||||
|
@ -416,13 +416,18 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
|
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
|
||||||
|
|
||||||
let currentAppIcon: PresentationAppIcon?
|
let currentAppIcon: PresentationAppIcon?
|
||||||
let appIcons = context.sharedContext.applicationBindings.getAvailableAlternateIcons()
|
var appIcons = context.sharedContext.applicationBindings.getAvailableAlternateIcons()
|
||||||
if let alternateIconName = context.sharedContext.applicationBindings.getAlternateIconName() {
|
if let alternateIconName = context.sharedContext.applicationBindings.getAlternateIconName() {
|
||||||
currentAppIcon = appIcons.filter { $0.name == alternateIconName }.first
|
currentAppIcon = appIcons.filter { $0.name == alternateIconName }.first
|
||||||
} else {
|
} else {
|
||||||
currentAppIcon = appIcons.filter { $0.isDefault }.first
|
currentAppIcon = appIcons.filter { $0.isDefault }.first
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
|
if premiumConfiguration.isPremiumDisabled {
|
||||||
|
appIcons = appIcons.filter { !$0.isPremium }
|
||||||
|
}
|
||||||
|
|
||||||
let availableAppIcons: Signal<[PresentationAppIcon], NoError> = .single(appIcons)
|
let availableAppIcons: Signal<[PresentationAppIcon], NoError> = .single(appIcons)
|
||||||
let currentAppIconName = ValuePromise<String?>()
|
let currentAppIconName = ValuePromise<String?>()
|
||||||
currentAppIconName.set(currentAppIcon?.name ?? "Blue")
|
currentAppIconName.set(currentAppIcon?.name ?? "Blue")
|
||||||
|
@ -640,31 +640,6 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
|
|
||||||
self.currentStickerPack = (info, items, installed)
|
self.currentStickerPack = (info, items, installed)
|
||||||
|
|
||||||
if installed {
|
|
||||||
let text: String
|
|
||||||
if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks {
|
|
||||||
text = self.presentationData.strings.StickerPack_RemoveStickerCount(info.count)
|
|
||||||
} else {
|
|
||||||
text = self.presentationData.strings.StickerPack_RemoveMaskCount(info.count)
|
|
||||||
}
|
|
||||||
self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemDestructiveColor, for: .normal)
|
|
||||||
self.buttonNode.setBackgroundImage(nil, for: [])
|
|
||||||
} else {
|
|
||||||
let text: String
|
|
||||||
if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks {
|
|
||||||
text = self.presentationData.strings.StickerPack_AddStickerCount(info.count)
|
|
||||||
} else {
|
|
||||||
text = self.presentationData.strings.StickerPack_AddMaskCount(info.count)
|
|
||||||
}
|
|
||||||
self.buttonNode.setTitle(text, with: Font.semibold(17.0), with: self.presentationData.theme.list.itemCheckColors.foregroundColor, for: .normal)
|
|
||||||
let roundedAccentBackground = generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in
|
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.setFillColor(self.presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
|
|
||||||
})?.stretchableImage(withLeftCapWidth: 11, topCapHeight: 11)
|
|
||||||
self.buttonNode.setBackgroundImage(roundedAccentBackground, for: [])
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.titleNode.attributedText == nil {
|
if self.titleNode.attributedText == nil {
|
||||||
if let titlePlaceholderNode = self.titlePlaceholderNode {
|
if let titlePlaceholderNode = self.titlePlaceholderNode {
|
||||||
self.titlePlaceholderNode = nil
|
self.titlePlaceholderNode = nil
|
||||||
@ -678,6 +653,9 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
|
|
||||||
updateLayout = true
|
updateLayout = true
|
||||||
|
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
var generalItems: [StickerPackItem] = []
|
var generalItems: [StickerPackItem] = []
|
||||||
var premiumItems: [StickerPackItem] = []
|
var premiumItems: [StickerPackItem] = []
|
||||||
|
|
||||||
@ -711,11 +689,39 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
addItem(item, false, false)
|
addItem(item, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !premiumItems.isEmpty {
|
if !premiumConfiguration.isPremiumDisabled {
|
||||||
for item in premiumItems {
|
if !premiumItems.isEmpty {
|
||||||
addItem(item, true, !hasPremium)
|
for item in premiumItems {
|
||||||
|
addItem(item, true, !hasPremium)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if installed {
|
||||||
|
let text: String
|
||||||
|
if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks {
|
||||||
|
text = self.presentationData.strings.StickerPack_RemoveStickerCount(Int32(entries.count))
|
||||||
|
} else {
|
||||||
|
text = self.presentationData.strings.StickerPack_RemoveMaskCount(Int32(entries.count))
|
||||||
|
}
|
||||||
|
self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemDestructiveColor, for: .normal)
|
||||||
|
self.buttonNode.setBackgroundImage(nil, for: [])
|
||||||
|
} else {
|
||||||
|
let text: String
|
||||||
|
if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks {
|
||||||
|
text = self.presentationData.strings.StickerPack_AddStickerCount(Int32(entries.count))
|
||||||
|
} else {
|
||||||
|
text = self.presentationData.strings.StickerPack_AddMaskCount(Int32(entries.count))
|
||||||
|
}
|
||||||
|
self.buttonNode.setTitle(text, with: Font.semibold(17.0), with: self.presentationData.theme.list.itemCheckColors.foregroundColor, for: .normal)
|
||||||
|
let roundedAccentBackground = generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(self.presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 11, topCapHeight: 11)
|
||||||
|
self.buttonNode.setBackgroundImage(roundedAccentBackground, for: [])
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
let previousEntries = self.currentEntries
|
let previousEntries = self.currentEntries
|
||||||
self.currentEntries = entries
|
self.currentEntries = entries
|
||||||
|
@ -209,7 +209,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
|||||||
animationNode.updateLayout(size: imageSize)
|
animationNode.updateLayout(size: imageSize)
|
||||||
|
|
||||||
if let additionalAnimationNode = self.additionalAnimationNode {
|
if let additionalAnimationNode = self.additionalAnimationNode {
|
||||||
additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245)
|
additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21.0, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245)
|
||||||
additionalAnimationNode.updateLayout(size: additionalAnimationNode.frame.size)
|
additionalAnimationNode.updateLayout(size: additionalAnimationNode.frame.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "black@270x270.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/black@270x270.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/black@270x270.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "premium@270x270.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/premium@270x270.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/premium@270x270.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "turbo@270x270.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/turbo@270x270.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/turbo@270x270.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -286,7 +286,14 @@ public final class AccountContextImpl: AccountContext {
|
|||||||
strongSelf.animatedEmojiStickers = stickers
|
strongSelf.animatedEmojiStickers = stickers
|
||||||
})
|
})
|
||||||
|
|
||||||
self.userLimitsConfigurationDisposable = (self.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false))
|
self.userLimitsConfigurationDisposable = (self.account.postbox.peerView(id: self.account.peerId)
|
||||||
|
|> mapToSignal { peerView -> Signal<EngineConfiguration.UserLimits, NoError> in
|
||||||
|
if let peer = peerView.peers[peerView.peerId] {
|
||||||
|
return self.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: peer.isPremium))
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -33,6 +33,7 @@ import TelegramAudio
|
|||||||
import DebugSettingsUI
|
import DebugSettingsUI
|
||||||
import BackgroundTasks
|
import BackgroundTasks
|
||||||
import UIKitRuntimeUtils
|
import UIKitRuntimeUtils
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
#if canImport(AppCenter)
|
#if canImport(AppCenter)
|
||||||
import AppCenter
|
import AppCenter
|
||||||
@ -622,6 +623,14 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appStoreId)") {
|
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appStoreId)") {
|
||||||
UIApplication.shared.openURL(url)
|
UIApplication.shared.openURL(url)
|
||||||
}
|
}
|
||||||
|
}, openSubscriptions: {
|
||||||
|
if #available(iOS 15, *), let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
||||||
|
Task {
|
||||||
|
try await AppStore.showManageSubscriptions(in: scene)
|
||||||
|
}
|
||||||
|
} else if let url = URL(string: "https://apps.apple.com/account/subscriptions") {
|
||||||
|
UIApplication.shared.openURL(url)
|
||||||
|
}
|
||||||
}, registerForNotifications: { completion in
|
}, registerForNotifications: { completion in
|
||||||
let _ = (self.context.get()
|
let _ = (self.context.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
@ -1030,6 +1030,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
actions.context = strongSelf.context
|
actions.context = strongSelf.context
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
if canAddMessageReactions(message: topMessage), let availableReactions = availableReactions, let allowedReactions = allowedReactions {
|
if canAddMessageReactions(message: topMessage), let availableReactions = availableReactions, let allowedReactions = allowedReactions {
|
||||||
var hasPremiumPlaceholder = false
|
var hasPremiumPlaceholder = false
|
||||||
filterReactions: for reaction in availableReactions.reactions {
|
filterReactions: for reaction in availableReactions.reactions {
|
||||||
@ -1067,7 +1069,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
largeApplicationAnimation: reaction.effectAnimation
|
largeApplicationAnimation: reaction.effectAnimation
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
if hasPremiumPlaceholder {
|
|
||||||
|
if hasPremiumPlaceholder && !premiumConfiguration.isPremiumDisabled {
|
||||||
actions.reactionItems.append(.premium)
|
actions.reactionItems.append(.premium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7973,8 +7976,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: nil, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: true, action: { _ in return false }), with: nil)
|
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: nil, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: true, action: { _ in return false }), with: nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
@ -12391,6 +12395,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func displayPremiumStickerTooltip(file: TelegramMediaFile, message: Message) {
|
private func displayPremiumStickerTooltip(file: TelegramMediaFile, message: Message) {
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
|
guard !premiumConfiguration.isPremiumDisabled else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var currentOverlayController: UndoOverlayController?
|
var currentOverlayController: UndoOverlayController?
|
||||||
|
|
||||||
self.window?.forEachController({ controller in
|
self.window?.forEachController({ controller in
|
||||||
|
@ -394,12 +394,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
if !chatPresentationInterfaceState.isPremium {
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
|
if !chatPresentationInterfaceState.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||||
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, iconSource: nil, action: { c, _ in
|
}, iconSource: nil, action: { c, _ in
|
||||||
c.dismiss(completion: {
|
c.dismiss(completion: {
|
||||||
controllerInteraction.navigationController()?.pushViewController(PremiumIntroScreen(context: context, source: .ads))
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let controller = PremiumDemoScreen(context: context, subject: .noAds, action: {
|
||||||
|
let controller = PremiumIntroScreen(context: context, source: .ads)
|
||||||
|
replaceImpl?(controller)
|
||||||
|
})
|
||||||
|
replaceImpl = { [weak controller] c in
|
||||||
|
controller?.replace(with: c)
|
||||||
|
}
|
||||||
|
controllerInteraction.navigationController()?.pushViewController(controller)
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -1225,8 +1234,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
|
@ -261,7 +261,7 @@ func chatMediaInputPanelGifModeEntries(theme: PresentationTheme, strings: Presen
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, trendingPacks: [FeaturedStickerPackItem], installedPacks: Set<ItemCollectionId>, premiumStickers: OrderedItemListView? = nil, trendingIsDismissed: Bool = false, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme, hasPremium: Bool) -> [ChatMediaInputGridEntry] {
|
func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, trendingPacks: [FeaturedStickerPackItem], installedPacks: Set<ItemCollectionId>, premiumStickers: OrderedItemListView? = nil, trendingIsDismissed: Bool = false, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme, hasPremium: Bool, isPremiumDisabled: Bool) -> [ChatMediaInputGridEntry] {
|
||||||
var entries: [ChatMediaInputGridEntry] = []
|
var entries: [ChatMediaInputGridEntry] = []
|
||||||
|
|
||||||
if hasSearch && view.lower == nil {
|
if hasSearch && view.lower == nil {
|
||||||
@ -284,7 +284,11 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
|
|||||||
savedStickerIds.insert(item.file.fileId.id)
|
savedStickerIds.insert(item.file.fileId.id)
|
||||||
let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id)
|
let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id)
|
||||||
let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: [])
|
let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: [])
|
||||||
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -3, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
if isPremiumDisabled && item.file.isPremiumSticker {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -3, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,8 +311,13 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
|
|||||||
if !savedStickerIds.contains(mediaId.id) {
|
if !savedStickerIds.contains(mediaId.id) {
|
||||||
let index = ItemCollectionItemIndex(index: Int32(i), id: mediaId.id)
|
let index = ItemCollectionItemIndex(index: Int32(i), id: mediaId.id)
|
||||||
let stickerItem = StickerPackItem(index: index, file: file, indexKeys: [])
|
let stickerItem = StickerPackItem(index: index, file: file, indexKeys: [])
|
||||||
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -2, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
|
||||||
addedCount += 1
|
if isPremiumDisabled && file.isPremiumSticker {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -2, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
||||||
|
addedCount += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,12 +339,16 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
|
|||||||
if let item = peerSpecificPack.items[i] as? StickerPackItem {
|
if let item = peerSpecificPack.items[i] as? StickerPackItem {
|
||||||
let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id)
|
let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id)
|
||||||
let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: [])
|
let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: [])
|
||||||
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -1, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: canManagePeerSpecificPack, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
if isPremiumDisabled && item.file.isPremiumSticker {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -1, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: canManagePeerSpecificPack, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let premiumStickers = premiumStickers, !premiumStickers.items.isEmpty && hasPremium {
|
if let premiumStickers = premiumStickers, !premiumStickers.items.isEmpty && hasPremium && !isPremiumDisabled {
|
||||||
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.premium.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_PremiumStickers.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
|
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.premium.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_PremiumStickers.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
|
||||||
for i in 0 ..< premiumStickers.items.count {
|
for i in 0 ..< premiumStickers.items.count {
|
||||||
if let item = premiumStickers.items[i].contents.get(RecentMediaItem.self) {
|
if let item = premiumStickers.items[i].contents.get(RecentMediaItem.self) {
|
||||||
@ -351,7 +364,11 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
|
|||||||
|
|
||||||
for entry in view.entries {
|
for entry in view.entries {
|
||||||
if let item = entry.item as? StickerPackItem {
|
if let item = entry.item as? StickerPackItem {
|
||||||
entries.append(.sticker(index: entry.index, stickerItem: item, stickerPackInfo: stickerPackInfos[entry.index.collectionId], canManagePeerSpecificPack: false, maybeManageable: hasAccessories, theme: theme, isLocked: item.file.isPremiumSticker && !hasPremium))
|
if isPremiumDisabled && item.file.isPremiumSticker {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
entries.append(.sticker(index: entry.index, stickerItem: item, stickerPackInfo: stickerPackInfos[entry.index.collectionId], canManagePeerSpecificPack: false, maybeManageable: hasAccessories, theme: theme, isLocked: item.file.isPremiumSticker && !hasPremium))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,6 +1119,8 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
return animatedEmojiStickers
|
return animatedEmojiStickers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
|
|
||||||
let previousView = Atomic<ItemCollectionsView?>(value: nil)
|
let previousView = Atomic<ItemCollectionsView?>(value: nil)
|
||||||
let transitionQueue = Queue()
|
let transitionQueue = Queue()
|
||||||
let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions, self.panelIsFocusedPromise.get(), ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager), temporaryPackOrder.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId))
|
let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions, self.panelIsFocusedPromise.get(), ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager), temporaryPackOrder.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId))
|
||||||
@ -1142,7 +1161,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
|
|
||||||
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, temporaryPackOrder: temporaryPackOrder, trendingIsDismissed: trendingIsDismissed, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, theme: theme, strings: strings, premiumStickers: hasPremium ? premiumStickers : nil, expanded: panelExpanded, reorderable: true)
|
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, temporaryPackOrder: temporaryPackOrder, trendingIsDismissed: trendingIsDismissed, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, theme: theme, strings: strings, premiumStickers: hasPremium ? premiumStickers : nil, expanded: panelExpanded, reorderable: true)
|
||||||
let gifPaneEntries = chatMediaInputPanelGifModeEntries(theme: theme, strings: strings, reactions: reactions, animatedEmojiStickers: animatedEmojiStickers, expanded: panelExpanded)
|
let gifPaneEntries = chatMediaInputPanelGifModeEntries(theme: theme, strings: strings, reactions: reactions, animatedEmojiStickers: animatedEmojiStickers, expanded: panelExpanded)
|
||||||
var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, trendingPacks: trendingPacks, installedPacks: installedPacks, premiumStickers: premiumStickers, trendingIsDismissed: trendingIsDismissed, strings: strings, theme: theme, hasPremium: hasPremium)
|
var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, trendingPacks: trendingPacks, installedPacks: installedPacks, premiumStickers: premiumStickers, trendingIsDismissed: trendingIsDismissed, strings: strings, theme: theme, hasPremium: hasPremium, isPremiumDisabled: premiumConfiguration.isPremiumDisabled)
|
||||||
|
|
||||||
if view.higher == nil {
|
if view.higher == nil {
|
||||||
var hasTopSeparator = true
|
var hasTopSeparator = true
|
||||||
@ -1471,8 +1490,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
@ -1578,8 +1598,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
@ -1730,8 +1751,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
|
@ -594,7 +594,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false)
|
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false)
|
||||||
let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false)
|
let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false, isPremiumDisabled: true)
|
||||||
|
|
||||||
let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries))
|
let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries))
|
||||||
return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty)
|
return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty)
|
||||||
@ -629,7 +629,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false)
|
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false)
|
||||||
let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false)
|
let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false, isPremiumDisabled: true)
|
||||||
|
|
||||||
let (previousPanelEntries, previousGridEntries) = previousMaskEntries.swap((panelEntries, gridEntries))
|
let (previousPanelEntries, previousGridEntries) = previousMaskEntries.swap((panelEntries, gridEntries))
|
||||||
return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: masksInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: masksInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty)
|
return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: masksInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: masksInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty)
|
||||||
|
@ -502,8 +502,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
@ -589,8 +590,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
|
@ -203,8 +203,9 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
|||||||
case .generic:
|
case .generic:
|
||||||
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
text = presentationData.strings.Premium_MaxSavedGifsFinalText
|
||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
|
@ -193,8 +193,9 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
|
@ -145,8 +145,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
|
@ -119,7 +119,7 @@ public final class NotificationViewControllerImpl {
|
|||||||
}, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in
|
}, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in
|
||||||
}, pushIdleTimerExtension: {
|
}, pushIdleTimerExtension: {
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}, openSettings: {}, openAppStorePage: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: {
|
}, openSettings: {}, openAppStorePage: {}, openSubscriptions: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: {
|
||||||
return nil
|
return nil
|
||||||
}, presentNativeController: { _ in
|
}, presentNativeController: { _ in
|
||||||
}, dismissNativeController: {
|
}, dismissNativeController: {
|
||||||
|
@ -219,6 +219,14 @@ private final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
return contentHeight
|
return contentHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateErrorIfNeeded() {
|
||||||
|
for (_, itemNode) in self.itemNodes {
|
||||||
|
if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode {
|
||||||
|
itemNode.animateErrorIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||||
@ -724,9 +732,12 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
|||||||
interaction.openSettings(.language)
|
interaction.openSettings(.language)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Telegram Premium", icon: PresentationResourcesSettings.premium, action: {
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
interaction.openSettings(.premium)
|
if !premiumConfiguration.isPremiumDisabled {
|
||||||
}))
|
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: presentationData.strings.Settings_Premium, icon: PresentationResourcesSettings.premium, action: {
|
||||||
|
interaction.openSettings(.premium)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/*items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Payment Method", icon: PresentationResourcesSettings.language, action: {
|
/*items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Payment Method", icon: PresentationResourcesSettings.language, action: {
|
||||||
interaction.openPaymentMethod()
|
interaction.openPaymentMethod()
|
||||||
@ -2633,7 +2644,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
})
|
})
|
||||||
strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true)
|
strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true)
|
||||||
case .done, .cancel:
|
case .done, .cancel:
|
||||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear))
|
|
||||||
strongSelf.view.endEditing(true)
|
strongSelf.view.endEditing(true)
|
||||||
if case .done = key {
|
if case .done = key {
|
||||||
guard let data = strongSelf.data else {
|
guard let data = strongSelf.data else {
|
||||||
@ -2646,6 +2656,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
let lastName = strongSelf.headerNode.editingContentNode.editingTextForKey(.lastName) ?? ""
|
let lastName = strongSelf.headerNode.editingContentNode.editingTextForKey(.lastName) ?? ""
|
||||||
let bio = strongSelf.state.updatingBio
|
let bio = strongSelf.state.updatingBio
|
||||||
|
|
||||||
|
if let bio = bio {
|
||||||
|
if Int32(bio.count) > strongSelf.context.userLimits.maxAboutLength {
|
||||||
|
for (_, section) in strongSelf.editingSections {
|
||||||
|
section.animateErrorIfNeeded()
|
||||||
|
}
|
||||||
|
strongSelf.hapticFeedback?.error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if peer.firstName != firstName || peer.lastName != lastName || (bio != nil && bio != cachedData.about) {
|
if peer.firstName != firstName || peer.lastName != lastName || (bio != nil && bio != cachedData.about) {
|
||||||
var updateNameSignal: Signal<Void, NoError> = .complete()
|
var updateNameSignal: Signal<Void, NoError> = .complete()
|
||||||
var hasProgress = false
|
var hasProgress = false
|
||||||
@ -2886,6 +2906,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
strongSelf.controller?.navigationItem.setLeftBarButton(nil, animated: true)
|
strongSelf.controller?.navigationItem.setLeftBarButton(nil, animated: true)
|
||||||
}
|
}
|
||||||
|
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear))
|
||||||
case .select:
|
case .select:
|
||||||
strongSelf.state = strongSelf.state.withSelectedMessageIds(Set())
|
strongSelf.state = strongSelf.state.withSelectedMessageIds(Set())
|
||||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||||
@ -2980,11 +3001,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .profile(strongSelf.peerId))
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
controller.sourceView = sourceView
|
if !premiumConfiguration.isPremiumDisabled {
|
||||||
controller.containerView = strongSelf.controller?.navigationController?.view
|
let controller = PremiumIntroScreen(context: strongSelf.context, source: .profile(strongSelf.peerId))
|
||||||
controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor
|
controller.sourceView = sourceView
|
||||||
strongSelf.controller?.push(controller)
|
controller.containerView = strongSelf.controller?.navigationController?.view
|
||||||
|
controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor
|
||||||
|
strongSelf.controller?.push(controller)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in
|
self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in
|
||||||
|
@ -29,7 +29,7 @@ final class PeerInfoScreenMultilineInputItem: PeerInfoScreenItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode {
|
final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode {
|
||||||
private let bottomSeparatorNode: ASDisplayNode
|
private let bottomSeparatorNode: ASDisplayNode
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
@ -118,4 +118,8 @@ private final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode
|
|||||||
|
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateErrorIfNeeded() {
|
||||||
|
self.itemNode?.animateErrorIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,10 @@ public class ShareRootControllerImpl {
|
|||||||
}, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in
|
}, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in
|
||||||
}, pushIdleTimerExtension: {
|
}, pushIdleTimerExtension: {
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}, openSettings: {}, openAppStorePage: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: {
|
}, openSettings: {
|
||||||
|
}, openAppStorePage: {
|
||||||
|
}, openSubscriptions: {
|
||||||
|
}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: {
|
||||||
return nil
|
return nil
|
||||||
}, presentNativeController: { _ in
|
}, presentNativeController: { _ in
|
||||||
}, dismissNativeController: {
|
}, dismissNativeController: {
|
||||||
|
@ -149,8 +149,9 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
|
|||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
if limit == premiumLimit {
|
if limit == premiumLimit || premiumConfiguration.isPremiumDisabled {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
|
||||||
} else {
|
} else {
|
||||||
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user