mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit 'c30fd83ee371a2406f9bf5ad5ec284f80de64823'
This commit is contained in:
commit
a8dea962b3
@ -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$@**?";
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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: {},
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user