Various fixes

This commit is contained in:
Ilya Laktyushin 2024-10-06 02:03:53 +04:00
parent 89d2ad8cf6
commit eb4519e192
12 changed files with 204 additions and 73 deletions

View File

@ -13008,6 +13008,8 @@ Sorry for the inconvenience.";
"Gift.Options.Gift.Filter.AllGifts" = "All Gifts";
"Gift.Options.Gift.Filter.Limited" = "Limited";
"Gift.Options.Gift.Limited" = "Limited";
"Gift.Options.Gift.SoldOut" = "Sold Out";
"Gift.Options.SoldOut.Text" = "Sorry, this gift is sold out.";
"PeerInfo.PaneGifts" = "Gifts";
@ -13039,6 +13041,10 @@ Sorry for the inconvenience.";
"Gift.Send.HideMyName.Info" = "Hide my name and message from visitors to %1$@'s profile. %2$@ will still see your name and message.";
"Gift.Send.Send" = "Send a Gift for";
"Gift.Send.Limited" = "Limited";
"Gift.Send.Left" = "left";
"Gift.Send.ErrorUnknown" = "An error occurred. Please try again.";
"Gift.Send.ErrorOutOfStock" = "Sorry, this gift is sold out. Please choose another gift.";
"Profile.SendGift" = "Send a Gift";
"Settings.SendGift" = "Send a Gift";
@ -13061,6 +13067,7 @@ Sorry for the inconvenience.";
"Notification.PremiumGift.YearsTitle_1" = "%@ Year Premium";
"Notification.PremiumGift.YearsTitle_any" = "%@ Years Premium";
"Notification.PremiumGift.More" = "more";
"Notification.PremiumGift.SubscriptionDescription" = "Subscription for exclusive Telegram features.";

View File

@ -931,13 +931,13 @@ public final class AvatarNode: ASDisplayNode {
if let repliesIcon = repliesIcon {
context.draw(repliesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - repliesIcon.size.width) / 2.0), y: floor((bounds.size.height - repliesIcon.size.height) / 2.0)), size: repliesIcon.size))
}
} else if case .anonymousSavedMessagesIcon = parameters.icon {
} else if case let .anonymousSavedMessagesIcon(isColored) = parameters.icon {
let factor = bounds.size.width / 60.0
context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0)
context.scaleBy(x: factor, y: -factor)
context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0)
if let theme = parameters.theme, theme.overallDarkAppearance {
if let theme = parameters.theme, theme.overallDarkAppearance, !isColored {
if let anonymousSavedMessagesDarkIcon = anonymousSavedMessagesDarkIcon {
context.draw(anonymousSavedMessagesDarkIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - anonymousSavedMessagesDarkIcon.size.width) / 2.0), y: floor((bounds.size.height - anonymousSavedMessagesDarkIcon.size.height) / 2.0)), size: anonymousSavedMessagesDarkIcon.size))
}

View File

@ -1558,19 +1558,23 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
applePayController.presentingViewController?.dismiss(animated: true, completion: nil)
}
let text: String
let text: String?
switch error {
case .precheckoutFailed:
text = strongSelf.presentationData.strings.Checkout_ErrorPrecheckoutFailed
case .paymentFailed:
text = strongSelf.presentationData.strings.Checkout_ErrorPaymentFailed
case .alreadyPaid:
text = strongSelf.presentationData.strings.Checkout_ErrorInvoiceAlreadyPaid
case .generic:
text = strongSelf.presentationData.strings.Checkout_ErrorGeneric
case .precheckoutFailed:
text = strongSelf.presentationData.strings.Checkout_ErrorPrecheckoutFailed
case .paymentFailed:
text = strongSelf.presentationData.strings.Checkout_ErrorPaymentFailed
case .alreadyPaid:
text = strongSelf.presentationData.strings.Checkout_ErrorInvoiceAlreadyPaid
case .generic:
text = strongSelf.presentationData.strings.Checkout_ErrorGeneric
default:
text = nil
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil)
if let text {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil)
}
strongSelf.failed()
}

View File

@ -588,6 +588,7 @@ public enum SendBotPaymentFormError {
case precheckoutFailed
case paymentFailed
case alreadyPaid
case starGiftOutOfStock
}
public enum SendBotPaymentResult {
@ -702,6 +703,8 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
return .fail(.paymentFailed)
} else if error.errorDescription == "INVOICE_ALREADY_PAID" {
return .fail(.alreadyPaid)
} else if error.errorDescription == "STARGIFT_USAGE_LIMITED" {
return .fail(.starGiftOutOfStock)
}
return .fail(.generic)
}

View File

