Various improvements

This commit is contained in:
Ilya Laktyushin 2024-05-28 13:06:02 +04:00
parent 27afb74b62
commit 51b262afac
8 changed files with 83 additions and 7 deletions

View File

@ -7799,6 +7799,7 @@ Sorry for the inconvenience.";
"Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again."; "Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again.";
"Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment."; "Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment.";
"Premium.Purchase.ErrorCantMakePayments" = "In-app purchases are not allowed on this device."; "Premium.Purchase.ErrorCantMakePayments" = "In-app purchases are not allowed on this device.";
"Premium.Purchase.ErrorTryLater" = "An error occurred. Please try again.";
"Premium.Restore.Success" = "Done"; "Premium.Restore.Success" = "Done";
"Premium.Restore.ErrorUnknown" = "An error occurred. Please try again."; "Premium.Restore.ErrorUnknown" = "An error occurred. Please try again.";
@ -12286,6 +12287,7 @@ Sorry for the inconvenience.";
"Stars.Transaction.FragmentTopUp.Title" = "Stars Top-Up"; "Stars.Transaction.FragmentTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment"; "Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment";
"Stars.Transaction.Unsupported.Title" = "Unsupported"; "Stars.Transaction.Unsupported.Title" = "Unsupported";
"Stars.Transaction.Refund" = "Refund";
"Stars.Transfer.Title" = "Confirm Your Purchase"; "Stars.Transfer.Title" = "Confirm Your Purchase";
"Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?"; "Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?";

View File

@ -184,6 +184,7 @@ public final class InAppPurchaseManager: NSObject {
case notAllowed case notAllowed
case cantMakePayments case cantMakePayments
case assignFailed case assignFailed
case tryLater
} }
public enum RestoreState { public enum RestoreState {
@ -220,6 +221,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 finishedSuccessfulTransactions = Set<String>()
private var onRestoreCompletion: ((RestoreState) -> Void)? private var onRestoreCompletion: ((RestoreState) -> Void)?
private let disposableSet = DisposableDict<String>() private let disposableSet = DisposableDict<String>()
@ -315,6 +318,12 @@ public final class InAppPurchaseManager: NSObject {
mappedError = .network mappedError = .network
case .paymentNotAllowed, .clientInvalid: case .paymentNotAllowed, .clientInvalid:
mappedError = .notAllowed mappedError = .notAllowed
case .unknown:
if let _ = error.userInfo["tryLater"] {
mappedError = .tryLater
} else {
mappedError = .generic
}
default: default:
mappedError = .generic mappedError = .generic
} }
@ -400,9 +409,15 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
let transactionState: TransactionState? let transactionState: TransactionState?
switch transaction.transactionState { switch transaction.transactionState {
case .purchased: case .purchased:
if transaction.payment.productIdentifier.contains(".topup."), let transactionIdentifier = transaction.transactionIdentifier, self.finishedSuccessfulTransactions.contains(transactionIdentifier) {
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") seems to be already reported, ask to try later")
transactionState = .failed(error: SKError(SKError.Code.unknown, userInfo: ["tryLater": true]))
queue.finishTransaction(transaction)
} else {
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
transactionState = .purchased(transactionId: transaction.transactionIdentifier) transactionState = .purchased(transactionId: transaction.transactionIdentifier)
transactionsToAssign.append(transaction) transactionsToAssign.append(transaction)
}
case .restored: case .restored:
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring") Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
let transactionIdentifier = transaction.transactionIdentifier let transactionIdentifier = transaction.transactionIdentifier
@ -490,6 +505,12 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
self.debugSaveReceipt(receiptData: receiptData) self.debugSaveReceipt(receiptData: receiptData)
#endif #endif
for transaction in transactionsToAssign {
if let transactionIdentifier = transaction.transactionIdentifier {
self.finishedSuccessfulTransactions.insert(transactionIdentifier)
}
}
self.disposableSet.set( self.disposableSet.set(
(purpose (purpose
|> castError(AssignAppStoreTransactionError.self) |> castError(AssignAppStoreTransactionError.self)

View File

@ -1163,6 +1163,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
case .assignFailed: case .assignFailed:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .tryLater:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .cancelled: case .cancelled:
break break
} }

View File

@ -955,6 +955,8 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
case .assignFailed: case .assignFailed:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .tryLater:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .cancelled: case .cancelled:
break break
} }

View File

@ -3113,6 +3113,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
case .assignFailed: case .assignFailed:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .tryLater:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .cancelled: case .cancelled:
break break
} }

View File

