Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-04-11 15:16:56 +03:00
commit 7382d413a6
11 changed files with 3892 additions and 3763 deletions

View File

@ -6444,3 +6444,5 @@ Sorry for the inconvenience.";
"Checkout.OptionalTipItemPlaceholder" = "Enter Custom";
"VoiceChat.ReminderNotify" = "We will notify you when it starts.";
"Checkout.SuccessfulTooltip" = "You paid %1$@ for %2$@.";

View File

@ -80,6 +80,7 @@ public final class BotCheckoutController: ViewController {
private let context: AccountContext
private let invoice: TelegramMediaInvoice
private let messageId: MessageId
private let completed: (String, MessageId?) -> Void
private var presentationData: PresentationData
@ -87,11 +88,12 @@ public final class BotCheckoutController: ViewController {
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.invoice = invoice
self.messageId = messageId
self.inputData = inputData
self.completed = completed
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -121,7 +123,7 @@ public final class BotCheckoutController: ViewController {
self?.present(c, in: .window(.root), with: a)
}, dismissAnimated: { [weak self] in
self?.dismiss()
})
}, completed: self.completed)
//displayNode.enableInteractiveDismiss = true

View File

@ -506,6 +506,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private let messageId: MessageId
private let present: (ViewController, Any?) -> Void
private let dismissAnimated: () -> Void
private let completed: (String, MessageId?) -> Void
private var stateValue = BotCheckoutControllerState()
private let state = ValuePromise(BotCheckoutControllerState(), ignoreRepeated: true)
@ -536,12 +537,13 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private var passwordTip: String?
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.context = context
self.messageId = messageId
self.present = present
self.dismissAnimated = dismissAnimated
self.completed = completed
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -1213,6 +1215,9 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
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
if let strongSelf = self {
strongSelf.inProgressDimNode.isUserInteractionEnabled = false
@ -1227,19 +1232,32 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
strongSelf.applePayController = nil
applePayController.presentingViewController?.dismiss(animated: true, completion: nil)
}
let proceedWithCompletion: (Bool, MessageId?) -> Void = { success, receiptMessageId in
guard let strongSelf = self else {
return
}
if success {
strongSelf.dismissAnimated()
strongSelf.completed(currencyValue, receiptMessageId)
} else {
strongSelf.dismissAnimated()
}
}
switch result {
case .done:
strongSelf.dismissAnimated()
case let .done(receiptMessageId):
proceedWithCompletion(true, receiptMessageId)
case let .externalVerificationRequired(url):
strongSelf.updateActionButton()
var dismissImpl: (() -> Void)?
let controller = BotCheckoutWebInteractionController(context: strongSelf.context, url: url, intent: .externalVerification({ _ in
dismissImpl?()
var dismissImpl: ((Bool) -> Void)?
let controller = BotCheckoutWebInteractionController(context: strongSelf.context, url: url, intent: .externalVerification({ success in
dismissImpl?(success)
}))
dismissImpl = { [weak controller] in
dismissImpl = { [weak controller] success in
controller?.dismiss()
self?.dismissAnimated()
proceedWithCompletion(success, nil)
}
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}

View File

@ -985,6 +985,11 @@ public final class Transaction {
assert(!self.disposed)
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) {
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
@ -3411,6 +3416,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) {
var remainingLimit = limit

View File

@ -342,7 +342,7 @@ public enum SendBotPaymentFormError {
}
public enum SendBotPaymentResult {
case done
case done(receiptMessageId: MessageId?)
case externalVerificationRequired(url: String)
}
@ -384,7 +384,27 @@ public func sendBotPaymentForm(account: Account, messageId: MessageId, formId: I
switch result {
case let .paymentResult(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):
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 info: BotPaymentRequestedInfo?
public let shippingOption: BotPaymentShippingOption?
public let credentialsTitle: String
public let invoiceMedia: TelegramMediaInvoice
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 {

File diff suppressed because one or more lines are too long

View File

@ -1879,7 +1879,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
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))
}
}
}

View File

@ -38,6 +38,7 @@ public enum UndoOverlayContent {
case sticker(account: Account, file: TelegramMediaFile, text: String)
case copy(text: String)
case mediaSaved(text: String)
case paymentSent(currencyValue: String, itemTitle: String)
}
public enum UndoOverlayAction {

View File

@ -265,6 +265,23 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
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
displayUndo = false
self.originalRemainingSeconds = 5
@ -738,7 +755,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content {
case .removedChat:
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
case .dice:
self.panelWrapperNode.clipsToBounds = true
@ -864,6 +881,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let factor: CGFloat = 0.07
verticalOffset = -3.0
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 {
preferredSize = iconSize
}