Paid media improvements

This commit is contained in:
Ilya Laktyushin 2024-06-23 00:03:49 +04:00
parent d02046973f
commit 9fe5a800e9
6 changed files with 108 additions and 32 deletions

View File

@ -12383,6 +12383,7 @@ Sorry for the inconvenience.";
"Stars.PaidContent.AmountTitle" = "ENTER UNLOCK COST";
"Stars.PaidContent.AmountPlaceholder" = "Stars to Unlock";
"Stars.PaidContent.AmountInfo" = "Users will have to transfer this amount of Stars to your channel in order to view this media.\n[More about Stars >]()";
"Stars.PaidContent.AmountInfo_URL" = "https://telegram.org";
"Stars.PaidContent.Create" = "Make This Media Paid";
"MediaEditor.AddLink" = "LINK";
@ -12420,7 +12421,7 @@ Sorry for the inconvenience.";
"Monetization.BalanceStarsWithdraw" = "Withdraw via Fragment";
"Monetization.BalanceStarsWithdrawShort" = "Withdraw";
"Monetization.BalanceStarsBuyAds" = "Buy Ads";
"Monetization.Balance.StarsInfo" = "You can withdraw Stars using Fragment, or use Stars to advertise your channel. [Learn More >]()";
"Monetization.Balance.StarsInfo" = "You can collect rewards for Stars using Fragment, or use Stars to advertise your channel. [Learn More >]()";
"Monetization.Balance.StarsInfo_URL" = "https://telegram.org";
"Monetization.StarsProceeds.Title" = "Rewards Overview";

View File

@ -0,0 +1,40 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
private func _internal_updateExtendedMediaById(account: Account, peerId: EnginePeer.Id, messageIds: [EngineMessage.Id]) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Peer? in
if let peer = transaction.getPeer(peerId) {
return peer
} else {
return nil
}
}
|> mapToSignal { peer -> Signal<Never, NoError> in
guard let peer = peer, let inputPeer = apiInputPeer(peer) else {
return .complete()
}
return account.network.request(Api.functions.messages.getExtendedMedia(peer: inputPeer, id: messageIds.map { $0.id }))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<Never, NoError> in
if let updates = updates {
account.stateManager.addUpdates(updates)
}
return .complete()
}
}
}
func _internal_updateExtendedMedia(account: Account, messageIds: [EngineMessage.Id]) -> Signal<Never, NoError> {
var signals: [Signal<Never, NoError>] = []
for (peerId, messageIds) in messagesIdsGroupedByPeerId(messageIds) {
signals.append(_internal_updateExtendedMediaById(account: account, peerId: peerId, messageIds: messageIds))
}
return combineLatest(signals)
|> ignoreValues
}

View File

@ -1431,6 +1431,10 @@ public extension TelegramEngine {
return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option)
}
public func updateExtendedMedia(messageIds: [EngineMessage.Id]) -> Signal<Never, NoError> {
return _internal_updateExtendedMedia(account: self.account, messageIds: messageIds)
}
public func getAllLocalChannels(count: Int) -> Signal<[EnginePeer.Id], NoError> {
return self.account.postbox.transaction { transaction -> [EnginePeer.Id] in
var result: [EnginePeer.Id] = []

View File

@ -756,6 +756,8 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
return .fail(.paymentFailed)
} else if error.errorDescription == "INVOICE_ALREADY_PAID" {
return .fail(.alreadyPaid)
} else if error.errorDescription == "MEDIA_ALREADY_PAID" {
return .fail(.alreadyPaid)
}
return .fail(.generic)
}

View File

