Support new API

This commit is contained in:
Ali 2021-03-26 18:33:46 +04:00
parent 3009fe790f
commit de34ab5efa
14 changed files with 1020 additions and 195 deletions

View File

@ -24,12 +24,14 @@ final class BotCheckoutControllerArguments {
fileprivate let openInfo: (BotCheckoutInfoControllerFocus) -> Void fileprivate let openInfo: (BotCheckoutInfoControllerFocus) -> Void
fileprivate let openPaymentMethod: () -> Void fileprivate let openPaymentMethod: () -> Void
fileprivate let openShippingMethod: () -> Void fileprivate let openShippingMethod: () -> Void
fileprivate let openTip: () -> Void
fileprivate init(account: Account, openInfo: @escaping (BotCheckoutInfoControllerFocus) -> Void, openPaymentMethod: @escaping () -> Void, openShippingMethod: @escaping () -> Void) { fileprivate init(account: Account, openInfo: @escaping (BotCheckoutInfoControllerFocus) -> Void, openPaymentMethod: @escaping () -> Void, openShippingMethod: @escaping () -> Void, openTip: @escaping () -> Void) {
self.account = account self.account = account
self.openInfo = openInfo self.openInfo = openInfo
self.openPaymentMethod = openPaymentMethod self.openPaymentMethod = openPaymentMethod
self.openShippingMethod = openShippingMethod self.openShippingMethod = openShippingMethod
self.openTip = openTip
} }
} }
@ -42,6 +44,7 @@ private enum BotCheckoutSection: Int32 {
enum BotCheckoutEntry: ItemListNodeEntry { enum BotCheckoutEntry: ItemListNodeEntry {
case header(PresentationTheme, TelegramMediaInvoice, String) case header(PresentationTheme, TelegramMediaInvoice, String)
case price(Int, PresentationTheme, String, String, Bool) case price(Int, PresentationTheme, String, String, Bool)
case tip(PresentationTheme, String, String)
case paymentMethod(PresentationTheme, String, String) case paymentMethod(PresentationTheme, String, String)
case shippingInfo(PresentationTheme, String, String) case shippingInfo(PresentationTheme, String, String)
case shippingMethod(PresentationTheme, String, String) case shippingMethod(PresentationTheme, String, String)
@ -66,18 +69,20 @@ enum BotCheckoutEntry: ItemListNodeEntry {
return 0 return 0
case let .price(index, _, _, _, _): case let .price(index, _, _, _, _):
return 1 + Int32(index) return 1 + Int32(index)
case .paymentMethod: case .tip:
return 10000 + 0
case .shippingInfo:
return 10000 + 1 return 10000 + 1
case .shippingMethod: case .paymentMethod:
return 10000 + 2 return 10000 + 2
case .nameInfo: case .shippingInfo:
return 10000 + 3 return 10000 + 3
case .emailInfo: case .shippingMethod:
return 10000 + 4 return 10000 + 4
case .phoneInfo: case .nameInfo:
return 10000 + 5 return 10000 + 5
case .emailInfo:
return 10000 + 6
case .phoneInfo:
return 10000 + 7
} }
} }
@ -119,6 +124,12 @@ enum BotCheckoutEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .tip(lhsTheme, lhsText, lhsValue):
if case let .tip(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .paymentMethod(lhsTheme, lhsText, lhsValue): case let .paymentMethod(lhsTheme, lhsText, lhsValue):
if case let .paymentMethod(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { if case let .paymentMethod(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true return true
@ -169,27 +180,31 @@ enum BotCheckoutEntry: ItemListNodeEntry {
return BotCheckoutHeaderItem(account: arguments.account, theme: theme, invoice: invoice, botName: botName, sectionId: self.section) return BotCheckoutHeaderItem(account: arguments.account, theme: theme, invoice: invoice, botName: botName, sectionId: self.section)
case let .price(_, theme, text, value, isFinal): case let .price(_, theme, text, value, isFinal):
return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, sectionId: self.section) return BotCheckoutPriceItem(theme: theme, title: text, label: value, isFinal: isFinal, sectionId: self.section)
case let .paymentMethod(theme, text, value): case let .tip(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openTip()
})
case let .paymentMethod(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openPaymentMethod() arguments.openPaymentMethod()
}) })
case let .shippingInfo(theme, text, value): case let .shippingInfo(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openInfo(.address(.street1)) arguments.openInfo(.address(.street1))
}) })
case let .shippingMethod(theme, text, value): case let .shippingMethod(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openShippingMethod() arguments.openShippingMethod()
}) })
case let .nameInfo(theme, text, value): case let .nameInfo(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openInfo(.name) arguments.openInfo(.name)
}) })
case let .emailInfo(theme, text, value): case let .emailInfo(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openInfo(.email) arguments.openInfo(.email)
}) })
case let .phoneInfo(theme, text, value): case let .phoneInfo(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openInfo(.phone) arguments.openInfo(.phone)
}) })
@ -206,12 +221,16 @@ private struct BotCheckoutControllerState: Equatable {
} }
} }
private func currentTotalPrice(paymentForm: BotPaymentForm?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?) -> Int64 { private func currentTotalPrice(paymentForm: BotPaymentForm?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?, currentTip: Int64?) -> Int64 {
guard let paymentForm = paymentForm else { guard let paymentForm = paymentForm else {
return 0 return 0
} }
var totalPrice: Int64 = 0 var totalPrice: Int64 = 0
if let currentTip = currentTip {
totalPrice += currentTip
}
var index = 0 var index = 0
for price in paymentForm.invoice.prices { for price in paymentForm.invoice.prices {
@ -235,7 +254,7 @@ private func currentTotalPrice(paymentForm: BotPaymentForm?, validatedFormInfo:
return totalPrice return totalPrice
} }
private func botCheckoutControllerEntries(presentationData: PresentationData, state: BotCheckoutControllerState, invoice: TelegramMediaInvoice, paymentForm: BotPaymentForm?, formInfo: BotPaymentRequestedInfo?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?, currentPaymentMethod: BotCheckoutPaymentMethod?, botPeer: Peer?) -> [BotCheckoutEntry] { private func botCheckoutControllerEntries(presentationData: PresentationData, state: BotCheckoutControllerState, invoice: TelegramMediaInvoice, paymentForm: BotPaymentForm?, formInfo: BotPaymentRequestedInfo?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?, currentPaymentMethod: BotCheckoutPaymentMethod?, currentTip: Int64?, botPeer: Peer?) -> [BotCheckoutEntry] {
var entries: [BotCheckoutEntry] = [] var entries: [BotCheckoutEntry] = []
var botName = "" var botName = ""
@ -246,6 +265,10 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
if let paymentForm = paymentForm { if let paymentForm = paymentForm {
var totalPrice: Int64 = 0 var totalPrice: Int64 = 0
if let currentTip = currentTip {
totalPrice += currentTip
}
var index = 0 var index = 0
for price in paymentForm.invoice.prices { for price in paymentForm.invoice.prices {
@ -275,6 +298,17 @@ private func botCheckoutControllerEntries(presentationData: PresentationData, st
} }
entries.append(.price(index, presentationData.theme, presentationData.strings.Checkout_TotalAmount, formatCurrencyAmount(totalPrice, currency: paymentForm.invoice.currency), true)) entries.append(.price(index, presentationData.theme, presentationData.strings.Checkout_TotalAmount, formatCurrencyAmount(totalPrice, currency: paymentForm.invoice.currency), true))
if let tip = paymentForm.invoice.tip {
let tipTitle: String
//TODO:localize
if tip.min == 0 {
tipTitle = "Tip (Optional)"
} else {
tipTitle = "Tip"
}
entries.append(.tip(presentationData.theme, tipTitle, "\(formatCurrencyAmount(currentTip ?? 0, currency: paymentForm.invoice.currency))"))
}
var paymentMethodTitle = "" var paymentMethodTitle = ""
if let currentPaymentMethod = currentPaymentMethod { if let currentPaymentMethod = currentPaymentMethod {
@ -383,12 +417,13 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private var presentationData: PresentationData private var presentationData: PresentationData
private let paymentFormAndInfo = Promise<(BotPaymentForm, BotPaymentRequestedInfo, BotPaymentValidatedFormInfo?, String?, BotCheckoutPaymentMethod?)?>(nil) private let paymentFormAndInfo = Promise<(BotPaymentForm, BotPaymentRequestedInfo, BotPaymentValidatedFormInfo?, String?, BotCheckoutPaymentMethod?, Int64?)?>(nil)
private var paymentFormValue: BotPaymentForm? private var paymentFormValue: BotPaymentForm?
private var currentFormInfo: BotPaymentRequestedInfo? private var currentFormInfo: BotPaymentRequestedInfo?
private var currentValidatedFormInfo: BotPaymentValidatedFormInfo? private var currentValidatedFormInfo: BotPaymentValidatedFormInfo?
private var currentShippingOptionId: String? private var currentShippingOptionId: String?
private var currentPaymentMethod: BotCheckoutPaymentMethod? private var currentPaymentMethod: BotCheckoutPaymentMethod?
private var currentTipAmount: Int64?
private var formRequestDisposable: Disposable? private var formRequestDisposable: Disposable?
private let actionButton: BotCheckoutActionButton private let actionButton: BotCheckoutActionButton
@ -408,6 +443,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
var openInfoImpl: ((BotCheckoutInfoControllerFocus) -> Void)? var openInfoImpl: ((BotCheckoutInfoControllerFocus) -> Void)?
var openTipImpl: (() -> Void)?
var openPaymentMethodImpl: (() -> Void)? var openPaymentMethodImpl: (() -> Void)?
var openShippingMethodImpl: (() -> Void)? var openShippingMethodImpl: (() -> Void)?
@ -417,12 +453,14 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
openPaymentMethodImpl?() openPaymentMethodImpl?()
}, openShippingMethod: { }, openShippingMethod: {
openShippingMethodImpl?() openShippingMethodImpl?()
}, openTip: {
openTipImpl?()
}) })
let signal: Signal<(ItemListPresentationData, (ItemListNodeState, Any)), NoError> = combineLatest(context.sharedContext.presentationData, self.state.get(), paymentFormAndInfo.get(), context.account.postbox.loadedPeerWithId(messageId.peerId)) let signal: Signal<(ItemListPresentationData, (ItemListNodeState, Any)), NoError> = combineLatest(context.sharedContext.presentationData, self.state.get(), paymentFormAndInfo.get(), context.account.postbox.loadedPeerWithId(messageId.peerId))
|> map { presentationData, state, paymentFormAndInfo, botPeer -> (ItemListPresentationData, (ItemListNodeState, Any)) in |> map { presentationData, state, paymentFormAndInfo, botPeer -> (ItemListPresentationData, (ItemListNodeState, Any)) in
let nodeState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: botCheckoutControllerEntries(presentationData: presentationData, state: state, invoice: invoice, paymentForm: paymentFormAndInfo?.0, formInfo: paymentFormAndInfo?.1, validatedFormInfo: paymentFormAndInfo?.2, currentShippingOptionId: paymentFormAndInfo?.3, currentPaymentMethod: paymentFormAndInfo?.4, botPeer: botPeer), style: .plain, focusItemTag: nil, emptyStateItem: nil, animateChanges: false) let nodeState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: botCheckoutControllerEntries(presentationData: presentationData, state: state, invoice: invoice, paymentForm: paymentFormAndInfo?.0, formInfo: paymentFormAndInfo?.1, validatedFormInfo: paymentFormAndInfo?.2, currentShippingOptionId: paymentFormAndInfo?.3, currentPaymentMethod: paymentFormAndInfo?.4, currentTip: paymentFormAndInfo?.5, botPeer: botPeer), style: .plain, focusItemTag: nil, emptyStateItem: nil, animateChanges: false)
return (ItemListPresentationData(presentationData), (nodeState, arguments)) return (ItemListPresentationData(presentationData), (nodeState, arguments))
} }
@ -450,7 +488,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
updatedCurrentShippingOptionId = currentShippingOptionId updatedCurrentShippingOptionId = currentShippingOptionId
} }
} }
strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, formInfo, validatedInfo, updatedCurrentShippingOptionId, strongSelf.currentPaymentMethod))) strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, formInfo, validatedInfo, updatedCurrentShippingOptionId, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
strongSelf.updateActionButton() strongSelf.updateActionButton()
} }
@ -461,7 +499,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
let applyPaymentMethod: (BotCheckoutPaymentMethod) -> Void = { [weak self] method in let applyPaymentMethod: (BotCheckoutPaymentMethod) -> Void = { [weak self] method in
if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo { if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo {
strongSelf.currentPaymentMethod = method strongSelf.currentPaymentMethod = method
strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod))) strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
} }
} }
@ -608,6 +646,43 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} }
} }
} }
openTipImpl = { [weak self] in
if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue {
//TODO:localize
let initialValue: String
if let tipAmount = strongSelf.currentTipAmount, let value = currencyToFractionalAmount(value: tipAmount, currency: paymentFormValue.invoice.currency) {
initialValue = "\(value)"
} else {
initialValue = "0"
}
let controller = tipEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: nil, title: "Tip", text: "Enter Tip Amount", placeholder: "", value: initialValue, apply: { value in
guard let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, var currentFormInfo = strongSelf.currentFormInfo, let value = value else {
return
}
let tipAmount = fractionalToCurrencyAmount(value: (Double(value) ?? 0.0), currency: paymentFormValue.invoice.currency) ?? 0
currentFormInfo.tipAmount = tipAmount
let _ = (validateBotPaymentForm(account: strongSelf.context.account, saveInfo: false, messageId: strongSelf.messageId, formInfo: currentFormInfo)
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
strongSelf.currentValidatedFormInfo = result
strongSelf.currentTipAmount = tipAmount
strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
strongSelf.updateActionButton()
})
})
strongSelf.present(controller, nil)
}
}
openPaymentMethodImpl = { [weak self] in openPaymentMethodImpl = { [weak self] in
if let strongSelf = self, let paymentForm = strongSelf.paymentFormValue { if let strongSelf = self, let paymentForm = strongSelf.paymentFormValue {
@ -629,7 +704,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
strongSelf.present(BotCheckoutPaymentShippingOptionSheetController(context: strongSelf.context, currency: paymentFormValue.invoice.currency, options: shippingOptions, currentId: strongSelf.currentShippingOptionId, applyValue: { id in strongSelf.present(BotCheckoutPaymentShippingOptionSheetController(context: strongSelf.context, currency: paymentFormValue.invoice.currency, options: shippingOptions, currentId: strongSelf.currentShippingOptionId, applyValue: { id in
if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo { if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo {
strongSelf.currentShippingOptionId = id strongSelf.currentShippingOptionId = id
strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod))) strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
strongSelf.updateActionButton() strongSelf.updateActionButton()
} }
@ -640,7 +715,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
let formAndMaybeValidatedInfo = fetchBotPaymentForm(postbox: context.account.postbox, network: context.account.network, messageId: messageId) let formAndMaybeValidatedInfo = fetchBotPaymentForm(postbox: context.account.postbox, network: context.account.network, messageId: messageId)
|> mapToSignal { paymentForm -> Signal<(BotPaymentForm, BotPaymentValidatedFormInfo?), BotPaymentFormRequestError> in |> mapToSignal { paymentForm -> Signal<(BotPaymentForm, BotPaymentValidatedFormInfo?), BotPaymentFormRequestError> in
if let current = paymentForm.savedInfo { if let current = paymentForm.savedInfo {
return validateBotPaymentForm(network: context.account.network, saveInfo: true, messageId: messageId, formInfo: current) return validateBotPaymentForm(account: context.account, saveInfo: true, messageId: messageId, formInfo: current)
|> mapError { _ -> BotPaymentFormRequestError in |> mapError { _ -> BotPaymentFormRequestError in
return .generic return .generic
} }
@ -661,7 +736,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
if let current = form.savedInfo { if let current = form.savedInfo {
savedInfo = current savedInfo = current
} else { } else {
savedInfo = BotPaymentRequestedInfo(name: nil, phone: nil, email: nil, shippingAddress: nil) savedInfo = BotPaymentRequestedInfo(name: nil, phone: nil, email: nil, shippingAddress: nil, tipAmount: nil)
} }
strongSelf.paymentFormValue = form strongSelf.paymentFormValue = form
strongSelf.currentFormInfo = savedInfo strongSelf.currentFormInfo = savedInfo
@ -670,7 +745,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials) strongSelf.currentPaymentMethod = .savedCredentials(savedCredentials)
} }
strongSelf.actionButton.isEnabled = true strongSelf.actionButton.isEnabled = true
strongSelf.paymentFormAndInfo.set(.single((form, savedInfo, validatedInfo, nil, strongSelf.currentPaymentMethod))) strongSelf.paymentFormAndInfo.set(.single((form, savedInfo, validatedInfo, nil, strongSelf.currentPaymentMethod, strongSelf.currentTipAmount)))
strongSelf.updateActionButton() strongSelf.updateActionButton()
} }
@ -692,7 +767,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} }
private func updateActionButton() { private func updateActionButton() {
let totalAmount = currentTotalPrice(paymentForm: self.paymentFormValue, validatedFormInfo: self.currentValidatedFormInfo, currentShippingOptionId: self.currentShippingOptionId) let totalAmount = currentTotalPrice(paymentForm: self.paymentFormValue, validatedFormInfo: self.currentValidatedFormInfo, currentShippingOptionId: self.currentShippingOptionId, currentTip: self.currentTipAmount)
let payString: String let payString: String
if let paymentForm = self.paymentFormValue, totalAmount > 0 { if let paymentForm = self.paymentFormValue, totalAmount > 0 {
payString = self.presentationData.strings.Checkout_PayPrice(formatCurrencyAmount(totalAmount, currency: paymentForm.invoice.currency)).0 payString = self.presentationData.strings.Checkout_PayPrice(formatCurrencyAmount(totalAmount, currency: paymentForm.invoice.currency)).0
@ -835,11 +910,14 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
var items: [PKPaymentSummaryItem] = [] var items: [PKPaymentSummaryItem] = []
var totalAmount: Int64 = 0 var totalAmount: Int64 = 0
for price in paymentForm.invoice.prices { for price in paymentForm.invoice.prices {
totalAmount += price.amount totalAmount += price.amount
let amount = NSDecimalNumber(value: Double(price.amount) * 0.01) if let fractional = currencyToFractionalAmount(value: price.amount, currency: paymentForm.invoice.currency) {
items.append(PKPaymentSummaryItem(label: price.label, amount: amount)) let amount = NSDecimalNumber(value: fractional)
items.append(PKPaymentSummaryItem(label: price.label, amount: amount))
}
} }
if let shippingOptions = strongSelf.currentValidatedFormInfo?.shippingOptions, let shippingOptionId = strongSelf.currentShippingOptionId { if let shippingOptions = strongSelf.currentValidatedFormInfo?.shippingOptions, let shippingOptionId = strongSelf.currentShippingOptionId {
@ -852,9 +930,21 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} }
} }
} }
let amount = NSDecimalNumber(value: Double(totalAmount) * 0.01) if let tipAmount = strongSelf.currentTipAmount {
items.append(PKPaymentSummaryItem(label: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), amount: amount)) totalAmount += tipAmount
//TODO:localize
if let fractional = currencyToFractionalAmount(value: tipAmount, currency: paymentForm.invoice.currency) {
let amount = NSDecimalNumber(value: fractional)
items.append(PKPaymentSummaryItem(label: "Tip", amount: amount))
}
}
if let fractionalTotal = currencyToFractionalAmount(value: totalAmount, currency: paymentForm.invoice.currency) {
let amount = NSDecimalNumber(value: fractionalTotal)
items.append(PKPaymentSummaryItem(label: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), amount: amount))
}
request.paymentSummaryItems = items request.paymentSummaryItems = items
@ -901,7 +991,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
self.inProgressDimNode.alpha = 1.0 self.inProgressDimNode.alpha = 1.0
self.actionButton.isEnabled = false self.actionButton.isEnabled = false
self.updateActionButton() self.updateActionButton()
self.payDisposable.set((sendBotPaymentForm(account: self.context.account, messageId: self.messageId, validatedInfoId: self.currentValidatedFormInfo?.id, shippingOptionId: self.currentShippingOptionId, credentials: credentials) |> deliverOnMainQueue).start(next: { [weak self] result in self.payDisposable.set((sendBotPaymentForm(account: self.context.account, messageId: self.messageId, formId: paymentForm.id, validatedInfoId: self.currentValidatedFormInfo?.id, shippingOptionId: self.currentShippingOptionId, credentials: credentials) |> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self { if let strongSelf = self {
strongSelf.inProgressDimNode.isUserInteractionEnabled = false strongSelf.inProgressDimNode.isUserInteractionEnabled = false
strongSelf.inProgressDimNode.alpha = 0.0 strongSelf.inProgressDimNode.alpha = 0.0

View File

@ -317,7 +317,7 @@ final class BotCheckoutInfoControllerNode: ViewControllerTracingNode, UIScrollVi
shippingAddress = BotPaymentShippingAddress(streetLine1: "", streetLine2: "", city: "", state: "", countryIso2: iso2, postCode: "") shippingAddress = BotPaymentShippingAddress(streetLine1: "", streetLine2: "", city: "", state: "", countryIso2: iso2, postCode: "")
} }
self.formInfo = BotPaymentRequestedInfo(name: self.formInfo.name, phone: self.formInfo.phone, email: self.formInfo.email, shippingAddress: BotPaymentShippingAddress(streetLine1: shippingAddress.streetLine1, streetLine2: shippingAddress.streetLine2, city: shippingAddress.city, state: shippingAddress.state, countryIso2: iso2, postCode: shippingAddress.postCode)) self.formInfo = BotPaymentRequestedInfo(name: self.formInfo.name, phone: self.formInfo.phone, email: self.formInfo.email, shippingAddress: BotPaymentShippingAddress(streetLine1: shippingAddress.streetLine1, streetLine2: shippingAddress.streetLine2, city: shippingAddress.city, state: shippingAddress.state, countryIso2: iso2, postCode: shippingAddress.postCode), tipAmount: self.formInfo.tipAmount)
self.addressItems?.country.text = name self.addressItems?.country.text = name
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
self.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) self.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)
@ -332,13 +332,13 @@ final class BotCheckoutInfoControllerNode: ViewControllerTracingNode, UIScrollVi
if let addressItems = self.addressItems, let current = self.formInfo.shippingAddress { if let addressItems = self.addressItems, let current = self.formInfo.shippingAddress {
address = BotPaymentShippingAddress(streetLine1: addressItems.address1.text, streetLine2: addressItems.address2.text, city: addressItems.city.text, state: addressItems.state.text, countryIso2: current.countryIso2, postCode: addressItems.postcode.text) address = BotPaymentShippingAddress(streetLine1: addressItems.address1.text, streetLine2: addressItems.address2.text, city: addressItems.city.text, state: addressItems.state.text, countryIso2: current.countryIso2, postCode: addressItems.postcode.text)
} }
return BotPaymentRequestedInfo(name: self.nameItem?.text, phone: self.phoneItem?.text, email: self.emailItem?.text, shippingAddress: address) return BotPaymentRequestedInfo(name: self.nameItem?.text, phone: self.phoneItem?.text, email: self.emailItem?.text, shippingAddress: address, tipAmount: self.formInfo.tipAmount)
} }
func verify() { func verify() {
self.isVerifying = true self.isVerifying = true
let formInfo = self.collectFormInfo() let formInfo = self.collectFormInfo()
self.verifyDisposable.set((validateBotPaymentForm(network: self.context.account.network, saveInfo: self.saveInfoItem.isOn, messageId: self.messageId, formInfo: formInfo) |> deliverOnMainQueue).start(next: { [weak self] result in self.verifyDisposable.set((validateBotPaymentForm(account: self.context.account, saveInfo: self.saveInfoItem.isOn, messageId: self.messageId, formInfo: formInfo) |> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self { if let strongSelf = self {
strongSelf.formInfoUpdated(formInfo, result) strongSelf.formInfoUpdated(formInfo, result)
} }

View File

@ -287,7 +287,7 @@ final class BotReceiptControllerNode: ItemListControllerNode {
super.init(controller: controller, navigationBar: navigationBar, updateNavigationOffset: updateNavigationOffset, state: signal) super.init(controller: controller, navigationBar: navigationBar, updateNavigationOffset: updateNavigationOffset, state: signal)
self.dataRequestDisposable = (requestBotPaymentReceipt(network: context.account.network, messageId: messageId) |> deliverOnMainQueue).start(next: { [weak self] receipt in self.dataRequestDisposable = (requestBotPaymentReceipt(account: context.account, messageId: messageId) |> deliverOnMainQueue).start(next: { [weak self] receipt in
if let strongSelf = self { if let strongSelf = self {
strongSelf.receiptData.set(.single((receipt.invoice, receipt.info, receipt.shippingOption, receipt.credentialsTitle))) strongSelf.receiptData.set(.single((receipt.invoice, receipt.info, receipt.shippingOption, receipt.credentialsTitle)))
} }

View File

@ -0,0 +1,461 @@
import Foundation
import UIKit
import SwiftSignalKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SyncCore
import TelegramPresentationData
import AccountContext
private final class TipEditInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
private var theme: PresentationTheme
private let backgroundNode: ASImageNode
private let textInputNode: EditableTextNode
private let placeholderNode: ASTextNode
private let clearButton: HighlightableButtonNode
var updateHeight: (() -> Void)?
var complete: (() -> Void)?
var textChanged: ((String) -> Void)?
private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0)
private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
var text: String {
get {
return self.textInputNode.attributedText?.string ?? ""
}
set {
self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor)
self.placeholderNode.isHidden = !newValue.isEmpty
self.clearButton.isHidden = newValue.isEmpty
}
}
var placeholder: String = "" {
didSet {
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
}
}
init(theme: PresentationTheme, placeholder: String) {
self.theme = theme
self.backgroundNode = ASImageNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
self.textInputNode = EditableTextNode()
self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor]
self.textInputNode.clipsToBounds = true
self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0)
self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
self.textInputNode.keyboardType = .default
self.textInputNode.autocapitalizationType = .sentences
self.textInputNode.returnKeyType = .done
self.textInputNode.autocorrectionType = .default
self.textInputNode.tintColor = theme.actionSheet.controlAccentColor
self.placeholderNode = ASTextNode()
self.placeholderNode.isUserInteractionEnabled = false
self.placeholderNode.displaysAsynchronously = false
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.clearButton = HighlightableButtonNode()
self.clearButton.imageNode.displaysAsynchronously = false
self.clearButton.imageNode.displayWithoutProcessing = true
self.clearButton.displaysAsynchronously = false
self.clearButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.actionSheet.inputClearButtonColor), for: [])
self.clearButton.isHidden = true
super.init()
self.textInputNode.delegate = self
self.addSubnode(self.backgroundNode)
self.addSubnode(self.textInputNode)
self.addSubnode(self.placeholderNode)
self.addSubnode(self.clearButton)
self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside)
}
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor
self.clearButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.actionSheet.inputClearButtonColor), for: [])
}
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
let backgroundInsets = self.backgroundInsets
let inputInsets = self.inputInsets
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom))
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
let placeholderSize = self.placeholderNode.measure(backgroundFrame.size)
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - 20.0, height: backgroundFrame.size.height)))
if let image = self.clearButton.image(for: []) {
transition.updateFrame(node: self.clearButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX - 8.0 - image.size.width, y: backgroundFrame.minY + floor((backgroundFrame.size.height - image.size.height) / 2.0)), size: image.size))
}
return panelHeight
}
func activateInput() {
self.textInputNode.becomeFirstResponder()
}
func deactivateInput() {
self.textInputNode.resignFirstResponder()
}
@objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
self.updateTextNodeText(animated: true)
self.textChanged?(editableTextNode.textView.text)
self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty
self.clearButton.isHidden = !self.placeholderNode.isHidden
}
func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text)
if updatedText.count > 40 {
self.textInputNode.layer.addShakeAnimation()
return false
}
if text == "\n" {
self.complete?()
return false
}
return true
}
private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat {
let backgroundInsets = self.backgroundInsets
let inputInsets = self.inputInsets
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right - 20.0, height: CGFloat.greatestFiniteMagnitude)).height))
return min(61.0, max(33.0, unboundTextFieldHeight))
}
private func updateTextNodeText(animated: Bool) {
let backgroundInsets = self.backgroundInsets
let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width)
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
if !self.bounds.size.height.isEqual(to: panelHeight) {
self.updateHeight?()
}
}
@objc func clearPressed() {
self.placeholderNode.isHidden = false
self.clearButton.isHidden = true
self.textInputNode.attributedText = nil
self.deactivateInput()
self.updateHeight?()
}
}
private final class TipEditAlertContentNode: AlertContentNode {
private let strings: PresentationStrings
private let title: String
private let text: String
private let titleNode: ASTextNode
private let textNode: ASTextNode
let inputFieldNode: TipEditInputFieldNode
private let actionNodesSeparator: ASDisplayNode
private let actionNodes: [TextAlertContentActionNode]
private let actionVerticalSeparators: [ASDisplayNode]
private let disposable = MetaDisposable()
private var validLayout: CGSize?
private let hapticFeedback = HapticFeedback()
var complete: (() -> Void)? {
didSet {
self.inputFieldNode.complete = self.complete
}
}
override var dismissOnOutsideTap: Bool {
return self.isUserInteractionEnabled
}
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?) {
self.strings = strings
self.title = title
self.text = text
self.titleNode = ASTextNode()
self.titleNode.maximumNumberOfLines = 2
self.textNode = ASTextNode()
self.textNode.maximumNumberOfLines = 8
self.inputFieldNode = TipEditInputFieldNode(theme: ptheme, placeholder: placeholder)
self.inputFieldNode.text = value ?? ""
self.actionNodesSeparator = ASDisplayNode()
self.actionNodesSeparator.isLayerBacked = true
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
return TextAlertContentActionNode(theme: theme, action: action)
}
var actionVerticalSeparators: [ASDisplayNode] = []
if actions.count > 1 {
for _ in 0 ..< actions.count - 1 {
let separatorNode = ASDisplayNode()
separatorNode.isLayerBacked = true
actionVerticalSeparators.append(separatorNode)
}
}
self.actionVerticalSeparators = actionVerticalSeparators
super.init()
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.inputFieldNode)
self.addSubnode(self.actionNodesSeparator)
for actionNode in self.actionNodes {
self.addSubnode(actionNode)
}
for separatorNode in self.actionVerticalSeparators {
self.addSubnode(separatorNode)
}
self.inputFieldNode.updateHeight = { [weak self] in
if let strongSelf = self {
if let _ = strongSelf.validLayout {
strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring))
}
}
}
self.updateTheme(theme)
}
deinit {
self.disposable.dispose()
}
var value: String {
return self.inputFieldNode.text
}
override func updateTheme(_ theme: AlertControllerTheme) {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.actionNodesSeparator.backgroundColor = theme.separatorColor
for actionNode in self.actionNodes {
actionNode.updateTheme(theme)
}
for separatorNode in self.actionVerticalSeparators {
separatorNode.backgroundColor = theme.separatorColor
}
if let size = self.validLayout {
_ = self.updateLayout(size: size, transition: .immediate)
}
}
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
var size = size
size.width = min(size.width, 270.0)
let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)
let hadValidLayout = self.validLayout != nil
self.validLayout = size
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
let spacing: CGFloat = 5.0
let titleSize = self.titleNode.measure(measureSize)
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
origin.y += titleSize.height + 4.0
let textSize = self.textNode.measure(measureSize)
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
origin.y += textSize.height + 6.0 + spacing
let actionButtonHeight: CGFloat = 44.0
var minActionsWidth: CGFloat = 0.0
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
let actionTitleInsets: CGFloat = 8.0
var effectiveActionLayout = TextAlertContentActionLayout.horizontal
for actionNode in self.actionNodes {
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
effectiveActionLayout = .vertical
}
switch effectiveActionLayout {
case .horizontal:
minActionsWidth += actionTitleSize.width + actionTitleInsets
case .vertical:
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
}
}
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0)
var contentWidth = max(titleSize.width, minActionsWidth)
contentWidth = max(contentWidth, 234.0)
var actionsHeight: CGFloat = 0.0
switch effectiveActionLayout {
case .horizontal:
actionsHeight = actionButtonHeight
case .vertical:
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
}
let resultWidth = contentWidth + insets.left + insets.right
let inputFieldWidth = resultWidth
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
let inputHeight = inputFieldHeight
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight))
transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0)
let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom)
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
var actionOffset: CGFloat = 0.0
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
var separatorIndex = -1
var nodeIndex = 0
for actionNode in self.actionNodes {
if separatorIndex >= 0 {
let separatorNode = self.actionVerticalSeparators[separatorIndex]
switch effectiveActionLayout {
case .horizontal:
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
case .vertical:
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
}
}
separatorIndex += 1
let currentActionWidth: CGFloat
switch effectiveActionLayout {
case .horizontal:
if nodeIndex == self.actionNodes.count - 1 {
currentActionWidth = resultSize.width - actionOffset
} else {
currentActionWidth = actionWidth
}
case .vertical:
currentActionWidth = resultSize.width
}
let actionNodeFrame: CGRect
switch effectiveActionLayout {
case .horizontal:
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
actionOffset += currentActionWidth
case .vertical:
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
actionOffset += actionButtonHeight
}
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
nodeIndex += 1
}
if !hadValidLayout {
self.inputFieldNode.activateInput()
}
return resultSize
}
func animateError() {
self.inputFieldNode.layer.addShakeAnimation()
self.hapticFeedback.error()
}
}
func tipEditController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, text: String, placeholder: String, doneButtonTitle: String? = nil, value: String?, apply: @escaping (String?) -> Void) -> AlertController {
var presentationData = sharedContext.currentPresentationData.with { $0 }
if let forceTheme = forceTheme {
presentationData = presentationData.withUpdated(theme: forceTheme)
}
var dismissImpl: ((Bool) -> Void)?
var applyImpl: (() -> Void)?
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
dismissImpl?(true)
}), TextAlertAction(type: .defaultAction, title: doneButtonTitle ?? presentationData.strings.Common_Done, action: {
applyImpl?()
})]
let contentNode = TipEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value)
contentNode.complete = {
applyImpl?()
}
applyImpl = { [weak contentNode] in
guard let contentNode = contentNode else {
return
}
dismissImpl?(true)
let previousValue = value ?? ""
let newValue = contentNode.value.trimmingCharacters(in: .whitespacesAndNewlines)
apply(previousValue != newValue || value == nil ? newValue : nil)
}
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
let presentationDataDisposable = sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in
var presentationData = presentationData
if let forceTheme = forceTheme {
presentationData = presentationData.withUpdated(theme: forceTheme)
}
controller?.theme = AlertControllerTheme(presentationData: presentationData)
contentNode?.inputFieldNode.updateTheme(presentationData.theme)
})
controller.dismissed = {
presentationDataDisposable.dispose()
}
dismissImpl = { [weak controller] animated in
contentNode.inputFieldNode.deactivateInput()
if animated {
controller?.dismissAnimated()
} else {
controller?.dismiss()
}
}
return controller
}

