Various improvements

This commit is contained in:
Ilya Laktyushin 2022-05-13 05:30:49 +04:00
parent 81f179be9d
commit 29aff4a8a6
36 changed files with 608 additions and 161 deletions

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -22,6 +22,7 @@ swift_library(
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
"//submodules/MeshAnimationCache:MeshAnimationCache",
"//submodules/Utils/RangeSet:RangeSet",
"//submodules/InAppPurchaseManager:InAppPurchaseManager",
],
visibility = [
"//visibility:public",

View File

@ -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 }

View File

@ -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",

View File

@ -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;

View File

@ -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,

View File

@ -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",
],
)

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View File

@ -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<PurchaseResult, PurchaseError> {
let payment = SKMutablePayment(product: product.skProduct)
payment.applicationUsername = "\(account.peerId.id._internalGetInt64Value())"
SKPaymentQueue.default().add(payment)
let productIdentifier = payment.productIdentifier
let signal = Signal<PurchaseResult, PurchaseError> { 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)
}
}
}
}
}

View File

@ -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",

View File

@ -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) {

View File

@ -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)

View File

@ -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())

View File

@ -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()
}

View File

@ -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:

View File

@ -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

View File

@ -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
}
}
}
}

View File

@ -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])

View File

@ -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<Api.messages.TranscribedAudio>) {
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<Api.messages.TranslatedText>) {
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<Api.Updates>) {
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<Api.Bool>) {
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]?

View File

@ -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

View File

@ -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, _, _, _, _):

View File

@ -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<Never, AssignAppStoreTransactionError> {
return account.network.request(Api.functions.payments.assignAppStoreTransaction(transactionId: transactionId))
|> mapError { _ -> AssignAppStoreTransactionError in
return .generic
}
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
account.stateManager.addUpdates(updates)
return .never()
}
}

View File

@ -36,5 +36,9 @@ public extension TelegramEngine {
public func clearBotPaymentInfo(info: BotPaymentInfo) -> Signal<Void, NoError> {
return _internal_clearBotPaymentInfo(network: self.account.network, info: info)
}
public func assignAppStoreTransaction(transactionId: String) -> Signal<Never, AssignAppStoreTransactionError> {
return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId)
}
}
}

View File

@ -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())

View File

@ -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())

View File

@ -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")

View File

@ -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,

View File

@ -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<TelegramAcco
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()
})
let inAppPurchaseManager = InAppPurchaseManager(premiumProductId: buildConfig.premiumIAPProductId)
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true, removeDatabaseOnError: true)
self.accountManager = accountManager
@ -748,7 +751,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
})
var setPresentationCall: ((PresentationCall?) -> 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)

View File

@ -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)
}

View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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<TelegramAccountManagerTypes>, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, 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<TelegramAccountManagerTypes>, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, inAppPurchaseManager: InAppPurchaseManager?, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, 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<CachedMediaResourceRepresentationResult, NoError> in
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)

View File

@ -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) {