diff --git a/build-system/example-configuration/variables.bzl b/build-system/example-configuration/variables.bzl
index 5403ae8176..26b6550f2e 100644
--- a/build-system/example-configuration/variables.bzl
+++ b/build-system/example-configuration/variables.bzl
@@ -8,6 +8,7 @@ telegram_is_internal_build = "false"
telegram_is_appstore_build = "true"
telegram_appstore_id = "686449807"
telegram_app_specific_url_scheme = "tg"
+telegram_premium_iap_product_id = ""
telegram_aps_environment = "production"
telegram_enable_siri = True
telegram_enable_icloud = True
diff --git a/build-system/prepare-build-variables-Telegram.sh b/build-system/prepare-build-variables-Telegram.sh
index ca3d9a9542..62862a6ddd 100755
--- a/build-system/prepare-build-variables-Telegram.sh
+++ b/build-system/prepare-build-variables-Telegram.sh
@@ -35,6 +35,7 @@ prepare_build_variables () {
IS_APPSTORE_BUILD \
APPSTORE_ID \
APP_SPECIFIC_URL_SCHEME \
+ PREMIUM_IAP_PRODUCT_ID \
TELEGRAM_DISABLE_EXTENSIONS \
)
@@ -66,6 +67,7 @@ prepare_build_variables () {
echo "telegram_is_appstore_build = \"$IS_APPSTORE_BUILD\"" >> "$VARIABLES_PATH"
echo "telegram_appstore_id = \"$APPSTORE_ID\"" >> "$VARIABLES_PATH"
echo "telegram_app_specific_url_scheme = \"$APP_SPECIFIC_URL_SCHEME\"" >> "$VARIABLES_PATH"
+ echo "telegram_premium_iap_product_id = \"$PREMIUM_IAP_PRODUCT_ID\"" >> "$VARIABLES_PATH"
echo "telegram_aps_environment = \"$APS_ENVIRONMENT\"" >> "$VARIABLES_PATH"
if [ "$TELEGRAM_DISABLE_EXTENSIONS" == "1" ]; then
diff --git a/build-system/verify.sh b/build-system/verify.sh
index 54fd0a9d30..fce7a6850c 100644
--- a/build-system/verify.sh
+++ b/build-system/verify.sh
@@ -15,6 +15,7 @@ export IS_INTERNAL_BUILD="false"
export IS_APPSTORE_BUILD="true"
export APPSTORE_ID="686449807"
export APP_SPECIFIC_URL_SCHEME="tgapp"
+export PREMIUM_IAP_PRODUCT_ID="org.telegram.telegramPremium.monthly"
if [ -z "$BUILD_NUMBER" ]; then
echo "BUILD_NUMBER is not defined"
diff --git a/submodules/AccountContext/BUILD b/submodules/AccountContext/BUILD
index ccac7c6b1b..b9c7a36100 100644
--- a/submodules/AccountContext/BUILD
+++ b/submodules/AccountContext/BUILD
@@ -22,6 +22,7 @@ swift_library(
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
"//submodules/MeshAnimationCache:MeshAnimationCache",
"//submodules/Utils/RangeSet:RangeSet",
+ "//submodules/InAppPurchaseManager:InAppPurchaseManager",
],
visibility = [
"//visibility:public",
diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift
index eac8787137..a56654c9fe 100644
--- a/submodules/AccountContext/Sources/AccountContext.swift
+++ b/submodules/AccountContext/Sources/AccountContext.swift
@@ -11,6 +11,7 @@ import Display
import DeviceLocationManager
import TemporaryCachedPeerDataManager
import MeshAnimationCache
+import InAppPurchaseManager
public final class TelegramApplicationOpenUrlCompletion {
public let completion: (Bool) -> Void
@@ -654,6 +655,7 @@ public protocol SharedAccountContext: AnyObject {
var locationManager: DeviceLocationManager? { get }
var callManager: PresentationCallManager? { get }
var contactDataManager: DeviceContactDataManager? { get }
+ var inAppPurchaseManager: InAppPurchaseManager? { get }
var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get }
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
diff --git a/submodules/BuildConfig/BUILD b/submodules/BuildConfig/BUILD
index 10c5c92dbb..83ee53988e 100644
--- a/submodules/BuildConfig/BUILD
+++ b/submodules/BuildConfig/BUILD
@@ -7,6 +7,7 @@ load(
"telegram_is_appstore_build",
"telegram_appstore_id",
"telegram_app_specific_url_scheme",
+ "telegram_premium_iap_product_id",
)
objc_library(
@@ -25,6 +26,7 @@ objc_library(
"-DAPP_CONFIG_IS_APPSTORE_BUILD={}".format(telegram_is_appstore_build),
"-DAPP_CONFIG_APPSTORE_ID={}".format(telegram_appstore_id),
"-DAPP_SPECIFIC_URL_SCHEME=\\\"{}\\\"".format(telegram_app_specific_url_scheme),
+ "-DAPP_CONFIG_PREMIUM_IAP_PRODUCT_ID=\\\"{}\\\"".format(telegram_premium_iap_product_id),
],
hdrs = glob([
"PublicHeaders/**/*.h",
diff --git a/submodules/BuildConfig/PublicHeaders/BuildConfig/BuildConfig.h b/submodules/BuildConfig/PublicHeaders/BuildConfig/BuildConfig.h
index d735dd74bc..a433870b93 100644
--- a/submodules/BuildConfig/PublicHeaders/BuildConfig/BuildConfig.h
+++ b/submodules/BuildConfig/PublicHeaders/BuildConfig/BuildConfig.h
@@ -18,6 +18,7 @@
@property (nonatomic, readonly) bool isAppStoreBuild;
@property (nonatomic, readonly) int64_t appStoreId;
@property (nonatomic, strong, readonly) NSString * _Nonnull appSpecificUrlScheme;
+@property (nonatomic, strong, readonly) NSString * _Nonnull premiumIAPProductId;
+ (DeviceSpecificEncryptionParameters * _Nonnull)deviceSpecificEncryptionParameters:(NSString * _Nonnull)rootPath baseAppBundleId:(NSString * _Nonnull)baseAppBundleId;
- (NSData * _Nullable)bundleDataWithAppToken:(NSData * _Nullable)appToken signatureDict:(NSDictionary * _Nullable)signatureDict;
diff --git a/submodules/BuildConfig/Sources/BuildConfig.m b/submodules/BuildConfig/Sources/BuildConfig.m
index 3dde1749ae..a589f33372 100644
--- a/submodules/BuildConfig/Sources/BuildConfig.m
+++ b/submodules/BuildConfig/Sources/BuildConfig.m
@@ -185,6 +185,10 @@ API_AVAILABLE(ios(10))
return @(APP_SPECIFIC_URL_SCHEME);
}
+- (NSString *)premiumIAPProductId {
+ return @(APP_CONFIG_PREMIUM_IAP_PRODUCT_ID);
+}
+
+ (NSString * _Nullable)bundleSeedId {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecClass,
diff --git a/submodules/InAppPurchaseManager/BUILD b/submodules/InAppPurchaseManager/BUILD
new file mode 100644
index 0000000000..ce210e161f
--- /dev/null
+++ b/submodules/InAppPurchaseManager/BUILD
@@ -0,0 +1,20 @@
+load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
+
+swift_library(
+ name = "InAppPurchaseManager",
+ module_name = "InAppPurchaseManager",
+ srcs = glob([
+ "Sources/**/*.swift",
+ ]),
+ copts = [
+ "-warnings-as-errors",
+ ],
+ deps = [
+ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
+ "//submodules/Postbox:Postbox",
+ "//submodules/TelegramCore:TelegramCore",
+ ],
+ visibility = [
+ "//visibility:public",
+ ],
+)
diff --git a/submodules/InAppPurchaseManager/Info.plist b/submodules/InAppPurchaseManager/Info.plist
new file mode 100644
index 0000000000..e1fe4cfb7b
--- /dev/null
+++ b/submodules/InAppPurchaseManager/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+
+
diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift
new file mode 100644
index 0000000000..5e5f5d7339
--- /dev/null
+++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift
@@ -0,0 +1,143 @@
+import Foundation
+import CoreLocation
+import SwiftSignalKit
+import StoreKit
+import Postbox
+import TelegramCore
+
+private final class PaymentTransactionContext {
+ var state: SKPaymentTransactionState?
+ let subscriber: (SKPaymentTransactionState) -> Void
+
+ init(subscriber: @escaping (SKPaymentTransactionState) -> Void) {
+ self.subscriber = subscriber
+ }
+}
+
+public final class InAppPurchaseManager: NSObject {
+ public final class Product {
+ let skProduct: SKProduct
+
+ init(skProduct: SKProduct) {
+ self.skProduct = skProduct
+ }
+
+ public var price: String {
+ let numberFormatter = NumberFormatter()
+ numberFormatter.numberStyle = .currency
+ numberFormatter.locale = self.skProduct.priceLocale
+ return numberFormatter.string(from: self.skProduct.price) ?? ""
+ }
+ }
+
+ public enum PurchaseResult {
+ case success
+ }
+
+ public enum PurchaseError {
+ case generic
+ }
+
+ private let premiumProductId: String
+
+ private var products: [Product] = []
+ private var productsPromise = Promise<[Product]>()
+ private var productRequest: SKProductsRequest?
+
+ private let stateQueue = Queue()
+ private var paymentContexts: [String: PaymentTransactionContext] = [:]
+
+ public init(premiumProductId: String) {
+ self.premiumProductId = premiumProductId
+
+ super.init()
+
+ SKPaymentQueue.default().add(self)
+ self.requestProducts()
+ }
+
+ deinit {
+
+ }
+
+ private func requestProducts() {
+ guard !self.premiumProductId.isEmpty else {
+ return
+ }
+ let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
+ productRequest.delegate = self
+ productRequest.start()
+
+ self.productRequest = productRequest
+ }
+
+
+ public var availableProducts: Signal<[Product], NoError> {
+ if self.products.isEmpty && self.productRequest == nil {
+ self.requestProducts()
+ }
+ return self.productsPromise.get()
+ }
+
+ public func buyProduct(_ product: Product, account: Account) -> Signal {
+ let payment = SKMutablePayment(product: product.skProduct)
+ payment.applicationUsername = "\(account.peerId.id._internalGetInt64Value())"
+ SKPaymentQueue.default().add(payment)
+
+ let productIdentifier = payment.productIdentifier
+ let signal = Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.stateQueue.async {
+ let paymentContext = PaymentTransactionContext(subscriber: { state in
+ switch state {
+ case .purchased, .restored:
+ subscriber.putNext(.success)
+ subscriber.putCompletion()
+ case .failed:
+ subscriber.putError(.generic)
+ case .deferred, .purchasing:
+ break
+ default:
+ break
+ }
+ })
+ self.paymentContexts[productIdentifier] = paymentContext
+
+ disposable.set(ActionDisposable { [weak paymentContext] in
+ self.stateQueue.async {
+ if let current = self.paymentContexts[productIdentifier], current === paymentContext {
+ self.paymentContexts.removeValue(forKey: productIdentifier)
+ }
+ }
+ })
+ }
+
+ return disposable
+ }
+ return signal
+ }
+}
+
+extension InAppPurchaseManager: SKProductsRequestDelegate {
+ public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
+ self.productRequest = nil
+
+ Queue.mainQueue().async {
+ self.productsPromise.set(.single(response.products.map { Product(skProduct: $0) }))
+ }
+ }
+}
+
+extension InAppPurchaseManager: SKPaymentTransactionObserver {
+ public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
+ if let transaction = transactions.first {
+ let productIdentifier = transaction.payment.productIdentifier
+ self.stateQueue.async {
+ if let context = self.paymentContexts[productIdentifier] {
+ context.subscriber(transaction.transactionState)
+ }
+ }
+ }
+ }
+}
diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD
index 6a0a5e17a1..3fe8dd5760 100644
--- a/submodules/PremiumUI/BUILD
+++ b/submodules/PremiumUI/BUILD
@@ -40,6 +40,8 @@ swift_library(
"//submodules/Components/BundleIconComponent:BundleIconComponent",
"//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent",
"//submodules/Components/Forms/PrefixSectionGroupComponent:PrefixSectionGroupComponent",
+ "//submodules/InAppPurchaseManager:InAppPurchaseManager",
+ "//submodules/ConfettiEffect:ConfettiEffect",
],
visibility = [
"//visibility:public",
diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift
index b326b49c5c..9617ed59a1 100644
--- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift
+++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift
@@ -13,6 +13,8 @@ import BundleIconComponent
import SolidRoundedButtonComponent
import Markdown
import SceneKit
+import InAppPurchaseManager
+import ConfettiEffect
private func deg2rad(_ number: Float) -> Float {
return number * .pi / 180
@@ -750,7 +752,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
let strings = environment.strings
let availableWidth = context.availableSize.width
- let sideInsets = sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right
+ let sideInsets = sideInset * 2.0 + environment.safeInsets.left + environment.safeInsets.right
var size = CGSize(width: context.availableSize.width, height: 0.0)
let overscroll = overscroll.update(
@@ -1060,11 +1062,15 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
+ let updateInProgress: (Bool) -> Void
+ let completion: () -> Void
- init(context: AccountContext) {
+ init(context: AccountContext, updateInProgress: @escaping (Bool) -> Void, completion: @escaping () -> Void) {
self.context = context
+ self.updateInProgress = updateInProgress
+ self.completion = completion
}
-
+
static func ==(lhs: PremiumIntroScreenComponent, rhs: PremiumIntroScreenComponent) -> Bool {
if lhs.context !== rhs.context {
return false
@@ -1073,12 +1079,68 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
}
final class State: ComponentState {
+ private let context: AccountContext
+ private let updateInProgress: (Bool) -> Void
+ private let completion: () -> Void
+
var topContentOffset: CGFloat?
var bottomContentOffset: CGFloat?
+
+ var inProgress = false
+ var premiumProduct: InAppPurchaseManager.Product?
+ private var disposable: Disposable?
+ private var actionDisposable = MetaDisposable()
+
+ init(context: AccountContext, updateInProgress: @escaping (Bool) -> Void, completion: @escaping () -> Void) {
+ self.context = context
+ self.updateInProgress = updateInProgress
+ self.completion = completion
+
+ super.init()
+
+ if let inAppPurchaseManager = context.sharedContext.inAppPurchaseManager {
+ self.disposable = (inAppPurchaseManager.availableProducts
+ |> deliverOnMainQueue).start(next: { [weak self] products in
+ if let strongSelf = self {
+ strongSelf.premiumProduct = products.first
+ strongSelf.updated(transition: .immediate)
+ }
+ })
+ }
+ }
+
+ deinit {
+ self.disposable?.dispose()
+ self.actionDisposable.dispose()
+ }
+
+ func buy() {
+ guard let inAppPurchaseManager = self.context.sharedContext.inAppPurchaseManager,
+ let premiumProduct = self.premiumProduct, !self.inProgress else {
+ return
+ }
+
+ self.inProgress = true
+ self.updateInProgress(true)
+ self.updated(transition: .immediate)
+
+ self.actionDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct, account: self.context.account)
+ |> deliverOnMainQueue).start(next: { [weak self] _ in
+ if let strongSelf = self {
+ strongSelf.completion()
+ }
+ }, error: { [weak self] _ in
+ if let strongSelf = self {
+ strongSelf.inProgress = false
+ strongSelf.updateInProgress(false)
+ strongSelf.updated(transition: .immediate)
+ }
+ }))
+ }
}
func makeState() -> State {
- return State()
+ return State(context: self.context, updateInProgress: self.updateInProgress, completion: self.completion)
}
static var body: Body {
@@ -1138,7 +1200,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
let sideInset: CGFloat = 16.0
let button = button.update(
component: SolidRoundedButtonComponent(
- title: environment.strings.Premium_SubscribeFor("$5").string,
+ title: environment.strings.Premium_SubscribeFor(state.premiumProduct?.price ?? "—").string,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: .black,
backgroundColors: [
@@ -1152,7 +1214,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
height: 50.0,
cornerRadius: 10.0,
gloss: true,
- action: {}
+ action: {
+ state.buy()
+ }
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 50.0),
transition: context.transition)
@@ -1278,8 +1342,18 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
public init(context: AccountContext) {
self.context = context
-
- super.init(context: context, component: PremiumIntroScreenComponent(context: context), navigationBarAppearance: .transparent)
+
+ var updateInProgressImpl: ((Bool) -> Void)?
+ var completionImpl: (() -> Void)?
+ super.init(context: context, component: PremiumIntroScreenComponent(
+ context: context,
+ updateInProgress: { inProgress in
+ updateInProgressImpl?(inProgress)
+ },
+ completion: {
+ completionImpl?()
+ }
+ ), navigationBarAppearance: .transparent)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -1287,6 +1361,23 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
self.navigationItem.setLeftBarButton(cancelItem, animated: false)
self.navigationPresentation = .modal
+
+ updateInProgressImpl = { [weak self] inProgress in
+ if let strongSelf = self {
+ strongSelf.navigationItem.leftBarButtonItem?.isEnabled = !inProgress
+ strongSelf.view.disablesInteractiveTransitionGestureRecognizer = inProgress
+ strongSelf.view.disablesInteractiveModalDismiss = inProgress
+ }
+ }
+
+ completionImpl = { [weak self] in
+ if let strongSelf = self {
+ strongSelf.view.addSubview(ConfettiView(frame: strongSelf.view.bounds))
+ Queue.mainQueue().after(2.0, {
+ self?.dismiss()
+ })
+ }
+ }
}
required public init(coder aDecoder: NSCoder) {
diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift
index a50009a06d..d90f3fff50 100644
--- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift
+++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift
@@ -186,15 +186,14 @@ private class PremiumLimitAnimationComponent: Component {
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - lineHeight), size: CGSize(width: availableSize.width, height: lineHeight))
self.container.frame = containerFrame
- self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0 - 1.0, height: lineHeight))
- self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0 + 1.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0 - 1.0, height: lineHeight))
+ self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
+ self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight))
if self.activeBackground.animation(forKey: "movement") == nil {
- self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0, y: lineHeight / 2.0)
+ self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0)
}
-
let countWidth: CGFloat
if let badgeText = component.badgeText {
switch badgeText.count {
@@ -219,10 +218,10 @@ private class PremiumLimitAnimationComponent: Component {
self.badgeView.bounds = CGRect(origin: .zero, size: badgeSize)
self.badgeView.center = CGPoint(x: availableSize.width / 2.0, y: 82.0)
- if self.badgeForeground.animation(forKey: "movement") == nil {
- self.badgeForeground.position = CGPoint(x: badgeSize.width * 3.0 / 2.0, y: badgeSize.height / 2.0)
- }
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: badgeSize.width * 3.0, height: badgeSize.height))
+ if self.badgeForeground.animation(forKey: "movement") == nil {
+ self.badgeForeground.position = CGPoint(x: badgeSize.width * 3.0 / 2.0 - self.badgeForeground.frame.width * 0.35, y: badgeSize.height / 2.0)
+ }
self.badgeIcon.frame = CGRect(x: 15.0, y: 9.0, width: 30.0, height: 30.0)
self.badgeCountLabel.frame = CGRect(x: badgeSize.width - countWidth - 11.0, y: 10.0, width: countWidth, height: 48.0)
diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift
index 623b5b8085..e57be1642b 100644
--- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift
+++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift
@@ -309,12 +309,13 @@ private func generatePremiumReactionIcon() -> UIImage? {
}
let colorsArray: [CGColor] = [
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xff7923).cgColor,
- UIColor(rgb: 0xff7923).cgColor
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x976FFF).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor
]
- var locations: [CGFloat] = [0.0, 0.15, 0.85, 1.0]
+ var locations: [CGFloat] = [0.0, 0.15, 0.5, 0.85, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
index 895699be86..25278fd18e 100644
--- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
+++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
@@ -792,10 +792,10 @@ public final class SolidRoundedButtonView: UIView {
}
if let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView {
- if buttonBackgroundAnimationView.layer.animation(forKey: "movement") == nil {
- buttonBackgroundAnimationView.center = CGPoint(x: buttonSize.width * 2.4 / 2.0, y: buttonSize.height / 2.0)
- }
buttonBackgroundAnimationView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: buttonSize.width * 2.4, height: buttonSize.height))
+ if buttonBackgroundAnimationView.layer.animation(forKey: "movement") == nil {
+ buttonBackgroundAnimationView.center = CGPoint(x: buttonSize.width * 2.4 / 2.0 - buttonBackgroundAnimationView.frame.width * 0.35, y: buttonSize.height / 2.0)
+ }
self.setupGradientAnimations()
}
diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift
index 4945e00a2e..7b282e7966 100644
--- a/submodules/TelegramApi/Sources/Api0.swift
+++ b/submodules/TelegramApi/Sources/Api0.swift
@@ -414,7 +414,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) }
dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) }
dict[1345295095] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) }
- dict[1080663248] = { return Api.MessageAction.parse_messageActionPaymentSent($0) }
+ dict[-1776926890] = { return Api.MessageAction.parse_messageActionPaymentSent($0) }
dict[-1892568281] = { return Api.MessageAction.parse_messageActionPaymentSentMe($0) }
dict[-2132731265] = { return Api.MessageAction.parse_messageActionPhoneCall($0) }
dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) }
@@ -985,6 +985,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) }
dict[816245886] = { return Api.messages.Stickers.parse_stickers($0) }
dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) }
+ dict[-1442723025] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) }
dict[1741309751] = { return Api.messages.TranslatedText.parse_translateNoResult($0) }
dict[-1575684144] = { return Api.messages.TranslatedText.parse_translateResultText($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
@@ -1739,6 +1740,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Stickers:
_1.serialize(buffer, boxed)
+ case let _1 as Api.messages.TranscribedAudio:
+ _1.serialize(buffer, boxed)
case let _1 as Api.messages.TranslatedText:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.VotesList:
diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift
index 0a7fe0a125..e99bde0f5d 100644
--- a/submodules/TelegramApi/Sources/Api10.swift
+++ b/submodules/TelegramApi/Sources/Api10.swift
@@ -1009,7 +1009,7 @@ public extension Api {
case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32)
case messageActionHistoryClear
case messageActionInviteToGroupCall(call: Api.InputGroupCall, users: [Int64])
- case messageActionPaymentSent(currency: String, totalAmount: Int64)
+ case messageActionPaymentSent(flags: Int32, currency: String, totalAmount: Int64, invoiceSlug: String?)
case messageActionPaymentSentMe(flags: Int32, currency: String, totalAmount: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, charge: Api.PaymentCharge)
case messageActionPhoneCall(flags: Int32, callId: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?)
case messageActionPinMessage
@@ -1170,12 +1170,14 @@ public extension Api {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
- case .messageActionPaymentSent(let currency, let totalAmount):
+ case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug):
if boxed {
- buffer.appendInt32(1080663248)
+ buffer.appendInt32(-1776926890)
}
+ serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(totalAmount, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {serializeString(invoiceSlug!, buffer: buffer, boxed: false)}
break
case .messageActionPaymentSentMe(let flags, let currency, let totalAmount, let payload, let info, let shippingOptionId, let charge):
if boxed {
@@ -1303,8 +1305,8 @@ public extension Api {
return ("messageActionHistoryClear", [])
case .messageActionInviteToGroupCall(let call, let users):
return ("messageActionInviteToGroupCall", [("call", String(describing: call)), ("users", String(describing: users))])
- case .messageActionPaymentSent(let currency, let totalAmount):
- return ("messageActionPaymentSent", [("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount))])
+ case .messageActionPaymentSent(let flags, let currency, let totalAmount, let invoiceSlug):
+ return ("messageActionPaymentSent", [("flags", String(describing: flags)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("invoiceSlug", String(describing: invoiceSlug))])
case .messageActionPaymentSentMe(let flags, let currency, let totalAmount, let payload, let info, let shippingOptionId, let charge):
return ("messageActionPaymentSentMe", [("flags", String(describing: flags)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("payload", String(describing: payload)), ("info", String(describing: info)), ("shippingOptionId", String(describing: shippingOptionId)), ("charge", String(describing: charge))])
case .messageActionPhoneCall(let flags, let callId, let reason, let duration):
@@ -1565,14 +1567,20 @@ public extension Api {
}
}
public static func parse_messageActionPaymentSent(_ reader: BufferReader) -> MessageAction? {
- var _1: String?
- _1 = parseString(reader)
- var _2: Int64?
- _2 = reader.readInt64()
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: String?
+ _2 = parseString(reader)
+ var _3: Int64?
+ _3 = reader.readInt64()
+ var _4: String?
+ if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
- if _c1 && _c2 {
- return Api.MessageAction.messageActionPaymentSent(currency: _1!, totalAmount: _2!)
+ let _c3 = _3 != nil
+ let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
+ if _c1 && _c2 && _c3 && _c4 {
+ return Api.MessageAction.messageActionPaymentSent(flags: _1!, currency: _2!, totalAmount: _3!, invoiceSlug: _4)
}
else {
return nil
diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift
index 0635fa8ff7..2acb37d32a 100644
--- a/submodules/TelegramApi/Sources/Api25.swift
+++ b/submodules/TelegramApi/Sources/Api25.swift
@@ -488,6 +488,42 @@ public extension Api.messages {
}
}
+public extension Api.messages {
+ enum TranscribedAudio: TypeConstructorDescription {
+ case transcribedAudio(text: String)
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .transcribedAudio(let text):
+ if boxed {
+ buffer.appendInt32(-1442723025)
+ }
+ serializeString(text, buffer: buffer, boxed: false)
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .transcribedAudio(let text):
+ return ("transcribedAudio", [("text", String(describing: text))])
+ }
+ }
+
+ public static func parse_transcribedAudio(_ reader: BufferReader) -> TranscribedAudio? {
+ var _1: String?
+ _1 = parseString(reader)
+ let _c1 = _1 != nil
+ if _c1 {
+ return Api.messages.TranscribedAudio.transcribedAudio(text: _1!)
+ }
+ else {
+ return nil
+ }
+ }
+
+ }
+}
public extension Api.messages {
enum TranslatedText: TypeConstructorDescription {
case translateNoResult
@@ -1464,95 +1500,3 @@ public extension Api.photos {
}
}
-public extension Api.photos {
- enum Photos: TypeConstructorDescription {
- case photos(photos: [Api.Photo], users: [Api.User])
- case photosSlice(count: Int32, photos: [Api.Photo], users: [Api.User])
-
- public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
- switch self {
- case .photos(let photos, let users):
- if boxed {
- buffer.appendInt32(-1916114267)
- }
- buffer.appendInt32(481674261)
- buffer.appendInt32(Int32(photos.count))
- for item in photos {
- item.serialize(buffer, true)
- }
- buffer.appendInt32(481674261)
- buffer.appendInt32(Int32(users.count))
- for item in users {
- item.serialize(buffer, true)
- }
- break
- case .photosSlice(let count, let photos, let users):
- if boxed {
- buffer.appendInt32(352657236)
- }
- serializeInt32(count, buffer: buffer, boxed: false)
- buffer.appendInt32(481674261)
- buffer.appendInt32(Int32(photos.count))
- for item in photos {
- item.serialize(buffer, true)
- }
- buffer.appendInt32(481674261)
- buffer.appendInt32(Int32(users.count))
- for item in users {
- item.serialize(buffer, true)
- }
- break
- }
- }
-
- public func descriptionFields() -> (String, [(String, Any)]) {
- switch self {
- case .photos(let photos, let users):
- return ("photos", [("photos", String(describing: photos)), ("users", String(describing: users))])
- case .photosSlice(let count, let photos, let users):
- return ("photosSlice", [("count", String(describing: count)), ("photos", String(describing: photos)), ("users", String(describing: users))])
- }
- }
-
- public static func parse_photos(_ reader: BufferReader) -> Photos? {
- var _1: [Api.Photo]?
- if let _ = reader.readInt32() {
- _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self)
- }
- var _2: [Api.User]?
- if let _ = reader.readInt32() {
- _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
- }
- let _c1 = _1 != nil
- let _c2 = _2 != nil
- if _c1 && _c2 {
- return Api.photos.Photos.photos(photos: _1!, users: _2!)
- }
- else {
- return nil
- }
- }
- public static func parse_photosSlice(_ reader: BufferReader) -> Photos? {
- var _1: Int32?
- _1 = reader.readInt32()
- var _2: [Api.Photo]?
- if let _ = reader.readInt32() {
- _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self)
- }
- var _3: [Api.User]?
- if let _ = reader.readInt32() {
- _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
- }
- let _c1 = _1 != nil
- let _c2 = _2 != nil
- let _c3 = _3 != nil
- if _c1 && _c2 && _c3 {
- return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!)
- }
- else {
- return nil
- }
- }
-
- }
-}
diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift
index ad5ef9bca8..58fa0773fb 100644
--- a/submodules/TelegramApi/Sources/Api26.swift
+++ b/submodules/TelegramApi/Sources/Api26.swift
@@ -1,3 +1,95 @@
+public extension Api.photos {
+ enum Photos: TypeConstructorDescription {
+ case photos(photos: [Api.Photo], users: [Api.User])
+ case photosSlice(count: Int32, photos: [Api.Photo], users: [Api.User])
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .photos(let photos, let users):
+ if boxed {
+ buffer.appendInt32(-1916114267)
+ }
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(photos.count))
+ for item in photos {
+ item.serialize(buffer, true)
+ }
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(users.count))
+ for item in users {
+ item.serialize(buffer, true)
+ }
+ break
+ case .photosSlice(let count, let photos, let users):
+ if boxed {
+ buffer.appendInt32(352657236)
+ }
+ serializeInt32(count, buffer: buffer, boxed: false)
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(photos.count))
+ for item in photos {
+ item.serialize(buffer, true)
+ }
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(users.count))
+ for item in users {
+ item.serialize(buffer, true)
+ }
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .photos(let photos, let users):
+ return ("photos", [("photos", String(describing: photos)), ("users", String(describing: users))])
+ case .photosSlice(let count, let photos, let users):
+ return ("photosSlice", [("count", String(describing: count)), ("photos", String(describing: photos)), ("users", String(describing: users))])
+ }
+ }
+
+ public static func parse_photos(_ reader: BufferReader) -> Photos? {
+ var _1: [Api.Photo]?
+ if let _ = reader.readInt32() {
+ _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self)
+ }
+ var _2: [Api.User]?
+ if let _ = reader.readInt32() {
+ _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
+ }
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ if _c1 && _c2 {
+ return Api.photos.Photos.photos(photos: _1!, users: _2!)
+ }
+ else {
+ return nil
+ }
+ }
+ public static func parse_photosSlice(_ reader: BufferReader) -> Photos? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: [Api.Photo]?
+ if let _ = reader.readInt32() {
+ _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self)
+ }
+ var _3: [Api.User]?
+ if let _ = reader.readInt32() {
+ _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
+ }
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ if _c1 && _c2 && _c3 {
+ return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!)
+ }
+ else {
+ return nil
+ }
+ }
+
+ }
+}
public extension Api.stats {
enum BroadcastStats: TypeConstructorDescription {
case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph, ivInteractionsGraph: Api.StatsGraph, viewsBySourceGraph: Api.StatsGraph, newFollowersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, recentMessageInteractions: [Api.MessageInteractionCounters])
diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift
index 09fd916287..fff5642c9c 100644
--- a/submodules/TelegramApi/Sources/Api27.swift
+++ b/submodules/TelegramApi/Sources/Api27.swift
@@ -6024,6 +6024,22 @@ public extension Api.functions.messages {
})
}
}
+public extension Api.functions.messages {
+ static func transcribeAudio(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(647928393)
+ peer.serialize(buffer, true)
+ serializeInt32(msgId, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "messages.transcribeAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranscribedAudio? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.TranscribedAudio?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.TranscribedAudio
+ }
+ return result
+ })
+ }
+}
public extension Api.functions.messages {
static func translateText(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, text: String?, fromLang: String?, toLang: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
@@ -6177,6 +6193,21 @@ public extension Api.functions.messages {
})
}
}
+public extension Api.functions.payments {
+ static func assignAppStoreTransaction(transactionId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1654235439)
+ serializeString(transactionId, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("transactionId", String(describing: transactionId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
+ let reader = BufferReader(buffer)
+ var result: Api.Updates?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.Updates
+ }
+ return result
+ })
+ }
+}
public extension Api.functions.payments {
static func clearSavedInfo(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
@@ -7222,11 +7253,11 @@ public extension Api.functions.upload {
}
}
public extension Api.functions.upload {
- static func getFileHashes(location: Api.InputFileLocation, offset: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) {
+ static func getFileHashes(location: Api.InputFileLocation, offset: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.FileHash]>) {
let buffer = Buffer()
- buffer.appendInt32(-956147407)
+ buffer.appendInt32(-1856595926)
location.serialize(buffer, true)
- serializeInt32(offset, buffer: buffer, boxed: false)
+ serializeInt64(offset, buffer: buffer, boxed: false)
return (FunctionDescription(name: "upload.getFileHashes", parameters: [("location", String(describing: location)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.FileHash]? in
let reader = BufferReader(buffer)
var result: [Api.FileHash]?
diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift
index dd34b5c563..ea07ece437 100644
--- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift
+++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift
@@ -40,7 +40,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return TelegramMediaAction(action: .phoneCall(callId: callId, discardReason: discardReason, duration: duration, isVideo: isVideo))
case .messageActionEmpty:
return nil
- case let .messageActionPaymentSent(currency, totalAmount):
+ case let .messageActionPaymentSent(_, currency, totalAmount, _):
return TelegramMediaAction(action: .paymentSent(currency: currency, totalAmount: totalAmount))
case .messageActionPaymentSentMe:
return nil
diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift
index 416aa4d2c0..9b84511abb 100644
--- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift
+++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift
@@ -456,6 +456,17 @@ extension Api.Updates {
}
extension Api.Updates {
+ var users: [Api.User] {
+ switch self {
+ case let .updates(_, users, _, _, _):
+ return users
+ case let .updatesCombined(_, users, _, _, _, _):
+ return users
+ default:
+ return []
+ }
+ }
+
var messages: [Api.Message] {
switch self {
case let .updates(updates, _, _, _, _):
diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift
new file mode 100644
index 0000000000..390fc57949
--- /dev/null
+++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift
@@ -0,0 +1,22 @@
+import Foundation
+import Postbox
+import MtProtoKit
+import SwiftSignalKit
+import TelegramApi
+
+
+public enum AssignAppStoreTransactionError {
+ case generic
+}
+
+func _internal_assignAppStoreTransaction(account: Account, transactionId: String) -> Signal {
+ return account.network.request(Api.functions.payments.assignAppStoreTransaction(transactionId: transactionId))
+ |> mapError { _ -> AssignAppStoreTransactionError in
+ return .generic
+ }
+ |> mapToSignal { updates -> Signal in
+ account.stateManager.addUpdates(updates)
+
+ return .never()
+ }
+}
diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift
index c953688813..73720b5c89 100644
--- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift
+++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift
@@ -36,5 +36,9 @@ public extension TelegramEngine {
public func clearBotPaymentInfo(info: BotPaymentInfo) -> Signal {
return _internal_clearBotPaymentInfo(network: self.account.network, info: info)
}
+
+ public func assignAppStoreTransaction(transactionId: String) -> Signal {
+ return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId)
+ }
}
}
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
index 0bbb3e2752..58778dfbb2 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
@@ -304,12 +304,13 @@ public struct PresentationResourcesChat {
}
let colorsArray: [CGColor] = [
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xff7923).cgColor,
- UIColor(rgb: 0xff7923).cgColor
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x976FFF).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor
]
- var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
+ var locations: [CGFloat] = [0.0, 0.35, 0.5, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
@@ -329,12 +330,13 @@ public struct PresentationResourcesChat {
}
let colorsArray: [CGColor] = [
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xff7923).cgColor,
- UIColor(rgb: 0xff7923).cgColor
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x976FFF).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor
]
- var locations: [CGFloat] = [0.0, 0.15, 0.85, 1.0]
+ var locations: [CGFloat] = [0.0, 0.15, 0.5, 0.85, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift
index b917061bee..fdd197f03b 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift
@@ -238,12 +238,13 @@ public struct PresentationResourcesChatList {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
let colorsArray: [CGColor] = [
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xff7923).cgColor,
- UIColor(rgb: 0xff7923).cgColor
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x976FFF).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor
]
- var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
+ var locations: [CGFloat] = [0.0, 0.35, 0.5, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift
index e17423dfc6..3b8b55c925 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift
@@ -50,6 +50,33 @@ public struct PresentationResourcesSettings {
drawBorder(context: context, rect: bounds)
})
+
+ public static let premium = generateImage(CGSize(width: 29.0, height: 29.0), contextGenerator: { size, context in
+ let bounds = CGRect(origin: CGPoint(), size: size)
+ context.clear(bounds)
+
+ let path = UIBezierPath(roundedRect: bounds, cornerRadius: 7.0)
+ context.addPath(path.cgPath)
+ context.clip()
+
+ let colorsArray: [CGColor] = [
+ UIColor(rgb: 0x6b93ff).cgColor,
+ UIColor(rgb: 0x6b93ff).cgColor,
+ UIColor(rgb: 0x8d77ff).cgColor,
+ UIColor(rgb: 0xb56eec).cgColor,
+ UIColor(rgb: 0xb56eec).cgColor
+ ]
+ var locations: [CGFloat] = [0.0, 0.15, 0.5, 0.85, 1.0]
+ let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
+
+ context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
+
+ if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/ButtonIcon"), color: UIColor(rgb: 0xffffff)), let cgImage = image.cgImage {
+ context.draw(cgImage, in: CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - image.size.width) / 2.0), y: floorToScreenPixels((bounds.height - image.size.height) / 2.0)), size: image.size))
+ }
+
+ drawBorder(context: context, rect: bounds)
+ })
public static let passport = renderIcon(name: "Settings/Menu/Passport")
public static let watch = renderIcon(name: "Settings/Menu/Watch")
diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD
index a50599ac01..064119386a 100644
--- a/submodules/TelegramUI/BUILD
+++ b/submodules/TelegramUI/BUILD
@@ -270,6 +270,7 @@ swift_library(
"//submodules/PremiumUI:PremiumUI",
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
"//submodules/Utils/RangeSet:RangeSet",
+ "//submodules/InAppPurchaseManager:InAppPurchaseManager",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift
index 4beacfcc2b..c38dce315d 100644
--- a/submodules/TelegramUI/Sources/AppDelegate.swift
+++ b/submodules/TelegramUI/Sources/AppDelegate.swift
@@ -34,6 +34,7 @@ import TelegramAudio
import DebugSettingsUI
import BackgroundTasks
import UIKitRuntimeUtils
+import InAppPurchaseManager
#if canImport(AppCenter)
import AppCenter
@@ -705,6 +706,8 @@ private func extractAccountManagerState(records: AccountRecordsView(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true, removeDatabaseOnError: true)
self.accountManager = accountManager
@@ -748,7 +751,7 @@ private func extractAccountManagerState(records: AccountRecordsView Void)?
- let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, sharedContainerPath: legacyBasePath, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, rootPath: rootPath, legacyBasePath: legacyBasePath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
+ let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, sharedContainerPath: legacyBasePath, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, inAppPurchaseManager: inAppPurchaseManager, rootPath: rootPath, legacyBasePath: legacyBasePath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
setPresentationCall?(call)
}, navigateToChat: { accountId, peerId, messageId in
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
diff --git a/submodules/TelegramUI/Sources/NotificationContentContext.swift b/submodules/TelegramUI/Sources/NotificationContentContext.swift
index cd7d5d198f..b8463210e1 100644
--- a/submodules/TelegramUI/Sources/NotificationContentContext.swift
+++ b/submodules/TelegramUI/Sources/NotificationContentContext.swift
@@ -136,7 +136,7 @@ public final class NotificationViewControllerImpl {
return nil
})
- sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
+ sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), inAppPurchaseManager: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
presentationDataPromise.set(sharedAccountContext!.presentationData)
}
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
index 0e2952325b..a108ce37c8 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
@@ -2306,12 +2306,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
let colorsArray: [CGColor] = [
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xa34ecf).cgColor,
- UIColor(rgb: 0xff7923).cgColor,
- UIColor(rgb: 0xff7923).cgColor
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x6B93FF).cgColor,
+ UIColor(rgb: 0x976FFF).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor,
+ UIColor(rgb: 0xE46ACE).cgColor
]
- var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
+ var locations: [CGFloat] = [0.0, 0.35, 0.5, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
index a612fe6fa5..b86363f91d 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
@@ -67,6 +67,7 @@ import TranslateUI
import ChatPresentationInterfaceState
import CreateExternalMediaStreamScreen
import PaymentMethodUI
+import PremiumUI
protocol PeerInfoScreenItem: AnyObject {
var id: AnyHashable { get }
@@ -420,6 +421,7 @@ private enum PeerInfoSettingsSection {
case appearance
case language
case stickers
+ case premium
case passport
case watch
case support
@@ -722,6 +724,10 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
interaction.openSettings(.language)
}))
+ items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Telegram Premium", icon: PresentationResourcesSettings.premium, action: {
+ interaction.openSettings(.premium)
+ }))
+
/*items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Payment Method", icon: PresentationResourcesSettings.language, action: {
interaction.openPaymentMethod()
}))*/
@@ -6176,6 +6182,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.controller?.push(themeSettingsController(context: self.context))
case .language:
self.controller?.push(LocalizationListController(context: self.context))
+ case .premium:
+ self.controller?.push(PremiumIntroScreen(context: self.context))
case .stickers:
if let settings = self.data?.globalSettings {
self.controller?.push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in
diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift
index 2b9d4a1521..92b1b84c23 100644
--- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift
+++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift
@@ -232,7 +232,7 @@ public class ShareRootControllerImpl {
return nil
})
- let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
+ let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), inAppPurchaseManager: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
presentationDataPromise.set(sharedContext.presentationData)
internalContext = InternalContext(sharedContext: sharedContext)
globalInternalContext = internalContext
diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift
index 468dc26f76..9350011751 100644
--- a/submodules/TelegramUI/Sources/SharedAccountContext.swift
+++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift
@@ -24,6 +24,7 @@ import PresentationDataUtils
import LocationUI
import AppLock
import WallpaperBackgroundNode
+import InAppPurchaseManager
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@@ -88,6 +89,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
public let contactDataManager: DeviceContactDataManager?
public let locationManager: DeviceLocationManager?
public var callManager: PresentationCallManager?
+ public var inAppPurchaseManager: InAppPurchaseManager?
private var callDisposable: Disposable?
private var callStateDisposable: Disposable?
@@ -161,7 +163,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private var spotlightDataContext: SpotlightDataContext?
private var widgetDataContext: WidgetDataContext?
- public init(mainWindow: Window1?, sharedContainerPath: String, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal, voipNotificationToken: Signal, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
+ public init(mainWindow: Window1?, sharedContainerPath: String, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, inAppPurchaseManager: InAppPurchaseManager?, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal, voipNotificationToken: Signal, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
assert(Queue.mainQueue().isCurrent())
precondition(!testHasInstance)
@@ -175,6 +177,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.navigateToChatImpl = navigateToChat
self.displayUpgradeProgress = displayUpgradeProgress
self.appLockContext = appLockContext
+ self.inAppPurchaseManager = inAppPurchaseManager
self.accountManager.mediaBox.fetchCachedResourceRepresentation = { (resource, representation) -> Signal in
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)
diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift
index 19ec98a606..18e0cf5643 100644
--- a/submodules/TelegramUI/Sources/TelegramRootController.swift
+++ b/submodules/TelegramUI/Sources/TelegramRootController.swift
@@ -130,12 +130,6 @@ public final class TelegramRootController: NavigationController {
self.accountSettingsController = accountSettingsController
self.rootTabController = tabBarController
self.pushViewController(tabBarController, animated: false)
-
-// Queue.mainQueue().after(1.0) {
-//// let screen = PremiumLimitScreen(context: self.context, subject: .pins, action: {})
-// let screen = PremiumIntroScreen(context: self.context, action: {})
-// self.chatListController?.push(screen)
-// }
}
public func updateRootControllers(showCallsTab: Bool) {