View File

@ -142,7 +142,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) } dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) } dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) }
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) } dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
dict[-1184160274] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } dict[430815881] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) } dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) } dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) } dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) }
@ -156,7 +156,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1109531342] = { return Api.Peer.parse_peerChannel($0) } dict[-1109531342] = { return Api.Peer.parse_peerChannel($0) }
dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) } dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) }
dict[572915951] = { return Api.messages.ExportedChatInvite.parse_exportedChatInviteReplaced($0) } dict[572915951] = { return Api.messages.ExportedChatInvite.parse_exportedChatInviteReplaced($0) }
dict[-1868808300] = { return Api.PaymentRequestedInfo.parse_paymentRequestedInfo($0) } dict[-2017173756] = { return Api.PaymentRequestedInfo.parse_paymentRequestedInfo($0) }
dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) } dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) }
dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) } dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) }
dict[9203775] = { return Api.UserStatus.parse_userStatusOffline($0) } dict[9203775] = { return Api.UserStatus.parse_userStatusOffline($0) }
@ -354,6 +354,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) } dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) }
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) } dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) } dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
dict[-717976187] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaInvoice($0) }
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) } dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) } dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) }
dict[-1111085620] = { return Api.messages.ExportedChatInvites.parse_exportedChatInvites($0) } dict[-1111085620] = { return Api.messages.ExportedChatInvites.parse_exportedChatInvites($0) }
@ -587,7 +588,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) } dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) } dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) } dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
dict[-1022713000] = { return Api.Invoice.parse_invoice($0) } dict[615970509] = { return Api.Invoice.parse_invoice($0) }
dict[1933519201] = { return Api.PeerSettings.parse_peerSettings($0) } dict[1933519201] = { return Api.PeerSettings.parse_peerSettings($0) }
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) } dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
dict[480546647] = { return Api.InputChatPhoto.parse_inputChatPhotoEmpty($0) } dict[480546647] = { return Api.InputChatPhoto.parse_inputChatPhotoEmpty($0) }
@ -637,7 +638,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) } dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) }
dict[-770990276] = { return Api.ChatPhoto.parse_chatPhoto($0) } dict[-770990276] = { return Api.ChatPhoto.parse_chatPhoto($0) }
dict[1869903447] = { return Api.PageCaption.parse_pageCaption($0) } dict[1869903447] = { return Api.PageCaption.parse_pageCaption($0) }
dict[1062645411] = { return Api.payments.PaymentForm.parse_paymentForm($0) } dict[-1928649707] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
dict[1342771681] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) } dict[1342771681] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
dict[863093588] = { return Api.messages.PeerDialogs.parse_peerDialogs($0) } dict[863093588] = { return Api.messages.PeerDialogs.parse_peerDialogs($0) }
dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) }
@ -754,6 +755,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) } dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
dict[-1970903652] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) } dict[-1970903652] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) }
dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) } dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) }
dict[894081801] = { return Api.BotInlineMessage.parse_botInlineMessageMediaInvoice($0) }
dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) } dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) }
dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) } dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) }
dict[-209768682] = { return Api.messages.FavedStickers.parse_favedStickers($0) } dict[-209768682] = { return Api.messages.FavedStickers.parse_favedStickers($0) }

