mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Various fixes
This commit is contained in:
parent
041b37b9af
commit
3e8e152ca5
@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
case notAllowed
|
case notAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum RestoreState {
|
||||||
|
case succeed
|
||||||
|
case failed
|
||||||
|
}
|
||||||
|
|
||||||
private final class PaymentTransactionContext {
|
private final class PaymentTransactionContext {
|
||||||
var state: SKPaymentTransactionState?
|
var state: SKPaymentTransactionState?
|
||||||
let subscriber: (TransactionState) -> Void
|
let subscriber: (TransactionState) -> Void
|
||||||
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
private let stateQueue = Queue()
|
private let stateQueue = Queue()
|
||||||
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
||||||
|
|
||||||
|
private var onRestoreCompletion: ((RestoreState) -> Void)?
|
||||||
|
|
||||||
private let disposableSet = DisposableDict<String>()
|
private let disposableSet = DisposableDict<String>()
|
||||||
|
|
||||||
public init(engine: TelegramEngine, premiumProductId: String) {
|
public init(engine: TelegramEngine, premiumProductId: String) {
|
||||||
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
guard !self.premiumProductId.isEmpty else {
|
guard !self.premiumProductId.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Requesting products")
|
||||||
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
||||||
productRequest.delegate = self
|
productRequest.delegate = self
|
||||||
productRequest.start()
|
productRequest.start()
|
||||||
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
return self.productsPromise.get()
|
return self.productsPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func restorePurchases(completion: @escaping (RestoreState) -> Void) {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Restoring purchases")
|
||||||
|
self.onRestoreCompletion = completion
|
||||||
|
|
||||||
|
let paymentQueue = SKPaymentQueue.default()
|
||||||
|
paymentQueue.restoreCompletedTransactions()
|
||||||
|
}
|
||||||
|
|
||||||
public func finishAllTransactions() {
|
public func finishAllTransactions() {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
|
||||||
|
|
||||||
let paymentQueue = SKPaymentQueue.default()
|
let paymentQueue = SKPaymentQueue.default()
|
||||||
let transactions = paymentQueue.transactions
|
let transactions = paymentQueue.transactions
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Buying product: \(product.skProduct.productIdentifier), price \(product.price)")
|
||||||
|
|
||||||
let payment = SKPayment(product: product.skProduct)
|
let payment = SKPayment(product: product.skProduct)
|
||||||
SKPaymentQueue.default().add(payment)
|
SKPaymentQueue.default().add(payment)
|
||||||
|
|
||||||
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
|
|||||||
self.productRequest = nil
|
self.productRequest = nil
|
||||||
|
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
self.productsPromise.set(.single(response.products.map { Product(skProduct: $0) }))
|
let products = response.products.map { Product(skProduct: $0) }
|
||||||
|
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Received products \(products.map({ $0.skProduct.productIdentifier }).joined(separator: ", "))")
|
||||||
|
self.productsPromise.set(.single(products))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,37 +210,46 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
let transactionState: TransactionState?
|
let transactionState: TransactionState?
|
||||||
switch transaction.transactionState {
|
switch transaction.transactionState {
|
||||||
case .purchased:
|
case .purchased:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
|
||||||
let transactionIdentifier = transaction.transactionIdentifier
|
let transactionIdentifier = transaction.transactionIdentifier
|
||||||
transactionState = .purchased(transactionId: transactionIdentifier)
|
transactionState = .purchased(transactionId: transactionIdentifier)
|
||||||
if let transactionIdentifier = transactionIdentifier {
|
if let transactionIdentifier = transactionIdentifier {
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}, completed: {
|
}, completed: {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}),
|
}),
|
||||||
forKey: transactionIdentifier
|
forKey: transactionIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .restored:
|
case .restored:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
|
||||||
let transactionIdentifier = transaction.transactionIdentifier
|
let transactionIdentifier = transaction.transactionIdentifier
|
||||||
transactionState = .restored(transactionId: transactionIdentifier)
|
transactionState = .restored(transactionId: transactionIdentifier)
|
||||||
if let transactionIdentifier = transactionIdentifier {
|
if let transactionIdentifier = transactionIdentifier {
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
|
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}, completed: {
|
}, completed: {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}),
|
}),
|
||||||
forKey: transactionIdentifier
|
forKey: transactionIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .failed:
|
case .failed:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
|
||||||
transactionState = .failed(error: transaction.error as? SKError)
|
transactionState = .failed(error: transaction.error as? SKError)
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
case .purchasing:
|
case .purchasing:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
||||||
transactionState = .purchasing
|
transactionState = .purchasing
|
||||||
case .deferred:
|
case .deferred:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
|
||||||
transactionState = .deferred
|
transactionState = .deferred
|
||||||
default:
|
default:
|
||||||
transactionState = nil
|
transactionState = nil
|
||||||
@ -230,4 +262,20 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||||
|
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished")
|
||||||
|
onRestoreCompletion(.succeed)
|
||||||
|
self.onRestoreCompletion = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
||||||
|
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")")
|
||||||
|
onRestoreCompletion(.failed)
|
||||||
|
self.onRestoreCompletion = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -382,6 +382,8 @@ private final class SectionGroupComponent: Component {
|
|||||||
buttonView = current
|
buttonView = current
|
||||||
} else {
|
} else {
|
||||||
buttonView = HighlightTrackingButton()
|
buttonView = HighlightTrackingButton()
|
||||||
|
buttonView.isMultipleTouchEnabled = false
|
||||||
|
buttonView.isExclusiveTouch = true
|
||||||
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
||||||
self.buttonViews[item.content.id] = buttonView
|
self.buttonViews[item.content.id] = buttonView
|
||||||
self.addSubview(buttonView)
|
self.addSubview(buttonView)
|
||||||
|
|||||||
@ -751,6 +751,8 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.buttonNode = HighlightTrackingButton()
|
self.buttonNode = HighlightTrackingButton()
|
||||||
|
self.buttonNode.isMultipleTouchEnabled = false
|
||||||
|
self.buttonNode.isExclusiveTouch = true
|
||||||
|
|
||||||
self.titleNode = ImmediateTextView()
|
self.titleNode = ImmediateTextView()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
|||||||
@ -156,7 +156,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||||
titleRightIcon = .mute
|
if titleCredibilityIcon != .verified {
|
||||||
|
titleRightIcon = .mute
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||||
|
|
||||||
|
var titleHorizontalOffset: CGFloat = 0.0
|
||||||
if let image = self.titleCredibilityIconNode.image {
|
if let image = self.titleCredibilityIconNode.image {
|
||||||
|
titleHorizontalOffset = -(image.size.width + 4.0) / 2.0
|
||||||
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||||
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||||
}
|
}
|
||||||
@ -2629,14 +2631,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
|
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
|
||||||
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||||
} else {
|
} else {
|
||||||
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
||||||
|
|
||||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||||
if usernameSize.width == 0.0 {
|
if usernameSize.width == 0.0 {
|
||||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||||
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||||
} else {
|
} else {
|
||||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||||
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2851,10 +2853,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
neutralTitleScale = 0.7
|
neutralTitleScale = 0.7
|
||||||
neutralSubtitleScale = 1.0
|
neutralSubtitleScale = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
|
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
|
||||||
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
|
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
|
||||||
|
|
||||||
|
var titleFrame = titleFrame
|
||||||
|
if !self.isAvatarExpanded {
|
||||||
|
titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
||||||
let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY)
|
let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY)
|
||||||
|
|
||||||
@ -2885,7 +2892,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
subtitleOffset = titleCollapseFraction * -2.0
|
subtitleOffset = titleCollapseFraction * -2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let rawTitleFrame = titleFrame
|
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||||
self.titleNodeRawContainer.frame = rawTitleFrame
|
self.titleNodeRawContainer.frame = rawTitleFrame
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||||
let rawSubtitleFrame = subtitleFrame
|
let rawSubtitleFrame = subtitleFrame
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user