Various fixes

This commit is contained in:
Ilya Laktyushin 2025-03-12 21:24:42 +04:00
parent 369116767a
commit eabf0985ef
11 changed files with 225 additions and 119 deletions

View File

@ -13573,6 +13573,7 @@ Sorry for the inconvenience.";
"FolderLinkPreview.ToastFolderAddedTitleV2" = "Folder {folder} Added"; "FolderLinkPreview.ToastFolderAddedTitleV2" = "Folder {folder} Added";
"Stars.Purchase.UpgradeStarGiftInfo" = "Buy Stars to upgrade your gift into a unique collectible."; "Stars.Purchase.UpgradeStarGiftInfo" = "Buy Stars to upgrade your gift into a unique collectible.";
"Stars.Purchase.TransferStarGiftInfo" = "Buy Stars to transfer ownership of your unique collectible.";
"Gift.Send.Upgrade" = "Make Unique for %@"; "Gift.Send.Upgrade" = "Make Unique for %@";
"Gift.Send.Upgrade.Info" = "Enable this to let %1$@ turn your gift into a unique collectible. [Learn More >]()"; "Gift.Send.Upgrade.Info" = "Enable this to let %1$@ turn your gift into a unique collectible. [Learn More >]()";

View File

@ -136,6 +136,7 @@ public enum StarsPurchasePurpose: Equatable {
case unlockMedia(requiredStars: Int64) case unlockMedia(requiredStars: Int64)
case starGift(peerId: EnginePeer.Id, requiredStars: Int64) case starGift(peerId: EnginePeer.Id, requiredStars: Int64)
case upgradeStarGift(requiredStars: Int64) case upgradeStarGift(requiredStars: Int64)
case transferStarGift(requiredStars: Int64)
case sendMessage(peerId: EnginePeer.Id, requiredStars: Int64) case sendMessage(peerId: EnginePeer.Id, requiredStars: Int64)
} }

View File

@ -129,7 +129,7 @@ public final class TextAlertContentActionNode: HighlightableButtonNode {
if let range = attributedString.string.range(of: "$") { if let range = attributedString.string.range(of: "$") {
attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string)) attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string))
attributedString.addAttribute(.foregroundColor, value: color, range: NSRange(range, in: attributedString.string)) attributedString.addAttribute(.foregroundColor, value: color, range: NSRange(range, in: attributedString.string))
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string)) attributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedString.string))
} }
self.setAttributedTitle(attributedString, for: []) self.setAttributedTitle(attributedString, for: [])

View File

