Merge commit 'c30fd83ee371a2406f9bf5ad5ec284f80de64823'

This commit is contained in:
Isaac 2024-05-28 17:50:58 +04:00
commit a8dea962b3
19 changed files with 238 additions and 72 deletions

View File

@ -7799,6 +7799,7 @@ Sorry for the inconvenience.";
"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.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.ErrorUnknown" = "An error occurred. Please try again.";
@ -12246,6 +12247,7 @@ Sorry for the inconvenience.";
"Stars.Intro.Transaction.FragmentTopUp.Title" = "Stars Top-Up";
"Stars.Intro.Transaction.FragmentTopUp.Subtitle" = "via Fragment";
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
"Stars.Intro.Transaction.Refund" = "Refund";
"Stars.Intro.PurchasedTitle" = "Stars Acquired";
"Stars.Intro.PurchasedText" = "**%@** added to your balance.";
@ -12269,6 +12271,7 @@ Sorry for the inconvenience.";
"Stars.Transaction.Via" = "Via";
"Stars.Transaction.To" = "To";
"Stars.Transaction.From" = "From";
"Stars.Transaction.Id" = "Transaction ID";
"Stars.Transaction.Date" = "Date";
"Stars.Transaction.Terms" = "Review the [Terms of Service]() for Stars.";
@ -12284,6 +12287,7 @@ Sorry for the inconvenience.";
"Stars.Transaction.FragmentTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment";
"Stars.Transaction.Unsupported.Title" = "Unsupported";
"Stars.Transaction.Refund" = "Refund";
"Stars.Transfer.Title" = "Confirm Your Purchase";
"Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?";

View File

@ -1043,7 +1043,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsTransactionsScreen(context: AccountContext, starsContext: StarsContext) -> ViewController
func makeStarsPurchaseScreen(context: AccountContext, starsContext: StarsContext, options: [StarsTopUpOption], peerId: EnginePeer.Id?, requiredStars: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>) -> ViewController
func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController
func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction) -> ViewController
func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt) -> ViewController

View File