View File

@ -3604,13 +3604,13 @@ public extension Api {
} }
public enum GroupCallParticipant: TypeConstructorDescription { public enum GroupCallParticipant: TypeConstructorDescription {
case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?, params: Api.DataJSON?) case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let params): case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating):
if boxed { if boxed {
buffer.appendInt32(-1184160274) buffer.appendInt32(430815881)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
@ -3620,15 +3620,14 @@ public extension Api {
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 6) != 0 {params!.serialize(buffer, true)}
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let params): case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating):
return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about), ("raiseHandRating", raiseHandRating), ("params", params)]) return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about), ("raiseHandRating", raiseHandRating)])
} }
} }
@ -3651,10 +3650,6 @@ public extension Api {
if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) } if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) }
var _8: Int64? var _8: Int64?
if Int(_1!) & Int(1 << 13) != 0 {_8 = reader.readInt64() } if Int(_1!) & Int(1 << 13) != 0 {_8 = reader.readInt64() }
var _9: Api.DataJSON?
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.DataJSON
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -3663,9 +3658,8 @@ public extension Api {
let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8)
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, params: _9)
} }
else { else {
return nil return nil
@ -3906,27 +3900,28 @@ public extension Api {
} }
public enum PaymentRequestedInfo: TypeConstructorDescription { public enum PaymentRequestedInfo: TypeConstructorDescription {
case paymentRequestedInfo(flags: Int32, name: String?, phone: String?, email: String?, shippingAddress: Api.PostAddress?) case paymentRequestedInfo(flags: Int32, name: String?, phone: String?, email: String?, shippingAddress: Api.PostAddress?, tipAmount: Int64?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .paymentRequestedInfo(let flags, let name, let phone, let email, let shippingAddress): case .paymentRequestedInfo(let flags, let name, let phone, let email, let shippingAddress, let tipAmount):
if boxed { if boxed {
buffer.appendInt32(-1868808300) buffer.appendInt32(-2017173756)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(name!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeString(name!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(phone!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(phone!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(email!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeString(email!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {shippingAddress!.serialize(buffer, true)} if Int(flags) & Int(1 << 3) != 0 {shippingAddress!.serialize(buffer, true)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)}
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .paymentRequestedInfo(let flags, let name, let phone, let email, let shippingAddress): case .paymentRequestedInfo(let flags, let name, let phone, let email, let shippingAddress, let tipAmount):
return ("paymentRequestedInfo", [("flags", flags), ("name", name), ("phone", phone), ("email", email), ("shippingAddress", shippingAddress)]) return ("paymentRequestedInfo", [("flags", flags), ("name", name), ("phone", phone), ("email", email), ("shippingAddress", shippingAddress), ("tipAmount", tipAmount)])
} }
} }
@ -3943,13 +3938,16 @@ public extension Api {
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.PostAddress _5 = Api.parse(reader, signature: signature) as? Api.PostAddress
} } } }
var _6: Int64?
if Int(_1!) & Int(1 << 4) != 0 {_6 = reader.readInt64() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 { let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
return Api.PaymentRequestedInfo.paymentRequestedInfo(flags: _1!, name: _2, phone: _3, email: _4, shippingAddress: _5) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.PaymentRequestedInfo.paymentRequestedInfo(flags: _1!, name: _2, phone: _3, email: _4, shippingAddress: _5, tipAmount: _6)
} }
else { else {
return nil return nil
@ -9078,6 +9076,7 @@ public extension Api {
case inputBotInlineMessageMediaVenue(flags: Int32, geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?) case inputBotInlineMessageMediaVenue(flags: Int32, geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?) case inputBotInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageGame(flags: Int32, replyMarkup: Api.ReplyMarkup?) case inputBotInlineMessageGame(flags: Int32, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -9149,6 +9148,21 @@ public extension Api {
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break break
case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let replyMarkup):
if boxed {
buffer.appendInt32(-717976187)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)}
invoice.serialize(buffer, true)
serializeBytes(payload, buffer: buffer, boxed: false)
serializeString(provider, buffer: buffer, boxed: false)
providerData.serialize(buffer, true)
serializeString(startParam, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
} }
} }
@ -9166,6 +9180,8 @@ public extension Api {
return ("inputBotInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)]) return ("inputBotInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageGame(let flags, let replyMarkup): case .inputBotInlineMessageGame(let flags, let replyMarkup):
return ("inputBotInlineMessageGame", [("flags", flags), ("replyMarkup", replyMarkup)]) return ("inputBotInlineMessageGame", [("flags", flags), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let replyMarkup):
return ("inputBotInlineMessageMediaInvoice", [("flags", flags), ("title", title), ("description", description), ("photo", photo), ("invoice", invoice), ("payload", payload), ("provider", provider), ("providerData", providerData), ("startParam", startParam), ("replyMarkup", replyMarkup)])
} }
} }
@ -9327,6 +9343,52 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_inputBotInlineMessageMediaInvoice(_ reader: BufferReader) -> InputBotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.InputWebDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.InputWebDocument
} }
var _5: Api.Invoice?
if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Invoice
}
var _6: Buffer?
_6 = parseBytes(reader)
var _7: String?
_7 = parseString(reader)
var _8: Api.DataJSON?
if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.DataJSON
}
var _9: String?
_9 = parseString(reader)
var _10: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_10 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = _8 != nil
let _c9 = _9 != nil
let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.InputBotInlineMessage.inputBotInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9!, replyMarkup: _10)
}
else {
return nil
}
}
} }
public enum KeyboardButtonRow: TypeConstructorDescription { public enum KeyboardButtonRow: TypeConstructorDescription {
@ -15020,13 +15082,13 @@ public extension Api {
} }
public enum Invoice: TypeConstructorDescription { public enum Invoice: TypeConstructorDescription {
case invoice(flags: Int32, currency: String, prices: [Api.LabeledPrice]) case invoice(flags: Int32, currency: String, prices: [Api.LabeledPrice], minTipAmount: Int64?, maxTipAmount: Int64?, defaultTipAmount: Int64?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .invoice(let flags, let currency, let prices): case .invoice(let flags, let currency, let prices, let minTipAmount, let maxTipAmount, let defaultTipAmount):
if boxed { if boxed {
buffer.appendInt32(-1022713000) buffer.appendInt32(615970509)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(currency, buffer: buffer, boxed: false) serializeString(currency, buffer: buffer, boxed: false)
@ -15035,14 +15097,17 @@ public extension Api {
for item in prices { for item in prices {
item.serialize(buffer, true) item.serialize(buffer, true)
} }
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(minTipAmount!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(maxTipAmount!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(defaultTipAmount!, buffer: buffer, boxed: false)}
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .invoice(let flags, let currency, let prices): case .invoice(let flags, let currency, let prices, let minTipAmount, let maxTipAmount, let defaultTipAmount):
return ("invoice", [("flags", flags), ("currency", currency), ("prices", prices)]) return ("invoice", [("flags", flags), ("currency", currency), ("prices", prices), ("minTipAmount", minTipAmount), ("maxTipAmount", maxTipAmount), ("defaultTipAmount", defaultTipAmount)])
} }
} }
@ -15055,11 +15120,20 @@ public extension Api {
if let _ = reader.readInt32() { if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LabeledPrice.self) _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LabeledPrice.self)
} }
var _4: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_4 = reader.readInt64() }
var _5: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_5 = reader.readInt64() }
var _6: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_6 = reader.readInt64() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
if _c1 && _c2 && _c3 { let _c4 = (Int(_1!) & Int(1 << 8) == 0) || _4 != nil
return Api.Invoice.invoice(flags: _1!, currency: _2!, prices: _3!) let _c5 = (Int(_1!) & Int(1 << 8) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 8) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.Invoice.invoice(flags: _1!, currency: _2!, prices: _3!, minTipAmount: _4, maxTipAmount: _5, defaultTipAmount: _6)
} }
else { else {
return nil return nil
@ -19173,6 +19247,7 @@ public extension Api {
case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, currency: String, totalAmount: Int64, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -19237,6 +19312,18 @@ public extension Api {
serializeString(vcard, buffer: buffer, boxed: false) serializeString(vcard, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break break
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
if boxed {
buffer.appendInt32(894081801)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)}
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(totalAmount, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
} }
} }
@ -19252,6 +19339,8 @@ public extension Api {
return ("botInlineMessageMediaVenue", [("flags", flags), ("geo", geo), ("title", title), ("address", address), ("provider", provider), ("venueId", venueId), ("venueType", venueType), ("replyMarkup", replyMarkup)]) return ("botInlineMessageMediaVenue", [("flags", flags), ("geo", geo), ("title", title), ("address", address), ("provider", provider), ("venueId", venueId), ("venueType", venueType), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup): case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
return ("botInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)]) return ("botInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
return ("botInlineMessageMediaInvoice", [("flags", flags), ("title", title), ("description", description), ("photo", photo), ("currency", currency), ("totalAmount", totalAmount), ("replyMarkup", replyMarkup)])
} }
} }
@ -19397,6 +19486,39 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_botInlineMessageMediaInvoice(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.WebDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.WebDocument
} }
var _5: String?
_5 = parseString(reader)
var _6: Int64?
_6 = reader.readInt64()
var _7: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.BotInlineMessage.botInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, currency: _5!, totalAmount: _6!, replyMarkup: _7)
}
else {
return nil
}
}
} }
public enum InputPeerNotifySettings: TypeConstructorDescription { public enum InputPeerNotifySettings: TypeConstructorDescription {

View File

@ -301,15 +301,16 @@ public struct payments {
} }
public enum PaymentForm: TypeConstructorDescription { public enum PaymentForm: TypeConstructorDescription {
case paymentForm(flags: Int32, botId: Int32, invoice: Api.Invoice, providerId: Int32, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: Api.PaymentSavedCredentials?, users: [Api.User]) case paymentForm(flags: Int32, formId: Int64, botId: Int32, invoice: Api.Invoice, providerId: Int32, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: Api.PaymentSavedCredentials?, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .paymentForm(let flags, let botId, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let savedInfo, let savedCredentials, let users): case .paymentForm(let flags, let formId, let botId, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let savedInfo, let savedCredentials, let users):
if boxed { if boxed {
buffer.appendInt32(1062645411) buffer.appendInt32(-1928649707)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(formId, buffer: buffer, boxed: false)
serializeInt32(botId, buffer: buffer, boxed: false) serializeInt32(botId, buffer: buffer, boxed: false)
invoice.serialize(buffer, true) invoice.serialize(buffer, true)
serializeInt32(providerId, buffer: buffer, boxed: false) serializeInt32(providerId, buffer: buffer, boxed: false)
@ -329,54 +330,57 @@ public struct payments {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .paymentForm(let flags, let botId, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let savedInfo, let savedCredentials, let users): case .paymentForm(let flags, let formId, let botId, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let savedInfo, let savedCredentials, let users):
return ("paymentForm", [("flags", flags), ("botId", botId), ("invoice", invoice), ("providerId", providerId), ("url", url), ("nativeProvider", nativeProvider), ("nativeParams", nativeParams), ("savedInfo", savedInfo), ("savedCredentials", savedCredentials), ("users", users)]) return ("paymentForm", [("flags", flags), ("formId", formId), ("botId", botId), ("invoice", invoice), ("providerId", providerId), ("url", url), ("nativeProvider", nativeProvider), ("nativeParams", nativeParams), ("savedInfo", savedInfo), ("savedCredentials", savedCredentials), ("users", users)])
} }
} }
public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? { public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? {
var _1: Int32? var _1: Int32?
_1 = reader.readInt32() _1 = reader.readInt32()
var _2: Int32? var _2: Int64?
_2 = reader.readInt32() _2 = reader.readInt64()
var _3: Api.Invoice? var _3: Int32?
_3 = reader.readInt32()
var _4: Api.Invoice?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.Invoice _4 = Api.parse(reader, signature: signature) as? Api.Invoice
} }
var _4: Int32? var _5: Int32?
_4 = reader.readInt32() _5 = reader.readInt32()
var _5: String?
_5 = parseString(reader)
var _6: String? var _6: String?
if Int(_1!) & Int(1 << 4) != 0 {_6 = parseString(reader) } _6 = parseString(reader)
var _7: Api.DataJSON? var _7: String?
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
var _8: Api.DataJSON?
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.DataJSON _8 = Api.parse(reader, signature: signature) as? Api.DataJSON
} } } }
var _8: Api.PaymentRequestedInfo? var _9: Api.PaymentRequestedInfo?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
} } } }
var _9: Api.PaymentSavedCredentials? var _10: Api.PaymentSavedCredentials?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.PaymentSavedCredentials _10 = Api.parse(reader, signature: signature) as? Api.PaymentSavedCredentials
} } } }
var _10: [Api.User]? var _11: [Api.User]?
if let _ = reader.readInt32() { if let _ = reader.readInt32() {
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
} }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
let _c4 = _4 != nil let _c4 = _4 != nil
let _c5 = _5 != nil let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil
let _c10 = _10 != nil let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { let _c11 = _11 != nil
return Api.payments.PaymentForm.paymentForm(flags: _1!, botId: _2!, invoice: _3!, providerId: _4!, url: _5!, nativeProvider: _6, nativeParams: _7, savedInfo: _8, savedCredentials: _9, users: _10!) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, invoice: _4!, providerId: _5!, url: _6!, nativeProvider: _7, nativeParams: _8, savedInfo: _9, savedCredentials: _10, users: _11!)
} }
else { else {
return nil return nil

View File

@ -4848,11 +4848,14 @@ public extension Api {
} }
} }
public struct payments { public struct payments {
public static func getPaymentForm(msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentForm>) { public static func getPaymentForm(flags: Int32, peer: Api.InputPeer, msgId: Int32, themeParams: Api.DataJSON?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentForm>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-1712285883) buffer.appendInt32(-1976353651)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.getPaymentForm", parameters: [("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentForm? in if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)}
return (FunctionDescription(name: "payments.getPaymentForm", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("themeParams", themeParams)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentForm? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.payments.PaymentForm? var result: Api.payments.PaymentForm?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -4862,11 +4865,12 @@ public extension Api {
}) })
} }
public static func getPaymentReceipt(msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentReceipt>) { public static func getPaymentReceipt(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentReceipt>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-1601001088) buffer.appendInt32(611897804)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.getPaymentReceipt", parameters: [("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentReceipt? in return (FunctionDescription(name: "payments.getPaymentReceipt", parameters: [("peer", peer), ("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentReceipt? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.payments.PaymentReceipt? var result: Api.payments.PaymentReceipt?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -4876,13 +4880,14 @@ public extension Api {
}) })
} }
public static func validateRequestedInfo(flags: Int32, msgId: Int32, info: Api.PaymentRequestedInfo) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.ValidatedRequestedInfo>) { public static func validateRequestedInfo(flags: Int32, peer: Api.InputPeer, msgId: Int32, info: Api.PaymentRequestedInfo) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.ValidatedRequestedInfo>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(1997180532) buffer.appendInt32(-619695760)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
info.serialize(buffer, true) info.serialize(buffer, true)
return (FunctionDescription(name: "payments.validateRequestedInfo", parameters: [("flags", flags), ("msgId", msgId), ("info", info)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ValidatedRequestedInfo? in return (FunctionDescription(name: "payments.validateRequestedInfo", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("info", info)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.ValidatedRequestedInfo? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.payments.ValidatedRequestedInfo? var result: Api.payments.ValidatedRequestedInfo?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -4892,15 +4897,17 @@ public extension Api {
}) })
} }
public static func sendPaymentForm(flags: Int32, msgId: Int32, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentResult>) { public static func sendPaymentForm(flags: Int32, formId: Int64, peer: Api.InputPeer, msgId: Int32, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentResult>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(730364339) buffer.appendInt32(1940324740)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(formId, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(requestedInfoId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeString(requestedInfoId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(shippingOptionId!, buffer: buffer, boxed: false)}
credentials.serialize(buffer, true) credentials.serialize(buffer, true)
return (FunctionDescription(name: "payments.sendPaymentForm", parameters: [("flags", flags), ("msgId", msgId), ("requestedInfoId", requestedInfoId), ("shippingOptionId", shippingOptionId), ("credentials", credentials)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentResult? in return (FunctionDescription(name: "payments.sendPaymentForm", parameters: [("flags", flags), ("formId", formId), ("peer", peer), ("msgId", msgId), ("requestedInfoId", requestedInfoId), ("shippingOptionId", shippingOptionId), ("credentials", credentials)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.PaymentResult? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.payments.PaymentResult? var result: Api.payments.PaymentResult?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -37,10 +37,17 @@ public struct BotPaymentPrice : Equatable {
} }
public struct BotPaymentInvoice : Equatable { public struct BotPaymentInvoice : Equatable {
public struct Tip: Equatable {
public var min: Int64
public var max: Int64
public var `default`: Int64
}
public let isTest: Bool public let isTest: Bool
public let requestedFields: BotPaymentInvoiceFields public let requestedFields: BotPaymentInvoiceFields
public let currency: String public let currency: String
public let prices: [BotPaymentPrice] public let prices: [BotPaymentPrice]
public let tip: Tip?
} }
public struct BotPaymentNativeProvider : Equatable { public struct BotPaymentNativeProvider : Equatable {
@ -67,16 +74,18 @@ public struct BotPaymentShippingAddress: Equatable {
} }
public struct BotPaymentRequestedInfo: Equatable { public struct BotPaymentRequestedInfo: Equatable {
public let name: String? public var name: String?
public let phone: String? public var phone: String?
public let email: String? public var email: String?
public let shippingAddress: BotPaymentShippingAddress? public var shippingAddress: BotPaymentShippingAddress?
public var tipAmount: Int64?
public init(name: String?, phone: String?, email: String?, shippingAddress: BotPaymentShippingAddress?) { public init(name: String?, phone: String?, email: String?, shippingAddress: BotPaymentShippingAddress?, tipAmount: Int64?) {
self.name = name self.name = name
self.phone = phone self.phone = phone
self.email = email self.email = email
self.shippingAddress = shippingAddress self.shippingAddress = shippingAddress
self.tipAmount = tipAmount
} }
} }
@ -96,6 +105,7 @@ public enum BotPaymentSavedCredentials: Equatable {
} }
public struct BotPaymentForm : Equatable { public struct BotPaymentForm : Equatable {
public let id: Int64
public let canSaveCredentials: Bool public let canSaveCredentials: Bool
public let passwordMissing: Bool public let passwordMissing: Bool
public let invoice: BotPaymentInvoice public let invoice: BotPaymentInvoice
@ -113,7 +123,7 @@ public enum BotPaymentFormRequestError {
extension BotPaymentInvoice { extension BotPaymentInvoice {
init(apiInvoice: Api.Invoice) { init(apiInvoice: Api.Invoice) {
switch apiInvoice { switch apiInvoice {
case let .invoice(flags, currency, prices): case let .invoice(flags, currency, prices, minTipAmount, maxTipAmount, defaultTipAmount):
var fields = BotPaymentInvoiceFields() var fields = BotPaymentInvoiceFields()
if (flags & (1 << 1)) != 0 { if (flags & (1 << 1)) != 0 {
fields.insert(.name) fields.insert(.name)
@ -136,12 +146,16 @@ extension BotPaymentInvoice {
if (flags & (1 << 7)) != 0 { if (flags & (1 << 7)) != 0 {
fields.insert(.emailAvailableToProvider) fields.insert(.emailAvailableToProvider)
} }
var parsedTip: BotPaymentInvoice.Tip?
if let minTipAmount = minTipAmount, let maxTipAmount = maxTipAmount, let defaultTipAmount = defaultTipAmount {
parsedTip = BotPaymentInvoice.Tip(min: minTipAmount, max: maxTipAmount, default: defaultTipAmount)
}
self.init(isTest: (flags & (1 << 0)) != 0, requestedFields: fields, currency: currency, prices: prices.map { self.init(isTest: (flags & (1 << 0)) != 0, requestedFields: fields, currency: currency, prices: prices.map {
switch $0 { switch $0 {
case let .labeledPrice(label, amount): case let .labeledPrice(label, amount):
return BotPaymentPrice(label: label, amount: amount) return BotPaymentPrice(label: label, amount: amount)
} }
}) }, tip: parsedTip)
} }
} }
} }
@ -149,7 +163,7 @@ extension BotPaymentInvoice {
extension BotPaymentRequestedInfo { extension BotPaymentRequestedInfo {
init(apiInfo: Api.PaymentRequestedInfo) { init(apiInfo: Api.PaymentRequestedInfo) {
switch apiInfo { switch apiInfo {
case let .paymentRequestedInfo(_, name, phone, email, shippingAddress): case let .paymentRequestedInfo(_, name, phone, email, shippingAddress, tipAmount):
var parsedShippingAddress: BotPaymentShippingAddress? var parsedShippingAddress: BotPaymentShippingAddress?
if let shippingAddress = shippingAddress { if let shippingAddress = shippingAddress {
switch shippingAddress { switch shippingAddress {
@ -157,20 +171,28 @@ extension BotPaymentRequestedInfo {
parsedShippingAddress = BotPaymentShippingAddress(streetLine1: streetLine1, streetLine2: streetLine2, city: city, state: state, countryIso2: countryIso2, postCode: postCode) parsedShippingAddress = BotPaymentShippingAddress(streetLine1: streetLine1, streetLine2: streetLine2, city: city, state: state, countryIso2: countryIso2, postCode: postCode)
} }
} }
self.init(name: name, phone: phone, email: email, shippingAddress: parsedShippingAddress) self.init(name: name, phone: phone, email: email, shippingAddress: parsedShippingAddress, tipAmount: tipAmount)
} }
} }
} }
public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<BotPaymentForm, BotPaymentFormRequestError> { public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<BotPaymentForm, BotPaymentFormRequestError> {
return network.request(Api.functions.payments.getPaymentForm(msgId: messageId.id)) return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> castError(BotPaymentFormRequestError.self)
|> mapToSignal { inputPeer -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return network.request(Api.functions.payments.getPaymentForm(flags: 0, peer: inputPeer, msgId: messageId.id, themeParams: nil))
|> `catch` { _ -> Signal<Api.payments.PaymentForm, BotPaymentFormRequestError> in |> `catch` { _ -> Signal<Api.payments.PaymentForm, BotPaymentFormRequestError> in
return .fail(.generic) return .fail(.generic)
} }
|> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in |> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
return postbox.transaction { transaction -> BotPaymentForm in return postbox.transaction { transaction -> BotPaymentForm in
switch result { switch result {
case let .paymentForm(flags, _, invoice, providerId, url, nativeProvider, nativeParams, savedInfo, savedCredentials, apiUsers): case let .paymentForm(flags, id, _, invoice, providerId, url, nativeProvider, nativeParams, savedInfo, savedCredentials, apiUsers):
var peers: [Peer] = [] var peers: [Peer] = []
for user in apiUsers { for user in apiUsers {
let parsed = TelegramUser(user: user) let parsed = TelegramUser(user: user)
@ -179,7 +201,7 @@ public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: M
updatePeers(transaction: transaction, peers: peers, update: { _, updated in updatePeers(transaction: transaction, peers: peers, update: { _, updated in
return updated return updated
}) })
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice) let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
var parsedNativeProvider: BotPaymentNativeProvider? var parsedNativeProvider: BotPaymentNativeProvider?
if let nativeProvider = nativeProvider, let nativeParams = nativeParams { if let nativeProvider = nativeProvider, let nativeParams = nativeParams {
@ -196,10 +218,12 @@ public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: M
parsedSavedCredentials = .card(id: id, title: title) parsedSavedCredentials = .card(id: id, title: title)
} }
} }
return BotPaymentForm(canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: providerId), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials) return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: providerId), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials)
} }
} |> mapError { _ -> BotPaymentFormRequestError in return .generic } }
|> mapError { _ -> BotPaymentFormRequestError in }
} }
}
} }
public enum ValidateBotPaymentFormError { public enum ValidateBotPaymentFormError {
@ -238,27 +262,39 @@ extension BotPaymentShippingOption {
} }
} }
public func validateBotPaymentForm(network: Network, saveInfo: Bool, messageId: MessageId, formInfo: BotPaymentRequestedInfo) -> Signal<BotPaymentValidatedFormInfo, ValidateBotPaymentFormError> { public func validateBotPaymentForm(account: Account, saveInfo: Bool, messageId: MessageId, formInfo: BotPaymentRequestedInfo) -> Signal<BotPaymentValidatedFormInfo, ValidateBotPaymentFormError> {
var flags: Int32 = 0 return account.postbox.transaction { transaction -> Api.InputPeer? in
if saveInfo { return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
flags |= (1 << 0)
} }
var infoFlags: Int32 = 0 |> castError(ValidateBotPaymentFormError.self)
if let _ = formInfo.name { |> mapToSignal { inputPeer -> Signal<BotPaymentValidatedFormInfo, ValidateBotPaymentFormError> in
infoFlags |= (1 << 0) guard let inputPeer = inputPeer else {
} return .fail(.generic)
if let _ = formInfo.phone { }
infoFlags |= (1 << 1)
} var flags: Int32 = 0
if let _ = formInfo.email { if saveInfo {
infoFlags |= (1 << 2) flags |= (1 << 0)
} }
var apiShippingAddress: Api.PostAddress? var infoFlags: Int32 = 0
if let address = formInfo.shippingAddress { if let _ = formInfo.name {
infoFlags |= (1 << 3) infoFlags |= (1 << 0)
apiShippingAddress = .postAddress(streetLine1: address.streetLine1, streetLine2: address.streetLine2, city: address.city, state: address.state, countryIso2: address.countryIso2, postCode: address.postCode) }
} if let _ = formInfo.phone {
return network.request(Api.functions.payments.validateRequestedInfo(flags: flags, msgId: messageId.id, info: .paymentRequestedInfo(flags: infoFlags, name: formInfo.name, phone: formInfo.phone, email: formInfo.email, shippingAddress: apiShippingAddress))) infoFlags |= (1 << 1)
}
if let _ = formInfo.email {
infoFlags |= (1 << 2)
}
var apiShippingAddress: Api.PostAddress?
if let address = formInfo.shippingAddress {
infoFlags |= (1 << 3)
apiShippingAddress = .postAddress(streetLine1: address.streetLine1, streetLine2: address.streetLine2, city: address.city, state: address.state, countryIso2: address.countryIso2, postCode: address.postCode)
}
if let _ = formInfo.tipAmount {
infoFlags |= (1 << 4)
}
return account.network.request(Api.functions.payments.validateRequestedInfo(flags: flags, peer: inputPeer, msgId: messageId.id, info: .paymentRequestedInfo(flags: infoFlags, name: formInfo.name, phone: formInfo.phone, email: formInfo.email, shippingAddress: apiShippingAddress, tipAmount: formInfo.tipAmount)))
|> mapError { error -> ValidateBotPaymentFormError in |> mapError { error -> ValidateBotPaymentFormError in
if error.errorDescription == "SHIPPING_NOT_AVAILABLE" { if error.errorDescription == "SHIPPING_NOT_AVAILABLE" {
return .shippingNotAvailable return .shippingNotAvailable
@ -286,6 +322,7 @@ public func validateBotPaymentForm(network: Network, saveInfo: Bool, messageId:
}) })
} }
} }
}
} }
public enum BotPaymentCredentials { public enum BotPaymentCredentials {
@ -306,46 +343,56 @@ public enum SendBotPaymentResult {
case externalVerificationRequired(url: String) case externalVerificationRequired(url: String)
} }
public func sendBotPaymentForm(account: Account, messageId: MessageId, validatedInfoId: String?, shippingOptionId: String?, credentials: BotPaymentCredentials) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> { public func sendBotPaymentForm(account: Account, messageId: MessageId, formId: Int64, validatedInfoId: String?, shippingOptionId: String?, credentials: BotPaymentCredentials) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
let apiCredentials: Api.InputPaymentCredentials return account.postbox.transaction { transaction -> Api.InputPeer? in
switch credentials { return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
case let .generic(data, saveOnServer): }
var credentialsFlags: Int32 = 0 |> castError(SendBotPaymentFormError.self)
if saveOnServer { |> mapToSignal { inputPeer -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
credentialsFlags |= (1 << 0) guard let inputPeer = inputPeer else {
return .fail(.generic)
}
let apiCredentials: Api.InputPaymentCredentials
switch credentials {
case let .generic(data, saveOnServer):
var credentialsFlags: Int32 = 0
if saveOnServer {
credentialsFlags |= (1 << 0)
}
apiCredentials = .inputPaymentCredentials(flags: credentialsFlags, data: .dataJSON(data: data))
case let .saved(id, tempPassword):
apiCredentials = .inputPaymentCredentialsSaved(id: id, tmpPassword: Buffer(data: tempPassword))
case let .applePay(data):
apiCredentials = .inputPaymentCredentialsApplePay(paymentData: .dataJSON(data: data))
}
var flags: Int32 = 0
if validatedInfoId != nil {
flags |= (1 << 0)
}
if shippingOptionId != nil {
flags |= (1 << 1)
}
return account.network.request(Api.functions.payments.sendPaymentForm(flags: flags, formId: formId, peer: inputPeer, msgId: messageId.id, requestedInfoId: validatedInfoId, shippingOptionId: shippingOptionId, credentials: apiCredentials))
|> map { result -> SendBotPaymentResult in
switch result {
case let .paymentResult(updates):
account.stateManager.addUpdates(updates)
return .done
case let .paymentVerificationNeeded(url):
return .externalVerificationRequired(url: url)
} }
apiCredentials = .inputPaymentCredentials(flags: credentialsFlags, data: .dataJSON(data: data))
case let .saved(id, tempPassword):
apiCredentials = .inputPaymentCredentialsSaved(id: id, tmpPassword: Buffer(data: tempPassword))
case let .applePay(data):
apiCredentials = .inputPaymentCredentialsApplePay(paymentData: .dataJSON(data: data))
}
var flags: Int32 = 0
if validatedInfoId != nil {
flags |= (1 << 0)
}
if shippingOptionId != nil {
flags |= (1 << 1)
}
return account.network.request(Api.functions.payments.sendPaymentForm(flags: flags, msgId: messageId.id, requestedInfoId: validatedInfoId, shippingOptionId: shippingOptionId, credentials: apiCredentials))
|> map { result -> SendBotPaymentResult in
switch result {
case let .paymentResult(updates):
account.stateManager.addUpdates(updates)
return .done
case let .paymentVerificationNeeded(url):
return .externalVerificationRequired(url: url)
} }
} |> `catch` { error -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
|> `catch` { error -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in if error.errorDescription == "BOT_PRECHECKOUT_FAILED" {
if error.errorDescription == "BOT_PRECHECKOUT_FAILED" { return .fail(.precheckoutFailed)
return .fail(.precheckoutFailed) } else if error.errorDescription == "PAYMENT_FAILED" {
} else if error.errorDescription == "PAYMENT_FAILED" { return .fail(.paymentFailed)
return .fail(.paymentFailed) } else if error.errorDescription == "INVOICE_ALREADY_PAID" {
} else if error.errorDescription == "INVOICE_ALREADY_PAID" { return .fail(.alreadyPaid)
return .fail(.alreadyPaid) }
return .fail(.generic)
} }
return .fail(.generic)
} }
} }
@ -356,16 +403,32 @@ public struct BotPaymentReceipt : Equatable {
public let credentialsTitle: String public let credentialsTitle: String
} }
public func requestBotPaymentReceipt(network: Network, messageId: MessageId) -> Signal<BotPaymentReceipt, NoError> { public enum RequestBotPaymentReceiptError {
return network.request(Api.functions.payments.getPaymentReceipt(msgId: messageId.id)) case generic
|> retryRequest }
|> map { result -> BotPaymentReceipt in
switch result { public func requestBotPaymentReceipt(account: Account, messageId: MessageId) -> Signal<BotPaymentReceipt, RequestBotPaymentReceiptError> {
case let .paymentReceipt(_, _, _, invoice, _, info, shipping, _, _, credentialsTitle, _): return account.postbox.transaction { transaction -> Api.InputPeer? in
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice) return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
let parsedInfo = info.flatMap(BotPaymentRequestedInfo.init) }
let shippingOption = shipping.flatMap(BotPaymentShippingOption.init) |> castError(RequestBotPaymentReceiptError.self)
return BotPaymentReceipt(invoice: parsedInvoice, info: parsedInfo, shippingOption: shippingOption, credentialsTitle: credentialsTitle) |> mapToSignal { inputPeer -> Signal<BotPaymentReceipt, RequestBotPaymentReceiptError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.payments.getPaymentReceipt(peer: inputPeer, msgId: messageId.id))
|> mapError { _ -> RequestBotPaymentReceiptError in
return .generic
}
|> map { result -> BotPaymentReceipt in
switch result {
case let .paymentReceipt(_, _, _, invoice, _, info, shipping, _, _, credentialsTitle, _):
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
let parsedInfo = info.flatMap(BotPaymentRequestedInfo.init)
let shippingOption = shipping.flatMap(BotPaymentShippingOption.init)
return BotPaymentReceipt(invoice: parsedInvoice, info: parsedInfo, shippingOption: shippingOption, credentialsTitle: credentialsTitle)
}
} }
} }
} }

View File

@ -19,6 +19,7 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable {
case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?) case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?)
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?) case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?) case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?)
case invoice(media: TelegramMediaInvoice, replyMarkup: ReplyMarkupMessageAttribute?)
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_v", orElse: 0) { switch decoder.decodeInt32ForKey("_v", orElse: 0) {
@ -30,6 +31,8 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable {
self = .mapLocation(media: decoder.decodeObjectForKey("l") as! TelegramMediaMap, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) self = .mapLocation(media: decoder.decodeObjectForKey("l") as! TelegramMediaMap, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 3: case 3:
self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 4:
self = .invoice(media: decoder.decodeObjectForKey("i") as! TelegramMediaInvoice, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
default: default:
self = .auto(caption: "", entities: nil, replyMarkup: nil) self = .auto(caption: "", entities: nil, replyMarkup: nil)
} }
@ -80,6 +83,14 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable {
} else { } else {
encoder.encodeNil(forKey: "m") encoder.encodeNil(forKey: "m")
} }
case let .invoice(media: media, replyMarkup):
encoder.encodeInt32(4, forKey: "_v")
encoder.encodeObject(media, forKey: "i")
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
encoder.encodeNil(forKey: "m")
}
} }
} }
@ -157,6 +168,18 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable {
} else { } else {
return false return false
} }
case let .invoice(lhsMedia, lhsReplyMarkup):
if case let .invoice(rhsMedia, rhsReplyMarkup) = rhs {
if !lhsMedia.isEqual(to: rhsMedia) {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
return true
} else {
return false
}
} }
} }
} }
@ -444,6 +467,19 @@ extension ChatContextResultMessage {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
} }
self = .contact(media: media, replyMarkup: parsedReplyMarkup) self = .contact(media: media, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaInvoice(flags, title, description, photo, currency, totalAmount, replyMarkup):
var parsedFlags = TelegramMediaInvoiceFlags()
if (flags & (1 << 3)) != 0 {
parsedFlags.insert(.isTest)
}
if (flags & (1 << 1)) != 0 {
parsedFlags.insert(.shippingAddressRequested)
}
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .invoice(media: TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: currency, totalAmount: totalAmount, startParam: "", flags: parsedFlags), replyMarkup: parsedReplyMarkup)
} }
} }
} }