@ -317,17 +317,19 @@ private final class ChatMessagePaymentAlertContentNode: AlertContentNode, ASGest
} }
} }
private class ChatMessagePaymentAlertController: AlertController { public class ChatMessagePaymentAlertController: AlertController {
private let context: AccountContext? private let context: AccountContext?
private let presentationData: PresentationData private let presentationData: PresentationData
private weak var parentNavigationController: NavigationController? private weak var parentNavigationController: NavigationController?
private let showBalance: Bool
private let balance = ComponentView<Empty>() private let balance = ComponentView<Empty>()
init(context: AccountContext?, presentationData: PresentationData, contentNode: AlertContentNode, navigationController: NavigationController?) { public init(context: AccountContext?, presentationData: PresentationData, contentNode: AlertContentNode, navigationController: NavigationController?, showBalance: Bool = true) {
self.context = context self.context = context
self.presentationData = presentationData self.presentationData = presentationData
self.parentNavigationController = navigationController self.parentNavigationController = navigationController
self.showBalance = showBalance
super.init(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) super.init(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
@ -350,16 +352,16 @@ private class ChatMessagePaymentAlertController: AlertController {
} }
} }
override func dismissAnimated() { public override func dismissAnimated() {
super.dismissAnimated() super.dismissAnimated()
self.animateOut() self.animateOut()
} }
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)
if let context = self.context, let _ = self.parentNavigationController { if let context = self.context, let _ = self.parentNavigationController, self.showBalance {
let insets = layout.insets(options: .statusBar) let insets = layout.insets(options: .statusBar)
let balanceSize = self.balance.update( let balanceSize = self.balance.update(
transition: .immediate, transition: .immediate,

View File

@ -216,6 +216,8 @@ final class GiftOptionsScreenComponent: Component {
private var starsStateDisposable: Disposable? private var starsStateDisposable: Disposable?
private var starsState: StarsContext.State? private var starsState: StarsContext.State?
private let optionsPromise = Promise<[StarsTopUpOption]?>(nil)
private var component: GiftOptionsScreenComponent? private var component: GiftOptionsScreenComponent?
private(set) weak var state: State? private(set) weak var state: State?
private var environment: EnvironmentType? private var environment: EnvironmentType?
@ -508,9 +510,19 @@ final class GiftOptionsScreenComponent: Component {
gift: transferGift, gift: transferGift,
peer: peer, peer: peer,
transferStars: gift.transferStars ?? 0, transferStars: gift.transferStars ?? 0,
commit: { [weak controller] in navigationController: controller.navigationController as? NavigationController,
commit: { [weak self, weak controller] in
let proceed: (Bool) -> Void = { waitForTopUp in
if waitForTopUp, let starsContext = context.starsContext {
let _ = (starsContext.onUpdate
|> deliverOnMainQueue).start(next: {
let _ = (context.engine.payments.transferStarGift(prepaid: gift.transferStars == 0, reference: reference, peerId: peer.id) let _ = (context.engine.payments.transferStarGift(prepaid: gift.transferStars == 0, reference: reference, peerId: peer.id)
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start()
})
} else {
let _ = (context.engine.payments.transferStarGift(prepaid: gift.transferStars == 0, reference: reference, peerId: peer.id)
|> deliverOnMainQueue).start()
}
guard let controller, let navigationController = controller.navigationController as? NavigationController else { guard let controller, let navigationController = controller.navigationController as? NavigationController else {
return return
@ -558,11 +570,36 @@ final class GiftOptionsScreenComponent: Component {
} }
navigationController.setViewControllers(controllers, animated: true) navigationController.setViewControllers(controllers, animated: true)
} }
if let completion = component.completion { if let completion = component.completion {
completion() completion()
} }
} }
if let self, let transferStars = gift.transferStars, transferStars > 0, let starsContext = context.starsContext, let starsState = self.starsState {
if starsState.balance < StarsAmount(value: transferStars, nanos: 0) {
let _ = (self.optionsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak controller] options in
let purchaseController = context.sharedContext.makeStarsPurchaseScreen(
context: context,
starsContext: starsContext,
options: options ?? [],
purpose: .transferStarGift(requiredStars: transferStars),
completion: { stars in
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
proceed(true)
}
)
controller?.push(purchaseController)
})
} else {
proceed(false)
}
} else {
proceed(false)
}
}
) )
controller.present(alertController, in: .window(.root)) controller.present(alertController, in: .window(.root))
} }
@ -590,6 +627,11 @@ final class GiftOptionsScreenComponent: Component {
self.state?.updated() self.state?.updated()
} }
}) })
if let state = component.starsContext.currentState, state.balance < StarsAmount(value: 100, nanos: 0) {
self.optionsPromise.set(component.context.engine.payments.starsTopUpOptions()
|> map(Optional.init))
}
} }
self.component = component self.component = component

View File