@ -231,7 +231,7 @@ private final class StarsContextImpl {
private extension StarsContext.State.Transaction { private extension StarsContext.State.Transaction {
init?(apiTransaction: Api.StarsTransaction, transaction: Transaction) { init?(apiTransaction: Api.StarsTransaction, transaction: Transaction) {
switch apiTransaction { switch apiTransaction {
case let .starsTransaction(_, id, stars, date, transactionPeer, title, description, photo): case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo):
let parsedPeer: StarsContext.State.Transaction.Peer let parsedPeer: StarsContext.State.Transaction.Peer
switch transactionPeer { switch transactionPeer {
case .starsTransactionPeerAppStore: case .starsTransactionPeerAppStore:
@ -250,7 +250,12 @@ private extension StarsContext.State.Transaction {
} }
parsedPeer = .peer(EnginePeer(peer)) parsedPeer = .peer(EnginePeer(peer))
} }
self.init(flags: [], id: id, count: stars, date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init))
var flags: Flags = []
if (apiFlags & (1 << 3)) != 0 {
flags.insert(.isRefund)
}
self.init(flags: flags, id: id, count: stars, date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init))
} }
} }
} }

View File

@ -663,6 +663,8 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
case .assignFailed: case .assignFailed:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .tryLater:
errorText = presentationData.strings.Premium_Purchase_ErrorTryLater
case .cancelled: case .cancelled:
break break
} }

View File

@ -125,6 +125,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let additional = Child(BalancedTextComponent.self) let additional = Child(BalancedTextComponent.self)
let button = Child(SolidRoundedButtonComponent.self) let button = Child(SolidRoundedButtonComponent.self)
let refundBackgound = Child(RoundedRectangle.self)
let refundText = Child(MultilineTextComponent.self)
return { context in return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
let controller = environment.controller let controller = environment.controller
@ -172,6 +175,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let toPeer: EnginePeer? let toPeer: EnginePeer?
let transactionPeer: StarsContext.State.Transaction.Peer? let transactionPeer: StarsContext.State.Transaction.Peer?
let photo: TelegramMediaWebFile? let photo: TelegramMediaWebFile?
let isRefund: Bool
var delayedCloseOnOpenPeer = true var delayedCloseOnOpenPeer = true
switch subject { switch subject {
@ -208,6 +212,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} }
transactionPeer = transaction.peer transactionPeer = transaction.peer
photo = transaction.photo photo = transaction.photo
isRefund = transaction.flags.contains(.isRefund)
case let .receipt(receipt): case let .receipt(receipt):
titleText = receipt.invoiceMedia.title titleText = receipt.invoiceMedia.title
descriptionText = receipt.invoiceMedia.description descriptionText = receipt.invoiceMedia.description
@ -222,6 +227,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} }
transactionPeer = nil transactionPeer = nil
photo = receipt.invoiceMedia.photo photo = receipt.invoiceMedia.photo
isRefund = false
delayedCloseOnOpenPeer = false delayedCloseOnOpenPeer = false
} }
@ -455,11 +461,45 @@ private final class StarsTransactionSheetContent: CombinedComponent {
originY += description.size.height + 10.0 originY += description.size.height + 10.0
} }
let amountSpacing: CGFloat = 3.0
var totalAmountWidth: CGFloat = amount.size.width + amountSpacing + amountStar.size.width
var amountOriginX: CGFloat = floor(context.availableSize.width - totalAmountWidth) / 2.0
if isRefund {
let refundText = refundText.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: strings.Stars_Transaction_Refund,
font: Font.medium(14.0),
textColor: theme.list.itemDisclosureActions.constructive.fillColor
))
),
availableSize: context.availableSize,
transition: .immediate
)
let refundBackground = refundBackgound.update(
component: RoundedRectangle(
color: theme.list.itemDisclosureActions.constructive.fillColor.withAlphaComponent(0.1),
cornerRadius: 6.0
),
availableSize: CGSize(width: refundText.size.width + 10.0, height: refundText.size.height + 4.0),
transition: .immediate
)
totalAmountWidth += amountSpacing * 2.0 + refundBackground.size.width
amountOriginX = floor(context.availableSize.width - totalAmountWidth) / 2.0
context.add(refundBackground
.position(CGPoint(x: amountOriginX + amount.size.width + amountSpacing + amountStar.size.width + amountSpacing * 2.0 + refundBackground.size.width / 2.0, y: originY + refundBackground.size.height / 2.0))
)
context.add(refundText
.position(CGPoint(x: amountOriginX + amount.size.width + amountSpacing + amountStar.size.width + amountSpacing * 2.0 + refundBackground.size.width / 2.0, y: originY + refundBackground.size.height / 2.0))
)
}
context.add(amount context.add(amount
.position(CGPoint(x: context.availableSize.width / 2.0 - 10.0, y: originY + amount.size.height / 2.0)) .position(CGPoint(x: amountOriginX + amount.size.width / 2.0, y: originY + amount.size.height / 2.0))
) )
context.add(amountStar context.add(amountStar
.position(CGPoint(x: context.availableSize.width / 2.0 + amount.size.width / 2.0 + amountStar.size.width / 2.0 - 7.0, y: originY + amountStar.size.height / 2.0)) .position(CGPoint(x: amountOriginX + amount.size.width + amountSpacing + amountStar.size.width / 2.0, y: originY + amountStar.size.height / 2.0))
) )
originY += amount.size.height + 20.0 originY += amount.size.height + 20.0