@ -68,6 +68,7 @@ private final class SheetContent: CombinedComponent {
private let context: AccountContext
private let starsContext: StarsContext
private let source: BotPaymentInvoiceSource
private let extendedMedia: [TelegramExtendedMedia]
private let invoice: TelegramMediaInvoice
private(set) var botPeer: EnginePeer?
@ -92,12 +93,14 @@ private final class SheetContent: CombinedComponent {
context: AccountContext,
starsContext: StarsContext,
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
invoice: TelegramMediaInvoice,
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
) {
self.context = context
self.starsContext = starsContext
self.source = source
self.extendedMedia = extendedMedia
self.invoice = invoice
super.init()
@ -150,7 +153,7 @@ private final class SheetContent: CombinedComponent {
self.optionsDisposable?.dispose()
}
func buy(requestTopUp: @escaping (@escaping () -> Void) -> Void, completion: @escaping () -> Void) {
func buy(requestTopUp: @escaping (@escaping () -> Void) -> Void, completion: @escaping (Bool) -> Void) {
guard let form, let balance else {
return
}
@ -164,7 +167,20 @@ private final class SheetContent: CombinedComponent {
let _ = (self.context.engine.payments.sendStarsPaymentForm(formId: form.id, source: self.source)
|> deliverOnMainQueue).start(next: { _ in
completion()
completion(true)
}, error: { [weak self] error in
guard let self else {
return
}
switch error {
case .alreadyPaid:
if !self.extendedMedia.isEmpty, case let .message(messageId) = self.source {
let _ = self.context.engine.messages.updateExtendedMedia(messageIds: [messageId]).startStandalone()
}
default:
break
}
completion(false)
})
}
@ -209,7 +225,7 @@ private final class SheetContent: CombinedComponent {
}
func makeState() -> State {
return State(context: self.context, starsContext: self.starsContext, source: self.source, invoice: self.invoice, inputData: self.inputData)
return State(context: self.context, starsContext: self.starsContext, source: self.source, extendedMedia: self.extendedMedia, invoice: self.invoice, inputData: self.inputData)
}
static var body: Body {
@ -481,37 +497,38 @@ private final class SheetContent: CombinedComponent {
let alertController = textAlertController(context: accountContext, title: nil, text: presentationData.strings.Stars_Transfer_Unavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
controller?.present(alertController, in: .window(.root))
}
}, completion: { [weak controller] in
let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 }
let text: String
if let _ = component.invoice.extendedMedia {
text = presentationData.strings.Stars_Transfer_UnlockedText( presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string
} else {
text = presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string
}
if let navigationController = controller?.navigationController {
Queue.mainQueue().after(0.5) {
if let lastController = navigationController.viewControllers.last as? ViewController {
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: text,
round: false,
undoText: nil
),
elevatedLayout: lastController is ChatController,
action: { _ in return true}
)
lastController.present(resultController, in: .window(.root))
}, completion: { [weak controller] success in
if success {
let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 }
let text: String
if let _ = component.invoice.extendedMedia {
text = presentationData.strings.Stars_Transfer_UnlockedText( presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string
} else {
text = presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string
}
if let navigationController = controller?.navigationController {
Queue.mainQueue().after(0.5) {
if let lastController = navigationController.viewControllers.last as? ViewController {
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: text,
round: false,
undoText: nil
),
elevatedLayout: lastController is ChatController,
action: { _ in return true}
)
lastController.present(resultController, in: .window(.root))
}
}
}
}
controller?.complete(paid: true)
controller?.complete(paid: success)
controller?.dismissAnimated()
starsContext.load(force: true)

View File

@ -67,6 +67,7 @@ private final class SheetContent: CombinedComponent {
let state = context.state
let theme = environment.theme.withModalBlocksBackground()
let strings = environment.strings
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let sideInset: CGFloat = 16.0
@ -200,7 +201,18 @@ private final class SheetContent: CombinedComponent {
}
amountFooter = AnyComponent(MultilineTextComponent(
text: .plain(amountInfoString),
maximumNumberOfLines: 0
maximumNumberOfLines: 0,
highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.2),
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
},
tapAction: { attributes, _ in
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
}
))
} else {
amountFooter = nil