View File

@ -110,7 +110,8 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
loop: for participant in participants { loop: for participant in participants {
switch participant { switch participant {
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating/*, params*/):
let params: Api.DataJSON? = nil
let peerId: PeerId let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -297,7 +298,8 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
loop: for participant in participants { loop: for participant in participants {
switch participant { switch participant {
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating/*, params*/):
let params: Api.DataJSON? = nil
let peerId: PeerId let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -545,7 +547,8 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
case let .updateGroupCallParticipants(_, participants, _): case let .updateGroupCallParticipants(_, participants, _):
loop: for participant in participants { loop: for participant in participants {
switch participant { switch participant {
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating/*, params*/):
let params: Api.DataJSON? = nil
let peerId: PeerId let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -1751,7 +1754,8 @@ public final class GroupCallParticipantsContext {
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
init(_ apiParticipant: Api.GroupCallParticipant) { init(_ apiParticipant: Api.GroupCallParticipant) {
switch apiParticipant { switch apiParticipant {
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating/*, params*/):
let params: Api.DataJSON? = nil
let peerId: PeerId let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -1814,7 +1818,8 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = [] var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
for participant in participants { for participant in participants {
switch participant { switch participant {
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating/*, params*/):
let params: Api.DataJSON? = nil
let peerId: PeerId let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):

View File

@ -141,5 +141,10 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
attributes.append(replyMarkup) attributes.append(replyMarkup)
} }
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
case let .invoice(media, replyMarkup):
if let replyMarkup = replyMarkup {
attributes.append(replyMarkup)
}
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
} }
} }

View File

@ -46,6 +46,28 @@ private func loadCurrencyFormatterEntries() -> [String: CurrencyFormatterEntry]
private let currencyFormatterEntries = loadCurrencyFormatterEntries() private let currencyFormatterEntries = loadCurrencyFormatterEntries()
public func fractionalToCurrencyAmount(value: Double, currency: String) -> Int64? {
guard let entry = currencyFormatterEntries[currency] ?? currencyFormatterEntries["USD"] else {
return nil
}
var factor: Double = 1.0
for _ in 0 ..< entry.decimalDigits {
factor *= 10.0
}
return Int64(value * factor)
}
public func currencyToFractionalAmount(value: Int64, currency: String) -> Double? {
guard let entry = currencyFormatterEntries[currency] ?? currencyFormatterEntries["USD"] else {
return nil
}
var factor: Double = 1.0
for _ in 0 ..< entry.decimalDigits {
factor *= 10.0
}
return Double(value) / factor
}
public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String { public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String {
if let entry = currencyFormatterEntries[currency] ?? currencyFormatterEntries["USD"] { if let entry = currencyFormatterEntries[currency] ?? currencyFormatterEntries["USD"] {
var result = "" var result = ""

View File

@ -619,6 +619,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if canSetupAutoremoveTimeout { if canSetupAutoremoveTimeout {
strongSelf.presentAutoremoveSetup() strongSelf.presentAutoremoveSetup()
} }
case .paymentSent:
for attribute in message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId))
break
}
}
return true
default: default:
break break
} }