diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 73405bd474..db3598e081 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15714,3 +15714,5 @@ Error: %8$@"; "Gift.Variants.CollectionInfo.CraftableModel_any" = "**%@** craftable models"; "Gift.Variants.ViewCraftableModels" = "View Craftable Models"; "Gift.Variants.ViewPrimaryModels" = "View Primary Models"; + +"Gift.View.OnSale" = "On sale for %@"; diff --git a/submodules/Components/SheetComponent/Sources/SheetComponent.swift b/submodules/Components/SheetComponent/Sources/SheetComponent.swift index 69ed8881fa..0122ac4a17 100644 --- a/submodules/Components/SheetComponent/Sources/SheetComponent.swift +++ b/submodules/Components/SheetComponent/Sources/SheetComponent.swift @@ -191,9 +191,7 @@ public final class SheetComponent: C self.scrollView = ScrollView() self.scrollView.delaysContentTouches = false - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - self.scrollView.contentInsetAdjustmentBehavior = .never - } + self.scrollView.contentInsetAdjustmentBehavior = .never self.scrollView.showsVerticalScrollIndicator = false self.scrollView.showsHorizontalScrollIndicator = false self.scrollView.alwaysBounceVertical = true @@ -351,6 +349,9 @@ public final class SheetComponent: C }) } else { var targetOffset: CGFloat = self.scrollView.contentSize.height + abs(contentView.frame.minY) + 6.0 + if self.currentHasInputHeight { + targetOffset += 330.0 + } if self.isCentered { targetOffset = self.frame.height + self.scrollView.frame.height * 0.5 } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 726a00f856..9b85255901 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -23,6 +23,7 @@ import ThemeCarouselItem import ThemeAccentColorScreen import WallpaperGridScreen import PeerNameColorItem +import DeviceModel private final class ThemeSettingsControllerArguments { let context: AccountContext @@ -487,7 +488,9 @@ private func themeSettingsControllerEntries( } entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased())) - entries.append(.sendWithCmdEnter(presentationData.theme, strings.Appearance_SendWithCmdEnter, chatSettings.sendWithCmdEnter)) + if DeviceModel.current.isIpad { + entries.append(.sendWithCmdEnter(presentationData.theme, strings.Appearance_SendWithCmdEnter, chatSettings.sendWithCmdEnter)) + } entries.append(.showNextMediaOnTap(presentationData.theme, strings.Appearance_ShowNextMediaOnTap, mediaSettings.showNextMediaOnTap)) entries.append(.showNextMediaOnTapInfo(presentationData.theme, strings.Appearance_ShowNextMediaOnTapInfo)) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift deleted file mode 100644 index 5d05bf9d1c..0000000000 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/ButtonsComponent.swift +++ /dev/null @@ -1,267 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import ComponentFlow -import TelegramCore -import BundleIconComponent -import MultilineTextComponent -import MoreButtonNode -import AccountContext -import TelegramPresentationData -import TelegramStringFormatting - -final class PriceButtonComponent: Component { - let price: CurrencyAmount - let dateTimeFormat: PresentationDateTimeFormat - - init( - price: CurrencyAmount, - dateTimeFormat: PresentationDateTimeFormat - ) { - self.price = price - self.dateTimeFormat = dateTimeFormat - } - - static func ==(lhs: PriceButtonComponent, rhs: PriceButtonComponent) -> Bool { - return lhs.price == rhs.price && lhs.dateTimeFormat == rhs.dateTimeFormat - } - - final class View: UIView { - private let backgroundView = UIView() - - private let icon = UIImageView() - private let text = ComponentView() - - private var component: PriceButtonComponent? - private weak var state: EmptyComponentState? - - override init(frame: CGRect) { - super.init(frame: frame) - - self.backgroundView.clipsToBounds = true - self.addSubview(self.backgroundView) - - self.backgroundView.addSubview(self.icon) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: PriceButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - let previousComponent = self.component - self.component = component - self.state = state - - if previousComponent?.price.currency != component.price.currency { - switch component.price.currency { - case .stars: - self.icon.image = UIImage(bundleImageName: "Premium/Stars/ButtonStar")?.withRenderingMode(.alwaysTemplate) - case .ton: - self.icon.image = UIImage(bundleImageName: "Ads/TonAbout")?.withRenderingMode(.alwaysTemplate) - } - } - - var backgroundSize = CGSize(width: 42.0, height: 30.0) - let textSize = self.text.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString( - string: formatCurrencyAmountText(component.price, dateTimeFormat: component.dateTimeFormat), - font: Font.semibold(11.0), - textColor: UIColor(rgb: 0xffffff) - )) - )), - environment: {}, - containerSize: availableSize - ) - let textFrame = CGRect(origin: CGPoint(x: 32.0, y: floorToScreenPixels((backgroundSize.height - textSize.height) / 2.0)), size: textSize) - if let textView = self.text.view { - if textView.superview == nil { - self.backgroundView.addSubview(textView) - } - transition.setFrame(view: textView, frame: textFrame) - } - backgroundSize.width += textSize.width - - self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0 - - let backgroundColor: UIColor = UIColor(rgb: 0xffffff, alpha: 0.1) - transition.setBackgroundColor(view: self.backgroundView, color: backgroundColor) - - let backgroundFrame = CGRect(origin: .zero, size: backgroundSize) - transition.setFrame(view: self.backgroundView, frame: backgroundFrame) - - if let iconSize = self.icon.image?.size { - let iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floorToScreenPixels((backgroundSize.height - iconSize.height) / 2.0)), size: iconSize) - transition.setFrame(view: self.icon, frame: iconFrame) - } - self.icon.tintColor = UIColor(rgb: 0xffffff) - - return backgroundSize - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - -final class ButtonsComponent: Component { - let theme: PresentationTheme - let isOverlay: Bool - let showMoreButton: Bool - let closePressed: () -> Void - let morePressed: (ASDisplayNode, ContextGesture?) -> Void - - init( - theme: PresentationTheme, - isOverlay: Bool, - showMoreButton: Bool, - closePressed: @escaping () -> Void, - morePressed: @escaping (ASDisplayNode, ContextGesture?) -> Void - ) { - self.theme = theme - self.isOverlay = isOverlay - self.showMoreButton = showMoreButton - self.closePressed = closePressed - self.morePressed = morePressed - } - - static func ==(lhs: ButtonsComponent, rhs: ButtonsComponent) -> Bool { - return lhs.theme === rhs.theme && lhs.isOverlay == rhs.isOverlay && lhs.showMoreButton == rhs.showMoreButton - } - - final class View: UIView { - private let backgroundView = UIView() - - private let closeButton = HighlightTrackingButton() - private let closeIcon = UIImageView() - private let moreNode = MoreButtonNode(theme: defaultPresentationTheme, size: CGSize(width: 36.0, height: 36.0), encircled: false) - - private var component: ButtonsComponent? - private weak var state: EmptyComponentState? - - override init(frame: CGRect) { - super.init(frame: frame) - - self.backgroundView.clipsToBounds = true - self.addSubview(self.backgroundView) - - self.closeIcon.image = generateCloseButtonImage() - self.moreNode.updateColor(.white, transition: .immediate) - - self.backgroundView.addSubview(self.moreNode.view) - self.backgroundView.addSubview(self.closeButton) - self.backgroundView.addSubview(self.closeIcon) - - self.closeButton.highligthedChanged = { [weak self] highlighted in - guard let self else { - return - } - if highlighted { - self.closeIcon.layer.removeAnimation(forKey: "opacity") - self.closeIcon.alpha = 0.6 - } else { - self.closeIcon.alpha = 1.0 - self.closeIcon.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - } - } - self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func closePressed() { - guard let component = self.component else { - return - } - component.closePressed() - } - - func update(component: ButtonsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - self.state = state - - let backgroundSize = CGSize(width: component.showMoreButton ? 70.0 : 30.0, height: 30.0) - self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0 - - let backgroundColor: UIColor = component.isOverlay ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x808084, alpha: 0.1) - let foregroundColor: UIColor = component.isOverlay ? .white : component.theme.actionSheet.inputClearButtonColor - transition.setBackgroundColor(view: self.backgroundView, color: backgroundColor) - transition.setTintColor(view: self.closeIcon, color: foregroundColor) - - let backgroundFrame = CGRect(origin: .zero, size: backgroundSize) - transition.setFrame(view: self.backgroundView, frame: backgroundFrame) - - transition.setFrame(view: self.moreNode.view, frame: CGRect(origin: CGPoint(x: -7.0, y: -4.0), size: CGSize(width: 36.0, height: 36.0))) - transition.setAlpha(view: self.moreNode.view, alpha: component.showMoreButton ? 1.0 : 0.0) - self.moreNode.action = { [weak self] node, gesture in - guard let self, let component = self.component else { - return - } - component.morePressed(node, gesture) - } - - let closeFrame = CGRect(origin: CGPoint(x: backgroundSize.width - 30.0 - (component.showMoreButton ? 3.0 : 0.0), y: 0.0), size: CGSize(width: 30.0, height: 30.0)) - transition.setFrame(view: self.closeIcon, frame: closeFrame) - transition.setFrame(view: self.closeButton, frame: closeFrame) - - return backgroundSize - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - -private func generateCloseButtonImage() -> UIImage? { - return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setStrokeColor(UIColor.white.cgColor) - - context.move(to: CGPoint(x: 10.0, y: 10.0)) - context.addLine(to: CGPoint(x: 20.0, y: 20.0)) - context.strokePath() - - context.move(to: CGPoint(x: 20.0, y: 10.0)) - context.addLine(to: CGPoint(x: 10.0, y: 20.0)) - context.strokePath() - })?.withRenderingMode(.alwaysTemplate) -} - -func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? { - return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(backgroundColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setStrokeColor(foregroundColor.cgColor) - - context.move(to: CGPoint(x: 10.0, y: 10.0)) - context.addLine(to: CGPoint(x: 20.0, y: 20.0)) - context.strokePath() - - context.move(to: CGPoint(x: 20.0, y: 10.0)) - context.addLine(to: CGPoint(x: 10.0, y: 20.0)) - context.strokePath() - }) -} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 9cbeafdeac..1f921373e4 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -46,6 +46,7 @@ import PeerTableCellComponent import AvatarComponent import GlassControls import GlassBarButtonComponent +import GlassBackgroundComponent private final class GiftViewSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -2398,8 +2399,6 @@ private final class GiftViewSheetContent: CombinedComponent { } static var body: Body { - let priceButton = Child(PlainButtonComponent.self) - let buttons = Child(GlassControlPanelComponent.self) let animation = Child(GiftCompositionComponent.self) let title = Child(MultilineTextComponent.self) @@ -2409,9 +2408,9 @@ private final class GiftViewSheetContent: CombinedComponent { let description = Child(MultilineTextComponent.self) let animatedDescription = Child(HStack.self) - let transferButton = Child(PlainButtonComponent.self) - let wearButton = Child(PlainButtonComponent.self) - let resellButton = Child(PlainButtonComponent.self) + let transferButton = Child(HeaderButtonComponent.self) + let wearButton = Child(HeaderButtonComponent.self) + let resellButton = Child(HeaderButtonComponent.self) let wearAvatar = Child(AvatarComponent.self) let wearPeerName = Child(MultilineTextComponent.self) @@ -2431,7 +2430,10 @@ private final class GiftViewSheetContent: CombinedComponent { let upgradePerks = Child(List.self) let upgradeKeepName = Child(PlainButtonComponent.self) let upgradePriceButton = Child(PlainButtonComponent.self) - let variantsMeasureDescription = Child(MultilineTextComponent.self) + let upgradeDescriptionMeasure = Child(MultilineTextComponent.self) + + let priceButtonMeasure = Child(MultilineTextWithEntitiesComponent.self) + let priceButton = Child(GlassBarButtonComponent.self) let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: []) @@ -2617,6 +2619,19 @@ private final class GiftViewSheetContent: CombinedComponent { headerSubject = nil } + var buttonsBackground: GlassControlGroupComponent.Background = .panel + if let uniqueGift, let backdropAttribute = uniqueGift.attributes.first(where: { attribute in + if case .backdrop = attribute { + return true + } else { + return false + } + }), case let .backdrop(_, _, _, outerColor, _, _, _) = backdropAttribute { + buttonsBackground = .color(UIColor(rgb: UInt32(bitPattern: outerColor)).mixedWith(.white, alpha: 0.2)) + } else if showUpgradePreview, let backgroundColor = giftCompositionExternalState.backgroundColor { + buttonsBackground = .color(backgroundColor.mixedWith(.white, alpha: 0.2)) + } + var ownerPeerId: EnginePeer.Id? if let uniqueGift { if case let .peerId(peerId) = uniqueGift.owner { @@ -2730,6 +2745,16 @@ private final class GiftViewSheetContent: CombinedComponent { let tableTextColor = theme.list.itemPrimaryTextColor let tableLinkColor = theme.list.itemAccentColor + var resellAmount: CurrencyAmount? + var selling = false + if let uniqueGift { + if uniqueGift.resellForTonOnly { + resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .ton }) + } else { + resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) + } + } + if let headerSubject { let animation = animation.update( component: GiftCompositionComponent( @@ -2925,16 +2950,16 @@ private final class GiftViewSheetContent: CombinedComponent { buttonColor = backgroundColor.mixedWith(.white, alpha: 0.2) } - let variantsMeasureDescription = variantsMeasureDescription.update( + let upgradeDescriptionMeasure = upgradeDescriptionMeasure.update( component: MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_ViewAllVariants, font: Font.semibold(13.0), textColor: .clear))), availableSize: context.availableSize, transition: .immediate ) - context.add(variantsMeasureDescription + context.add(upgradeDescriptionMeasure .position(CGPoint(x: -10000.0, y: -10000.0)) ) - let variantsButtonSize = CGSize(width: variantsMeasureDescription.size.width + 87.0, height: 24.0) + let variantsButtonSize = CGSize(width: upgradeDescriptionMeasure.size.width + 87.0, height: 24.0) let upgradeTitle = upgradeTitle.update( component: MultilineTextComponent( @@ -3153,7 +3178,7 @@ private final class GiftViewSheetContent: CombinedComponent { } } if let releasedBy = uniqueGift.releasedBy, let peer = state.peerMap[releasedBy], let addressName = peer.addressName { - descriptionText = strings.Gift_Unique_CollectibleBy("#\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))", "[@\(addressName)]()").string + descriptionText = strings.Gift_View_ReleasedBy("[@\(addressName)]()").string hasDescriptionButton = true releasedByPeer = peer } @@ -3300,6 +3325,88 @@ private final class GiftViewSheetContent: CombinedComponent { }) descriptionOffset += subtitle.size.height } + + if let resellAmount { + if incoming || ownerPeerId == component.context.account.peerId { + var valueString = formatCurrencyAmountText(resellAmount, dateTimeFormat: environment.dateTimeFormat) + switch resellAmount.currency { + case .stars: + valueString = "⭐️\(valueString)" + case .ton: + valueString = "💎\(valueString)" + } + let priceButtonAttributedString = NSMutableAttributedString(string: strings.Gift_View_OnSale(valueString).string, font: Font.regular(13.0), textColor: .white) + let starRange = (priceButtonAttributedString.string as NSString).range(of: "⭐️") + if starRange.location != NSNotFound { + priceButtonAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: starRange) + priceButtonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: starRange) + } + let tonRange = (priceButtonAttributedString.string as NSString).range(of: "💎") + if tonRange.location != NSNotFound { + priceButtonAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 1, file: nil, custom: .ton(tinted: true)), range: tonRange) + priceButtonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: tonRange) + } + + let priceButtonMeasure = priceButtonMeasure.update( + component: MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: .white, + text: .plain(priceButtonAttributedString) + ), + availableSize: context.availableSize, + transition: context.transition + ) + context.add(priceButtonMeasure + .position(CGPoint(x: -10000.0, y: -10000.0)) + ) + + var buttonColor: UIColor = UIColor(rgb: 0xffffff, alpha: 0.1) + if case let .color(color) = buttonsBackground { + buttonColor = color + } + + let priceButtonSize = CGSize(width: priceButtonMeasure.size.width + 18.0, height: 19.0) + let priceButton = priceButton.update( + component: GlassBarButtonComponent( + size: priceButtonSize, + backgroundColor: buttonColor, + isDark: true, + state: .tintedGlass, + component: AnyComponentWithIdentity(id: "label", component: AnyComponent( + MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: .white, + text: .plain(priceButtonAttributedString) + ) + )), + action: { [weak state] _ in + state?.resellGift(update: true) + } + ), + availableSize: priceButtonSize, + transition: context.transition + ) + headerComponents.append({ + context.add(priceButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + priceButton.size.height / 2.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + }) + + descriptionText = "" + originY += 7.0 + } + if case let .uniqueGift(_, recipientPeerId) = component.subject, recipientPeerId != nil { + } else if ownerPeerId != component.context.account.peerId { + selling = true + } + } + var useDescriptionTint = false if !descriptionText.isEmpty { var linkColor = theme.actionSheet.controlAccentColor @@ -3787,18 +3894,19 @@ private final class GiftViewSheetContent: CombinedComponent { let buttonWidth = floor(context.availableSize.width - sideInset * 2.0 - buttonSpacing * CGFloat(buttonsCount - 1)) / CGFloat(buttonsCount) let buttonHeight: CGFloat = 58.0 + var buttonColor: UIColor = UIColor(rgb: 0xffffff, alpha: 0.1) + if case let .color(color) = buttonsBackground { + buttonColor = color + } + var buttonOriginX = sideInset if canTransfer { let transferButton = transferButton.update( - component: PlainButtonComponent( - content: AnyComponent( - HeaderButtonComponent( - title: strings.Gift_View_Header_Transfer, - iconName: "Premium/Collectible/Transfer", - isLocked: isMyHostedUniqueGift - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: strings.Gift_View_Header_Transfer, + buttonColor: buttonColor, + iconName: "Premium/Collectible/Transfer", + isLocked: isMyHostedUniqueGift, action: { [weak state] in state?.transferGift() } @@ -3819,19 +3927,15 @@ private final class GiftViewSheetContent: CombinedComponent { } let wearButton = wearButton.update( - component: PlainButtonComponent( - content: AnyComponent( - HeaderButtonComponent( - title: isWearing ? strings.Gift_View_Header_TakeOff : strings.Gift_View_Header_Wear, - iconName: isWearing ? "Premium/Collectible/Unwear" : "Premium/Collectible/Wear" - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: isWearing ? strings.Gift_View_Header_TakeOff : strings.Gift_View_Header_Wear, + buttonColor: buttonColor, + iconName: isWearing ? "Premium/Collectible/Unwear" : "Premium/Collectible/Wear", action: { [weak state] in if let state { if isWearing { state.commitTakeOff() - + state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_TookOff("\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))").string) } else { if let controller = controller() as? GiftViewScreen { @@ -3851,7 +3955,7 @@ private final class GiftViewSheetContent: CombinedComponent { canWear = component.context.isPremium } let _ = (ApplicationSpecificNotice.getStarGiftWearTips(accountManager: component.context.sharedContext.accountManager) - |> deliverOnMainQueue).start(next: { [weak state] count in + |> deliverOnMainQueue).start(next: { [weak state] count in guard let state else { return } @@ -3882,15 +3986,11 @@ private final class GiftViewSheetContent: CombinedComponent { if canResell { let resellButton = resellButton.update( - component: PlainButtonComponent( - content: AnyComponent( - HeaderButtonComponent( - title: (uniqueGift.resellAmounts ?? []).isEmpty ? strings.Gift_View_Sell : strings.Gift_View_Unlist, - iconName: (uniqueGift.resellAmounts ?? []).isEmpty ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist", - isLocked: isMyHostedUniqueGift - ) - ), - effectAlignment: .center, + component: HeaderButtonComponent( + title: (uniqueGift.resellAmounts ?? []).isEmpty ? strings.Gift_View_Sell : strings.Gift_View_Unlist, + buttonColor: buttonColor, + iconName: (uniqueGift.resellAmounts ?? []).isEmpty ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist", + isLocked: isMyHostedUniqueGift, action: { [weak state] in state?.resellGift() } @@ -4440,44 +4540,6 @@ private final class GiftViewSheetContent: CombinedComponent { component() } - var resellAmount: CurrencyAmount? - var selling = false - if let uniqueGift { - if uniqueGift.resellForTonOnly { - resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .ton }) - } else { - resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) - } - if let resellAmount, wearPeerNameChild == nil { - if incoming || ownerPeerId == component.context.account.peerId { - let priceButton = priceButton.update( - component: PlainButtonComponent( - content: AnyComponent( - PriceButtonComponent(price: resellAmount, dateTimeFormat: environment.dateTimeFormat) - ), - effectAlignment: .center, - action: { [weak state] in - state?.resellGift(update: true) - }, - animateScale: false - ), - availableSize: CGSize(width: 150.0, height: 30.0), - transition: context.transition - ) - context.add(priceButton - .position(CGPoint(x: environment.safeInsets.left + 16.0 + priceButton.size.width / 2.0, y: 76.0 + priceButton.size.height / 2.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) - } - if case let .uniqueGift(_, recipientPeerId) = component.subject, recipientPeerId != nil { - } else if ownerPeerId != component.context.account.peerId { - selling = true - } - } - } - - var isChatTheme = false if let controller = controller() as? GiftViewScreen, controller.openChatTheme != nil { isChatTheme = true @@ -5155,20 +5217,7 @@ private final class GiftViewSheetContent: CombinedComponent { ) originY += upgradePriceButton.size.height } - - var buttonsBackground: GlassControlGroupComponent.Background = .panel - if let uniqueGift, let backdropAttribute = uniqueGift.attributes.first(where: { attribute in - if case .backdrop = attribute { - return true - } else { - return false - } - }), case let .backdrop(_, _, _, outerColor, _, _, _) = backdropAttribute { - buttonsBackground = .color(UIColor(rgb: UInt32(bitPattern: outerColor)).mixedWith(.white, alpha: 0.2)) - } else if showUpgradePreview, let backgroundColor = giftCompositionExternalState.backgroundColor { - buttonsBackground = .color(backgroundColor.mixedWith(.white, alpha: 0.2)) - } - + var isBackButton = false if state.inWearPreview || state.inUpgradePreview { isBackButton = true @@ -5233,6 +5282,11 @@ private final class GiftViewSheetContent: CombinedComponent { )) } + var buttonsIsDark = theme.overallDarkAppearance + if case .color = buttonsBackground { + buttonsIsDark = true + } + let buttons = buttons.update( component: GlassControlPanelComponent( theme: theme, @@ -5246,6 +5300,7 @@ private final class GiftViewSheetContent: CombinedComponent { background: buttonsBackground ), centerAlignmentIfPossible: true, + isDark: buttonsIsDark, tag: state.controlButtonsTag ), availableSize: CGSize(width: context.availableSize.width - 16.0 * 2.0, height: 44.0), @@ -6095,25 +6150,34 @@ private final class GiftViewContextReferenceContentSource: ContextReferenceConte } } -private final class HeaderButtonComponent: CombinedComponent { +private final class HeaderButtonComponent: Component { let title: String + let buttonColor: UIColor let iconName: String let isLocked: Bool + let action: () -> Void public init( title: String, + buttonColor: UIColor, iconName: String, - isLocked: Bool = false + isLocked: Bool = false, + action: @escaping () -> Void ) { self.title = title + self.buttonColor = buttonColor self.iconName = iconName self.isLocked = isLocked + self.action = action } static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool { if lhs.title != rhs.title { return false } + if lhs.buttonColor != rhs.buttonColor { + return false + } if lhs.iconName != rhs.iconName { return false } @@ -6123,79 +6187,205 @@ private final class HeaderButtonComponent: CombinedComponent { return true } - static var body: Body { - let background = Child(RoundedRectangle.self) - let title = Child(MultilineTextComponent.self) - let icon = Child(BundleIconComponent.self) - let lockIcon = Child(BundleIconComponent.self) + final class View: UIView { + private var component: HeaderButtonComponent? + private weak var componentState: EmptyComponentState? - return { context in - let component = context.component + private let backgroundView = GlassBackgroundView() + private let title = ComponentView() + private let icon = ComponentView() + private let lockIcon = ComponentView() + private let button = HighlightTrackingButton() + + override init(frame: CGRect) { + super.init(frame: frame) - let background = background.update( - component: RoundedRectangle( - color: UIColor.white.withAlphaComponent(0.16), - cornerRadius: 16.0 + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addSubview(self.button) + + self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func buttonPressed() { + if let component = self.component { + component.action() + } + } + + func update(component: HeaderButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.componentState = state + + let bounds = CGRect(origin: .zero, size: availableSize) + + self.backgroundView.update(size: bounds.size, cornerRadius: 16.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)), isInteractive: true, transition: transition) + transition.setFrame(view: self.backgroundView, frame: bounds) + + let iconSize = self.icon.update( + transition: transition, + component: AnyComponent( + BundleIconComponent( + name: component.iconName, + tintColor: UIColor.white + ) ), - availableSize: context.availableSize, - transition: .immediate + environment: {}, + containerSize: availableSize ) - context.add(background - .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + if let iconView = self.icon.view { + if iconView.superview == nil { + iconView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(iconView) + } + iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: floorToScreenPixels(22.0 - iconSize.height * 0.5)), size: iconSize) + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.title, + font: Font.regular(11.0), + textColor: UIColor.white, + paragraphAlignment: .natural + )), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - 16.0, height: availableSize.height) ) - let icon = icon.update( - component: BundleIconComponent( - name: component.iconName, - tintColor: UIColor.white - ), - availableSize: context.availableSize, - transition: .immediate - ) - context.add(icon - .position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0)) - ) - - let title = title.update( - component: MultilineTextComponent( - text: .plain(NSAttributedString( - string: component.title, - font: Font.regular(11.0), - textColor: UIColor.white, - paragraphAlignment: .natural - )), - horizontalAlignment: .center, - maximumNumberOfLines: 1 - ), - availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height), - transition: .immediate - ) - var totalTitleWidth = title.size.width - var titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 + var totalTitleWidth = titleSize.width + var titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0 if component.isLocked { let titleSpacing: CGFloat = 3.0 - let lockIcon = lockIcon.update( - component: BundleIconComponent( - name: "Chat List/StatusLockIcon", - tintColor: UIColor.white + + let lockIconSize = self.lockIcon.update( + transition: transition, + component: AnyComponent( + BundleIconComponent( + name: "Chat List/StatusLockIcon", + tintColor: .white + ) ), - availableSize: context.availableSize, - transition: .immediate + environment: {}, + containerSize: availableSize ) - totalTitleWidth += lockIcon.size.width + titleSpacing - titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 - context.add(lockIcon - .position(CGPoint(x: titleOriginX + lockIcon.size.width / 2.0, y: 42.0)) - ) - titleOriginX += lockIcon.size.width + titleSpacing + totalTitleWidth += lockIconSize.width + titleSpacing + titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0 + + if let lockIconView = self.lockIcon.view { + if lockIconView.superview == nil { + lockIconView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(lockIconView) + } + lockIconView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - lockIconSize.height * 0.5)), size: lockIconSize) + } + titleOriginX += lockIconSize.width + titleSpacing } - context.add(title - .position(CGPoint(x: titleOriginX + title.size.width / 2.0, y: 42.0)) - ) - return context.availableSize + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.backgroundView.contentView.addSubview(titleView) + } + titleView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - titleSize.height * 0.5)), size: titleSize) + } + + self.button.frame = bounds + + return availableSize } } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } + +// static var body: Body { +// let background = Child(RoundedRectangle.self) +// let title = Child(MultilineTextComponent.self) +// let icon = Child(BundleIconComponent.self) +// let lockIcon = Child(BundleIconComponent.self) +// +// return { context in +// let component = context.component +// +// let background = background.update( +// component: RoundedRectangle( +// color: UIColor.white.withAlphaComponent(0.16), +// cornerRadius: 16.0 +// ), +// availableSize: context.availableSize, +// transition: .immediate +// ) +// context.add(background +// .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) +// ) +// +// let icon = icon.update( +// component: BundleIconComponent( +// name: component.iconName, +// tintColor: UIColor.white +// ), +// availableSize: context.availableSize, +// transition: .immediate +// ) +// context.add(icon +// .position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0)) +// ) +// +// let title = title.update( +// component: MultilineTextComponent( +// text: .plain(NSAttributedString( +// string: component.title, +// font: Font.regular(11.0), +// textColor: UIColor.white, +// paragraphAlignment: .natural +// )), +// horizontalAlignment: .center, +// maximumNumberOfLines: 1 +// ), +// availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height), +// transition: .immediate +// ) +// var totalTitleWidth = title.size.width +// var titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 +// if component.isLocked { +// let titleSpacing: CGFloat = 3.0 +// let lockIcon = lockIcon.update( +// component: BundleIconComponent( +// name: "Chat List/StatusLockIcon", +// tintColor: UIColor.white +// ), +// availableSize: context.availableSize, +// transition: .immediate +// ) +// totalTitleWidth += lockIcon.size.width + titleSpacing +// titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0 +// context.add(lockIcon +// .position(CGPoint(x: titleOriginX + lockIcon.size.width / 2.0, y: 42.0)) +// ) +// titleOriginX += lockIcon.size.width + titleSpacing +// } +// context.add(title +// .position(CGPoint(x: titleOriginX + title.size.width / 2.0, y: 42.0)) +// ) +// +// return context.availableSize +// } +// } } private struct GiftViewConfiguration { diff --git a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift index 44e6dcb841..351a2144d4 100644 --- a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift +++ b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift @@ -36,6 +36,7 @@ public final class GlassControlPanelComponent: Component { public let rightItem: Item? public let centralItem: Item? public let centerAlignmentIfPossible: Bool + public let isDark: Bool? public let tag: AnyObject? public init( @@ -44,6 +45,7 @@ public final class GlassControlPanelComponent: Component { centralItem: Item?, rightItem: Item?, centerAlignmentIfPossible: Bool = false, + isDark: Bool? = nil, tag: AnyObject? = nil ) { self.theme = theme @@ -51,6 +53,7 @@ public final class GlassControlPanelComponent: Component { self.centralItem = centralItem self.rightItem = rightItem self.centerAlignmentIfPossible = centerAlignmentIfPossible + self.isDark = isDark self.tag = tag } @@ -70,6 +73,9 @@ public final class GlassControlPanelComponent: Component { if lhs.centerAlignmentIfPossible != rhs.centerAlignmentIfPossible { return false } + if lhs.isDark != rhs.isDark { + return false + } if lhs.tag !== rhs.tag { return false } @@ -298,7 +304,7 @@ public final class GlassControlPanelComponent: Component { } transition.setFrame(view: self.glassContainerView, frame: CGRect(origin: CGPoint(), size: availableSize)) - self.glassContainerView.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition) + self.glassContainerView.update(size: availableSize, isDark: component.isDark ?? component.theme.overallDarkAppearance, transition: transition) return availableSize } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift index 27ddd942a1..ba7530fe28 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift @@ -89,7 +89,7 @@ private func generateNumberOffsets() -> [CGPoint] { CGPoint(x: 0.9900000000000001, y: 0.0), CGPoint(x: 0.66, y: 0.0), CGPoint(x: 0.5775, y: 0.0), - CGPoint(x: 1.2375, y: 0.0), + CGPoint(x: 0.7425, y: 0.0), CGPoint(x: 0.9900000000000001, y: 0.0), CGPoint(x: 1.32, y: 0.0), CGPoint(x: 1.155, y: 0.0),