mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Various improvements
This commit is contained in:
@@ -129,6 +129,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
private var recenterOnTag: NSObject?
|
||||
|
||||
private var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||
private var sendPaidMessageStars: StarsAmount?
|
||||
|
||||
private var starImage: (UIImage, PresentationTheme)?
|
||||
|
||||
@@ -223,24 +224,29 @@ final class GiftSetupScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
switch component.subject {
|
||||
case .premium:
|
||||
if self.payWithStars {
|
||||
case let .premium(product):
|
||||
if self.payWithStars, let starsPrice = product.starsPrice, let peer = self.peerMap[component.peerId] {
|
||||
//TODO:localize
|
||||
let controller = textAlertController(
|
||||
context: component.context,
|
||||
title: "Send a Gift",
|
||||
text: "Are you sure you want to gift **Telegram Premium** to Alicia for **1500 Stars**?",
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Cancel", action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Confirm", action: { [weak self] in
|
||||
if let self {
|
||||
self.proceedWithPremiumGift()
|
||||
}
|
||||
})
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
environment.controller()?.present(controller, in: .window(.root))
|
||||
if let balance = component.context.starsContext?.currentState?.balance, balance.value < starsPrice {
|
||||
self.proceedWithStarGift()
|
||||
} else {
|
||||
let priceString = presentationStringsFormattedNumber(Int32(starsPrice), environment.dateTimeFormat.groupingSeparator)
|
||||
let controller = textAlertController(
|
||||
context: component.context,
|
||||
title: "Send a Gift",
|
||||
text: "Are you sure you want to gift **Telegram Premium** to \(peer.compactDisplayTitle) for **\(priceString) Stars**?",
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Cancel", action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Confirm", action: { [weak self] in
|
||||
if let self {
|
||||
self.proceedWithStarGift()
|
||||
}
|
||||
})
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
environment.controller()?.present(controller, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
self.proceedWithPremiumGift()
|
||||
}
|
||||
@@ -343,7 +349,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
private func proceedWithStarGift() {
|
||||
guard let component = self.component, case let .starGift(starGift) = component.subject, let starsContext = component.context.starsContext, let starsState = starsContext.currentState else {
|
||||
guard let component = self.component, let starsContext = component.context.starsContext, let starsState = starsContext.currentState else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -351,9 +357,24 @@ final class GiftSetupScreenComponent: Component {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let peerId = component.peerId
|
||||
|
||||
var finalPrice = starGift.price
|
||||
if self.includeUpgrade, let upgradeStars = starGift.upgradeStars {
|
||||
finalPrice += upgradeStars
|
||||
let entities = generateChatInputTextEntities(self.textInputState.text)
|
||||
|
||||
var finalPrice: Int64
|
||||
let source: BotPaymentInvoiceSource
|
||||
switch component.subject {
|
||||
case let .premium(product):
|
||||
if let option = product.starsGiftOption {
|
||||
finalPrice = option.amount
|
||||
source = .premiumGift(peerId: peerId, option: option, text: self.textInputState.text.string, entities: entities)
|
||||
} else {
|
||||
fatalError()
|
||||
}
|
||||
case let .starGift(starGift):
|
||||
finalPrice = starGift.price
|
||||
if self.includeUpgrade, let upgradeStars = starGift.upgradeStars {
|
||||
finalPrice += upgradeStars
|
||||
}
|
||||
source = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities)
|
||||
}
|
||||
|
||||
let proceed = { [weak self] in
|
||||
@@ -364,10 +385,6 @@ final class GiftSetupScreenComponent: Component {
|
||||
self.inProgress = true
|
||||
self.state?.updated()
|
||||
|
||||
let entities = generateChatInputTextEntities(self.textInputState.text)
|
||||
let source: BotPaymentInvoiceSource = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities)
|
||||
|
||||
|
||||
let completion = component.completion
|
||||
|
||||
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|
||||
@@ -384,7 +401,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel, case let .starGift(starGift) = component.subject {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) }
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
@@ -544,9 +561,10 @@ final class GiftSetupScreenComponent: Component {
|
||||
|
||||
let _ = (component.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: component.context.account.peerId)
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: component.context.account.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars(id: component.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer, accountPeer in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer, accountPeer, sendPaidMessageStars in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@@ -556,6 +574,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
if let accountPeer {
|
||||
self.peerMap[accountPeer.id] = accountPeer
|
||||
}
|
||||
self.sendPaidMessageStars = sendPaidMessageStars
|
||||
|
||||
self.state?.updated()
|
||||
})
|
||||
@@ -661,15 +680,15 @@ final class GiftSetupScreenComponent: Component {
|
||||
}
|
||||
)
|
||||
|
||||
self.optionsDisposable = (component.context.engine.payments.starsTopUpOptions()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] options in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.options = options
|
||||
})
|
||||
|
||||
if case let .starGift(gift) = component.subject {
|
||||
self.optionsDisposable = (component.context.engine.payments.starsTopUpOptions()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] options in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.options = options
|
||||
})
|
||||
|
||||
if let _ = gift.upgradeStars {
|
||||
self.previewPromise.set(
|
||||
component.context.engine.payments.starGiftUpgradePreview(giftId: gift.id)
|
||||
@@ -732,9 +751,6 @@ final class GiftSetupScreenComponent: Component {
|
||||
let total: Int32 = availability.total
|
||||
let position = CGFloat(remains) / CGFloat(total)
|
||||
let sold = total - remains
|
||||
//let remainsString = presentationStringsFormattedNumber(remains, environment.dateTimeFormat.groupingSeparator)
|
||||
//let soldString = presentationStringsFormattedNumber(total - remains, environment.dateTimeFormat.groupingSeparator)
|
||||
//let totalString = presentationStringsFormattedNumber(total, environment.dateTimeFormat.groupingSeparator)
|
||||
let remainingCountSize = self.remainingCount.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(RemainingCountComponent(
|
||||
@@ -772,50 +788,53 @@ final class GiftSetupScreenComponent: Component {
|
||||
|
||||
var introSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(Rectangle(color: .clear, height: 346.0, tag: self.introPlaceholderTag))))
|
||||
introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(ListMultilineTextFieldItemComponent(
|
||||
externalState: self.textInputState,
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
initialText: "",
|
||||
resetText: self.resetText.flatMap {
|
||||
return ListMultilineTextFieldItemComponent.ResetText(value: $0)
|
||||
},
|
||||
placeholder: environment.strings.Gift_Send_Customize_MessagePlaceholder,
|
||||
autocapitalizationType: .sentences,
|
||||
autocorrectionType: .yes,
|
||||
returnKeyType: .done,
|
||||
characterLimit: Int(giftConfiguration.maxCaptionLength),
|
||||
displayCharacterLimit: true,
|
||||
emptyLineHandling: .notAllowed,
|
||||
formatMenuAvailability: .available([.bold, .italic, .underline, .strikethrough, .spoiler]),
|
||||
updated: { _ in
|
||||
},
|
||||
returnKeyAction: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let titleView = self.introSection.findTaggedView(tag: self.textInputTag) as? ListMultilineTextFieldItemComponent.View {
|
||||
titleView.endEditing(true)
|
||||
}
|
||||
},
|
||||
textUpdateTransition: .spring(duration: 0.4),
|
||||
inputMode: self.currentInputMode,
|
||||
toggleInputMode: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
switch self.currentInputMode {
|
||||
case .keyboard:
|
||||
self.currentInputMode = .emoji
|
||||
case .emoji:
|
||||
self.currentInputMode = .keyboard
|
||||
}
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
},
|
||||
tag: self.textInputTag
|
||||
))))
|
||||
self.resetText = nil
|
||||
|
||||
if self.sendPaidMessageStars == nil {
|
||||
introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(ListMultilineTextFieldItemComponent(
|
||||
externalState: self.textInputState,
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
initialText: "",
|
||||
resetText: self.resetText.flatMap {
|
||||
return ListMultilineTextFieldItemComponent.ResetText(value: $0)
|
||||
},
|
||||
placeholder: environment.strings.Gift_Send_Customize_MessagePlaceholder,
|
||||
autocapitalizationType: .sentences,
|
||||
autocorrectionType: .yes,
|
||||
returnKeyType: .done,
|
||||
characterLimit: Int(giftConfiguration.maxCaptionLength),
|
||||
displayCharacterLimit: true,
|
||||
emptyLineHandling: .notAllowed,
|
||||
formatMenuAvailability: .available([.bold, .italic, .underline, .strikethrough, .spoiler]),
|
||||
updated: { _ in
|
||||
},
|
||||
returnKeyAction: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let titleView = self.introSection.findTaggedView(tag: self.textInputTag) as? ListMultilineTextFieldItemComponent.View {
|
||||
titleView.endEditing(true)
|
||||
}
|
||||
},
|
||||
textUpdateTransition: .spring(duration: 0.4),
|
||||
inputMode: self.currentInputMode,
|
||||
toggleInputMode: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
switch self.currentInputMode {
|
||||
case .keyboard:
|
||||
self.currentInputMode = .emoji
|
||||
case .emoji:
|
||||
self.currentInputMode = .keyboard
|
||||
}
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
},
|
||||
tag: self.textInputTag
|
||||
))))
|
||||
self.resetText = nil
|
||||
}
|
||||
|
||||
let footerAttributes = MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor),
|
||||
@@ -838,19 +857,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
case .starGift:
|
||||
//TODO:unmock
|
||||
//TODO:localize
|
||||
if self.textInputState.hasText {
|
||||
introFooter = AnyComponent(MultilineTextComponent(
|
||||
text: .markdown(
|
||||
text: "**\(peerName)** charges **250 Stars** for each message. That price has been added to the cost of the gift.",
|
||||
attributes: footerAttributes
|
||||
),
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
} else {
|
||||
introFooter = nil
|
||||
}
|
||||
introFooter = nil
|
||||
}
|
||||
|
||||
let introSectionSize = self.introSection.update(
|
||||
@@ -900,9 +907,8 @@ final class GiftSetupScreenComponent: Component {
|
||||
let subject: ChatGiftPreviewItem.Subject
|
||||
switch component.subject {
|
||||
case let .premium(product):
|
||||
if self.payWithStars {
|
||||
//TODO:unmock
|
||||
subject = .premium(months: product.months, amount: 1500, currency: "XTR")
|
||||
if self.payWithStars, let starsPrice = product.starsPrice {
|
||||
subject = .premium(months: product.months, amount: starsPrice, currency: "XTR")
|
||||
} else {
|
||||
let (currency, amount) = product.storeProduct?.priceCurrencyAndAmount ?? ("USD", 1)
|
||||
subject = .premium(months: product.months, amount: amount, currency: currency)
|
||||
@@ -917,7 +923,6 @@ final class GiftSetupScreenComponent: Component {
|
||||
peers.append(peer)
|
||||
}
|
||||
|
||||
//TODO:unmock
|
||||
let introContentSize = self.introContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
@@ -960,11 +965,12 @@ final class GiftSetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
switch component.subject {
|
||||
case .premium:
|
||||
//TODO:unmock
|
||||
//TODO:localize
|
||||
if "".isEmpty {
|
||||
let starsFooterRawString = "Your balance is **# 147 988**. [Get More Stars >]()"
|
||||
case let .premium(product):
|
||||
if let starsPrice = product.starsPrice {
|
||||
let balance = component.context.starsContext?.currentState?.balance.value ?? 0
|
||||
let balanceString = presentationStringsFormattedNumber(Int32(balance), environment.dateTimeFormat.groupingSeparator)
|
||||
|
||||
let starsFooterRawString = environment.strings.Gift_Send_PayWithStars_Info("# \(balanceString)").string
|
||||
let starsFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(starsFooterRawString, attributes: footerAttributes))
|
||||
|
||||
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme {
|
||||
@@ -977,7 +983,8 @@ final class GiftSetupScreenComponent: Component {
|
||||
starsFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: starsFooterText.string))
|
||||
}
|
||||
|
||||
let starsAttributedText = NSMutableAttributedString(string: "Pay with #1500", font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor)
|
||||
let priceString = presentationStringsFormattedNumber(Int32(starsPrice), environment.dateTimeFormat.groupingSeparator)
|
||||
let starsAttributedText = NSMutableAttributedString(string: environment.strings.Gift_Send_PayWithStars("#\(priceString)").string, font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor)
|
||||
let range = (starsAttributedText.string as NSString).range(of: "#")
|
||||
if range.location != NSNotFound {
|
||||
starsAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: range)
|
||||
@@ -997,6 +1004,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
text: .plain(starsFooterText),
|
||||
maximumNumberOfLines: 0,
|
||||
highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1),
|
||||
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
@@ -1005,9 +1013,18 @@ final class GiftSetupScreenComponent: Component {
|
||||
}
|
||||
},
|
||||
tapAction: { [weak self] _, _ in
|
||||
guard let _ = self else {
|
||||
guard let self, let component = self.component, let controller = self.environment?.controller(), let starsContext = component.context.starsContext else {
|
||||
return
|
||||
}
|
||||
let _ = (self.optionsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { options in
|
||||
let purchaseController = component.context.sharedContext.makeStarsPurchaseScreen(context: component.context, starsContext: starsContext, options: options ?? [], purpose: .generic, completion: { stars in
|
||||
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
})
|
||||
controller.push(purchaseController)
|
||||
})
|
||||
}
|
||||
)),
|
||||
items: [
|
||||
@@ -1243,10 +1260,9 @@ final class GiftSetupScreenComponent: Component {
|
||||
let buttonString: String
|
||||
switch component.subject {
|
||||
case let .premium(product):
|
||||
if self.payWithStars {
|
||||
//TODO:unmock
|
||||
let amountString = presentationStringsFormattedNumber(Int32(1500), presentationData.dateTimeFormat.groupingSeparator)
|
||||
buttonString = "\(environment.strings.Gift_Send_Send) # \(amountString)"
|
||||
if self.payWithStars, let starsPrice = product.starsPrice {
|
||||
let amountString = presentationStringsFormattedNumber(Int32(starsPrice), presentationData.dateTimeFormat.groupingSeparator)
|
||||
buttonString = "\(environment.strings.Gift_Send_Send) # \(amountString)"
|
||||
} else {
|
||||
let amountString = product.price
|
||||
buttonString = "\(environment.strings.Gift_Send_Send) \(amountString)"
|
||||
@@ -1256,13 +1272,9 @@ final class GiftSetupScreenComponent: Component {
|
||||
if self.includeUpgrade, let upgradePrice = starGift.upgradeStars {
|
||||
finalPrice += upgradePrice
|
||||
}
|
||||
//TODO:unmock
|
||||
if self.textInputState.hasText {
|
||||
finalPrice += 250
|
||||
}
|
||||
let amountString = presentationStringsFormattedNumber(Int32(finalPrice), presentationData.dateTimeFormat.groupingSeparator)
|
||||
let buttonTitle = isSelfGift ? environment.strings.Gift_Send_Buy : environment.strings.Gift_Send_Send
|
||||
buttonString = "\(buttonTitle) # \(amountString)"
|
||||
buttonString = "\(buttonTitle) # \(amountString)"
|
||||
if let availability = starGift.availability, availability.remains == 0 {
|
||||
buttonIsEnabled = false
|
||||
}
|
||||
@@ -1272,7 +1284,8 @@ final class GiftSetupScreenComponent: Component {
|
||||
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.starImage?.0 {
|
||||
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
|
||||
buttonAttributedString.addAttribute(.foregroundColor, value: environment.theme.list.itemCheckColors.foregroundColor, range: NSRange(range, in: buttonAttributedString.string))
|
||||
buttonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: buttonAttributedString.string))
|
||||
buttonAttributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: buttonAttributedString.string))
|
||||
buttonAttributedString.addAttribute(.kern, value: 2.0, range: NSRange(range, in: buttonAttributedString.string))
|
||||
}
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
@@ -1744,6 +1757,7 @@ private struct GiftConfiguration {
|
||||
|
||||
public struct PremiumGiftProduct: Equatable {
|
||||
public let giftOption: CachedPremiumGiftOption
|
||||
public let starsGiftOption: CachedPremiumGiftOption?
|
||||
public let storeProduct: InAppPurchaseManager.Product?
|
||||
public let discount: Int?
|
||||
|
||||
@@ -1759,12 +1773,18 @@ public struct PremiumGiftProduct: Equatable {
|
||||
return self.storeProduct?.price ?? formatCurrencyAmount(self.giftOption.amount, currency: self.giftOption.currency)
|
||||
}
|
||||
|
||||
public var pricePerMonth: String {
|
||||
return self.storeProduct?.pricePerMonth(Int(self.months)) ?? ""
|
||||
public var starsPrice: Int64? {
|
||||
return self.starsGiftOption?.amount
|
||||
}
|
||||
|
||||
public init(giftOption: CachedPremiumGiftOption, storeProduct: InAppPurchaseManager.Product?, discount: Int?) {
|
||||
public init(
|
||||
giftOption: CachedPremiumGiftOption,
|
||||
starsGiftOption: CachedPremiumGiftOption?,
|
||||
storeProduct: InAppPurchaseManager.Product?,
|
||||
discount: Int?
|
||||
) {
|
||||
self.giftOption = giftOption
|
||||
self.starsGiftOption = starsGiftOption
|
||||
self.storeProduct = storeProduct
|
||||
self.discount = discount
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user