mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Update API
This commit is contained in:
parent
6933a189a4
commit
100be3217f
@ -7814,9 +7814,23 @@ Sorry for the inconvenience.";
|
|||||||
"Premium.Gift.Years_1" = "%@ Year";
|
"Premium.Gift.Years_1" = "%@ Year";
|
||||||
"Premium.Gift.Years_any" = "%@ Years";
|
"Premium.Gift.Years_any" = "%@ Years";
|
||||||
|
|
||||||
|
"Premium.GiftedTitle" = "Telegram Premium";
|
||||||
|
|
||||||
"Premium.GiftedTitle.3Month" = "[%@]() has gifted you a 3-month subscription for Telegram Premium";
|
"Premium.GiftedTitle.3Month" = "[%@]() has gifted you a 3-month subscription for Telegram Premium";
|
||||||
"Premium.GiftedTitle.6Month" = "[%@]() has gifted you a 6-month subscription for Telegram Premium";
|
"Premium.GiftedTitle.6Month" = "[%@]() has gifted you a 6-month subscription for Telegram Premium";
|
||||||
"Premium.GiftedTitle.12Month" = "[%@]() has gifted you a 12-month subscription for Telegram Premium";
|
"Premium.GiftedTitle.12Month" = "[%@]() has gifted you a 12-month subscription for Telegram Premium";
|
||||||
"Premium.GiftedDescription" = "You now have access to additional features.";
|
"Premium.GiftedDescription" = "You now have access to additional features.";
|
||||||
|
|
||||||
|
"Premium.GiftedTitleYou.3Month" = "You gifted [%@]() a 3-month subscription for Telegram Premium";
|
||||||
|
"Premium.GiftedTitleYou.6Month" = "You gifted [%@]() a 6-month subscription for Telegram Premium";
|
||||||
|
"Premium.GiftedTitleYou.12Month" = "You gifted [%@]() a 12-month subscription for Telegram Premium";
|
||||||
|
"Premium.GiftedDescriptionYou" = "They now have access to additional features.";
|
||||||
|
|
||||||
"SettingsSearch.DeleteAccount.DeleteMyAccount" = " ";
|
"SettingsSearch.DeleteAccount.DeleteMyAccount" = " ";
|
||||||
|
|
||||||
|
"Notification.PremiumGift.Sent" = "%1$@ sent you a gift for %2$@";
|
||||||
|
"Notification.PremiumGift.SentYou" = "You sent a gift for %@";
|
||||||
|
|
||||||
|
"Notification.PremiumGift.Title" = "Telegram Premium";
|
||||||
|
"Notification.PremiumGift.Subtitle" = "for %@";
|
||||||
|
"Notification.PremiumGift.View" = "View";
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
} else if #available(iOS 11.2, *) {
|
} else if #available(iOS 11.2, *) {
|
||||||
return self.skProduct.subscriptionPeriod != nil
|
return self.skProduct.subscriptionPeriod != nil
|
||||||
} else {
|
} else {
|
||||||
return !self.id.contains(".monthly")
|
return self.id.contains(".monthly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +89,11 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
|
|
||||||
private final class PaymentTransactionContext {
|
private final class PaymentTransactionContext {
|
||||||
var state: SKPaymentTransactionState?
|
var state: SKPaymentTransactionState?
|
||||||
|
var targetPeerId: PeerId?
|
||||||
let subscriber: (TransactionState) -> Void
|
let subscriber: (TransactionState) -> Void
|
||||||
|
|
||||||
init(subscriber: @escaping (TransactionState) -> Void) {
|
init(targetPeerId: PeerId?, subscriber: @escaping (TransactionState) -> Void) {
|
||||||
|
self.targetPeerId = targetPeerId
|
||||||
self.subscriber = subscriber
|
self.subscriber = subscriber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,10 +171,15 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func buyProduct(_ product: Product) -> Signal<PurchaseState, PurchaseError> {
|
public func buyProduct(_ product: Product, targetPeerId: PeerId? = nil) -> Signal<PurchaseState, PurchaseError> {
|
||||||
if !self.canMakePayments {
|
if !self.canMakePayments {
|
||||||
return .fail(.cantMakePayments)
|
return .fail(.cantMakePayments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !product.isSubscription && targetPeerId == nil {
|
||||||
|
return .fail(.cantMakePayments)
|
||||||
|
}
|
||||||
|
|
||||||
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
||||||
|
|
||||||
Logger.shared.log("InAppPurchaseManager", "Buying: account \(accountPeerId), product \(product.skProduct.productIdentifier), price \(product.price)")
|
Logger.shared.log("InAppPurchaseManager", "Buying: account \(accountPeerId), product \(product.skProduct.productIdentifier), price \(product.price)")
|
||||||
@ -186,7 +193,7 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self.stateQueue.async {
|
self.stateQueue.async {
|
||||||
let paymentContext = PaymentTransactionContext(subscriber: { state in
|
let paymentContext = PaymentTransactionContext(targetPeerId: targetPeerId, subscriber: { state in
|
||||||
switch state {
|
switch state {
|
||||||
case let .purchased(transactionId), let .restored(transactionId):
|
case let .purchased(transactionId), let .restored(transactionId):
|
||||||
if let transactionId = transactionId {
|
if let transactionId = transactionId {
|
||||||
@ -264,6 +271,9 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||||
self.stateQueue.async {
|
self.stateQueue.async {
|
||||||
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
||||||
|
|
||||||
|
let paymentContexts = self.paymentContexts
|
||||||
|
|
||||||
var transactionsToAssign: [SKPaymentTransaction] = []
|
var transactionsToAssign: [SKPaymentTransaction] = []
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
if let applicationUsername = transaction.payment.applicationUsername, applicationUsername != accountPeerId {
|
if let applicationUsername = transaction.payment.applicationUsername, applicationUsername != accountPeerId {
|
||||||
@ -306,8 +316,19 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ")
|
let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ")
|
||||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]")
|
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]")
|
||||||
|
|
||||||
|
let transaction = transactionsToAssign.first
|
||||||
|
|
||||||
|
|
||||||
|
let purpose: AppStoreTransactionPurpose
|
||||||
|
if let productIdentifier = transaction?.payment.productIdentifier, let targetPeerId = paymentContexts[productIdentifier]?.targetPeerId {
|
||||||
|
purpose = .gift(targetPeerId)
|
||||||
|
} else {
|
||||||
|
purpose = .subscription
|
||||||
|
}
|
||||||
|
|
||||||
|
let receiptData = getReceiptData() ?? Data()
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), purpose: .subscription).start(error: { [weak self] _ in
|
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose).start(error: { [weak self] _ in
|
||||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transactions [\(transactionIds)] failed to assign")
|
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transactions [\(transactionIds)] failed to assign")
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
self?.stateQueue.async {
|
self?.stateQueue.async {
|
||||||
|
|||||||
@ -752,9 +752,9 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
let updateInProgress: (Bool) -> Void
|
let updateInProgress: (Bool) -> Void
|
||||||
let present: (ViewController) -> Void
|
let present: (ViewController) -> Void
|
||||||
let push: (ViewController) -> Void
|
let push: (ViewController) -> Void
|
||||||
let completion: () -> Void
|
let completion: (Int32) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void) {
|
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.updateInProgress = updateInProgress
|
self.updateInProgress = updateInProgress
|
||||||
@ -778,7 +778,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
private let updateInProgress: (Bool) -> Void
|
private let updateInProgress: (Bool) -> Void
|
||||||
private let present: (ViewController) -> Void
|
private let present: (ViewController) -> Void
|
||||||
private let completion: () -> Void
|
private let completion: (Int32) -> Void
|
||||||
|
|
||||||
var topContentOffset: CGFloat?
|
var topContentOffset: CGFloat?
|
||||||
var bottomContentOffset: CGFloat?
|
var bottomContentOffset: CGFloat?
|
||||||
@ -795,7 +795,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
private var paymentDisposable = MetaDisposable()
|
private var paymentDisposable = MetaDisposable()
|
||||||
private var activationDisposable = MetaDisposable()
|
private var activationDisposable = MetaDisposable()
|
||||||
|
|
||||||
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void) {
|
init(context: AccountContext, peerId: PeerId, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping (Int32) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.updateInProgress = updateInProgress
|
self.updateInProgress = updateInProgress
|
||||||
@ -845,17 +845,25 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let duration: Int32
|
||||||
|
switch product.id {
|
||||||
|
case "org.telegram.telegramPremium.twelveMonths":
|
||||||
|
duration = 86400 * 365
|
||||||
|
case "org.telegram.telegramPremium.sixMonths":
|
||||||
|
duration = 86400 * 180
|
||||||
|
case "org.telegram.telegramPremium.threeMonths":
|
||||||
|
duration = 86400 * 90
|
||||||
|
default:
|
||||||
|
duration = 0
|
||||||
|
}
|
||||||
|
|
||||||
// addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
|
// addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
|
||||||
|
|
||||||
self.inProgress = true
|
self.inProgress = true
|
||||||
self.updateInProgress(true)
|
self.updateInProgress(true)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
let _ = (self.context.engine.payments.canPurchasePremium()
|
self.paymentDisposable.set((inAppPurchaseManager.buyProduct(product, targetPeerId: self.peerId)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
|
||||||
if let strongSelf = self {
|
|
||||||
if available {
|
|
||||||
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(product)
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
if let strongSelf = self, case .purchased = status {
|
if let strongSelf = self, case .purchased = status {
|
||||||
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
|
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
|
||||||
@ -877,7 +885,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
strongSelf.updateInProgress(false)
|
strongSelf.updateInProgress(false)
|
||||||
|
|
||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
strongSelf.completion()
|
// strongSelf.completion(duration)
|
||||||
|
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
||||||
|
|
||||||
@ -893,7 +901,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
strongSelf.updateInProgress(false)
|
strongSelf.updateInProgress(false)
|
||||||
|
|
||||||
strongSelf.updated(transition: .easeInOut(duration: 0.25))
|
strongSelf.updated(transition: .easeInOut(duration: 0.25))
|
||||||
strongSelf.completion()
|
strongSelf.completion(duration)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -928,13 +936,6 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
strongSelf.inProgress = false
|
|
||||||
strongSelf.updateInProgress(false)
|
|
||||||
strongSelf.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateIsFocused(_ isFocused: Bool) {
|
func updateIsFocused(_ isFocused: Bool) {
|
||||||
@ -1216,7 +1217,7 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
|||||||
var updateInProgressImpl: ((Bool) -> Void)?
|
var updateInProgressImpl: ((Bool) -> Void)?
|
||||||
var pushImpl: ((ViewController) -> Void)?
|
var pushImpl: ((ViewController) -> Void)?
|
||||||
// var presentImpl: ((ViewController) -> Void)?
|
// var presentImpl: ((ViewController) -> Void)?
|
||||||
var completionImpl: (() -> Void)?
|
var completionImpl: ((Int32) -> Void)?
|
||||||
super.init(context: context, component: PremiumGiftScreenComponent(
|
super.init(context: context, component: PremiumGiftScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
@ -1229,8 +1230,8 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
|||||||
push: { c in
|
push: { c in
|
||||||
pushImpl?(c)
|
pushImpl?(c)
|
||||||
},
|
},
|
||||||
completion: {
|
completion: { duration in
|
||||||
completionImpl?()
|
completionImpl?(duration)
|
||||||
}
|
}
|
||||||
), navigationBarAppearance: .transparent)
|
), navigationBarAppearance: .transparent)
|
||||||
|
|
||||||
@ -1256,9 +1257,15 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
|||||||
self?.push(c)
|
self?.push(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
completionImpl = { [weak self] in
|
completionImpl = { [weak self] duration in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.view.addSubview(ConfettiView(frame: strongSelf.view.bounds))
|
let navigationController = strongSelf.navigationController
|
||||||
|
strongSelf.dismiss()
|
||||||
|
let introController = PremiumIntroScreen(context: context, source: .gift(from: context.account.peerId, to: peerId, duration: duration))
|
||||||
|
navigationController?.pushViewController(introController, animated: true)
|
||||||
|
Queue.mainQueue().after(0.1, {
|
||||||
|
introController.view.addSubview(ConfettiView(frame: introController.view.bounds))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,8 +38,9 @@ public enum PremiumSource: Equatable {
|
|||||||
case appIcons
|
case appIcons
|
||||||
case deeplink(String?)
|
case deeplink(String?)
|
||||||
case profile(PeerId)
|
case profile(PeerId)
|
||||||
|
case gift(from: PeerId, to: PeerId, duration: Int32)
|
||||||
|
|
||||||
var identifier: String {
|
var identifier: String? {
|
||||||
switch self {
|
switch self {
|
||||||
case .settings:
|
case .settings:
|
||||||
return "settings"
|
return "settings"
|
||||||
@ -73,6 +74,8 @@ public enum PremiumSource: Equatable {
|
|||||||
return "double_limits__about"
|
return "double_limits__about"
|
||||||
case let .profile(id):
|
case let .profile(id):
|
||||||
return "profile__\(id.id._internalGetInt64Value())"
|
return "profile__\(id.id._internalGetInt64Value())"
|
||||||
|
case .gift:
|
||||||
|
return nil
|
||||||
case let .deeplink(reference):
|
case let .deeplink(reference):
|
||||||
if let reference = reference {
|
if let reference = reference {
|
||||||
return "deeplink_\(reference)"
|
return "deeplink_\(reference)"
|
||||||
@ -695,8 +698,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
strongSelf.promoConfiguration = promoConfiguration
|
strongSelf.promoConfiguration = promoConfiguration
|
||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
|
|
||||||
|
if let identifier = source.identifier {
|
||||||
var jsonString: String = "{"
|
var jsonString: String = "{"
|
||||||
jsonString += "\"source\": \"\(source.identifier)\","
|
jsonString += "\"source\": \"\(identifier)\","
|
||||||
|
|
||||||
jsonString += "\"data\": {\"premium_promo_order\":["
|
jsonString += "\"data\": {\"premium_promo_order\":["
|
||||||
var isFirst = true
|
var isFirst = true
|
||||||
@ -712,6 +716,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
if let data = jsonString.data(using: .utf8), let json = JSON(data: data) {
|
if let data = jsonString.data(using: .utf8), let json = JSON(data: data) {
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_show", data: json)
|
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_show", data: json)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (_, video) in promoConfiguration.videos {
|
for (_, video) in promoConfiguration.videos {
|
||||||
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
||||||
@ -815,7 +820,15 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
let textString: String
|
let textString: String
|
||||||
if let _ = context.component.otherPeerName {
|
if let _ = context.component.otherPeerName {
|
||||||
|
if case let .gift(fromId, _, _) = context.component.source {
|
||||||
|
if fromId == context.component.context.account.peerId {
|
||||||
|
textString = strings.Premium_GiftedDescriptionYou
|
||||||
|
} else {
|
||||||
|
textString = strings.Premium_GiftedDescription
|
||||||
|
}
|
||||||
|
} else {
|
||||||
textString = strings.Premium_PersonalDescription
|
textString = strings.Premium_PersonalDescription
|
||||||
|
}
|
||||||
} else if context.component.isPremium == true {
|
} else if context.component.isPremium == true {
|
||||||
textString = strings.Premium_SubscribedDescription
|
textString = strings.Premium_SubscribedDescription
|
||||||
} else {
|
} else {
|
||||||
@ -1036,8 +1049,17 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var isGiftView = false
|
||||||
|
if case let .gift(fromId, _, _) = context.component.source {
|
||||||
|
if fromId == context.component.context.account.peerId {
|
||||||
|
isGiftView = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let termsString: MultilineTextComponent.TextContent
|
let termsString: MultilineTextComponent.TextContent
|
||||||
if let promoConfiguration = context.state.promoConfiguration {
|
if isGiftView {
|
||||||
|
termsString = .plain(NSAttributedString())
|
||||||
|
} else if let promoConfiguration = context.state.promoConfiguration {
|
||||||
let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont)
|
let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont)
|
||||||
termsString = .plain(attributedString)
|
termsString = .plain(attributedString)
|
||||||
} else {
|
} else {
|
||||||
@ -1229,7 +1251,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let otherPeerName: Signal<String?, NoError>
|
let otherPeerName: Signal<String?, NoError>
|
||||||
if case let .profile(peerId) = source {
|
if case let .gift(fromPeerId, toPeerId, _) = source {
|
||||||
|
let otherPeerId = fromPeerId != context.account.peerId ? fromPeerId : toPeerId
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: otherPeerId))
|
||||||
|
|> map { peer -> String? in
|
||||||
|
return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
}
|
||||||
|
} else if case let .profile(peerId) = source {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> map { peer -> String? in
|
|> map { peer -> String? in
|
||||||
@ -1418,7 +1447,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
let titleString: String
|
let titleString: String
|
||||||
if state.isPremium == true {
|
if case .gift = context.component.source {
|
||||||
|
titleString = environment.strings.Premium_GiftedTitle
|
||||||
|
} else if state.isPremium == true {
|
||||||
titleString = environment.strings.Premium_SubscribedTitle
|
titleString = environment.strings.Premium_SubscribedTitle
|
||||||
} else {
|
} else {
|
||||||
titleString = environment.strings.Premium_Title
|
titleString = environment.strings.Premium_Title
|
||||||
@ -1444,9 +1475,43 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: accentColor), linkAttribute: { _ in
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: accentColor), linkAttribute: { _ in
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let secondaryTitleText: String
|
||||||
|
if let otherPeerName = state.otherPeerName {
|
||||||
|
if case .profile = context.component.source {
|
||||||
|
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
||||||
|
} else if case let .gift(fromPeerId, _, duration) = context.component.source {
|
||||||
|
if fromPeerId == context.component.context.account.peerId {
|
||||||
|
if duration >= 86400 * 365 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
||||||
|
} else if duration >= 86400 * 180 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_6Month(otherPeerName).string
|
||||||
|
} else if duration >= 86400 * 90 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_3Month(otherPeerName).string
|
||||||
|
} else {
|
||||||
|
secondaryTitleText = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if duration >= 86400 * 365 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_12Month(otherPeerName).string
|
||||||
|
} else if duration >= 86400 * 180 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_6Month(otherPeerName).string
|
||||||
|
} else if duration >= 86400 * 90 {
|
||||||
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_3Month(otherPeerName).string
|
||||||
|
} else {
|
||||||
|
secondaryTitleText = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
secondaryTitleText = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
secondaryTitleText = ""
|
||||||
|
}
|
||||||
|
|
||||||
let secondaryTitle = secondaryTitle.update(
|
let secondaryTitle = secondaryTitle.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .markdown(text: state.otherPeerName.flatMap({ environment.strings.Premium_PersonalTitle($0).string }) ?? "", attributes: markdownAttributes),
|
text: .markdown(text: secondaryTitleText, attributes: markdownAttributes),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
truncationType: .end,
|
truncationType: .end,
|
||||||
maximumNumberOfLines: 2,
|
maximumNumberOfLines: 2,
|
||||||
@ -1558,7 +1623,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
.opacity(max(0.0, 1.0 - titleAlpha * 1.8))
|
.opacity(max(0.0, 1.0 - titleAlpha * 1.8))
|
||||||
)
|
)
|
||||||
|
|
||||||
if state.isPremium == true {
|
var isGiftView = false
|
||||||
|
if case let .gift(fromId, _, _) = context.component.source {
|
||||||
|
if fromId == context.component.context.account.peerId {
|
||||||
|
isGiftView = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.isPremium == true || isGiftView {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let sideInset: CGFloat = 16.0
|
let sideInset: CGFloat = 16.0
|
||||||
|
|||||||
@ -0,0 +1,78 @@
|
|||||||
|
import Foundation
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import AppBundle
|
||||||
|
|
||||||
|
private let starsCount = 9
|
||||||
|
public final class PremiumStarsNode: ASDisplayNode {
|
||||||
|
private let starNodes: [ASImageNode]
|
||||||
|
private var timer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
let image = UIImage(bundleImageName: "Premium/ReactionsStar")
|
||||||
|
var starNodes: [ASImageNode] = []
|
||||||
|
for _ in 0 ..< starsCount {
|
||||||
|
let node = ASImageNode()
|
||||||
|
node.isLayerBacked = true
|
||||||
|
node.alpha = 0.0
|
||||||
|
node.image = image
|
||||||
|
node.displaysAsynchronously = false
|
||||||
|
starNodes.append(node)
|
||||||
|
}
|
||||||
|
self.starNodes = starNodes
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
for node in starNodes {
|
||||||
|
self.addSubnode(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
self.setup(firstTime: true)
|
||||||
|
|
||||||
|
self.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||||
|
self?.setup()
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
self.timer?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.timer?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(firstTime: Bool = false) {
|
||||||
|
let size: CGSize
|
||||||
|
if self.frame.width > 0.0 {
|
||||||
|
size = self.frame.size
|
||||||
|
} else {
|
||||||
|
size = CGSize(width: 32.0, height: 32.0)
|
||||||
|
}
|
||||||
|
let starSize = CGSize(width: 6.0, height: 8.0)
|
||||||
|
|
||||||
|
for node in self.starNodes {
|
||||||
|
if node.layer.animation(forKey: "transform.scale") == nil && node.layer.animation(forKey: "opacity") == nil {
|
||||||
|
let x = CGFloat.random(in: 0 ..< size.width)
|
||||||
|
let y = CGFloat.random(in: 0 ..< size.width)
|
||||||
|
|
||||||
|
let randomTargetScale = CGFloat.random(in: 0.8 ..< 1.0)
|
||||||
|
node.bounds = CGRect(origin: .zero, size: starSize)
|
||||||
|
node.position = CGPoint(x: x, y: y)
|
||||||
|
|
||||||
|
node.alpha = 1.0
|
||||||
|
|
||||||
|
let duration = CGFloat.random(in: 0.4 ..< 0.65)
|
||||||
|
let delay = firstTime ? CGFloat.random(in: 0.0 ..< 0.25) : 0.0
|
||||||
|
node.layer.animateScale(from: 0.001, to: randomTargetScale, duration: duration, delay: delay, removeOnCompletion: false, completion: { [weak self, weak node] _ in
|
||||||
|
let duration = CGFloat.random(in: 0.3 ..< 0.35)
|
||||||
|
node?.alpha = 0.0
|
||||||
|
node?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak self, weak node] _ in
|
||||||
|
node?.layer.removeAllAnimations()
|
||||||
|
self?.setup()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -323,71 +323,6 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let starsCount = 7
|
|
||||||
private final class StarsNode: ASDisplayNode {
|
|
||||||
private let starNodes: [ASImageNode]
|
|
||||||
private var timer: SwiftSignalKit.Timer?
|
|
||||||
|
|
||||||
override init() {
|
|
||||||
let image = UIImage(bundleImageName: "Premium/ReactionsStar")
|
|
||||||
var starNodes: [ASImageNode] = []
|
|
||||||
for _ in 0 ..< starsCount {
|
|
||||||
let node = ASImageNode()
|
|
||||||
node.alpha = 0.0
|
|
||||||
node.image = image
|
|
||||||
node.displaysAsynchronously = false
|
|
||||||
starNodes.append(node)
|
|
||||||
}
|
|
||||||
self.starNodes = starNodes
|
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
for node in starNodes {
|
|
||||||
self.addSubnode(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setup(firstTime: true)
|
|
||||||
|
|
||||||
self.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
|
||||||
self?.setup()
|
|
||||||
}, queue: Queue.mainQueue())
|
|
||||||
self.timer?.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.timer?.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setup(firstTime: Bool = false) {
|
|
||||||
let size = CGSize(width: 32.0, height: 32.0)
|
|
||||||
let starSize = CGSize(width: 6.0, height: 8.0)
|
|
||||||
|
|
||||||
for node in self.starNodes {
|
|
||||||
if node.layer.animation(forKey: "transform.scale") == nil && node.layer.animation(forKey: "opacity") == nil {
|
|
||||||
let x = CGFloat.random(in: 0 ..< size.width)
|
|
||||||
let y = CGFloat.random(in: 0 ..< size.width)
|
|
||||||
|
|
||||||
let randomTargetScale = CGFloat.random(in: 0.8 ..< 1.0)
|
|
||||||
node.bounds = CGRect(origin: .zero, size: starSize)
|
|
||||||
node.position = CGPoint(x: x, y: y)
|
|
||||||
|
|
||||||
node.alpha = 1.0
|
|
||||||
|
|
||||||
let duration = CGFloat.random(in: 0.4 ..< 0.65)
|
|
||||||
let delay = firstTime ? CGFloat.random(in: 0.0 ..< 0.25) : 0.0
|
|
||||||
node.layer.animateScale(from: 0.001, to: randomTargetScale, duration: duration, delay: delay, removeOnCompletion: false, completion: { [weak self, weak node] _ in
|
|
||||||
let duration = CGFloat.random(in: 0.3 ..< 0.35)
|
|
||||||
node?.alpha = 0.0
|
|
||||||
node?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak self, weak node] _ in
|
|
||||||
node?.layer.removeAllAnimations()
|
|
||||||
self?.setup()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
|
final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
|
||||||
var isExtracted: Bool = false
|
var isExtracted: Bool = false
|
||||||
|
|
||||||
@ -395,7 +330,7 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
|
|||||||
private let backgroundMaskNode: ASImageNode
|
private let backgroundMaskNode: ASImageNode
|
||||||
private let backgroundOverlayNode: ASImageNode
|
private let backgroundOverlayNode: ASImageNode
|
||||||
private let imageNode: ASImageNode
|
private let imageNode: ASImageNode
|
||||||
private var starsNode: StarsNode?
|
private var starsNode: PremiumStarsNode?
|
||||||
|
|
||||||
private let maskContainerNode: ASDisplayNode
|
private let maskContainerNode: ASDisplayNode
|
||||||
private let maskImageNode: ASImageNode
|
private let maskImageNode: ASImageNode
|
||||||
@ -459,7 +394,7 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
|
|||||||
self.view.insertSubview(backgroundView, at: 0)
|
self.view.insertSubview(backgroundView, at: 0)
|
||||||
self.backgroundView = backgroundView
|
self.backgroundView = backgroundView
|
||||||
|
|
||||||
let starsNode = StarsNode()
|
let starsNode = PremiumStarsNode()
|
||||||
starsNode.frame = CGRect(origin: .zero, size: CGSize(width: 32.0, height: 32.0))
|
starsNode.frame = CGRect(origin: .zero, size: CGSize(width: 32.0, height: 32.0))
|
||||||
self.backgroundView?.contentView.addSubview(starsNode.view)
|
self.backgroundView?.contentView.addSubview(starsNode.view)
|
||||||
self.starsNode = starsNode
|
self.starsNode = starsNode
|
||||||
|
|||||||
@ -307,6 +307,7 @@ func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var secondaryActionDisabled = false
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
context.sharedContext.presentationData,
|
context.sharedContext.presentationData,
|
||||||
peers,
|
peers,
|
||||||
@ -339,7 +340,10 @@ func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDat
|
|||||||
let footerItem = DeleteAccountFooterItem(theme: presentationData.theme, title: buttonTitle, secondaryTitle: presentationData.strings.DeleteAccount_Continue, action: {
|
let footerItem = DeleteAccountFooterItem(theme: presentationData.theme, title: buttonTitle, secondaryTitle: presentationData.strings.DeleteAccount_Continue, action: {
|
||||||
cancelImpl()
|
cancelImpl()
|
||||||
}, secondaryAction: {
|
}, secondaryAction: {
|
||||||
|
if !secondaryActionDisabled {
|
||||||
|
secondaryActionDisabled = true
|
||||||
proceedImpl?()
|
proceedImpl?()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_DeleteMyAccountTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_DeleteMyAccountTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
@ -462,7 +466,7 @@ func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDat
|
|||||||
let presentGlobalController = context.sharedContext.presentGlobalController
|
let presentGlobalController = context.sharedContext.presentGlobalController
|
||||||
let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: true).start(completed: {
|
let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: true).start(completed: {
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
presentGlobalController(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.DeleteAccount_Success), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
presentGlobalController(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.DeleteAccount_Success), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -348,7 +348,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[70813275] = { return Api.InputStickeredMedia.parse_inputStickeredMediaDocument($0) }
|
dict[70813275] = { return Api.InputStickeredMedia.parse_inputStickeredMediaDocument($0) }
|
||||||
dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) }
|
dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) }
|
||||||
dict[1147243133] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($0) }
|
dict[1147243133] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($0) }
|
||||||
dict[-764193027] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumSubscription($0) }
|
dict[-1502273946] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumSubscription($0) }
|
||||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||||
dict[-1881255857] = { return Api.InputThemeSettings.parse_inputThemeSettings($0) }
|
dict[-1881255857] = { return Api.InputThemeSettings.parse_inputThemeSettings($0) }
|
||||||
|
|||||||
@ -6229,13 +6229,12 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.payments {
|
public extension Api.functions.payments {
|
||||||
static func assignAppStoreTransaction(flags: Int32, receipt: Buffer, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func assignAppStoreTransaction(receipt: Buffer, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(296120783)
|
buffer.appendInt32(-2131921795)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
|
||||||
serializeBytes(receipt, buffer: buffer, boxed: false)
|
serializeBytes(receipt, buffer: buffer, boxed: false)
|
||||||
purpose.serialize(buffer, true)
|
purpose.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|||||||
@ -457,7 +457,7 @@ public extension Api {
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
enum InputStorePaymentPurpose: TypeConstructorDescription {
|
enum InputStorePaymentPurpose: TypeConstructorDescription {
|
||||||
case inputStorePaymentGiftPremium(userId: Api.InputUser)
|
case inputStorePaymentGiftPremium(userId: Api.InputUser)
|
||||||
case inputStorePaymentPremiumSubscription
|
case inputStorePaymentPremiumSubscription(flags: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -467,11 +467,11 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
userId.serialize(buffer, true)
|
userId.serialize(buffer, true)
|
||||||
break
|
break
|
||||||
case .inputStorePaymentPremiumSubscription:
|
case .inputStorePaymentPremiumSubscription(let flags):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-764193027)
|
buffer.appendInt32(-1502273946)
|
||||||
}
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -480,8 +480,8 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .inputStorePaymentGiftPremium(let userId):
|
case .inputStorePaymentGiftPremium(let userId):
|
||||||
return ("inputStorePaymentGiftPremium", [("userId", String(describing: userId))])
|
return ("inputStorePaymentGiftPremium", [("userId", String(describing: userId))])
|
||||||
case .inputStorePaymentPremiumSubscription:
|
case .inputStorePaymentPremiumSubscription(let flags):
|
||||||
return ("inputStorePaymentPremiumSubscription", [])
|
return ("inputStorePaymentPremiumSubscription", [("flags", String(describing: flags))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,7 +499,15 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static func parse_inputStorePaymentPremiumSubscription(_ reader: BufferReader) -> InputStorePaymentPurpose? {
|
public static func parse_inputStorePaymentPremiumSubscription(_ reader: BufferReader) -> InputStorePaymentPurpose? {
|
||||||
return Api.InputStorePaymentPurpose.inputStorePaymentPremiumSubscription
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
if _c1 {
|
||||||
|
return Api.InputStorePaymentPurpose.inputStorePaymentPremiumSubscription(flags: _1!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,7 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
|
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
|
||||||
public let themeEmoticon: String?
|
public let themeEmoticon: String?
|
||||||
public let photo: TelegramMediaImage?
|
public let photo: TelegramMediaImage?
|
||||||
|
public let giftPremiumUrl: String?
|
||||||
|
|
||||||
public let peerIds: Set<PeerId>
|
public let peerIds: Set<PeerId>
|
||||||
public let messageIds: Set<MessageId>
|
public let messageIds: Set<MessageId>
|
||||||
@ -84,11 +85,12 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
self.autoremoveTimeout = .unknown
|
self.autoremoveTimeout = .unknown
|
||||||
self.themeEmoticon = nil
|
self.themeEmoticon = nil
|
||||||
self.photo = nil
|
self.photo = nil
|
||||||
|
self.giftPremiumUrl = nil
|
||||||
self.peerIds = Set()
|
self.peerIds = Set()
|
||||||
self.messageIds = Set()
|
self.messageIds = Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: TelegramMediaImage?) {
|
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: TelegramMediaImage?, giftPremiumUrl: String?) {
|
||||||
self.about = about
|
self.about = about
|
||||||
self.botInfo = botInfo
|
self.botInfo = botInfo
|
||||||
self.peerStatusSettings = peerStatusSettings
|
self.peerStatusSettings = peerStatusSettings
|
||||||
@ -103,6 +105,7 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
self.autoremoveTimeout = autoremoveTimeout
|
self.autoremoveTimeout = autoremoveTimeout
|
||||||
self.themeEmoticon = themeEmoticon
|
self.themeEmoticon = themeEmoticon
|
||||||
self.photo = photo
|
self.photo = photo
|
||||||
|
self.giftPremiumUrl = giftPremiumUrl
|
||||||
|
|
||||||
self.peerIds = Set<PeerId>()
|
self.peerIds = Set<PeerId>()
|
||||||
|
|
||||||
@ -144,6 +147,8 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
self.photo = nil
|
self.photo = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.giftPremiumUrl = decoder.decodeOptionalStringForKey("gpu")
|
||||||
|
|
||||||
self.peerIds = Set<PeerId>()
|
self.peerIds = Set<PeerId>()
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
@ -197,6 +202,12 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "ph")
|
encoder.encodeNil(forKey: "ph")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let giftPremiumUrl = self.giftPremiumUrl, !giftPremiumUrl.isEmpty {
|
||||||
|
encoder.encodeString(giftPremiumUrl, forKey: "gpu")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "gpu")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: CachedPeerData) -> Bool {
|
public func isEqual(to: CachedPeerData) -> Bool {
|
||||||
@ -215,58 +226,62 @@ public final class CachedUserData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedAbout(_ about: String?) -> CachedUserData {
|
public func withUpdatedAbout(_ about: String?) -> CachedUserData {
|
||||||
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
|
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
|
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
|
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
|
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
|
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
|
public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
|
public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
|
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
|
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
|
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData {
|
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData {
|
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedUserData {
|
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedUserData {
|
||||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo)
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, giftPremiumUrl: self.giftPremiumUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedGiftPremiumUrl(_ giftPremiumUrl: String?) -> CachedUserData {
|
||||||
|
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, giftPremiumUrl: giftPremiumUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,15 +17,14 @@ public enum AppStoreTransactionPurpose {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_sendAppStoreReceipt(account: Account, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
func _internal_sendAppStoreReceipt(account: Account, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||||
|
var purposeSignal: Signal<Api.InputStorePaymentPurpose, NoError>
|
||||||
|
switch purpose {
|
||||||
|
case .subscription, .restore:
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if case .restore = purpose {
|
if case .restore = purpose {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
purposeSignal = .single(.inputStorePaymentPremiumSubscription(flags: flags))
|
||||||
var purposeSignal: Signal<Api.InputStorePaymentPurpose, NoError>
|
|
||||||
switch purpose {
|
|
||||||
case .subscription, .restore:
|
|
||||||
purposeSignal = .single(.inputStorePaymentPremiumSubscription)
|
|
||||||
case let .gift(peerId):
|
case let .gift(peerId):
|
||||||
purposeSignal = account.postbox.loadedPeerWithId(peerId)
|
purposeSignal = account.postbox.loadedPeerWithId(peerId)
|
||||||
|> mapToSignal { peer -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
|> mapToSignal { peer -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||||
@ -40,7 +39,7 @@ func _internal_sendAppStoreReceipt(account: Account, receipt: Data, purpose: App
|
|||||||
return purposeSignal
|
return purposeSignal
|
||||||
|> castError(AssignAppStoreTransactionError.self)
|
|> castError(AssignAppStoreTransactionError.self)
|
||||||
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, receipt: Buffer(data: receipt), purpose: purpose))
|
return account.network.request(Api.functions.payments.assignAppStoreTransaction(receipt: Buffer(data: receipt), purpose: purpose))
|
||||||
|> mapError { error -> AssignAppStoreTransactionError in
|
|> mapError { error -> AssignAppStoreTransactionError in
|
||||||
if error.errorCode == 406 {
|
if error.errorCode == 406 {
|
||||||
return .serverProvided
|
return .serverProvided
|
||||||
|
|||||||
@ -234,7 +234,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
previous = CachedUserData()
|
previous = CachedUserData()
|
||||||
}
|
}
|
||||||
switch fullUser {
|
switch fullUser {
|
||||||
case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, profilePhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, _):
|
case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, profilePhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, giftPremiumUrl):
|
||||||
let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:))
|
let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:))
|
||||||
let isBlocked = (userFullFlags & (1 << 0)) != 0
|
let isBlocked = (userFullFlags & (1 << 0)) != 0
|
||||||
let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0
|
let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0
|
||||||
@ -256,6 +256,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
||||||
.withUpdatedThemeEmoticon(userFullThemeEmoticon)
|
.withUpdatedThemeEmoticon(userFullThemeEmoticon)
|
||||||
.withUpdatedPhoto(photo)
|
.withUpdatedPhoto(photo)
|
||||||
|
.withUpdatedGiftPremiumUrl(giftPremiumUrl)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -652,8 +652,15 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
}
|
}
|
||||||
case let .webViewData(text):
|
case let .webViewData(text):
|
||||||
attributedString = NSAttributedString(string: strings.Notification_WebAppSentData(text).string, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_WebAppSentData(text).string, font: titleFont, textColor: primaryTextColor)
|
||||||
case .giftPremium:
|
case let .giftPremium(currency, amount, _):
|
||||||
attributedString = nil
|
let price = formatCurrencyAmount(amount, currency: currency)
|
||||||
|
if message.author?.id == accountPeerId {
|
||||||
|
attributedString = addAttributesToStringWithRanges(strings.Notification_PremiumGift_SentYou(price)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||||
|
} else {
|
||||||
|
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])
|
||||||
|
attributes[1] = boldAttributes
|
||||||
|
attributedString = addAttributesToStringWithRanges(strings.Notification_PremiumGift_Sent(authorName, price)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||||
|
}
|
||||||
case .unknown:
|
case .unknown:
|
||||||
attributedString = nil
|
attributedString = nil
|
||||||
}
|
}
|
||||||
|
|||||||
21
submodules/TelegramUI/Images.xcassets/Components/Gift.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Components/Gift.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "gift.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Components/Gift.imageset/gift.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/Gift.imageset/gift.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
@ -770,6 +770,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
case .setChatTheme:
|
case .setChatTheme:
|
||||||
strongSelf.presentThemeSelection()
|
strongSelf.presentThemeSelection()
|
||||||
return true
|
return true
|
||||||
|
case let .giftPremium(_, _, duration):
|
||||||
|
let fromPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? strongSelf.context.account.peerId : message.id.peerId
|
||||||
|
let toPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? message.id.peerId : strongSelf.context.account.peerId
|
||||||
|
let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration))
|
||||||
|
strongSelf.push(controller)
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,6 +99,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
isAction = true
|
isAction = true
|
||||||
if case .phoneCall = action.action {
|
if case .phoneCall = action.action {
|
||||||
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
|
} else if case .giftPremium = action.action {
|
||||||
|
result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
} else {
|
} else {
|
||||||
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
}
|
}
|
||||||
|
|||||||
374
submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift
Normal file
374
submodules/TelegramUI/Sources/ChatMessageGiftItemNode.swift
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import AccountContext
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import TextFormat
|
||||||
|
import LocalizedPeerData
|
||||||
|
import UrlEscaping
|
||||||
|
import TelegramStringFormatting
|
||||||
|
import WallpaperBackgroundNode
|
||||||
|
import ReactionSelectionNode
|
||||||
|
|
||||||
|
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||||
|
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||||
|
private let labelNode: TextNode
|
||||||
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
|
private var backgroundColorNode: ASDisplayNode
|
||||||
|
private let backgroundMaskNode: ASImageNode
|
||||||
|
private var linkHighlightingNode: LinkHighlightingNode?
|
||||||
|
|
||||||
|
private let mediaBackgroundNode: NavigationBackgroundNode
|
||||||
|
private let titleNode: TextNode
|
||||||
|
private let subtitleNode: TextNode
|
||||||
|
|
||||||
|
private let giftNode: ASImageNode
|
||||||
|
|
||||||
|
private let buttonNode: HighlightTrackingButtonNode
|
||||||
|
private let buttonStarsNode: PremiumStarsNode
|
||||||
|
private let buttonTitleNode: TextNode
|
||||||
|
|
||||||
|
private var cachedMaskBackgroundImage: (CGPoint, UIImage, [CGRect])?
|
||||||
|
private var absoluteRect: (CGRect, CGSize)?
|
||||||
|
|
||||||
|
required init() {
|
||||||
|
self.labelNode = TextNode()
|
||||||
|
self.labelNode.isUserInteractionEnabled = false
|
||||||
|
self.labelNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
self.backgroundColorNode = ASDisplayNode()
|
||||||
|
self.backgroundMaskNode = ASImageNode()
|
||||||
|
|
||||||
|
self.mediaBackgroundNode = NavigationBackgroundNode(color: .clear)
|
||||||
|
self.mediaBackgroundNode.clipsToBounds = true
|
||||||
|
self.mediaBackgroundNode.cornerRadius = 24.0
|
||||||
|
|
||||||
|
self.titleNode = TextNode()
|
||||||
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
self.titleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
self.subtitleNode = TextNode()
|
||||||
|
self.subtitleNode.isUserInteractionEnabled = false
|
||||||
|
self.subtitleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
self.buttonNode = HighlightTrackingButtonNode()
|
||||||
|
self.buttonNode.clipsToBounds = true
|
||||||
|
self.buttonNode.cornerRadius = 17.0
|
||||||
|
|
||||||
|
self.giftNode = ASImageNode()
|
||||||
|
self.giftNode.isUserInteractionEnabled = false
|
||||||
|
self.giftNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
self.buttonStarsNode = PremiumStarsNode()
|
||||||
|
|
||||||
|
self.buttonTitleNode = TextNode()
|
||||||
|
self.buttonTitleNode.isUserInteractionEnabled = false
|
||||||
|
self.buttonTitleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.labelNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.mediaBackgroundNode)
|
||||||
|
self.addSubnode(self.titleNode)
|
||||||
|
self.addSubnode(self.subtitleNode)
|
||||||
|
self.addSubnode(self.giftNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.buttonNode)
|
||||||
|
self.buttonNode.addSubnode(self.buttonStarsNode)
|
||||||
|
self.addSubnode(self.buttonTitleNode)
|
||||||
|
|
||||||
|
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if highlighted {
|
||||||
|
strongSelf.buttonNode.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.buttonNode.alpha = 0.4
|
||||||
|
strongSelf.buttonTitleNode.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.buttonTitleNode.alpha = 0.4
|
||||||
|
} else {
|
||||||
|
strongSelf.buttonNode.alpha = 1.0
|
||||||
|
strongSelf.buttonNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
strongSelf.buttonTitleNode.alpha = 1.0
|
||||||
|
strongSelf.buttonTitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func buttonPressed() {
|
||||||
|
guard let item = self.item else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||||
|
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||||
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
|
let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode)
|
||||||
|
let makeButtonTitleLayout = TextNode.asyncLayout(self.buttonTitleNode)
|
||||||
|
|
||||||
|
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
|
||||||
|
|
||||||
|
return { item, layoutConstants, _, _, _ in
|
||||||
|
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
||||||
|
|
||||||
|
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||||
|
let imageSize = CGSize(width: 220.0, height: 210.0)
|
||||||
|
|
||||||
|
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: item.message, accountPeerId: item.context.account.peerId)
|
||||||
|
|
||||||
|
let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText
|
||||||
|
|
||||||
|
var duration: String = ""
|
||||||
|
for media in item.message.media {
|
||||||
|
if let action = media as? TelegramMediaAction {
|
||||||
|
switch action.action {
|
||||||
|
case let .giftPremium(_, _, durationValue):
|
||||||
|
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(timeIntervalString(strings: item.presentationData.strings, value: durationValue)).string
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_Title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: duration, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
var labelRects = labelLayout.linesRects()
|
||||||
|
if labelRects.count > 1 {
|
||||||
|
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
||||||
|
for i in 0 ..< sortedIndices.count {
|
||||||
|
let index = sortedIndices[i]
|
||||||
|
for j in -1 ... 1 {
|
||||||
|
if j != 0 && index + j >= 0 && index + j < sortedIndices.count {
|
||||||
|
if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 {
|
||||||
|
labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width)
|
||||||
|
labelRects[index].size.width = labelRects[index + j].size.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0 ..< labelRects.count {
|
||||||
|
labelRects[i] = labelRects[i].insetBy(dx: -6.0, dy: floor((labelRects[i].height - 20.0) / 2.0))
|
||||||
|
labelRects[i].size.height = 20.0
|
||||||
|
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundMaskImage: (CGPoint, UIImage)?
|
||||||
|
var backgroundMaskUpdated = false
|
||||||
|
if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects {
|
||||||
|
backgroundMaskImage = (currentOffset, currentImage)
|
||||||
|
} else {
|
||||||
|
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects)
|
||||||
|
backgroundMaskUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + imageSize.height + 18.0)
|
||||||
|
|
||||||
|
return (backgroundSize.width, { boundingWidth in
|
||||||
|
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
|
||||||
|
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
|
|
||||||
|
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: labelLayout.size.height + 16.0), size: imageSize)
|
||||||
|
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||||
|
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
|
||||||
|
|
||||||
|
strongSelf.mediaBackgroundNode.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
|
||||||
|
strongSelf.mediaBackgroundNode.update(size: mediaBackgroundFrame.size, transition: .immediate)
|
||||||
|
strongSelf.buttonNode.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
|
||||||
|
|
||||||
|
strongSelf.giftNode.image = UIImage(bundleImageName: "Components/Gift")
|
||||||
|
if let image = strongSelf.giftNode.image {
|
||||||
|
strongSelf.giftNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - image.size.width) / 2.0), y: mediaBackgroundFrame.minY + 14.0), size: image.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = labelApply()
|
||||||
|
let _ = titleApply()
|
||||||
|
let _ = subtitleApply()
|
||||||
|
let _ = buttonTitleApply()
|
||||||
|
|
||||||
|
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: 2.0), size: labelLayout.size)
|
||||||
|
strongSelf.labelNode.frame = labelFrame
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 121.0), size: titleLayout.size)
|
||||||
|
strongSelf.titleNode.frame = titleFrame
|
||||||
|
|
||||||
|
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY - 1.0), size: subtitleLayout.size)
|
||||||
|
strongSelf.subtitleNode.frame = subtitleFrame
|
||||||
|
|
||||||
|
let buttonTitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonTitleLayout.size.width) / 2.0), y: subtitleFrame.maxY + 18.0), size: buttonTitleLayout.size)
|
||||||
|
strongSelf.buttonTitleNode.frame = buttonTitleFrame
|
||||||
|
|
||||||
|
let buttonSize = CGSize(width: buttonTitleLayout.size.width + 38.0, height: 34.0)
|
||||||
|
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonSize.width) / 2.0), y: subtitleFrame.maxY + 10.0), size: buttonSize)
|
||||||
|
strongSelf.buttonStarsNode.frame = CGRect(origin: .zero, size: buttonSize)
|
||||||
|
|
||||||
|
let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
|
||||||
|
if let (offset, image) = backgroundMaskImage {
|
||||||
|
if strongSelf.backgroundNode == nil {
|
||||||
|
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||||
|
strongSelf.backgroundNode = backgroundNode
|
||||||
|
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||||
|
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if backgroundMaskUpdated, let backgroundNode = strongSelf.backgroundNode {
|
||||||
|
if labelRects.count == 1 {
|
||||||
|
backgroundNode.clipsToBounds = true
|
||||||
|
backgroundNode.cornerRadius = labelRects[0].height / 2.0
|
||||||
|
backgroundNode.view.mask = nil
|
||||||
|
} else {
|
||||||
|
backgroundNode.clipsToBounds = false
|
||||||
|
backgroundNode.cornerRadius = 0.0
|
||||||
|
backgroundNode.view.mask = strongSelf.backgroundMaskNode.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundNode = strongSelf.backgroundNode {
|
||||||
|
backgroundNode.frame = CGRect(origin: CGPoint(x: baseBackgroundFrame.minX + offset.x, y: baseBackgroundFrame.minY + offset.y), size: image.size)
|
||||||
|
if let (rect, size) = strongSelf.absoluteRect {
|
||||||
|
strongSelf.updateAbsoluteRect(rect, within: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.backgroundMaskNode.image = image
|
||||||
|
strongSelf.backgroundMaskNode.frame = CGRect(origin: CGPoint(), size: image.size)
|
||||||
|
strongSelf.backgroundColorNode.frame = CGRect(origin: CGPoint(), size: image.size)
|
||||||
|
|
||||||
|
strongSelf.cachedMaskBackgroundImage = (offset, image, labelRects)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||||
|
self.absoluteRect = (rect, containerSize)
|
||||||
|
|
||||||
|
if let backgroundNode = self.backgroundNode {
|
||||||
|
var backgroundFrame = backgroundNode.frame
|
||||||
|
backgroundFrame.origin.x += rect.minX
|
||||||
|
backgroundFrame.origin.y += rect.minY
|
||||||
|
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||||
|
if let backgroundNode = self.backgroundNode {
|
||||||
|
backgroundNode.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||||
|
if let backgroundNode = self.backgroundNode {
|
||||||
|
backgroundNode.offsetSpring(value: value, duration: duration, damping: damping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||||
|
if let item = self.item {
|
||||||
|
var rects: [(CGRect, CGRect)]?
|
||||||
|
let textNodeFrame = self.labelNode.frame
|
||||||
|
if let point = point {
|
||||||
|
if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)) {
|
||||||
|
let possibleNames: [String] = [
|
||||||
|
TelegramTextAttributes.URL,
|
||||||
|
TelegramTextAttributes.PeerMention,
|
||||||
|
TelegramTextAttributes.PeerTextMention,
|
||||||
|
TelegramTextAttributes.BotCommand,
|
||||||
|
TelegramTextAttributes.Hashtag
|
||||||
|
]
|
||||||
|
for name in possibleNames {
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: name)] {
|
||||||
|
rects = self.labelNode.lineAndAttributeRects(name: name, at: index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let rects = rects {
|
||||||
|
var mappedRects: [CGRect] = []
|
||||||
|
for i in 0 ..< rects.count {
|
||||||
|
let lineRect = rects[i].0
|
||||||
|
var itemRect = rects[i].1
|
||||||
|
itemRect.origin.x = floor((textNodeFrame.size.width - lineRect.width) / 2.0) + itemRect.origin.x
|
||||||
|
mappedRects.append(itemRect)
|
||||||
|
}
|
||||||
|
|
||||||
|
let linkHighlightingNode: LinkHighlightingNode
|
||||||
|
if let current = self.linkHighlightingNode {
|
||||||
|
linkHighlightingNode = current
|
||||||
|
} else {
|
||||||
|
let serviceColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
|
linkHighlightingNode = LinkHighlightingNode(color: serviceColor.linkHighlight)
|
||||||
|
linkHighlightingNode.inset = 2.5
|
||||||
|
self.linkHighlightingNode = linkHighlightingNode
|
||||||
|
self.insertSubnode(linkHighlightingNode, belowSubnode: self.labelNode)
|
||||||
|
}
|
||||||
|
linkHighlightingNode.frame = self.labelNode.frame.offsetBy(dx: 0.0, dy: 1.5)
|
||||||
|
linkHighlightingNode.updateRects(mappedRects)
|
||||||
|
} else if let linkHighlightingNode = self.linkHighlightingNode {
|
||||||
|
self.linkHighlightingNode = nil
|
||||||
|
linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in
|
||||||
|
linkHighlightingNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||||
|
let textNodeFrame = self.labelNode.frame
|
||||||
|
if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)), gesture == .tap {
|
||||||
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
|
var concealed = true
|
||||||
|
if let (attributeText, fullText) = self.labelNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||||
|
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
||||||
|
}
|
||||||
|
return .url(url: url, concealed: concealed)
|
||||||
|
} else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention {
|
||||||
|
return .peerMention(peerMention.peerId, peerMention.mention)
|
||||||
|
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||||
|
return .textMention(peerName)
|
||||||
|
} else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String {
|
||||||
|
return .botCommand(botCommand)
|
||||||
|
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||||
|
return .hashtag(hashtag.peerName, hashtag.hashtag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) {
|
||||||
|
return .openMessage
|
||||||
|
} else if self.mediaBackgroundNode.frame.contains(point) {
|
||||||
|
return .openMessage
|
||||||
|
} else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4059,7 +4059,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) {
|
if !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport), let cachedData = data.cachedData as? CachedUserData, let _ = cachedData.giftPremiumUrl {
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_GiftPremium, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_GiftPremium, icon: { theme in
|
||||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user