Various fixes

This commit is contained in:
Ilya Laktyushin 2024-08-07 14:58:25 +02:00
parent c504c1d70e
commit c400ccda24
8 changed files with 128 additions and 21 deletions

View File

@ -1005,6 +1005,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsTransactionsScreen(context: AccountContext, starsContext: StarsContext) -> ViewController
func makeStarsPurchaseScreen(context: AccountContext, starsContext: StarsContext, options: [Any], purpose: StarsPurchasePurpose, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, extendedMedia: [TelegramExtendedMedia], inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController
func makeStarsSubscriptionTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, link: String, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, navigateToPeer: @escaping (EnginePeer) -> Void) -> ViewController
func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction, peer: EnginePeer) -> ViewController
func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt) -> ViewController
func makeStarsSubscriptionScreen(context: AccountContext, subscription: StarsContext.State.Subscription, update: @escaping (Bool) -> Void) -> ViewController

View File

@ -22,13 +22,25 @@ import TextFormat
private final class InviteLinkEditControllerArguments {
let context: AccountContext
let updateState: ((InviteLinkEditControllerState) -> InviteLinkEditControllerState) -> Void
let focusOnItem: (InviteLinksEditEntryTag) -> Void
let errorWithItem: (InviteLinksEditEntryTag) -> Void
let scrollToUsage: () -> Void
let dismissInput: () -> Void
let revoke: () -> Void
init(context: AccountContext, updateState: @escaping ((InviteLinkEditControllerState) -> InviteLinkEditControllerState) -> Void, scrollToUsage: @escaping () -> Void, dismissInput: @escaping () -> Void, revoke: @escaping () -> Void) {
init(
context: AccountContext,
updateState: @escaping ((InviteLinkEditControllerState) -> InviteLinkEditControllerState) -> Void,
focusOnItem: @escaping (InviteLinksEditEntryTag) -> Void,
errorWithItem: @escaping (InviteLinksEditEntryTag) -> Void,
scrollToUsage: @escaping () -> Void,
dismissInput: @escaping () -> Void,
revoke: @escaping () -> Void)
{
self.context = context
self.updateState = updateState
self.focusOnItem = focusOnItem
self.errorWithItem = errorWithItem
self.scrollToUsage = scrollToUsage
self.dismissInput = dismissInput
self.revoke = revoke
@ -45,6 +57,7 @@ private enum InviteLinksEditSection: Int32 {
}
private enum InviteLinksEditEntryTag: ItemListItemTag {
case subscriptionFee
case usage
func isEqual(to other: ItemListItemTag) -> Bool {
@ -79,7 +92,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
case subscriptionFeeToggle(PresentationTheme, String, Bool, Bool)
case subscriptionFee(PresentationTheme, String, Bool, Int64?, String)
case subscriptionFee(PresentationTheme, String, Bool, Int64?, String, Int64?)
case subscriptionFeeInfo(PresentationTheme, String)
case requestApproval(PresentationTheme, String, Bool, Bool)
@ -182,8 +195,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
} else {
return false
}
case let .subscriptionFee(lhsTheme, lhsText, lhsValue, lhsEnabled, lhsLabel):
if case let .subscriptionFee(rhsTheme, rhsText, rhsValue, rhsEnabled, rhsLabel) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled, lhsLabel == rhsLabel {
case let .subscriptionFee(lhsTheme, lhsText, lhsValue, lhsEnabled, lhsLabel, lhsMaxValue):
if case let .subscriptionFee(rhsTheme, rhsText, rhsValue, rhsEnabled, rhsLabel, rhsMaxValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled, lhsLabel == rhsLabel, lhsMaxValue == rhsMaxValue {
return true
} else {
return false
@ -300,17 +313,26 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
}
return updatedState
}
if value {
Queue.mainQueue().after(0.1) {
arguments.focusOnItem(.subscriptionFee)
}
}
})
case let .subscriptionFee(_, placeholder, enabled, value, label):
case let .subscriptionFee(_, placeholder, enabled, value, label, maxValue):
let title = NSMutableAttributedString(string: "⭐️", font: Font.semibold(18.0), textColor: .white)
if let range = title.string.range(of: "⭐️") {
title.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: title.string))
title.addAttribute(.baselineOffset, value: -1.0, range: NSRange(range, in: title.string))
}
return ItemListSingleLineInputItem(context: arguments.context, presentationData: presentationData, title: title, text: value.flatMap { "\($0)" } ?? "", placeholder: placeholder, label: label, type: .number, spacing: 3.0, enabled: enabled, sectionId: self.section, textUpdated: { text in
return ItemListSingleLineInputItem(context: arguments.context, presentationData: presentationData, title: title, text: value.flatMap { "\($0)" } ?? "", placeholder: placeholder, label: label, type: .number, spacing: 3.0, enabled: enabled, tag: InviteLinksEditEntryTag.subscriptionFee, sectionId: self.section, textUpdated: { text in
arguments.updateState { state in
var updatedState = state
if let value = Int64(text) {
if var value = Int64(text) {
if let maxValue, value > maxValue {
value = maxValue
arguments.errorWithItem(.subscriptionFee)
}
updatedState.subscriptionFee = value
} else {
updatedState.subscriptionFee = nil
@ -457,7 +479,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
}
}
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData, starsState: StarsRevenueStats?) -> [InviteLinksEditEntry] {
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData, starsState: StarsRevenueStats?, configuration: StarsSubscriptionConfiguration) -> [InviteLinksEditEntry] {
var entries: [InviteLinksEditEntry] = []
entries.append(.titleHeader(presentationData.theme, presentationData.strings.InviteLink_Create_LinkNameTitle.uppercased()))
@ -472,9 +494,9 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
if state.subscriptionEnabled {
var label: String = ""
if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0, let starsState {
label = formatTonUsdValue(subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat)
label = "\(formatTonUsdValue(subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat)) / month"
}
entries.append(.subscriptionFee(presentationData.theme, "Stars amount per month", isEditingEnabled, state.subscriptionFee, label))
entries.append(.subscriptionFee(presentationData.theme, "Stars amount per month", isEditingEnabled, state.subscriptionFee, label, configuration.maxFee))
}
let infoText: String
if let _ = invite, state.subscriptionEnabled {
@ -585,9 +607,15 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
var dismissImpl: (() -> Void)?
var dismissInputImpl: (() -> Void)?
var scrollToUsageImpl: (() -> Void)?
var focusImpl: ((InviteLinksEditEntryTag) -> Void)?
var errorImpl: ((InviteLinksEditEntryTag) -> Void)?
let arguments = InviteLinkEditControllerArguments(context: context, updateState: { f in
updateState(f)
}, focusOnItem: { tag in
focusImpl?(tag)
}, errorWithItem: { tag in
errorImpl?(tag)
}, scrollToUsage: {
scrollToUsageImpl?()
}, dismissInput: {
@ -648,6 +676,8 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
let configuration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let previousState = Atomic<InviteLinkEditControllerState?>(value: nil)
let signal = combineLatest(
presentationData,
@ -762,7 +792,7 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(invite == nil ? presentationData.strings.InviteLink_Create_Title : presentationData.strings.InviteLink_Create_EditTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, isGroup: isGroup, isPublic: isPublic, presentationData: presentationData, starsState: starsState), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, isGroup: isGroup, isPublic: isPublic, presentationData: presentationData, starsState: starsState, configuration: configuration), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
return (controllerState, (listState, arguments))
}
@ -806,5 +836,41 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
dismissImpl = { [weak controller] in
controller?.dismiss()
}
focusImpl = { [weak controller] targetTag in
controller?.forEachItemNode { itemNode in
if let itemNode = itemNode as? ItemListSingleLineInputItemNode, let tag = itemNode.tag, tag.isEqual(to: targetTag) {
itemNode.focus()
}
}
}
let hapticFeedback = HapticFeedback()
errorImpl = { [weak controller] targetTag in
hapticFeedback.error()
controller?.forEachItemNode { itemNode in
if let itemNode = itemNode as? ItemListSingleLineInputItemNode, let tag = itemNode.tag, tag.isEqual(to: targetTag) {
itemNode.animateError()
}
}
}
return controller
}
private struct StarsSubscriptionConfiguration {
static var defaultValue: StarsSubscriptionConfiguration {
return StarsSubscriptionConfiguration(maxFee: 2500)
}
let maxFee: Int64?
fileprivate init(maxFee: Int64?) {
self.maxFee = maxFee
}
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double {
return StarsSubscriptionConfiguration(maxFee: Int64(value))
} else {
return .defaultValue
}
}
}

View File

@ -141,6 +141,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
private let textNode: TextFieldNode
private let clearIconNode: ASImageNode
private let clearButtonNode: HighlightableButtonNode
private let labelNode: TextNode
private var item: ItemListSingleLineInputItem?
@ -171,12 +172,17 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
self.clearButtonNode = HighlightableButtonNode()
self.labelNode = TextNode()
self.labelNode.isUserInteractionEnabled = false
super.init(layerBacked: false, dynamicBounce: false)
self.addSubnode(self.titleNode.textNode)
self.addSubnode(self.textNode)
self.addSubnode(self.clearIconNode)
self.addSubnode(self.clearButtonNode)
self.addSubnode(self.textNode)
self.addSubnode(self.labelNode)
self.clearButtonNode.addTarget(self, action: #selector(self.clearButtonPressed), forControlEvents: .touchUpInside)
self.clearButtonNode.highligthedChanged = { [weak self] highlighted in
@ -218,6 +224,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
let makeTitleLayout = TextNode.asyncLayout(self.titleNode.textNode)
let makeTitleWithEntitiesLayout = TextNodeWithEntities.asyncLayout(self.titleNode)
let makeMeasureTitleSizeLayout = TextNode.asyncLayout(self.measureTitleSizeNode)
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
let currentItem = self.item
@ -262,6 +269,8 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
let (measureTitleLayout, measureTitleSizeApply) = makeMeasureTitleSizeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "A", font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize)), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - 32.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label ?? "", font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - 32.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let separatorHeight = UIScreenPixel
let contentSize = CGSize(width: params.width, height: max(titleLayout.size.height, measureTitleLayout.size.height) + 22.0)
@ -310,6 +319,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((layout.contentSize.height - titleLayout.size.height) / 2.0)), size: titleLayout.size)
let _ = measureTitleSizeApply()
let _ = labelApply()
let secureEntry: Bool
let capitalizationType: UITextAutocapitalizationType
@ -379,6 +389,8 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset + titleLayout.size.width + item.spacing, y: 0.0), size: CGSize(width: max(1.0, params.width - (leftInset + rightInset + titleLayout.size.width + item.spacing)), height: layout.contentSize.height - 2.0))
strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: layoutSize.width - rightInset - labelLayout.size.width, y: floorToScreenPixels((layout.contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
switch item.alignment {
case .default:
strongSelf.textNode.textField.textAlignment = .natural

View File

@ -911,6 +911,7 @@ private final class StarsSubscriptionsContextImpl {
private let disposable = MetaDisposable()
private var stateDisposable: Disposable?
private let updateDisposable = MetaDisposable()
init(account: Account, starsContext: StarsContext) {
assert(Queue.mainQueue().isCurrent())
@ -931,6 +932,7 @@ private final class StarsSubscriptionsContextImpl {
assert(Queue.mainQueue().isCurrent())
self.disposable.dispose()
self.stateDisposable?.dispose()
self.updateDisposable.dispose()
}
func loadMore() {
@ -978,6 +980,7 @@ private final class StarsSubscriptionsContextImpl {
updatedState.subscriptions[index] = updatedSubscription
}
self.updateState(updatedState)
self.updateDisposable.set(_internal_updateStarsSubscription(account: self.account, peerId: self.account.peerId, subscriptionId: id, cancel: cancel).startStrict())
}
}

View File

@ -93,9 +93,5 @@ public extension TelegramEngine {
public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
return _internal_sendStarsPaymentForm(account: self.account, formId: formId, source: source)
}
public func updateStarsSubscription(peerId: EnginePeer.Id, subscriptionId: String, cancel: Bool) -> Signal<Never, UpdateStarsSubsciptionError> {
return _internal_updateStarsSubscription(account: self.account, peerId: peerId, subscriptionId: subscriptionId, cancel: cancel)
}
}
}

View File

@ -29,6 +29,7 @@ private final class SheetContent: CombinedComponent {
let source: BotPaymentInvoiceSource
let extendedMedia: [TelegramExtendedMedia]
let inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
let navigateToPeer: (EnginePeer) -> Void
let dismiss: () -> Void
init(
@ -38,6 +39,7 @@ private final class SheetContent: CombinedComponent {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void,
dismiss: @escaping () -> Void
) {
self.context = context
@ -46,6 +48,7 @@ private final class SheetContent: CombinedComponent {
self.source = source
self.extendedMedia = extendedMedia
self.inputData = inputData
self.navigateToPeer = navigateToPeer
self.dismiss = dismiss
}
@ -77,6 +80,7 @@ private final class SheetContent: CombinedComponent {
private var peerDisposable: Disposable?
private(set) var balance: Int64?
private(set) var form: BotPaymentForm?
private(set) var navigateToPeer: (EnginePeer) -> Void
private var stateDisposable: Disposable?
@ -96,13 +100,15 @@ private final class SheetContent: CombinedComponent {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
invoice: TelegramMediaInvoice,
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void
) {
self.context = context
self.starsContext = starsContext
self.source = source
self.extendedMedia = extendedMedia
self.invoice = invoice
self.navigateToPeer = navigateToPeer
super.init()
@ -159,6 +165,7 @@ private final class SheetContent: CombinedComponent {
return
}
let navigateToPeer = self.navigateToPeer
let action = { [weak self] in
guard let self else {
return
@ -167,8 +174,19 @@ private final class SheetContent: CombinedComponent {
self.updated()
let _ = (self.context.engine.payments.sendStarsPaymentForm(formId: form.id, source: self.source)
|> deliverOnMainQueue).start(next: { _ in
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self else {
return
}
completion(true)
if case let .starsChatSubscription(link) = self.source {
let _ = (self.context.engine.peers.joinLinkInformation(link)
|> deliverOnMainQueue).startStandalone(next: { result in
if case let .alreadyJoined(peer) = result {
navigateToPeer(peer)
}
})
}
}, error: { [weak self] error in
guard let self else {
return
@ -235,7 +253,7 @@ private final class SheetContent: CombinedComponent {
}
func makeState() -> State {
return State(context: self.context, starsContext: self.starsContext, source: self.source, extendedMedia: self.extendedMedia, 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, navigateToPeer: self.navigateToPeer)
}
static var body: Body {
@ -639,6 +657,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
private let source: BotPaymentInvoiceSource
private let extendedMedia: [TelegramExtendedMedia]
private let inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
private let navigateToPeer: (EnginePeer) -> Void
init(
context: AccountContext,
@ -646,7 +665,8 @@ private final class StarsTransferSheetComponent: CombinedComponent {
invoice: TelegramMediaInvoice,
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void
) {
self.context = context
self.starsContext = starsContext
@ -654,6 +674,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
self.source = source
self.extendedMedia = extendedMedia
self.inputData = inputData
self.navigateToPeer = navigateToPeer
}
static func ==(lhs: StarsTransferSheetComponent, rhs: StarsTransferSheetComponent) -> Bool {
@ -687,6 +708,7 @@ private final class StarsTransferSheetComponent: CombinedComponent {
source: context.component.source,
extendedMedia: context.component.extendedMedia,
inputData: context.component.inputData,
navigateToPeer: context.component.navigateToPeer,
dismiss: {
animateOut.invoke(Action { _ in
if let controller = controller() {
@ -747,6 +769,7 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
source: BotPaymentInvoiceSource,
extendedMedia: [TelegramExtendedMedia] = [],
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>,
navigateToPeer: @escaping (EnginePeer) -> Void = { _ in },
completion: @escaping (Bool) -> Void
) {
self.context = context
@ -761,7 +784,8 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
invoice: invoice,
source: source,
extendedMedia: extendedMedia,
inputData: inputData
inputData: inputData,
navigateToPeer: navigateToPeer
),
navigationBarAppearance: .none,
statusBarStyle: .ignore,

View File

@ -323,7 +323,8 @@ func openResolvedUrlImpl(
}
}
let _ = (starsInputData |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { _ in
let controller = context.sharedContext.makeStarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: .starsChatSubscription(hash: link), extendedMedia: [], inputData: starsInputData, completion: { _ in
let controller = context.sharedContext.makeStarsSubscriptionTransferScreen(context: context, starsContext: starsContext, invoice: invoice, link: link, inputData: starsInputData, navigateToPeer: { peer in
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
})
navigationController?.pushViewController(controller)
})

View File

@ -2743,6 +2743,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, extendedMedia: extendedMedia, inputData: inputData, completion: completion)
}
public func makeStarsSubscriptionTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, link: String, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, navigateToPeer: @escaping (EnginePeer) -> Void) -> ViewController {
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: .starsChatSubscription(hash: link), extendedMedia: [], inputData: inputData, navigateToPeer: navigateToPeer, completion: { _ in })
}
public func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction, peer: EnginePeer) -> ViewController {
return StarsTransactionScreen(context: context, subject: .transaction(transaction, peer))
}