@ -49,6 +49,7 @@ swift_library(
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent", "//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent", "//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -12,6 +12,7 @@ import AppBundle
import AvatarNode import AvatarNode
import Markdown import Markdown
import GiftItemComponent import GiftItemComponent
import ChatMessagePaymentAlertController
private final class GiftTransferAlertContentNode: AlertContentNode { private final class GiftTransferAlertContentNode: AlertContentNode {
private let context: AccountContext private let context: AccountContext
@ -251,7 +252,14 @@ private final class GiftTransferAlertContentNode: AlertContentNode {
} }
} }
public func giftTransferAlertController(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, transferStars: Int64, commit: @escaping () -> Void) -> AlertController { public func giftTransferAlertController(
context: AccountContext,
gift: StarGift.UniqueGift,
peer: EnginePeer,
transferStars: Int64,
navigationController: NavigationController?,
commit: @escaping () -> Void
) -> AlertController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let strings = presentationData.strings let strings = presentationData.strings
@ -267,7 +275,6 @@ public func giftTransferAlertController(context: AccountContext, gift: StarGift.
} }
var dismissImpl: ((Bool) -> Void)? var dismissImpl: ((Bool) -> Void)?
var contentNode: GiftTransferAlertContentNode?
let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: {
dismissImpl?(true) dismissImpl?(true)
commit() commit()
@ -275,9 +282,9 @@ public func giftTransferAlertController(context: AccountContext, gift: StarGift.
dismissImpl?(true) dismissImpl?(true)
})] })]
contentNode = GiftTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions) let contentNode = GiftTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions)
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode, navigationController: navigationController, showBalance: transferStars > 0)
dismissImpl = { [weak controller] animated in dismissImpl = { [weak controller] animated in
if animated { if animated {
controller?.dismissAnimated() controller?.dismissAnimated()

View File

@ -147,13 +147,7 @@ private final class GiftViewSheetContent: CombinedComponent {
var keepOriginalInfo = false var keepOriginalInfo = false
private var optionsDisposable: Disposable? private let optionsPromise = Promise<[StarsTopUpOption]?>(nil)
private(set) var options: [StarsTopUpOption] = [] {
didSet {
self.optionsPromise.set(self.options)
}
}
private let optionsPromise = ValuePromise<[StarsTopUpOption]?>(nil)
init( init(
context: AccountContext, context: AccountContext,
@ -269,13 +263,8 @@ private final class GiftViewSheetContent: CombinedComponent {
} }
if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < StarsAmount(value: 100, nanos: 0) { if let starsContext = context.starsContext, let state = starsContext.currentState, state.balance < StarsAmount(value: 100, nanos: 0) {
self.optionsDisposable = (context.engine.payments.starsTopUpOptions() self.optionsPromise.set(context.engine.payments.starsTopUpOptions()
|> deliverOnMainQueue).start(next: { [weak self] options in |> map(Optional.init))
guard let self else {
return
}
self.options = options
})
} }
} }

View File

@ -1016,7 +1016,26 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
guard let self else { guard let self else {
return return
} }
if self.context.isPremium {
let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone() let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone()
} else {
let text = strings.Gift_View_TooltipPremiumWearing
let tooltipController = UndoOverlayController(
presentationData: presentationData,
content: .premiumPaywall(title: nil, text: text, customUndoText: nil, timeout: nil, linkAction: nil),
position: .bottom,
animateInAsReplacement: false,
appearance: UndoOverlayController.Appearance(sideInset: 16.0, bottomInset: 62.0),
action: { [weak self] action in
if let self, case .info = action {
let premiumController = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .messageEffects, forceDark: false, dismissed: nil)
self.parentController?.push(premiumController)
}
return false
}
)
self.parentController?.present(tooltipController, in: .current)
}
}) })
}))) })))
} }

View File