@ -184,6 +184,7 @@ public final class InAppPurchaseManager: NSObject {
case notAllowed
case cantMakePayments
case assignFailed
case tryLater
}
public enum RestoreState {
@ -219,6 +220,8 @@ public final class InAppPurchaseManager: NSObject {
private let stateQueue = Queue()
private var paymentContexts: [String: PaymentTransactionContext] = [:]
private var finishedSuccessfulTransactions = Set<String>()
private var onRestoreCompletion: ((RestoreState) -> Void)?
@ -315,6 +318,12 @@ public final class InAppPurchaseManager: NSObject {
mappedError = .network
case .paymentNotAllowed, .clientInvalid:
mappedError = .notAllowed
case .unknown:
if let _ = error.userInfo["tryLater"] {
mappedError = .tryLater
} else {
mappedError = .generic
}
default:
mappedError = .generic
}
@ -400,9 +409,15 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
let transactionState: TransactionState?
switch transaction.transactionState {
case .purchased:
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
transactionState = .purchased(transactionId: transaction.transactionIdentifier)
transactionsToAssign.append(transaction)
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")
transactionState = .purchased(transactionId: transaction.transactionIdentifier)
transactionsToAssign.append(transaction)
}
case .restored:
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
let transactionIdentifier = transaction.transactionIdentifier
@ -490,6 +505,12 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
self.debugSaveReceipt(receiptData: receiptData)
#endif
for transaction in transactionsToAssign {
if let transactionIdentifier = transaction.transactionIdentifier {
self.finishedSuccessfulTransactions.insert(transactionIdentifier)
}
}
self.disposableSet.set(
(purpose
|> castError(AssignAppStoreTransactionError.self)

View File

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

View File

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

View File

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

View File

@ -199,11 +199,18 @@ private final class StarsContextImpl {
return
}
var transactions = state.transactions
transactions.insert(.init(id: "tmp_\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil), at: 0)
transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil), at: 0)
self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: state.balance + balance, transactions: transactions, canLoadMore: state.canLoadMore, isLoading: state.isLoading))
}
fileprivate func updateBalance(_ balance: Int64) {
guard let state = self._state else {
return
}
self.updateState(StarsContext.State(flags: [], balance: balance, transactions: state.transactions, canLoadMore: state.canLoadMore, isLoading: state.isLoading))
}
func loadMore() {
assert(Queue.mainQueue().isCurrent())
@ -231,7 +238,7 @@ private final class StarsContextImpl {
private extension StarsContext.State.Transaction {
init?(apiTransaction: Api.StarsTransaction, transaction: Transaction) {
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
switch transactionPeer {
case .starsTransactionPeerAppStore:
@ -250,7 +257,12 @@ private extension StarsContext.State.Transaction {
}
parsedPeer = .peer(EnginePeer(peer))
}
self.init(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))
}
}
}
@ -258,6 +270,17 @@ private extension StarsContext.State.Transaction {
public final class StarsContext {
public struct State: Equatable {
public struct Transaction: Equatable {
public struct Flags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let isRefund = Flags(rawValue: 1 << 0)
public static let isLocal = Flags(rawValue: 1 << 1)
}
public enum Peer: Equatable {
case appStore
case playMarket
@ -267,6 +290,7 @@ public final class StarsContext {
case peer(EnginePeer)
}
public let flags: Flags
public let id: String
public let count: Int64
public let date: Int32
@ -276,6 +300,7 @@ public final class StarsContext {
public let photo: TelegramMediaWebFile?
public init(
flags: Flags,
id: String,
count: Int64,
date: Int32,
@ -284,6 +309,7 @@ public final class StarsContext {
description: String?,
photo: TelegramMediaWebFile?
) {
self.flags = flags
self.id = id
self.count = count
self.date = date
@ -374,6 +400,13 @@ public final class StarsContext {
}
}
fileprivate func updateBalance(_ balance: Int64) {
self.impl.with {
$0.updateBalance(balance)
}
}
public func load(force: Bool) {
self.impl.with {
$0.load(force: force)
@ -395,6 +428,7 @@ public final class StarsContext {
private final class StarsTransactionsContextImpl {
private let account: Account
private weak var starsContext: StarsContext?
private let peerId: EnginePeer.Id
private let subject: StarsTransactionsContext.Subject
@ -412,6 +446,7 @@ private final class StarsTransactionsContextImpl {
assert(Queue.mainQueue().isCurrent())
self.account = account
self.starsContext = starsContext
self.peerId = starsContext.peerId
self.subject = subject
@ -449,13 +484,13 @@ private final class StarsTransactionsContextImpl {
if filteredTransactions != initialTransactions {
var existingIds = Set<String>()
for transaction in self._state.transactions {
if !transaction.id.hasPrefix("tmp_") {
if !transaction.flags.contains(.isLocal) {
existingIds.insert(transaction.id)
}
}
var updatedState = self._state
updatedState.transactions.removeAll(where: { $0.id.hasPrefix("tmp_") })
updatedState.transactions.removeAll(where: { $0.flags.contains(.isLocal) })
for transaction in filteredTransactions.reversed() {
if !existingIds.contains(transaction.id) {
updatedState.transactions.insert(transaction, at: 0)
@ -499,6 +534,8 @@ private final class StarsTransactionsContextImpl {
updatedState.isLoading = false
updatedState.canLoadMore = self.nextOffset != nil
self.updateState(updatedState)
self.starsContext?.updateBalance(status.balance)
}))
}

View File

@ -11930,8 +11930,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
self.chatLocation = .peer(id: peerId)
}
if isSettings {
self.starsContext = context.starsContext
if isSettings, let starsContext = context.starsContext {
self.starsContext = starsContext
starsContext.load()
} else {
self.starsContext = nil
}

View File

@ -166,7 +166,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
}
static var body: Body {
let overscroll = Child(Rectangle.self)
// let overscroll = Child(Rectangle.self)
// let fade = Child(RoundedRectangle.self)
let text = Child(BalancedTextComponent.self)
let list = Child(VStack<Empty>.self)
@ -188,21 +188,21 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
let sideInsets = sideInset * 2.0 + environment.safeInsets.left + environment.safeInsets.right
var size = CGSize(width: context.availableSize.width, height: 0.0)
var topBackgroundColor = theme.list.plainBackgroundColor
let bottomBackgroundColor = theme.list.blocksBackgroundColor
if theme.overallDarkAppearance {
topBackgroundColor = bottomBackgroundColor
}
let overscroll = overscroll.update(
component: Rectangle(color: topBackgroundColor),
availableSize: CGSize(width: context.availableSize.width, height: 1000),
transition: context.transition
)
context.add(overscroll
.position(CGPoint(x: overscroll.size.width / 2.0, y: -overscroll.size.height / 2.0))
)
// var topBackgroundColor = theme.list.plainBackgroundColor
// let bottomBackgroundColor = theme.list.blocksBackgroundColor
// if theme.overallDarkAppearance {
// topBackgroundColor = bottomBackgroundColor
// }
//
// let overscroll = overscroll.update(
// component: Rectangle(color: topBackgroundColor),
// availableSize: CGSize(width: context.availableSize.width, height: 1000),
// transition: context.transition
// )
// context.add(overscroll
// .position(CGPoint(x: overscroll.size.width / 2.0, y: -overscroll.size.height / 2.0))
// )
//
// let fade = fade.update(
// component: RoundedRectangle(
// colors: [
@ -663,6 +663,8 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
case .assignFailed:
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
case .tryLater:
errorText = presentationData.strings.Premium_Purchase_ErrorTryLater
case .cancelled:
break
}

View File

@ -8,11 +8,11 @@ import MultilineTextComponent
import TelegramPresentationData
import PresentationDataUtils
import SolidRoundedButtonComponent
import AnimatedTextComponent
final class StarsBalanceComponent: Component {
let theme: PresentationTheme
let strings: PresentationStrings
let dateTimeFormat: PresentationDateTimeFormat
let count: Int64
let purchaseAvailable: Bool
let buy: () -> Void
@ -20,12 +20,14 @@ final class StarsBalanceComponent: Component {
init(
theme: PresentationTheme,
strings: PresentationStrings,
dateTimeFormat: PresentationDateTimeFormat,
count: Int64,
purchaseAvailable: Bool,
buy: @escaping () -> Void
) {
self.theme = theme
self.strings = strings
self.dateTimeFormat = dateTimeFormat
self.count = count
self.purchaseAvailable = purchaseAvailable
self.buy = buy
@ -38,6 +40,9 @@ final class StarsBalanceComponent: Component {
if lhs.strings !== rhs.strings {
return false
}
if lhs.dateTimeFormat != rhs.dateTimeFormat {
return false
}
if lhs.purchaseAvailable != rhs.purchaseAvailable {
return false
}
@ -68,26 +73,17 @@ final class StarsBalanceComponent: Component {
}
func update(component: StarsBalanceComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let isFirstTime = self.component == nil
self.component = component
let sideInset: CGFloat = 16.0
var contentHeight: CGFloat = sideInset
var animatedTextItems: [AnimatedTextComponent.Item] = []
animatedTextItems.append(AnimatedTextComponent.Item(
id: 1,
isUnbreakable: true,
content: .number(Int(component.count), minDigits: 1)
))
let balanceString = presentationStringsFormattedNumber(Int32(component.count), component.dateTimeFormat.groupingSeparator)
let titleSize = self.title.update(
transition: isFirstTime ? .immediate : .easeInOut(duration: 0.2),
transition: .immediate,
component: AnyComponent(
AnimatedTextComponent(
font: Font.with(size: 48.0, design: .round, weight: .semibold),
color: component.theme.list.itemPrimaryTextColor,
items: animatedTextItems
MultilineTextComponent(
text: .plain(NSAttributedString(string: balanceString, font: Font.with(size: 48.0, design: .round, weight: .semibold), textColor: component.theme.list.itemPrimaryTextColor))
)
),
environment: {},

View File

@ -125,6 +125,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let additional = Child(BalancedTextComponent.self)
let button = Child(SolidRoundedButtonComponent.self)
let refundBackgound = Child(RoundedRectangle.self)
let refundText = Child(MultilineTextComponent.self)
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
let controller = environment.controller
@ -172,6 +175,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let toPeer: EnginePeer?
let transactionPeer: StarsContext.State.Transaction.Peer?
let photo: TelegramMediaWebFile?
let isRefund: Bool
var delayedCloseOnOpenPeer = true
switch subject {
@ -208,6 +212,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
transactionPeer = transaction.peer
photo = transaction.photo
isRefund = transaction.flags.contains(.isRefund)
case let .receipt(receipt):
titleText = receipt.invoiceMedia.title
descriptionText = receipt.invoiceMedia.description
@ -222,6 +227,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
transactionPeer = nil
photo = receipt.invoiceMedia.photo
isRefund = false
delayedCloseOnOpenPeer = false
}
@ -254,6 +260,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
imageSubject = .photo(photo)
} else if let transactionPeer {
imageSubject = .transactionPeer(transactionPeer)
} else if let toPeer {
imageSubject = .transactionPeer(.peer(toPeer))
} else {
imageSubject = .none
}
@ -297,7 +305,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let toPeer {
tableItems.append(.init(
id: "to",
title: strings.Stars_Transaction_To,
title: count < 0 ? strings.Stars_Transaction_To : strings.Stars_Transaction_From,
component: AnyComponent(
Button(
content: AnyComponent(
@ -455,11 +463,45 @@ private final class StarsTransactionSheetContent: CombinedComponent {
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
.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
.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
@ -1097,6 +1139,9 @@ private final class TransactionCellComponent: Component {
)
func brokenLine(_ string: String) -> String {
if string.count > 30 {
return string
}
let middleIndex = string.index(string.startIndex, offsetBy: string.count / 2)
var newString = string
newString.insert("\n", at: middleIndex)

View File

@ -16,6 +16,16 @@ import AvatarNode
import BundleIconComponent
import PhotoResources
private extension StarsContext.State.Transaction {
var extendedId: String {
if self.count > 0 {
return "\(id)_in"
} else {
return "\(id)_out"
}
}
}
final class StarsTransactionsListPanelComponent: Component {
typealias EnvironmentType = StarsTransactionsPanelEnvironment
@ -161,7 +171,7 @@ final class StarsTransactionsListPanelComponent: Component {
continue
}
let item = self.items[index]
let id = item.id
let id = item.extendedId
validIds.insert(id)
var itemTransition = transition
@ -186,7 +196,7 @@ final class StarsTransactionsListPanelComponent: Component {
let itemTitle: String
let itemSubtitle: String?
let itemDate: String
var itemDate: String
switch item.peer {
case let .peer(peer):
if let title = item.title {
@ -225,6 +235,9 @@ final class StarsTransactionsListPanelComponent: Component {
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemDate = stringForMediumCompactDate(timestamp: item.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)
if item.flags.contains(.isRefund) {
itemDate += " \(environment.strings.Stars_Intro_Transaction_Refund)"
}
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
titleComponents.append(
@ -272,7 +285,7 @@ final class StarsTransactionsListPanelComponent: Component {
guard let self, let component = self.component else {
return
}
if !item.id.hasPrefix("tmp_") {
if !item.flags.contains(.isLocal) {
component.action(item)
}
}
@ -320,7 +333,7 @@ final class StarsTransactionsListPanelComponent: Component {
let bottomOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height)
let loadMore = bottomOffset < 100.0
if environment.isCurrent, loadMore {
let lastId = self.items.last?.id
let lastId = self.items.last?.extendedId
if lastId != self.currentLoadMoreId || lastId == nil {
self.currentLoadMoreId = lastId
component.transactionsContext.loadMore()
@ -344,14 +357,14 @@ final class StarsTransactionsListPanelComponent: Component {
return
}
let wasEmpty = self.items.isEmpty
let hadTemporaryTransactions = self.items.contains(where: { $0.id.hasPrefix("tmp_") })
let hadLocalTransactions = self.items.contains(where: { $0.flags.contains(.isLocal) })
self.items = status.transactions
if !status.isLoading {
self.currentLoadMoreId = nil
}
if !self.isUpdating {
state?.updated(transition: wasEmpty || hadTemporaryTransactions ? .immediate : .easeInOut(duration: 0.2))
state?.updated(transition: wasEmpty || hadLocalTransactions ? .immediate : .easeInOut(duration: 0.2))
}
})
}
@ -533,7 +546,7 @@ private final class AvatarComponent: Component {
imageNode = current
} else {
imageNode = TransformImageNode()
imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates]
imageNode.contentAnimations = [.subsequentUpdates]
self.addSubview(imageNode.view)
self.imageNode = imageNode

View File

@ -504,7 +504,7 @@ final class StarsTransactionsScreenComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - sideInsets - 8.0, height: 240.0)
)
let descriptionFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - descriptionSize.width) / 2.0), y: contentHeight), size: descriptionSize)
let descriptionFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - descriptionSize.width) / 2.0), y: contentHeight + 20.0 - floor(descriptionSize.height / 2.0)), size: descriptionSize)
if let descriptionView = self.descriptionView.view {
if descriptionView.superview == nil {
self.scrollView.addSubview(descriptionView)
@ -527,6 +527,7 @@ final class StarsTransactionsScreenComponent: Component {
StarsBalanceComponent(
theme: environment.theme,
strings: environment.strings,
dateTimeFormat: environment.dateTimeFormat,
count: self.starsState?.balance ?? 0,
purchaseAvailable: !premiumConfiguration.areStarsDisabled,
buy: { [weak self] in

View File

@ -414,19 +414,27 @@ private final class SheetContent: CombinedComponent {
}
}, completion: { [weak controller] in
let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 }
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string,
round: false,
undoText: nil
),
elevatedLayout: true,
action: { _ in return true})
controller?.present(resultController, in: .window(.root))
if let navigationController = controller?.navigationController {
Queue.mainQueue().after(0.5) {
if let lastController = navigationController.viewControllers.last as? ViewController {
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string,
round: false,
undoText: nil
),
elevatedLayout: lastController is ChatController,
action: { _ in return true}
)
lastController.present(resultController, in: .window(.root))
}
}
}
controller?.complete(paid: true)
controller?.dismissAnimated()
starsContext.load(force: true)
@ -549,15 +557,18 @@ private final class StarsTransferSheetComponent: CombinedComponent {
public final class StarsTransferScreen: ViewControllerComponentContainer {
private let context: AccountContext
private let completion: (Bool) -> Void
public init(
context: AccountContext,
starsContext: StarsContext,
invoice: TelegramMediaInvoice,
source: BotPaymentInvoiceSource,
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>,
completion: @escaping (Bool) -> Void
) {
self.context = context
self.completion = completion
super.init(
context: context,
@ -578,10 +589,23 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
starsContext.load(force: false)
}
deinit {
self.complete(paid: false)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var didComplete = false
fileprivate func complete(paid: Bool) {
guard !self.didComplete else {
return
}
self.didComplete = true
self.completion(paid)
}
public func dismissAnimated() {
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
view.dismissAnimated()

View File

@ -2924,7 +2924,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.dismissInput()
if let receiptMessageId = invoice.receiptMessageId {
if invoice.currency == "XTR" {
let _ = (strongSelf.context.engine.payments.requestBotPaymentReceipt(messageId: message.id)
let _ = (strongSelf.context.engine.payments.requestBotPaymentReceipt(messageId: receiptMessageId)
|> deliverOnMainQueue).start(next: { [weak self] receipt in
guard let strongSelf = self else {
return
@ -2957,7 +2957,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(messageId), inputData: starsInputData)
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(messageId), inputData: starsInputData, completion: { _ in })
strongSelf.push(controller)
})
} else {

View File

@ -449,6 +449,10 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode {
self.resultsText = resultsText
}
if self.alwaysShowTotalMessagesCount {
resultsTextTransition = .immediate
}
let resultsTextSize = resultsText.update(
transition: resultsTextTransition,
component: AnyComponent(AnimatedTextComponent(

View File

@ -837,7 +837,7 @@ func openResolvedUrlImpl(
}
}
let _ = (starsInputData |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { _ in
let controller = context.sharedContext.makeStarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: .slug(slug), inputData: starsInputData)
let controller = context.sharedContext.makeStarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: .slug(slug), inputData: starsInputData, completion: { _ in })
navigationController.pushViewController(controller)
})
} else {

View File

@ -2627,8 +2627,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StarsPurchaseScreen(context: context, starsContext: starsContext, options: options, peerId: peerId, requiredStars: requiredStars, modal: true, completion: completion)
}
public func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>) -> ViewController {
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, inputData: inputData)
public func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController {
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, inputData: inputData, completion: completion)
}
public func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction) -> ViewController {

View File

@ -877,7 +877,19 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
}
let _ = (starsInputData |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { _ in
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .slug(slug), inputData: starsInputData)
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(
context: strongSelf.context,
starsContext: starsContext,
invoice: invoice,
source: .slug(slug),
inputData: starsInputData,
completion: { [weak self] paid in
guard let self else {
return
}
self.sendInvoiceClosedEvent(slug: slug, result: paid ? .paid : .cancelled)
}
)
navigationController.pushViewController(controller)
})
} else {