mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
7382d413a6
@ -6444,3 +6444,5 @@ Sorry for the inconvenience.";
|
|||||||
"Checkout.OptionalTipItemPlaceholder" = "Enter Custom";
|
"Checkout.OptionalTipItemPlaceholder" = "Enter Custom";
|
||||||
|
|
||||||
"VoiceChat.ReminderNotify" = "We will notify you when it starts.";
|
"VoiceChat.ReminderNotify" = "We will notify you when it starts.";
|
||||||
|
|
||||||
|
"Checkout.SuccessfulTooltip" = "You paid %1$@ for %2$@.";
|
||||||
|
@ -80,6 +80,7 @@ public final class BotCheckoutController: ViewController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let invoice: TelegramMediaInvoice
|
private let invoice: TelegramMediaInvoice
|
||||||
private let messageId: MessageId
|
private let messageId: MessageId
|
||||||
|
private let completed: (String, MessageId?) -> Void
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
|
||||||
@ -87,11 +88,12 @@ public final class BotCheckoutController: ViewController {
|
|||||||
|
|
||||||
private let inputData: Promise<BotCheckoutController.InputData?>
|
private let inputData: Promise<BotCheckoutController.InputData?>
|
||||||
|
|
||||||
public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>) {
|
public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>, completed: @escaping (String, MessageId?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.invoice = invoice
|
self.invoice = invoice
|
||||||
self.messageId = messageId
|
self.messageId = messageId
|
||||||
self.inputData = inputData
|
self.inputData = inputData
|
||||||
|
self.completed = completed
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -121,7 +123,7 @@ public final class BotCheckoutController: ViewController {
|
|||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissAnimated: { [weak self] in
|
}, dismissAnimated: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
})
|
}, completed: self.completed)
|
||||||
|
|
||||||
//displayNode.enableInteractiveDismiss = true
|
//displayNode.enableInteractiveDismiss = true
|
||||||
|
|
||||||
|
@ -506,6 +506,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
private let messageId: MessageId
|
private let messageId: MessageId
|
||||||
private let present: (ViewController, Any?) -> Void
|
private let present: (ViewController, Any?) -> Void
|
||||||
private let dismissAnimated: () -> Void
|
private let dismissAnimated: () -> Void
|
||||||
|
private let completed: (String, MessageId?) -> Void
|
||||||
|
|
||||||
private var stateValue = BotCheckoutControllerState()
|
private var stateValue = BotCheckoutControllerState()
|
||||||
private let state = ValuePromise(BotCheckoutControllerState(), ignoreRepeated: true)
|
private let state = ValuePromise(BotCheckoutControllerState(), ignoreRepeated: true)
|
||||||
@ -536,12 +537,13 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
private var passwordTip: String?
|
private var passwordTip: String?
|
||||||
private var passwordTipDisposable: Disposable?
|
private var passwordTipDisposable: Disposable?
|
||||||
|
|
||||||
init(controller: BotCheckoutController?, navigationBar: NavigationBar, updateNavigationOffset: @escaping (CGFloat) -> Void, context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void) {
|
init(controller: BotCheckoutController?, navigationBar: NavigationBar, updateNavigationOffset: @escaping (CGFloat) -> Void, context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise<BotCheckoutController.InputData?>, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void, completed: @escaping (String, MessageId?) -> Void) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.context = context
|
self.context = context
|
||||||
self.messageId = messageId
|
self.messageId = messageId
|
||||||
self.present = present
|
self.present = present
|
||||||
self.dismissAnimated = dismissAnimated
|
self.dismissAnimated = dismissAnimated
|
||||||
|
self.completed = completed
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -1213,6 +1215,9 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
tipAmount = 0
|
tipAmount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let totalAmount = currentTotalPrice(paymentForm: paymentForm, validatedFormInfo: self.currentValidatedFormInfo, currentShippingOptionId: self.currentShippingOptionId, currentTip: self.currentTipAmount)
|
||||||
|
let currencyValue = formatCurrencyAmount(totalAmount, currency: paymentForm.invoice.currency)
|
||||||
|
|
||||||
self.payDisposable.set((sendBotPaymentForm(account: self.context.account, messageId: self.messageId, formId: paymentForm.id, validatedInfoId: self.currentValidatedFormInfo?.id, shippingOptionId: self.currentShippingOptionId, tipAmount: tipAmount, 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, tipAmount: tipAmount, 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
|
||||||
@ -1228,18 +1233,31 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
applePayController.presentingViewController?.dismiss(animated: true, completion: nil)
|
applePayController.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch result {
|
let proceedWithCompletion: (Bool, MessageId?) -> Void = { success, receiptMessageId in
|
||||||
case .done:
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
strongSelf.dismissAnimated()
|
strongSelf.dismissAnimated()
|
||||||
|
strongSelf.completed(currencyValue, receiptMessageId)
|
||||||
|
} else {
|
||||||
|
strongSelf.dismissAnimated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case let .done(receiptMessageId):
|
||||||
|
proceedWithCompletion(true, receiptMessageId)
|
||||||
case let .externalVerificationRequired(url):
|
case let .externalVerificationRequired(url):
|
||||||
strongSelf.updateActionButton()
|
strongSelf.updateActionButton()
|
||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: ((Bool) -> Void)?
|
||||||
let controller = BotCheckoutWebInteractionController(context: strongSelf.context, url: url, intent: .externalVerification({ _ in
|
let controller = BotCheckoutWebInteractionController(context: strongSelf.context, url: url, intent: .externalVerification({ success in
|
||||||
dismissImpl?()
|
dismissImpl?(success)
|
||||||
}))
|
}))
|
||||||
dismissImpl = { [weak controller] in
|
dismissImpl = { [weak controller] success in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
self?.dismissAnimated()
|
proceedWithCompletion(success, nil)
|
||||||
}
|
}
|
||||||
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
|
@ -986,6 +986,11 @@ public final class Transaction {
|
|||||||
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func scanTopMessages(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (Message) -> Bool) {
|
||||||
|
assert(!self.disposed)
|
||||||
|
self.postbox?.scanTopMessages(peerId: peerId, namespace: namespace, limit: limit, f)
|
||||||
|
}
|
||||||
|
|
||||||
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||||
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
|
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
|
||||||
}
|
}
|
||||||
@ -3412,6 +3417,26 @@ public final class Postbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func scanTopMessages(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (Message) -> Bool) {
|
||||||
|
let lowerBound = MessageIndex.lowerBound(peerId: peerId, namespace: namespace)
|
||||||
|
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
||||||
|
var remainingLimit = limit
|
||||||
|
while remainingLimit > 0 {
|
||||||
|
let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: nil, from: index, includeFrom: false, to: lowerBound, limit: 10)
|
||||||
|
remainingLimit -= 10
|
||||||
|
for message in messages {
|
||||||
|
if !f(self.renderIntermediateMessage(message)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let last = messages.last {
|
||||||
|
index = last.index
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||||
var remainingLimit = limit
|
var remainingLimit = limit
|
||||||
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
||||||
|
@ -342,7 +342,7 @@ public enum SendBotPaymentFormError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SendBotPaymentResult {
|
public enum SendBotPaymentResult {
|
||||||
case done
|
case done(receiptMessageId: MessageId?)
|
||||||
case externalVerificationRequired(url: String)
|
case externalVerificationRequired(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,27 @@ public func sendBotPaymentForm(account: Account, messageId: MessageId, formId: I
|
|||||||
switch result {
|
switch result {
|
||||||
case let .paymentResult(updates):
|
case let .paymentResult(updates):
|
||||||
account.stateManager.addUpdates(updates)
|
account.stateManager.addUpdates(updates)
|
||||||
return .done
|
var receiptMessageId: MessageId?
|
||||||
|
for apiMessage in updates.messages {
|
||||||
|
if let message = StoreMessage(apiMessage: apiMessage) {
|
||||||
|
for media in message.media {
|
||||||
|
if let action = media as? TelegramMediaAction {
|
||||||
|
if case .paymentSent = action.action {
|
||||||
|
for attribute in message.attributes {
|
||||||
|
if let reply = attribute as? ReplyMessageAttribute {
|
||||||
|
if reply.messageId == messageId {
|
||||||
|
if case let .Id(id) = message.id {
|
||||||
|
receiptMessageId = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .done(receiptMessageId: receiptMessageId)
|
||||||
case let .paymentVerificationNeeded(url):
|
case let .paymentVerificationNeeded(url):
|
||||||
return .externalVerificationRequired(url: url)
|
return .externalVerificationRequired(url: url)
|
||||||
}
|
}
|
||||||
@ -402,13 +422,35 @@ public func sendBotPaymentForm(account: Account, messageId: MessageId, formId: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct BotPaymentReceipt {
|
public struct BotPaymentReceipt : Equatable {
|
||||||
public let invoice: BotPaymentInvoice
|
public let invoice: BotPaymentInvoice
|
||||||
public let info: BotPaymentRequestedInfo?
|
public let info: BotPaymentRequestedInfo?
|
||||||
public let shippingOption: BotPaymentShippingOption?
|
public let shippingOption: BotPaymentShippingOption?
|
||||||
public let credentialsTitle: String
|
public let credentialsTitle: String
|
||||||
public let invoiceMedia: TelegramMediaInvoice
|
public let invoiceMedia: TelegramMediaInvoice
|
||||||
public let tipAmount: Int64?
|
public let tipAmount: Int64?
|
||||||
|
|
||||||
|
public static func ==(lhs: BotPaymentReceipt, rhs: BotPaymentReceipt) -> Bool {
|
||||||
|
if lhs.invoice != rhs.invoice {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.info != rhs.info {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.shippingOption != rhs.shippingOption {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.credentialsTitle != rhs.credentialsTitle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !lhs.invoiceMedia.isEqual(to: rhs.invoiceMedia) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.tipAmount != rhs.tipAmount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RequestBotPaymentReceiptError {
|
public enum RequestBotPaymentReceiptError {
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1879,7 +1879,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
|
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
})
|
})
|
||||||
strongSelf.present(BotCheckoutController(context: strongSelf.context, invoice: invoice, messageId: messageId, inputData: inputData), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
strongSelf.present(BotCheckoutController(context: strongSelf.context, invoice: invoice, messageId: messageId, inputData: inputData, completed: { currencyValue, receiptMessageId in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .paymentSent(currencyValue: currencyValue, itemTitle: invoice.title), elevatedLayout: false, action: { action in
|
||||||
|
guard let strongSelf = self, let receiptMessageId = receiptMessageId else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .info = action {
|
||||||
|
strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: receiptMessageId), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}), in: .current)
|
||||||
|
}), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ public enum UndoOverlayContent {
|
|||||||
case sticker(account: Account, file: TelegramMediaFile, text: String)
|
case sticker(account: Account, file: TelegramMediaFile, text: String)
|
||||||
case copy(text: String)
|
case copy(text: String)
|
||||||
case mediaSaved(text: String)
|
case mediaSaved(text: String)
|
||||||
|
case paymentSent(currencyValue: String, itemTitle: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UndoOverlayAction {
|
public enum UndoOverlayAction {
|
||||||
|
@ -265,6 +265,23 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
string.addAttribute(.font, value: Font.regular(14.0), range: range)
|
string.addAttribute(.font, value: Font.regular(14.0), range: range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.textNode.attributedText = string
|
||||||
|
displayUndo = false
|
||||||
|
self.originalRemainingSeconds = 5
|
||||||
|
case let .paymentSent(currencyValue, itemTitle):
|
||||||
|
self.avatarNode = nil
|
||||||
|
self.iconNode = nil
|
||||||
|
self.iconCheckNode = nil
|
||||||
|
self.animationNode = AnimationNode(animation: "anim_payment", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
|
||||||
|
self.animatedStickerNode = nil
|
||||||
|
|
||||||
|
let (rawString, attributes) = presentationData.strings.Checkout_SuccessfulTooltip(currencyValue, itemTitle)
|
||||||
|
|
||||||
|
let string = NSMutableAttributedString(attributedString: NSAttributedString(string: rawString, font: Font.regular(14.0), textColor: .white))
|
||||||
|
for (_, range) in attributes {
|
||||||
|
string.addAttribute(.font, value: Font.semibold(14.0), range: range)
|
||||||
|
}
|
||||||
|
|
||||||
self.textNode.attributedText = string
|
self.textNode.attributedText = string
|
||||||
displayUndo = false
|
displayUndo = false
|
||||||
self.originalRemainingSeconds = 5
|
self.originalRemainingSeconds = 5
|
||||||
@ -738,7 +755,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
switch content {
|
switch content {
|
||||||
case .removedChat:
|
case .removedChat:
|
||||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved:
|
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent:
|
||||||
break
|
break
|
||||||
case .dice:
|
case .dice:
|
||||||
self.panelWrapperNode.clipsToBounds = true
|
self.panelWrapperNode.clipsToBounds = true
|
||||||
@ -864,6 +881,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
let factor: CGFloat = 0.07
|
let factor: CGFloat = 0.07
|
||||||
verticalOffset = -3.0
|
verticalOffset = -3.0
|
||||||
preferredSize = CGSize(width: floor(iconSize.width * factor), height: floor(iconSize.height * factor))
|
preferredSize = CGSize(width: floor(iconSize.width * factor), height: floor(iconSize.height * factor))
|
||||||
|
} else if case .paymentSent = self.content {
|
||||||
|
let factor: CGFloat = 0.08
|
||||||
|
preferredSize = CGSize(width: floor(iconSize.width * factor), height: floor(iconSize.height * factor))
|
||||||
} else {
|
} else {
|
||||||
preferredSize = iconSize
|
preferredSize = iconSize
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user