@ -241,6 +241,8 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
textString = strings.Stars_Purchase_StarGiftInfo(component.peers.first?.value.compactDisplayTitle ?? "").string textString = strings.Stars_Purchase_StarGiftInfo(component.peers.first?.value.compactDisplayTitle ?? "").string
case .upgradeStarGift: case .upgradeStarGift:
textString = strings.Stars_Purchase_UpgradeStarGiftInfo textString = strings.Stars_Purchase_UpgradeStarGiftInfo
case .transferStarGift:
textString = strings.Stars_Purchase_TransferStarGiftInfo
case let .sendMessage(peerId, _): case let .sendMessage(peerId, _):
if peerId.namespace == Namespaces.Peer.CloudUser { if peerId.namespace == Namespaces.Peer.CloudUser {
textString = strings.Stars_Purchase_SendMessageInfo(component.peers.first?.value.compactDisplayTitle ?? "").string textString = strings.Stars_Purchase_SendMessageInfo(component.peers.first?.value.compactDisplayTitle ?? "").string
@ -828,7 +830,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
titleText = strings.Stars_Purchase_GetStars titleText = strings.Stars_Purchase_GetStars
case .gift: case .gift:
titleText = strings.Stars_Purchase_GiftStars titleText = strings.Stars_Purchase_GiftStars
case let .topUp(requiredStars, _), let .transfer(_, requiredStars), let .reactions(_, requiredStars), let .subscription(_, requiredStars, _), let .unlockMedia(requiredStars), let .starGift(_, requiredStars), let .upgradeStarGift(requiredStars), let .sendMessage(_, requiredStars): case let .topUp(requiredStars, _), let .transfer(_, requiredStars), let .reactions(_, requiredStars), let .subscription(_, requiredStars, _), let .unlockMedia(requiredStars), let .starGift(_, requiredStars), let .upgradeStarGift(requiredStars), let .transferStarGift(requiredStars), let .sendMessage(_, requiredStars):
titleText = strings.Stars_Purchase_StarsNeeded(Int32(requiredStars)) titleText = strings.Stars_Purchase_StarsNeeded(Int32(requiredStars))
} }
@ -1274,6 +1276,8 @@ private extension StarsPurchasePurpose {
return requiredStars return requiredStars
case let .upgradeStarGift(requiredStars): case let .upgradeStarGift(requiredStars):
return requiredStars return requiredStars
case let .transferStarGift(requiredStars):
return requiredStars
case let .sendMessage(_, requiredStars): case let .sendMessage(_, requiredStars):
return requiredStars return requiredStars
default: default:

View File

@ -2883,11 +2883,24 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
} }
let optionsPromise = Promise<[StarsTopUpOption]?>(nil)
if let state = context.starsContext?.currentState, state.balance < StarsAmount(value: 100, nanos: 0) {
optionsPromise.set(context.engine.payments.starsTopUpOptions()
|> map(Optional.init))
}
presentTransferAlertImpl = { [weak controller] peer in presentTransferAlertImpl = { [weak controller] peer in
guard let controller, case let .starGiftTransfer(_, _, gift, transferStars, _, _) = source else { guard let controller, case let .starGiftTransfer(_, _, gift, transferStars, _, _) = source else {
return return
} }
let alertController = giftTransferAlertController(context: context, gift: gift, peer: peer, transferStars: transferStars, commit: { [weak controller] in let alertController = giftTransferAlertController(
context: context,
gift: gift,
peer: peer,
transferStars: transferStars,
navigationController: controller.navigationController as? NavigationController,
commit: { [weak controller] in
let proceed: (Bool) -> Void = { waitForTopUp in
completion?([peer.id]) completion?([peer.id])
guard let controller, let navigationController = controller.navigationController as? NavigationController else { guard let controller, let navigationController = controller.navigationController as? NavigationController else {
@ -2938,7 +2951,34 @@ public final class SharedAccountContextImpl: SharedAccountContext {
lastController.present(tooltipController, in: .window(.root)) lastController.present(tooltipController, in: .window(.root))
} }
} }
}
if transferStars > 0, let starsContext = context.starsContext, let starsState = starsContext.currentState {
if starsState.balance < StarsAmount(value: transferStars, nanos: 0) {
let _ = (optionsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak controller] options in
let purchaseController = context.sharedContext.makeStarsPurchaseScreen(
context: context,
starsContext: starsContext,
options: options ?? [],
purpose: .transferStarGift(requiredStars: transferStars),
completion: { stars in
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
proceed(true)
}
)
controller?.push(purchaseController)
}) })
} else {
proceed(false)
}
} else {
proceed(false)
}
}
)
controller.present(alertController, in: .window(.root)) controller.present(alertController, in: .window(.root))
} }