Various improvements [skip ci]

This commit is contained in:
Ilya Laktyushin
2025-02-12 22:15:02 +04:00
parent 8c523ec741
commit 2f2a27f0d2
130 changed files with 5126 additions and 1700 deletions

View File

@@ -80,6 +80,7 @@ final class GiftOptionsScreenComponent: Component {
case limited
case inStock
case stars(Int64)
case transfer
init(rawValue: Int64) {
switch rawValue {
@@ -89,6 +90,8 @@ final class GiftOptionsScreenComponent: Component {
self = .limited
case -2:
self = .inStock
case -3:
self = .transfer
default:
self = .stars(rawValue)
}
@@ -102,6 +105,8 @@ final class GiftOptionsScreenComponent: Component {
return -1
case .inStock:
return -2
case .transfer:
return -3
case let .stars(stars):
return stars
}
@@ -134,48 +139,72 @@ final class GiftOptionsScreenComponent: Component {
private let tabSelector = ComponentView<Empty>()
private var starsFilter: StarsFilter = .all
private var _effectiveStarGifts: ([StarGift.Gift], StarsFilter)?
private var effectiveStarGifts: [StarGift.Gift]? {
private var _effectiveStarGifts: ([StarGift], StarsFilter)?
private var effectiveStarGifts: [StarGift]? {
get {
if let (currentGifts, currentFilter) = self._effectiveStarGifts, currentFilter == self.starsFilter {
return currentGifts
} else if let allGifts = self.state?.starGifts {
var sortedGifts = allGifts
if self.component?.hasBirthday == true {
var updatedGifts: [StarGift.Gift] = []
for gift in allGifts {
if gift.flags.contains(.isBirthdayGift) {
updatedGifts.append(gift)
if case .transfer = self.starsFilter {
let filteredGifts: [StarGift] = self.state?.transferStarGifts?.compactMap { gift in
if case .unique = gift.gift {
return gift.gift
} else {
return nil
}
}
for gift in allGifts {
if !gift.flags.contains(.isBirthdayGift) {
updatedGifts.append(gift)
} ?? []
self._effectiveStarGifts = (filteredGifts, self.starsFilter)
return filteredGifts
} else {
var sortedGifts = allGifts
if self.component?.hasBirthday == true {
var updatedGifts: [StarGift] = []
for starGift in allGifts {
if case let .generic(gift) = starGift {
if gift.flags.contains(.isBirthdayGift) {
updatedGifts.append(starGift)
}
}
}
for starGift in allGifts {
if case let .generic(gift) = starGift {
if !gift.flags.contains(.isBirthdayGift) {
updatedGifts.append(starGift)
}
}
}
sortedGifts = updatedGifts
}
sortedGifts = updatedGifts
let filteredGifts: [StarGift] = sortedGifts.filter {
switch self.starsFilter {
case .all:
return true
case .limited:
if case let .generic(gift) = $0 {
if gift.availability != nil {
return true
}
}
case .inStock:
if case let .generic(gift) = $0 {
if gift.availability == nil || gift.availability!.remains > 0 {
return true
}
}
case let .stars(stars):
if case let .generic(gift) = $0 {
if gift.price == stars {
return true
}
}
case .transfer:
break
}
return false
}
self._effectiveStarGifts = (filteredGifts, self.starsFilter)
return filteredGifts
}
let filteredGifts: [StarGift.Gift] = sortedGifts.filter {
switch self.starsFilter {
case .all:
return true
case .limited:
if $0.availability != nil {
return true
}
case .inStock:
if $0.availability == nil || $0.availability!.remains > 0 {
return true
}
case let .stars(stars):
if $0.price == stars {
return true
}
}
return false
}
self._effectiveStarGifts = (filteredGifts, self.starsFilter)
return filteredGifts
} else {
return nil
}
@@ -341,16 +370,28 @@ final class GiftOptionsScreenComponent: Component {
}
var ribbon: GiftItemComponent.Ribbon?
if let _ = gift.soldOut {
ribbon = GiftItemComponent.Ribbon(
text: environment.strings.Gift_Options_Gift_SoldOut,
color: .red
)
} else if let _ = gift.availability {
ribbon = GiftItemComponent.Ribbon(
text: environment.strings.Gift_Options_Gift_Limited,
color: .blue
)
var isSoldOut = false
if case let .generic(gift) = gift {
if let _ = gift.soldOut {
ribbon = GiftItemComponent.Ribbon(
text: environment.strings.Gift_Options_Gift_SoldOut,
color: .red
)
isSoldOut = true
} else if let _ = gift.availability {
ribbon = GiftItemComponent.Ribbon(
text: environment.strings.Gift_Options_Gift_Limited,
color: .blue
)
}
}
let subject: GiftItemComponent.Subject
switch gift {
case let .generic(gift):
subject = .starGift(gift: gift, price: "⭐️ \(gift.price)")
case let .unique(gift):
subject = .uniqueGift(gift: gift)
}
let _ = visibleItem.update(
@@ -362,9 +403,9 @@ final class GiftOptionsScreenComponent: Component {
context: component.context,
theme: environment.theme,
peer: nil,
subject: .starGift(gift: gift, price: "⭐️ \(gift.price)"),
subject: subject,
ribbon: ribbon,
isSoldOut: gift.soldOut != nil
isSoldOut: isSoldOut
)
),
effectAlignment: .center,
@@ -377,22 +418,25 @@ final class GiftOptionsScreenComponent: Component {
} else {
mainController = controller
}
if gift.availability?.remains == 0 {
let giftController = GiftViewScreen(
context: component.context,
subject: .soldOutGift(gift)
)
mainController.push(giftController)
} else {
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift),
completion: component.completion
)
mainController.push(giftController)
if case let .generic(gift) = gift {
if gift.availability?.remains == 0 {
let giftController = GiftViewScreen(
context: component.context,
subject: .soldOutGift(gift)
)
mainController.push(giftController)
} else {
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift),
completion: component.completion
)
mainController.push(giftController)
}
} else if case let .unique(gift) = gift {
self.transferGift(gift)
}
}
}
},
@@ -442,6 +486,71 @@ final class GiftOptionsScreenComponent: Component {
}
}
func transferGift(_ transferGift: StarGift.UniqueGift) {
guard let component = self.component, let environment = self.environment, let peer = self.state?.peer, let controller = environment.controller() else {
return
}
guard let gift = self.state?.transferStarGifts?.first(where: { gift in
if case let .unique(gift) = gift.gift, gift.slug == transferGift.slug {
return true
} else {
return false
}
}), let reference = gift.reference else {
return
}
//TODO:unmock
let context = component.context
let alertController = giftTransferAlertController(
context: context,
gift: transferGift,
peer: peer,
transferStars: gift.transferStars ?? 0,
commit: { [weak controller] in
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 {
return
}
var controllers = navigationController.viewControllers
controllers = controllers.filter { !($0 is ContactSelectionController) && !($0 is GiftOptionsScreen) }
if peer.id.namespace == Namespaces.Peer.CloudChannel {
if let controller = context.sharedContext.makePeerInfoController(
context: context,
updatedPresentationData: nil,
peer: peer._asPeer(),
mode: .gifts,
avatarInitiallyExpanded: false,
fromChat: false,
requestsContext: nil
) {
controllers.append(controller)
}
} else {
var foundController = false
for controller in controllers.reversed() {
if let chatController = controller as? ChatController, case .peer(id: peer.id) = chatController.chatLocation {
chatController.hintPlayNextOutgoingGift()
foundController = true
break
}
}
if !foundController {
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
chatController.hintPlayNextOutgoingGift()
controllers.append(chatController)
}
}
navigationController.setViewControllers(controllers, animated: true)
}
)
controller.present(alertController, in: .window(.root))
}
func update(component: GiftOptionsScreenComponent, availableSize: CGSize, state: State, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
self.isUpdating = true
defer {
@@ -489,6 +598,9 @@ final class GiftOptionsScreenComponent: Component {
let _ = bottomContentInset
let _ = sectionSpacing
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
let isPremiumDisabled = premiumConfiguration.isPremiumDisabled
let isSelfGift = component.peerId == component.context.account.peerId
let isChannelGift = component.peerId.namespace == Namespaces.Peer.CloudChannel
@@ -642,6 +754,8 @@ final class GiftOptionsScreenComponent: Component {
premiumTitleString = strings.Gift_Options_GiftSelf_Title
} else if isChannelGift {
premiumTitleString = strings.Gift_Options_GiftChannel_Title
} else if isPremiumDisabled {
premiumTitleString = strings.Gift_Options_Gift_Title
} else {
premiumTitleString = strings.Gift_Options_Premium_Title
}
@@ -674,6 +788,8 @@ final class GiftOptionsScreenComponent: Component {
premiumDescriptionRawString = strings.Gift_Options_GiftSelf_Text
} else if isChannelGift {
premiumDescriptionRawString = strings.Gift_Options_GiftChannel_Text(peerName).string
} else if isPremiumDisabled {
premiumDescriptionRawString = strings.Gift_Options_Gift_Text(peerName).string
} else {
premiumDescriptionRawString = strings.Gift_Options_Premium_Text(peerName).string
}
@@ -701,9 +817,13 @@ final class GiftOptionsScreenComponent: Component {
guard let self, let component = self.component, let environment = self.environment else {
return
}
let introController = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .settings, forceDark: false, dismissed: nil)
introController.navigationPresentation = .modal
let introController: ViewController
if isPremiumDisabled {
introController = component.context.sharedContext.makeStarsIntroScreen(context: component.context)
} else {
introController = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .settings, forceDark: false, dismissed: nil)
introController.navigationPresentation = .modal
}
if let controller = environment.controller() as? GiftOptionsScreen {
let mainController: ViewController
if let parentController = controller.parentController() {
@@ -731,11 +851,12 @@ final class GiftOptionsScreenComponent: Component {
let optionSpacing: CGFloat = 10.0
let optionWidth = (availableSize.width - sideInset * 2.0 - optionSpacing * 2.0) / 3.0
if isSelfGift || isChannelGift {
if isSelfGift || isChannelGift || isPremiumDisabled {
contentHeight += 6.0
} else {
if let premiumProducts = state.premiumProducts {
let premiumOptionSize = CGSize(width: optionWidth, height: 178.0)
//TODO:unmock
let premiumOptionSize = CGSize(width: optionWidth, height: 178.0 + 23.0)
var validIds: [AnyHashable] = []
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: premiumOptionSize)
@@ -765,6 +886,7 @@ final class GiftOptionsScreenComponent: Component {
title = strings.Gift_Options_Premium_Months(3)
}
//TODO:unmock
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(
@@ -777,6 +899,7 @@ final class GiftOptionsScreenComponent: Component {
subject: .premium(months: product.months, price: product.price),
title: title,
subtitle: strings.Gift_Options_Premium_Premium,
label: "or **#1500**",
ribbon: product.discount.flatMap {
GiftItemComponent.Ribbon(
text: "-\($0)%",
@@ -923,13 +1046,23 @@ final class GiftOptionsScreenComponent: Component {
title: strings.Gift_Options_Gift_Filter_AllGifts
))
if let transferStarGifts = self.state?.transferStarGifts, !transferStarGifts.isEmpty {
//TODO:localize
tabSelectorItems.append(TabSelectorComponent.Item(
id: AnyHashable(StarsFilter.transfer.rawValue),
title: "My Gifts"
))
}
var hasLimited = false
var starsAmountsSet = Set<Int64>()
if let starGifts = self.state?.starGifts {
for product in starGifts {
starsAmountsSet.insert(product.price)
if product.availability != nil {
hasLimited = true
for gift in starGifts {
if case let .generic(gift) = gift {
starsAmountsSet.insert(gift.price)
if gift.availability != nil {
hasLimited = true
}
}
}
}
@@ -1041,7 +1174,10 @@ final class GiftOptionsScreenComponent: Component {
fileprivate var peer: EnginePeer?
fileprivate var premiumProducts: [PremiumGiftProduct]?
fileprivate var starGifts: [StarGift.Gift]?
fileprivate var starGifts: [StarGift]?
private let starGiftsContext: ProfileGiftsContext
fileprivate var transferStarGifts: [ProfileGiftsContext.State.StarGift]?
init(
context: AccountContext,
@@ -1050,6 +1186,8 @@ final class GiftOptionsScreenComponent: Component {
) {
self.context = context
self.starGiftsContext = ProfileGiftsContext(account: context.account, peerId: context.account.peerId)
super.init()
let availableProducts: Signal<[InAppPurchaseManager.Product], NoError>
@@ -1065,8 +1203,9 @@ final class GiftOptionsScreenComponent: Component {
TelegramEngine.EngineData.Item.Peer.Peer.init(id: peerId)
),
availableProducts,
context.engine.payments.cachedStarGifts()
).start(next: { [weak self] peer, availableProducts, starGifts in
context.engine.payments.cachedStarGifts(),
self.starGiftsContext.state
).start(next: { [weak self] peer, availableProducts, starGifts, profileGiftsState in
guard let self, let peer else {
return
}
@@ -1109,15 +1248,16 @@ final class GiftOptionsScreenComponent: Component {
}
self.premiumProducts = premiumProducts.sorted(by: { $0.months < $1.months })
}
}
self.starGifts = starGifts?.compactMap { gift in
if case let .generic(gift) = gift {
return gift
} else {
return nil
self.transferStarGifts = profileGiftsState.gifts.compactMap { gift in
if case .unique = gift.gift {
return gift
} else {
return nil
}
}
}
self.starGifts = starGifts
self.updated()
})
@@ -1188,3 +1328,14 @@ open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScree
super.containerLayoutUpdated(layout, transition: transition)
}
}
private extension StarGift {
var id: String {
switch self {
case let .generic(gift):
return "\(gift.id)"
case let .unique(gift):
return gift.slug
}
}
}