@ -6224,7 +6224,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
for contentNode in self.contentNodes {
if contentNode is ChatMessageMediaBubbleContentNode {
if contentNode is ChatMessageMediaBubbleContentNode || contentNode is ChatMessageGiftBubbleContentNode {
contentNode.visibility = mapVisibility(effectiveMediaVisibility, boundsSize: self.bounds.size, insets: self.insets, to: contentNode)
} else {
contentNode.visibility = mapVisibility(effectiveVisibility, boundsSize: self.bounds.size, insets: self.insets, to: contentNode)

View File

@ -50,6 +50,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private let buttonStarsNode: PremiumStarsNode
private let buttonTitleNode: TextNode
private let moreTextNode: TextNode
private var maskView: UIImageView?
private var maskOverlayView: UIView?
@ -61,6 +63,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private var isExpanded: Bool = false
private var appliedIsExpanded: Bool = false
private var isStarGift = false
private var currentProgressDisposable: Disposable?
override public var visibility: ListViewItemNodeVisibility {
@ -138,6 +142,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.ribbonTextNode.isUserInteractionEnabled = false
self.ribbonTextNode.displaysAsynchronously = false
self.moreTextNode = TextNode()
self.moreTextNode.isUserInteractionEnabled = false
self.moreTextNode.displaysAsynchronously = false
super.init()
self.addSubnode(self.labelNode)
@ -147,6 +155,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.textClippingNode.addSubnode(self.subtitleNode.textNode)
self.addSubnode(self.placeholderNode)
self.addSubnode(self.animationNode)
self.addSubnode(self.moreTextNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addSubnode(self.buttonStarsNode)
@ -183,6 +192,19 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.currentProgressDisposable?.dispose()
}
override public func didLoad() {
super.didLoad()
self.maskView = UIImageView()
let maskOverlayView = UIView()
maskOverlayView.alpha = 0.0
maskOverlayView.backgroundColor = .white
self.maskOverlayView = maskOverlayView
self.maskView?.addSubview(maskOverlayView)
}
@objc private func buttonPressed() {
guard let item = self.item else {
return
@ -190,6 +212,14 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default, progress: self.makeProgress()))
}
private func expandPressed() {
self.isExpanded = !self.isExpanded
guard let item = self.item else{
return
}
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
}
private func makeProgress() -> Promise<Bool> {
let progress = Promise<Bool>()
self.currentProgressDisposable?.dispose()
@ -263,6 +293,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let makeButtonTitleLayout = TextNode.asyncLayout(self.buttonTitleNode)
let makeRibbonTextLayout = TextNode.asyncLayout(self.ribbonTextNode)
let makeMeasureTextLayout = TextNode.asyncLayout(nil)
let makeMoreTextLayout = TextNode.asyncLayout(self.moreTextNode)
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
@ -290,6 +321,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var ribbonTitle = ""
var hasServiceMessage = true
var textSpacing: CGFloat = 0.0
var isStarGift = false
for media in item.message.media {
if let action = media as? TelegramMediaAction {
switch action.action {
@ -377,6 +409,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
hasServiceMessage = false
}
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted):
isStarGift = true
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
if let giftText, !giftText.isEmpty {
@ -439,8 +472,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let (moreLayout, moreApply) = makeMoreTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_More, font: Font.semibold(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let attributedText: NSAttributedString
if let _ = animationFile {
if !entities.isEmpty {
attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.monospace(13.0), blockQuoteFont: Font.regular(13.0), message: nil)
} else {
attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(
@ -578,6 +613,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
}
strongSelf.item = item
strongSelf.isStarGift = isStarGift
strongSelf.updateVisibility()
@ -599,6 +635,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
))
let _ = buttonTitleApply()
let _ = ribbonTextApply()
let _ = moreApply()
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: 2.0), size: labelLayout.size)
strongSelf.labelNode.frame = labelFrame
@ -606,7 +643,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
strongSelf.titleNode.frame = titleFrame
let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY + textSpacing), size: CGSize(width: boundingWidth, height: clippedTextHeight))
let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight))
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY + textSpacing), size: subtitleLayout.size)
strongSelf.subtitleNode.textNode.frame = CGRect(origin: .zero, size: subtitleLayout.size)
@ -617,10 +654,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
animation.animator.updateFrame(layer: strongSelf.textClippingNode.layer, frame: clippingTextFrame, completion: nil)
}
if let maskView = strongSelf.maskView, let maskOverlayView = strongSelf.maskOverlayView {
animation.animator.updateFrame(layer: maskView.layer, frame: CGRect(origin: .zero, size: CGSize(width: boundingWidth, height: clippingTextFrame.size.height)), completion: nil)
animation.animator.updateFrame(layer: maskOverlayView.layer, frame: CGRect(origin: .zero, size: CGSize(width: boundingWidth, height: clippingTextFrame.size.height)), completion: nil)
animation.animator.updateFrame(layer: maskView.layer, frame: CGRect(origin: .zero, size: CGSize(width: clippingTextFrame.width, height: clippingTextFrame.height)), completion: nil)
animation.animator.updateFrame(layer: maskOverlayView.layer, frame: CGRect(origin: .zero, size: CGSize(width: clippingTextFrame.width, height: clippingTextFrame.height)), completion: nil)
}
animation.animator.updateFrame(layer: strongSelf.moreTextNode.layer, frame: CGRect(origin: CGPoint(x: clippingTextFrame.maxX - moreLayout.size.width, y: clippingTextFrame.maxY - moreLayout.size.height), size: moreLayout.size), completion: nil)
if !subtitleLayout.spoilers.isEmpty {
let dustColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText
@ -734,24 +771,16 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.updateAbsoluteRect(rect, within: size)
}
if canExpand {
if strongSelf.maskView?.image == nil {
strongSelf.maskView?.image = generateMaskImage()
if canExpand, let maskView = strongSelf.maskView {
if maskView.image == nil {
maskView.image = generateMaskImage()
}
strongSelf.textClippingNode.view.mask = strongSelf.maskView
// var expandIconFrame: CGRect = .zero
// if let icon = strongSelf.expandIcon.image {
// expandIconFrame = CGRect(origin: CGPoint(x: boundingWidth - icon.size.width - 19.0, y: backgroundFrame.maxY - icon.size.height - 6.0), size: icon.size)
// if wasHidden || isFirstTime {
// strongSelf.expandIcon.position = expandIconFrame.center
// } else {
// animation.animator.updatePosition(layer: strongSelf.expandIcon.layer, position: expandIconFrame.center, completion: nil)
// }
// strongSelf.expandIcon.bounds = CGRect(origin: .zero, size: expandIconFrame.size)
// }
animation.animator.updateAlpha(layer: strongSelf.moreTextNode.layer, alpha: strongSelf.isExpanded ? 0.0 : 1.0, completion: nil)
} else {
strongSelf.textClippingNode.view.mask = nil
strongSelf.moreTextNode.alpha = 0.0
}
switch strongSelf.visibility {
@ -876,6 +905,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
if self.buttonNode.frame.contains(point) {
return ChatMessageBubbleContentTapAction(content: .ignore)
} else if self.textClippingNode.frame.contains(point) && !self.isExpanded && !self.moreTextNode.alpha.isZero {
self.expandPressed()
return ChatMessageBubbleContentTapAction(content: .ignore)
} else if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) {
return ChatMessageBubbleContentTapAction(content: .openMessage)
} else if self.mediaBackgroundContent?.frame.contains(point) == true {
@ -934,7 +966,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
if !alreadySeen && self.animationNode.isPlaying {
item.controllerInteraction.playNextOutgoingGift = false
Queue.mainQueue().after(1.0) {
Queue.mainQueue().after(self.isStarGift ? 0.1 : 1.0) {
item.controllerInteraction.animateDiceSuccess(false, true)
}
}
@ -943,7 +976,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
private func generateMaskImage() -> UIImage? {
return generateImage(CGSize(width: 140, height: 30), rotatedContext: { size, context in
return generateImage(CGSize(width: 100.0, height: 30.0), rotatedContext: { size, context in
context.clear(CGRect(origin: .zero, size: size))
context.setFillColor(UIColor.white.cgColor)
@ -956,7 +989,7 @@ private func generateMaskImage() -> UIImage? {
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.setBlendMode(.copy)
context.clip(to: CGRect(origin: CGPoint(x: 10.0, y: 8.0), size: CGSize(width: 130.0, height: 22.0)))
context.drawLinearGradient(gradient, start: CGPoint(x: 10.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 22.0, right: 130.0))
context.clip(to: CGRect(origin: CGPoint(x: 10.0, y: 12.0), size: CGSize(width: 130.0, height: 18.0)))
context.drawLinearGradient(gradient, start: CGPoint(x: 30.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 18.0, right: 70.0))
}

View File

@ -64,6 +64,7 @@ public final class GiftItemComponent: Component {
let ribbon: Ribbon?
let isLoading: Bool
let isHidden: Bool
let isSoldOut: Bool
public init(
context: AccountContext,
@ -75,7 +76,8 @@ public final class GiftItemComponent: Component {
price: String,
ribbon: Ribbon? = nil,
isLoading: Bool = false,
isHidden: Bool = false
isHidden: Bool = false,
isSoldOut: Bool = false
) {
self.context = context
self.theme = theme
@ -87,6 +89,7 @@ public final class GiftItemComponent: Component {
self.ribbon = ribbon
self.isLoading = isLoading
self.isHidden = isHidden
self.isSoldOut = isSoldOut
}
public static func ==(lhs: GiftItemComponent, rhs: GiftItemComponent) -> Bool {
@ -120,6 +123,9 @@ public final class GiftItemComponent: Component {
if lhs.isHidden != rhs.isHidden {
return false
}
if lhs.isSoldOut != rhs.isSoldOut {
return false
}
return true
}
@ -284,14 +290,26 @@ public final class GiftItemComponent: Component {
}
}
let buttonColor: UIColor
var isStars = false
if component.isSoldOut {
buttonColor = component.theme.list.itemDestructiveColor
} else if component.price.containsEmoji {
buttonColor = UIColor(rgb: 0xd3720a)
isStars = true
} else {
buttonColor = component.theme.list.itemAccentColor
}
let buttonSize = self.button.update(
transition: transition,
component: AnyComponent(
ButtonContentComponent(
context: component.context,
text: component.price,
color: component.price.containsEmoji ? UIColor(rgb: 0xd3720a) : component.theme.list.itemAccentColor,
isStars: component.price.containsEmoji)
color: buttonColor,
isStars: isStars
)
),
environment: {},
containerSize: availableSize

View File

@ -25,6 +25,7 @@ import GiftItemComponent
import InAppPurchaseManager
import TabSelectorComponent
import GiftSetupScreen
import UndoUI
final class GiftOptionsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -173,6 +174,20 @@ final class GiftOptionsScreenComponent: Component {
self.updateScrolling(transition: .immediate)
}
private func dismissAllTooltips(controller: ViewController) {
controller.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
return true
})
controller.window?.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
})
}
private func updateScrolling(transition: ComponentTransition) {
guard let environment = self.environment, let component = self.component else {
return
@ -274,6 +289,7 @@ final class GiftOptionsScreenComponent: Component {
self.starsItems[itemId] = visibleItem
}
let isSoldOut = gift.availability?.remains == 0
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(
@ -284,13 +300,14 @@ final class GiftOptionsScreenComponent: Component {
theme: environment.theme,
peer: nil,
subject: .starGift(gift.id, gift.file),
price: "⭐️ \(gift.price)",
price: isSoldOut ? environment.strings.Gift_Options_Gift_SoldOut : "⭐️ \(gift.price)",
ribbon: gift.availability != nil ?
GiftItemComponent.Ribbon(
text: environment.strings.Gift_Options_Gift_Limited,
color: .blue
)
: nil
: nil,
isSoldOut: isSoldOut
)
),
effectAlignment: .center,
@ -303,13 +320,25 @@ final class GiftOptionsScreenComponent: Component {
} else {
mainController = controller
}
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift),
completion: component.completion
)
mainController.push(giftController)
if gift.availability?.remains == 0 {
self.dismissAllTooltips(controller: mainController)
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .sticker(context: component.context, file: gift.file, loop: false, title: nil, text: presentationData.strings.Gift_Options_SoldOut_Text, undoText: nil, customAction: nil),
elevatedLayout: false,
action: { _ in return true }
)
mainController.present(resultController, in: .window(.root))
} else {
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift),
completion: component.completion
)
mainController.push(giftController)
}
}
}
},

View File

@ -43,6 +43,7 @@ swift_library(
"//submodules/TelegramUI/Components/EmojiSuggestionsComponent",
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
"//submodules/InAppPurchaseManager",
"//submodules/Components/BlurredBackgroundComponent",
],
visibility = [
"//visibility:public",

View File

@ -258,7 +258,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.isUserInteractionEnabled = false
itemNode?.visibility = .visible(1.0, .infinite)
itemNode!.visibility = .visible(1.0, .infinite)
messageNodes.append(itemNode!)
self.initialBubbleHeight = itemNode?.frame.height

View File

@ -29,6 +29,7 @@ import ChatPresentationInterfaceState
import AudioToolbox
import TextFormat
import InAppPurchaseManager
import BlurredBackgroundComponent
final class GiftSetupScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -78,6 +79,9 @@ final class GiftSetupScreenComponent: Component {
private let introContent = ComponentView<Empty>()
private let introSection = ComponentView<Empty>()
private let hideSection = ComponentView<Empty>()
private let buttonBackground = ComponentView<Empty>()
private let buttonSeparator = SimpleLayer()
private let button = ComponentView<Empty>()
private var ignoreScrolling: Bool = false
@ -356,6 +360,27 @@ final class GiftSetupScreenComponent: Component {
}
starsContext.load(force: true)
}, error: { [weak self] error in
guard let self, let controller = self.environment?.controller() else {
return
}
self.inProgress = false
self.state?.updated()
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
var errorText: String?
switch error {
case .starGiftOutOfStock:
errorText = presentationData.strings.Gift_Send_ErrorOutOfStock
default:
errorText = presentationData.strings.Gift_Send_ErrorUnknown
}
if let errorText = errorText {
let alertController = textAlertController(context: component.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
controller.present(alertController, in: .window(.root))
}
})
})
}
@ -826,6 +851,34 @@ final class GiftSetupScreenComponent: Component {
self.starImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: environment.theme.list.itemCheckColors.foregroundColor)!, environment.theme)
}
let buttonHeight: CGFloat = 50.0
let bottomPanelPadding: CGFloat = 12.0
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
let bottomPanelAlpha: CGFloat = 1.0
let bottomPanelSize = self.buttonBackground.update(
transition: transition,
component: AnyComponent(BlurredBackgroundComponent(
color: environment.theme.rootController.tabBar.backgroundColor
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: bottomPanelHeight)
)
self.buttonSeparator.backgroundColor = environment.theme.rootController.tabBar.separatorColor.cgColor
self.buttonSeparator.opacity = Float(bottomPanelAlpha)
if let view = self.buttonBackground.view {
if view.superview == nil {
self.addSubview(view)
self.layer.addSublayer(self.buttonSeparator)
}
view.alpha = bottomPanelAlpha
view.frame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height), size: bottomPanelSize)
self.buttonSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelSize.height), size: CGSize(width: availableSize.width, height: UIScreenPixel))
}
var buttonIsEnabled = true
let buttonString: String
switch component.subject {
case let .premium(product):
@ -834,6 +887,9 @@ final class GiftSetupScreenComponent: Component {
case let .starGift(starGift):
let amountString = presentationStringsFormattedNumber(Int32(starGift.price), presentationData.dateTimeFormat.groupingSeparator)
buttonString = "\(environment.strings.Gift_Send_Send) # \(amountString)"
if let availability = starGift.availability, availability.remains == 0 {
buttonIsEnabled = false
}
}
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
@ -856,20 +912,20 @@ final class GiftSetupScreenComponent: Component {
id: AnyHashable(0),
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
),
isEnabled: true,
isEnabled: buttonIsEnabled,
displaysProgress: self.inProgress,
action: { [weak self] in
self?.proceed()
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50)
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: buttonHeight)
)
if let buttonView = self.button.view {
if buttonView.superview == nil {
self.addSubview(buttonView)
}
buttonView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - buttonSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - buttonSize.height), size: buttonSize)
buttonView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - buttonSize.width) / 2.0), y: availableSize.height - bottomPanelHeight + bottomPanelPadding), size: buttonSize)
}
if self.textInputState.isEditing, let emojiSuggestion = self.textInputState.currentEmojiSuggestion, emojiSuggestion.disposable == nil {

View File

@ -119,6 +119,7 @@ public class RemainingCountComponent: Component {
private let badgeForeground: SimpleLayer
private let badgeLabel: BadgeLabelView
private let badgeLeftLabel = ComponentView<Empty>()
private let badgeLabelMaskView = UIImageView()
private var badgeTailPosition: CGFloat = 0.0
@ -737,7 +738,6 @@ final class BadgeLabelView: UIView {
}
private var itemViews: [Int: StackView] = [:]
private var staticLabel = UILabel()
init() {
super.init(frame: .zero)
@ -752,7 +752,6 @@ final class BadgeLabelView: UIView {
var color: UIColor = .white {
didSet {
self.staticLabel.textColor = self.color
for (_, view) in self.itemViews {
view.color = self.color
}
@ -760,25 +759,6 @@ final class BadgeLabelView: UIView {
}
func update(value: String, transition: ComponentTransition) -> CGSize {
if value.contains(" ") {
for (_, view) in self.itemViews {
view.isHidden = true
}
if self.staticLabel.superview == nil {
self.staticLabel.textColor = self.color
self.staticLabel.font = font
self.addSubview(self.staticLabel)
}
self.staticLabel.text = value
let size = self.staticLabel.sizeThatFits(CGSize(width: 100.0, height: 100.0))
self.staticLabel.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: labelHeight))
return CGSize(width: ceil(self.staticLabel.bounds.width), height: ceil(self.staticLabel.bounds.height))
}
let string = value
let stringArray = Array(string.map { String($0) }.reversed())