mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
WIP: Paywall
This commit is contained in:
parent
02eebc19ca
commit
f57e82072f
@ -30,6 +30,8 @@ swift_library(
|
||||
"//Swiftgram/SGSimpleSettings:SGSimpleSettings",
|
||||
"//Swiftgram/SGStrings:SGStrings",
|
||||
"//Swiftgram/SGSwiftUI:SGSwiftUI",
|
||||
"//Swiftgram/SGIAP:SGIAP",
|
||||
"//Swiftgram/SGPayWall:SGPayWall",
|
||||
"//submodules/LegacyUI:LegacyUI",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/Postbox:Postbox",
|
||||
@ -40,7 +42,7 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/UndoUI:UndoUI"
|
||||
] + flex_dependency,
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -14,6 +14,7 @@ import PresentationDataUtils
|
||||
// Optional
|
||||
import SGSimpleSettings
|
||||
import SGLogging
|
||||
import SGPayWall
|
||||
import OverlayStatusController
|
||||
#if DEBUG
|
||||
import FLEX
|
||||
@ -265,12 +266,14 @@ struct SessionBackupManagerView: View {
|
||||
wrapperController?.present(controller, in: .window(.root), with: nil)
|
||||
|
||||
Task {
|
||||
let (view, accountsWithInfo) = await combineLatest(signal, signal2).awaitable()
|
||||
backupSessionsFromView(view, accountsWithInfo: accountsWithInfo.1)
|
||||
withAnimation {
|
||||
sessions = getBackedSessions()
|
||||
if let result = try? await combineLatest(signal, signal2).awaitable() {
|
||||
let (view, accountsWithInfo) = result
|
||||
backupSessionsFromView(view, accountsWithInfo: accountsWithInfo.1)
|
||||
withAnimation {
|
||||
sessions = getBackedSessions()
|
||||
}
|
||||
controller.dismiss()
|
||||
}
|
||||
controller.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
@ -833,13 +836,13 @@ private enum SGDebugControllerSection: Int32, SGItemListSection {
|
||||
private enum SGDebugDisclosureLink: String {
|
||||
case sessionBackupManager
|
||||
case messageFilter
|
||||
case debugIAP
|
||||
}
|
||||
|
||||
private enum SGDebugActions: String {
|
||||
case flexing
|
||||
case fileManager
|
||||
case clearRegDateCache
|
||||
case debugIAP
|
||||
}
|
||||
|
||||
private enum SGDebugToggles: String {
|
||||
@ -863,7 +866,7 @@ private func SGDebugControllerEntries(presentationData: PresentationData) -> [SG
|
||||
#if DEBUG
|
||||
entries.append(.action(id: id.count, section: .base, actionType: .flexing, text: "FLEX", kind: .generic))
|
||||
entries.append(.action(id: id.count, section: .base, actionType: .fileManager, text: "FileManager", kind: .generic))
|
||||
entries.append(.action(id: id.count, section: .base, actionType: .debugIAP, text: "Buy", kind: .generic))
|
||||
entries.append(.disclosure(id: id.count, section: .base, link: .debugIAP, text: "Pro"))
|
||||
#endif
|
||||
|
||||
if SGSimpleSettings.shared.b {
|
||||
@ -977,6 +980,22 @@ public func sgDebugController(context: AccountContext) -> ViewController {
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
case .debugIAP:
|
||||
#if DEBUG
|
||||
if #available(iOS 13.0, *) {
|
||||
if let sgIAPManager = context.sharedContext.SGIAP {
|
||||
presentControllerImpl?(sgPayWallController(presentationData: presentationData, SGIAPManager: sgIAPManager), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
} else {
|
||||
presentControllerImpl?(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: "Update OS to access this feature", timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}, action: { actionType in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -1022,10 +1041,6 @@ public func sgDebugController(context: AccountContext) -> ViewController {
|
||||
nil)
|
||||
}
|
||||
#endif
|
||||
case .debugIAP:
|
||||
#if DEBUG
|
||||
preconditionFailure("IAP")
|
||||
#endif
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -12,7 +12,8 @@ swift_library(
|
||||
deps = [
|
||||
"//Swiftgram/SGLogging:SGLogging",
|
||||
"//Swiftgram/SGConfig:SGConfig",
|
||||
"//submodules/AppBundle:AppBundle"
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2,6 +2,7 @@ import StoreKit
|
||||
import SGConfig
|
||||
import SGLogging
|
||||
import AppBundle
|
||||
import Combine
|
||||
|
||||
private final class CurrencyFormatterEntry {
|
||||
public let symbol: String
|
||||
@ -89,13 +90,16 @@ private func fractionalValueToCurrencyAmount(value: Double, currency: String) ->
|
||||
public extension Notification.Name {
|
||||
static let SGIAPHelperPurchaseNotification = Notification.Name("SGIAPPurchaseNotification")
|
||||
static let SGIAPHelperErrorNotification = Notification.Name("SGIAPErrorNotification")
|
||||
static let SGIAPHelperProductsUpdatedNotification = Notification.Name("SGIAPProductsUpdatedNotification")
|
||||
}
|
||||
|
||||
public final class SGIAP: NSObject {
|
||||
public final class SGIAPManager: NSObject {
|
||||
private var productRequest: SKProductsRequest?
|
||||
private var productsRequestCompletion: (([SKProduct]) -> Void)?
|
||||
private var purchaseCompletion: ((Bool, Error?) -> Void)?
|
||||
|
||||
public private(set) var availableProducts: [SGProduct] = []
|
||||
private var finishedSuccessfulTransactions = Set<String>()
|
||||
|
||||
public final class SGProduct: Equatable {
|
||||
private lazy var numberFormatter: NumberFormatter = {
|
||||
@ -105,7 +109,7 @@ public final class SGIAP: NSObject {
|
||||
return numberFormatter
|
||||
}()
|
||||
|
||||
let skProduct: SKProduct
|
||||
public let skProduct: SKProduct
|
||||
|
||||
init(skProduct: SKProduct) {
|
||||
self.skProduct = skProduct
|
||||
@ -198,7 +202,7 @@ public final class SGIAP: NSObject {
|
||||
|
||||
}
|
||||
|
||||
public init(foo: Bool = false) {
|
||||
public init(foo: Bool = false) { // I don't want to override init, idk why
|
||||
super.init()
|
||||
|
||||
SKPaymentQueue.default().add(self)
|
||||
@ -220,7 +224,7 @@ public final class SGIAP: NSObject {
|
||||
}
|
||||
|
||||
private func requestProducts() {
|
||||
SGLogger.shared.log("SGIAP", "Requesting products...")
|
||||
SGLogger.shared.log("SGIAP", "Requesting products for \(SG_CONFIG.iaps.count) ids...")
|
||||
let productRequest = SKProductsRequest(productIdentifiers: Set(SG_CONFIG.iaps))
|
||||
|
||||
productRequest.delegate = self
|
||||
@ -242,13 +246,18 @@ public final class SGIAP: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
extension SGIAP: SKProductsRequestDelegate {
|
||||
extension SGIAPManager: SKProductsRequestDelegate {
|
||||
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
self.productRequest = nil
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let products = response.products
|
||||
SGLogger.shared.log("SGIAP", "Received products \(products.map({ $0.productIdentifier }).joined(separator: ", "))")
|
||||
SGLogger.shared.log("SGIAP", "Received products (\(products.count)): \(products.map({ $0.productIdentifier }).joined(separator: ", "))")
|
||||
let currentlyAvailableProducts = self.availableProducts
|
||||
self.availableProducts = products.map({ SGProduct(skProduct: $0) })
|
||||
if currentlyAvailableProducts != self.availableProducts {
|
||||
NotificationCenter.default.post(name: .SGIAPHelperProductsUpdatedNotification, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,13 +267,14 @@ extension SGIAP: SKProductsRequestDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
extension SGIAP: SKPaymentTransactionObserver {
|
||||
extension SGIAPManager: SKPaymentTransactionObserver {
|
||||
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||
SGLogger.shared.log("SGIAP", "paymentQueue transactions \(transactions.count)")
|
||||
for transaction in transactions {
|
||||
SGLogger.shared.log("SGIAP", "Transaction state for \(transaction.payment.productIdentifier): \(transaction.transactionState)")
|
||||
SGLogger.shared.log("SGIAP", "Transaction \(transaction.transactionIdentifier ?? "nil") state for product \(transaction.payment.productIdentifier): \(transaction.transactionState.description)")
|
||||
switch transaction.transactionState {
|
||||
case .purchased, .restored:
|
||||
SGLogger.shared.log("SGIAP", "Sending SGIAPHelperPurchaseNotification for \(transaction.transactionIdentifier ?? "nil")")
|
||||
NotificationCenter.default.post(name: .SGIAPHelperPurchaseNotification, object: transaction)
|
||||
case .purchasing, .deferred:
|
||||
break
|
||||
@ -272,11 +282,12 @@ extension SGIAP: SKPaymentTransactionObserver {
|
||||
if let transactionError = transaction.error as NSError?,
|
||||
let localizedDescription = transaction.error?.localizedDescription,
|
||||
transactionError.code != SKError.paymentCancelled.rawValue {
|
||||
SGLogger.shared.log("SGIAP", "Transaction Error: \(localizedDescription)")
|
||||
SGLogger.shared.log("SGIAP", "Transaction Error []: \(localizedDescription)")
|
||||
}
|
||||
SGLogger.shared.log("SGIAP", "Sending SGIAPHelperErrorNotification for \(transaction.transactionIdentifier ?? "nil")")
|
||||
NotificationCenter.default.post(name: .SGIAPHelperErrorNotification, object: transaction)
|
||||
default:
|
||||
SGLogger.shared.log("SGIAP", "Unknown transaction state \(transaction.transactionState). Finishing transaction.")
|
||||
SGLogger.shared.log("SGIAP", "Unknown transaction \(transaction.transactionIdentifier ?? "nil") state \(transaction.transactionState). Finishing transaction.")
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
}
|
||||
}
|
||||
@ -326,3 +337,24 @@ public func getPurchaceReceiptData() -> Data? {
|
||||
}
|
||||
return receiptData
|
||||
}
|
||||
|
||||
|
||||
extension SKPaymentTransactionState {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .purchasing:
|
||||
return "Purchasing"
|
||||
case .purchased:
|
||||
return "Purchased"
|
||||
case .failed:
|
||||
return "Failed"
|
||||
case .restored:
|
||||
return "Restored"
|
||||
case .deferred:
|
||||
return "Deferred"
|
||||
@unknown default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
23
Swiftgram/SGPayWall/BUILD
Normal file
23
Swiftgram/SGPayWall/BUILD
Normal file
@ -0,0 +1,23 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "SGPayWall",
|
||||
module_name = "SGPayWall",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//Swiftgram/SGIAP:SGIAP",
|
||||
"//Swiftgram/SGLogging:SGLogging",
|
||||
"//Swiftgram/SGSimpleSettings:SGSimpleSettings",
|
||||
"//Swiftgram/SGSwiftUI:SGSwiftUI",
|
||||
"//Swiftgram/SGStrings:SGStrings",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
150
Swiftgram/SGPayWall/Sources/SGPayWall.swift
Normal file
150
Swiftgram/SGPayWall/Sources/SGPayWall.swift
Normal file
@ -0,0 +1,150 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SGSwiftUI
|
||||
import SGIAP
|
||||
import TelegramPresentationData
|
||||
import LegacyUI
|
||||
import Display
|
||||
import SGConfig
|
||||
// import SGStrings
|
||||
|
||||
|
||||
struct SGPerk: Identifiable {
|
||||
let id = UUID()
|
||||
let title: String
|
||||
let description: String
|
||||
let icon: String
|
||||
}
|
||||
|
||||
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
struct SGPayWallView: View {
|
||||
|
||||
weak var wrapperController: LegacyController?
|
||||
let SGIAP: SGIAPManager
|
||||
|
||||
let perks = [
|
||||
SGPerk(title: "Premium Features", description: "Access all premium features and tools", icon: "star.fill"),
|
||||
SGPerk(title: "No Ads", description: "Enjoy an ad-free experience", icon: "banner.slash"),
|
||||
SGPerk(title: "Cloud Sync", description: "Sync your data across all devices", icon: "cloud.fill"),
|
||||
SGPerk(title: "Priority Support", description: "Get priority customer support", icon: "questionmark.circle.fill"),
|
||||
SGPerk(title: "Advanced Stats", description: "Access detailed analytics and insights", icon: "chart.bar.fill"),
|
||||
SGPerk(title: "Premium Features", description: "Access all premium features and tools", icon: "star.fill"),
|
||||
SGPerk(title: "No Ads", description: "Enjoy an ad-free experience", icon: "banner.slash"),
|
||||
SGPerk(title: "Cloud Sync", description: "Sync your data across all devices", icon: "cloud.fill"),
|
||||
SGPerk(title: "Priority Support", description: "Get priority customer support", icon: "questionmark.circle.fill"),
|
||||
SGPerk(title: "Advanced Stats", description: "Access detailed analytics and insights", icon: "chart.bar.fill"),
|
||||
SGPerk(title: "Advanced Stats", description: "Access detailed analytics and insights", icon: "chart.bar.fill"),
|
||||
SGPerk(title: "Premium Features", description: "Access all premium features and tools", icon: "star.fill"),
|
||||
SGPerk(title: "No Ads", description: "Enjoy an ad-free experience", icon: "banner.slash"),
|
||||
SGPerk(title: "Cloud Sync", description: "Sync your data across all devices", icon: "cloud.fill"),
|
||||
SGPerk(title: "Priority Support", description: "Get priority customer support", icon: "questionmark.circle.fill"),
|
||||
SGPerk(title: "Advanced Stats", description: "Access detailed analytics and insights", icon: "chart.bar.fill"),
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack(spacing: 0) {
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
ForEach(perks) { perk in
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: perk.icon)
|
||||
.font(.title)
|
||||
.foregroundColor(.blue)
|
||||
.frame(width: 32)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(perk.title)
|
||||
.font(.headline)
|
||||
Text(perk.description)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 24)
|
||||
}
|
||||
|
||||
VStack(spacing: 12) {
|
||||
Button(action: {
|
||||
for availableProduct in SGIAP.availableProducts {
|
||||
if SG_CONFIG.iaps.contains(availableProduct.skProduct.productIdentifier ) {
|
||||
SGIAP.purchaseProduct(availableProduct, completion: { _ in })
|
||||
}
|
||||
}
|
||||
SGIAP.buyProduct(product: product)
|
||||
}) {
|
||||
Text("Unlock Premium - $9.99")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.blue)
|
||||
.cornerRadius(16)
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
SGIAP.restorePurchases(completion: { _ in })
|
||||
}) {
|
||||
Text("Restore Purchases")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
.padding(24)
|
||||
.background(
|
||||
Rectangle()
|
||||
.fill(Color(UIColor.systemBackground))
|
||||
.shadow(radius: 8, y: -4)
|
||||
)
|
||||
}.navigationBarItems(trailing: closeButtonView)
|
||||
|
||||
}
|
||||
.colorScheme(.dark)
|
||||
}
|
||||
|
||||
private var closeButtonView: some View {
|
||||
Button(action: {
|
||||
wrapperController?.dismiss(animated: true)
|
||||
}) {
|
||||
if #available(iOS 15.0, *) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.headline)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.headline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
public func sgPayWallController(presentationData: PresentationData? = nil, SGIAPManager: SGIAPManager) -> ViewController {
|
||||
// let theme = presentationData?.theme ?? (UITraitCollection.current.userInterfaceStyle == .dark ? defaultDarkColorPresentationTheme : defaultPresentationTheme)
|
||||
let theme = defaultDarkColorPresentationTheme
|
||||
let strings = presentationData?.strings ?? defaultPresentationStrings
|
||||
|
||||
let legacyController = LegacySwiftUIController(
|
||||
presentation: .modal(animateIn: true),
|
||||
theme: theme,
|
||||
strings: strings
|
||||
)
|
||||
// legacyController.displayNavigationBar = false
|
||||
legacyController.statusBar.statusBarStyle = .White
|
||||
legacyController.attemptNavigation = { _ in return false }
|
||||
let swiftUIView = SGPayWallView(wrapperController: legacyController, SGIAP: SGIAPManager)
|
||||
let controller = UIHostingController(rootView: swiftUIView, ignoreSafeArea: true)
|
||||
legacyController.bind(controller: controller)
|
||||
|
||||
return legacyController
|
||||
}
|
@ -36,7 +36,11 @@ public struct SignalError<E>: Error {
|
||||
}
|
||||
}
|
||||
|
||||
// Extension for Signals with Error types
|
||||
public struct SignalCompleted: Error {}
|
||||
|
||||
// Extension for Signals
|
||||
// NoError can be marked a
|
||||
// try? await signal.awaitable()
|
||||
extension Signal {
|
||||
@available(iOS 13.0, *)
|
||||
public func awaitable(file: String = #file, line: Int = #line) async throws -> T {
|
||||
@ -57,46 +61,26 @@ extension Signal {
|
||||
disposable?.dispose()
|
||||
},
|
||||
error: { error in
|
||||
if let error = error as? Error {
|
||||
continuation.resume(throwing: error)
|
||||
} else {
|
||||
continuation.resume(throwing: SignalError(error))
|
||||
}
|
||||
disposable?.dispose()
|
||||
},
|
||||
completed: {
|
||||
disposable?.dispose()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extension for Signals with NoError
|
||||
extension Signal where E == NoError {
|
||||
@available(iOS 13.0, *)
|
||||
public func awaitable(file: String = #file, line: Int = #line) async -> T {
|
||||
return await withCheckedContinuation { continuation in
|
||||
var disposable: Disposable?
|
||||
let hasResumed = Atomic<Bool>(value: false)
|
||||
disposable = self.start(
|
||||
next: { value in
|
||||
if !hasResumed.with({ $0 }) {
|
||||
let _ = hasResumed.swap(true)
|
||||
continuation.resume(returning: value)
|
||||
if let error = error as? Error {
|
||||
continuation.resume(throwing: error)
|
||||
} else {
|
||||
continuation.resume(throwing: SignalError(error))
|
||||
}
|
||||
} else {
|
||||
#if DEBUG
|
||||
// Consider using awaitableStream() or |> take(1)
|
||||
assertionFailure("awaitable Signal emitted more than one value. \(file):\(line)")
|
||||
// I don't even know what we should consider here. awaitableStream?
|
||||
assertionFailure("awaitable Signal emitted an error after a value. \(file):\(line)")
|
||||
#endif
|
||||
}
|
||||
disposable?.dispose()
|
||||
},
|
||||
error: { _ in
|
||||
// This will never be called for NoError
|
||||
disposable?.dispose()
|
||||
},
|
||||
completed: {
|
||||
if !hasResumed.with({ $0 }) {
|
||||
let _ = hasResumed.swap(true)
|
||||
continuation.resume(throwing: SignalCompleted())
|
||||
}
|
||||
disposable?.dispose()
|
||||
}
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
sgdeps = [
|
||||
"//Swiftgram/SGSimpleSettings:SGSimpleSettings"
|
||||
"//Swiftgram/SGSimpleSettings:SGSimpleSettings",
|
||||
"//Swiftgram/SGIAP:SGIAP"
|
||||
]
|
||||
|
||||
swift_library(
|
||||
|
@ -1,4 +1,5 @@
|
||||
import SGSimpleSettings
|
||||
import SGIAP
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
@ -935,6 +936,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
var immediateExperimentalUISettings: ExperimentalUISettings { get }
|
||||
// MARK: Swiftgram
|
||||
var immediateSGStatus: SGStatus { get }
|
||||
var SGIAP: SGIAPManager? { get }
|
||||
var currentInAppNotificationSettings: Atomic<InAppNotificationSettings> { get }
|
||||
var currentMediaInputSettings: Atomic<MediaInputSettings> { get }
|
||||
var currentStickerSettings: Atomic<StickerSettings> { get }
|
||||
|
@ -19,6 +19,7 @@ sgdeps = [
|
||||
"//Swiftgram/SGDebugUI:SGDebugUI",
|
||||
"//Swiftgram/SGInputToolbar:SGInputToolbar",
|
||||
"//Swiftgram/SGIAP:SGIAP",
|
||||
"//Swiftgram/SGPayWall:SGPayWall"
|
||||
# "//Swiftgram/SGContentAnalysis:SGContentAnalysis"
|
||||
]
|
||||
|
||||
|
@ -1348,6 +1348,12 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
authContextReadyDisposable.set(nil)
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
// MARK: Swiftgram
|
||||
if #available(iOS 13.0, *) {
|
||||
self.setupIAP()
|
||||
}
|
||||
|
||||
|
||||
let logoutDataSignal: Signal<(AccountManager, Set<PeerId>), NoError> = self.sharedContextPromise.get()
|
||||
@ -3067,22 +3073,24 @@ extension AppDelegate {
|
||||
|
||||
func setupIAP() {
|
||||
NotificationCenter.default.addObserver(forName: .SGIAPHelperPurchaseNotification, object: nil, queue: nil) { [weak self] notification in
|
||||
SGLogger.shared.log("SGIAP", "Got SGIAPHelperPurchaseNotification")
|
||||
guard let strongSelf = self else { return }
|
||||
if let transaction = notification.object as? SKPaymentTransaction {
|
||||
let _ = (strongSelf.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
SGLogger.shared.log("SGIAP", "Got context for SGIAPHelperPurchaseNotification")
|
||||
guard let context = context else {
|
||||
SGLogger.shared.log("SGIAP", "Empty app context (how?)")
|
||||
|
||||
SGLogger.shared.log("SGIAP", "Finishing transaction")
|
||||
SGLogger.shared.log("SGIAP", "Finishing transaction \(transaction.transactionIdentifier ?? "nil")")
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
return
|
||||
}
|
||||
let _ = Task {
|
||||
await strongSelf.sendReceiptForVerification(primaryContext: context.context)
|
||||
await strongSelf.fetchSGStatus(primaryContext: context.context)
|
||||
SGLogger.shared.log("SGIAP", "Finishing transaction")
|
||||
SGLogger.shared.log("SGIAP", "Finishing transaction \(transaction.transactionIdentifier ?? "nil")")
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
}
|
||||
})
|
||||
@ -3100,7 +3108,7 @@ extension AppDelegate {
|
||||
SGSimpleSettings.shared.primaryUserId = String(primaryUserId)
|
||||
}
|
||||
|
||||
var primaryContext = await getContextForUserId(context: context, userId: primaryUserId).awaitable()
|
||||
var primaryContext = try? await getContextForUserId(context: context, userId: primaryUserId).awaitable()
|
||||
if let primaryContext = primaryContext {
|
||||
SGLogger.shared.log("SGIAP", "Got primary context for user id: \(primaryContext.account.peerId.id._internalGetInt64Value())")
|
||||
return primaryContext
|
||||
@ -3136,8 +3144,10 @@ extension AppDelegate {
|
||||
if let deviceToken, let apiToken {
|
||||
do {
|
||||
let _ = try await postSGReceipt(token: apiToken,
|
||||
deviceToken: deviceToken,
|
||||
encodedReceiptData: encodedReceiptData).awaitable()
|
||||
deviceToken: deviceToken,
|
||||
encodedReceiptData: encodedReceiptData).awaitable()
|
||||
} catch let error as SignalCompleted {
|
||||
let _ = error
|
||||
} catch {
|
||||
SGLogger.shared.log("SGIAP", "Error: \(error)")
|
||||
}
|
||||
@ -3155,7 +3165,7 @@ extension AppDelegate {
|
||||
SGLogger.shared.log("SGIAP", "Asking user id \(userId) to keep connection: true")
|
||||
primaryContext.account.network.shouldKeepConnection.set(.single(true))
|
||||
}
|
||||
let iqtpResponse = await sgIqtpQuery(engine: primaryContext.engine, query: makeIqtpQuery(0, "s")).awaitable()
|
||||
let iqtpResponse = try? await sgIqtpQuery(engine: primaryContext.engine, query: makeIqtpQuery(0, "s")).awaitable()
|
||||
guard let iqtpResponse = iqtpResponse else {
|
||||
SGLogger.shared.log("SGIAP", "IQTP response is nil!")
|
||||
// if !currentShouldKeepConnection {
|
||||
@ -3165,7 +3175,7 @@ extension AppDelegate {
|
||||
return
|
||||
}
|
||||
SGLogger.shared.log("SGIAP", "Got IQTP response: \(iqtpResponse)")
|
||||
let _ = await updateSGStatusInteractively(accountManager: primaryContext.sharedContext.accountManager, { value in
|
||||
let _ = try? await updateSGStatusInteractively(accountManager: primaryContext.sharedContext.accountManager, { value in
|
||||
var value = value
|
||||
|
||||
let newStatus: Int64
|
||||
|
@ -243,12 +243,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
private var experimentalUISettingsDisposable: Disposable?
|
||||
|
||||
// MARK: Swiftgram
|
||||
private var immediateSGStatusValue = Atomic<SGStatus>(value: SGStatus.default)
|
||||
public var immediateSGStatus: SGStatus {
|
||||
return self.immediateSGStatusValue.with { $0 }
|
||||
}
|
||||
private var sgStatusDisposable: Disposable?
|
||||
|
||||
public var SGIAP: SGIAPManager?
|
||||
|
||||
public var presentGlobalController: (ViewController, Any?) -> Void = { _, _ in }
|
||||
public var presentCrossfadeController: () -> Void = {}
|
||||
@ -485,6 +486,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
let _ = immediateSGStatusValue.swap(settings)
|
||||
}
|
||||
})
|
||||
self.initSGIAP(isMainApp: applicationBindings.isMainApp)
|
||||
//
|
||||
|
||||
let _ = self.contactDataManager?.personNameDisplayOrder().start(next: { order in
|
||||
let _ = updateContactSettingsInteractively(accountManager: accountManager, { settings in
|
||||
@ -3037,3 +3040,14 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: Swiftgram
|
||||
extension SharedAccountContextImpl {
|
||||
func initSGIAP(isMainApp: Bool) {
|
||||
if isMainApp {
|
||||
self.SGIAP = SGIAPManager()
|
||||
} else {
|
||||
self.SGIAP = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user