mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit 'ef5b8537e0'
# Conflicts: # submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift # submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift
This commit is contained in:
commit
4c6b273fe1
147 changed files with 8916 additions and 4310 deletions
|
|
@ -1 +1 @@
|
|||
c796824aa8245ce7309426caa3c4816024abd71d21f3805988aa953a4e826169
|
||||
c796824aa8245ce7309426caa3c4816024abd71d21f3805988aa953a4e826168
|
||||
|
|
|
|||
|
|
@ -15459,6 +15459,8 @@ Error: %8$@";
|
|||
"Notification.StarsGiftOffer.Expired" = "%1$@ didn't respond to your offer for %2$@ within %3$@ – your %4$@ have been refunded";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.Expired.Hours_1" = "%@ hours";
|
||||
"Notification.StarsGiftOffer.Expired.Hours_any" = "%@ hours";
|
||||
"Notification.StarsGiftOffer.ExpiredYou" = "The offer from %1$@ to buy your %2$@ for %3$@ has expired";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_any" = "%@ Stars";
|
||||
|
|
@ -15623,3 +15625,6 @@ Error: %8$@";
|
|||
"Chat.GiftPurchaseOffer.AcceptConfirmation.BadValue" = "The value of this gift is **%@** higher than the offer.";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Confirm" = "Confirm Sale";
|
||||
|
||||
|
||||
"Notification.PremiumGift.DaysTitle_1" = "%@ Day Premium";
|
||||
"Notification.PremiumGift.DaysTitle_any" = "%@ Days Premium";
|
||||
|
|
|
|||
|
|
@ -1409,8 +1409,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?) -> ViewController
|
||||
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
|
||||
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
|
||||
func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
func makeGiftUpgradeVariantsPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]) -> ViewController
|
||||
func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController
|
||||
func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController
|
||||
func makeGiftDemoScreen(context: AccountContext) -> ViewController
|
||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/MinimizedContainer",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/LiquidLens",
|
||||
"//submodules/TelegramUI/Components/TabSelectionRecognizer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import LegacyMessageInputPanelInputView
|
|||
import ReactionSelectionNode
|
||||
import TopMessageReactions
|
||||
import GlassBackgroundComponent
|
||||
import LiquidLens
|
||||
import TabSelectionRecognizer
|
||||
|
||||
private let legacyButtonSize = CGSize(width: 88.0, height: 49.0)
|
||||
private let glassButtonSize = CGSize(width: 72.0, height: 62.0)
|
||||
|
|
@ -911,13 +913,21 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
private let makeEntityInputView: () -> AttachmentTextInputPanelInputView?
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private var backgroundView: GlassBackgroundView?
|
||||
private var liquidLensView: LiquidLensView?
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let scrollNode: ASScrollNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private let selectionNode: ASImageNode
|
||||
private var buttonViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
|
||||
private var itemsContainer = UIView()
|
||||
private var itemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var selectedItemsContainer = UIView()
|
||||
private var selectedItemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var itemSizes: [AnyHashable: CGSize] = [:]
|
||||
|
||||
private var tabSelectionRecognizer: TabSelectionRecognizer?
|
||||
private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)?
|
||||
|
||||
private var textInputPanelNode: AttachmentTextInputPanelNode?
|
||||
private var progressNode: LoadingProgressNode?
|
||||
|
|
@ -987,7 +997,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
|
||||
self.mainButtonNode = MainButtonNode()
|
||||
self.secondaryButtonNode = MainButtonNode()
|
||||
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
|
|
@ -995,7 +1005,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
switch style {
|
||||
case .glass:
|
||||
self.scrollNode.cornerRadius = glassButtonSize.height * 0.5
|
||||
self.scrollNode.addSubnode(self.selectionNode)
|
||||
case .legacy:
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.separatorNode)
|
||||
|
|
@ -1419,6 +1428,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
self.view.accessibilityTraits = .tabBar
|
||||
}
|
||||
|
||||
func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
if let layout = self.validLayout {
|
||||
let _ = self.update(layout: layout, buttons: self.buttons, isSelecting: self.isSelecting, selectionCount: self.selectionCount, elevateProgress: self.elevateProgress, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func mainButtonPressed() {
|
||||
self.onMainButtonPressed()
|
||||
}
|
||||
|
|
@ -1473,6 +1488,88 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
private func item(at point: CGPoint) -> AnyHashable? {
|
||||
let contentOffset = self.scrollNode.view.contentOffset.x
|
||||
let point = point.offsetBy(dx: contentOffset, dy: 0.0)
|
||||
var closestItem: (AnyHashable, CGFloat)?
|
||||
for (id, itemView) in self.itemViews {
|
||||
if itemView.frame.contains(point) {
|
||||
return id
|
||||
} else {
|
||||
let distance = abs(point.x - itemView.center.x)
|
||||
if let closestItemValue = closestItem {
|
||||
if closestItemValue.1 > distance {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
} else {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
}
|
||||
}
|
||||
return closestItem?.0
|
||||
}
|
||||
|
||||
@objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) {
|
||||
guard let liquidLensView = self.liquidLensView else {
|
||||
return
|
||||
}
|
||||
let location = recognizer.location(in: liquidLensView.contentView)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] {
|
||||
let startX = itemView.frame.minX - 4.0
|
||||
self.selectionGestureState = (startX, startX, itemId)
|
||||
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
case .changed:
|
||||
if var selectionGestureState = self.selectionGestureState {
|
||||
selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self.view).x
|
||||
if let itemId = self.item(at: location) {
|
||||
selectionGestureState.itemId = itemId
|
||||
}
|
||||
self.selectionGestureState = selectionGestureState
|
||||
self.requestLayout(transition: .immediate)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
self.selectionGestureState = nil
|
||||
if case .ended = recognizer.state {
|
||||
guard let index = self.buttons.firstIndex(where: { AnyHashable($0.key) == selectionGestureState.itemId }) else {
|
||||
return
|
||||
}
|
||||
let button = self.buttons[index]
|
||||
if self.selectionChanged(button) {
|
||||
self.selectedIndex = index
|
||||
if self.buttons.count > 5, let button = self.itemViews[button.key] {
|
||||
let transition = ComponentTransition.spring(duration: 0.4)
|
||||
|
||||
let scrollView = self.scrollNode.view
|
||||
let targetRect = button.frame.insetBy(dx: -35.0, dy: 0.0)
|
||||
|
||||
var newBounds = scrollView.bounds
|
||||
if targetRect.minX < scrollView.bounds.minX {
|
||||
newBounds.origin.x = targetRect.minX
|
||||
}
|
||||
else if targetRect.maxX > scrollView.bounds.maxX {
|
||||
newBounds.origin.x = targetRect.maxX - scrollView.bounds.width
|
||||
}
|
||||
let minX = 0.0
|
||||
let maxX = scrollView.contentSize.width - scrollView.bounds.width
|
||||
newBounds.origin.x = max(minX, min(newBounds.origin.x, maxX))
|
||||
|
||||
transition.setBounds(view: scrollView, bounds: newBounds)
|
||||
self.updateItemContainers(contentOffset: newBounds.minX, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func updateViews(transition: ComponentTransition) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
|
|
@ -1486,18 +1583,16 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
case .legacy:
|
||||
panelSideInset = 3.0
|
||||
}
|
||||
|
||||
let visibleRect = self.scrollNode.bounds.insetBy(dx: -180.0, dy: 0.0)
|
||||
|
||||
|
||||
var distanceBetweenNodes = floorToScreenPixels((width - panelSideInset * 2.0 - self.buttonSize.width) / CGFloat(max(1, self.buttons.count - 1)))
|
||||
let internalWidth = distanceBetweenNodes * CGFloat(self.buttons.count - 1)
|
||||
var buttonWidth = self.buttonSize.width
|
||||
var leftNodeOriginX: CGFloat
|
||||
var maxButtonsToFit = 6
|
||||
var maxButtonsToFit = 5
|
||||
switch self.panelStyle {
|
||||
case .glass:
|
||||
leftNodeOriginX = layout.safeInsets.left + 3.0 + buttonWidth / 2.0
|
||||
if layout.size.width < 400.0 {
|
||||
if layout.size.width < 420.0 {
|
||||
maxButtonsToFit = 5
|
||||
}
|
||||
case .legacy:
|
||||
|
|
@ -1521,25 +1616,28 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
var selectionFrame = CGRect()
|
||||
var mostRightX = 0.0
|
||||
for i in 0 ..< self.buttons.count {
|
||||
let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0)
|
||||
let originX = floorToScreenPixels(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: CGSize(width: buttonWidth, height: self.buttonSize.height))
|
||||
mostRightX = buttonFrame.maxX
|
||||
if !visibleRect.intersects(buttonFrame) {
|
||||
continue
|
||||
}
|
||||
|
||||
let type = self.buttons[i]
|
||||
let _ = validIds.insert(type.key)
|
||||
|
||||
var buttonTransition = transition
|
||||
let buttonView: ComponentHostView<Empty>
|
||||
if let current = self.buttonViews[type.key] {
|
||||
let selectedButtonView: ComponentHostView<Empty>
|
||||
if let current = self.itemViews[type.key], let currentSelected = self.selectedItemViews[type.key] {
|
||||
buttonView = current
|
||||
selectedButtonView = currentSelected
|
||||
} else {
|
||||
buttonTransition = .immediate
|
||||
buttonView = ComponentHostView<Empty>()
|
||||
self.buttonViews[type.key] = buttonView
|
||||
self.scrollNode.view.addSubview(buttonView)
|
||||
self.itemViews[type.key] = buttonView
|
||||
self.itemsContainer.addSubview(buttonView)
|
||||
|
||||
selectedButtonView = ComponentHostView<Empty>()
|
||||
self.selectedItemViews[type.key] = selectedButtonView
|
||||
self.selectedItemsContainer.addSubview(selectedButtonView)
|
||||
}
|
||||
|
||||
if case let .app(bot) = type {
|
||||
|
|
@ -1578,7 +1676,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
style: self.panelStyle == .glass ? .glass : .legacy,
|
||||
type: type,
|
||||
isFirstOrLast: i == 0 || i == self.buttons.count - 1,
|
||||
isSelected: i == self.selectedIndex,
|
||||
isSelected: false,
|
||||
strings: self.presentationData.strings,
|
||||
theme: self.presentationData.theme,
|
||||
action: { [weak self] in
|
||||
|
|
@ -1587,7 +1685,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
strongSelf.selectedIndex = i
|
||||
strongSelf.updateViews(transition: .init(animation: .curve(duration: 0.2, curve: .spring)))
|
||||
|
||||
if strongSelf.buttons.count > 5, let button = strongSelf.buttonViews[type.key] {
|
||||
if strongSelf.buttons.count > 5, let button = strongSelf.itemViews[type.key] {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(button.frame.insetBy(dx: -35.0, dy: 0.0), animated: true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1601,11 +1699,32 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
environment: {},
|
||||
containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height)
|
||||
)
|
||||
self.itemSizes[type.key] = actualButtonSize
|
||||
|
||||
let _ = selectedButtonView.update(
|
||||
transition: buttonTransition,
|
||||
component: AnyComponent(AttachButtonComponent(
|
||||
context: self.context,
|
||||
style: self.panelStyle == .glass ? .glass : .legacy,
|
||||
type: type,
|
||||
isFirstOrLast: i == 0 || i == self.buttons.count - 1,
|
||||
isSelected: true,
|
||||
strings: self.presentationData.strings,
|
||||
theme: self.presentationData.theme,
|
||||
action: {
|
||||
},
|
||||
longPressAction: {
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height)
|
||||
)
|
||||
|
||||
if i == self.selectedIndex {
|
||||
selectionFrame = CGRect(origin: CGPoint(x: buttonFrame.midX - actualButtonSize.width * 0.5, y: buttonFrame.minY), size: actualButtonSize)
|
||||
}
|
||||
buttonTransition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
buttonTransition.setFrame(view: selectedButtonView, frame: buttonFrame)
|
||||
var accessibilityTitle = ""
|
||||
switch type {
|
||||
case .gallery:
|
||||
|
|
@ -1634,14 +1753,14 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
buttonView.accessibilityTraits = [.button]
|
||||
}
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.buttonViews {
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.buttonViews.removeValue(forKey: id)
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
selectionFrame = selectionFrame.insetBy(dx: 1.0, dy: 4.0)
|
||||
|
|
@ -1916,31 +2035,54 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
textPanelWidth = layout.size.width - panelSideInset * 2.0
|
||||
}
|
||||
|
||||
self.updateViews(transition: .immediate)
|
||||
|
||||
var selectionFrame = CGRect()
|
||||
if self.selectedIndex >= 0 && self.selectedIndex < self.buttons.count, let itemView = self.itemViews[self.buttons[self.selectedIndex].key], let itemSize = self.itemSizes[self.buttons[self.selectedIndex].key] {
|
||||
let contentOffset = self.scrollNode.view.contentOffset.x
|
||||
selectionFrame = CGRect(origin: CGPoint(x: itemView.center.x - itemSize.width / 2.0 - contentOffset, y: itemView.frame.minY), size: itemSize).insetBy(dx: -3.0, dy: 0.0)
|
||||
}
|
||||
|
||||
let lensSelection: (x: CGFloat, width: CGFloat)
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
lensSelection = (selectionGestureState.currentX, selectionFrame.width)
|
||||
} else {
|
||||
lensSelection = (selectionFrame.minX, selectionFrame.width)
|
||||
}
|
||||
|
||||
let glassPanelHeight: CGFloat = 62.0
|
||||
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: self.buttonSize.height + insets.bottom))
|
||||
if case .glass = self.panelStyle {
|
||||
let backgroundView: GlassBackgroundView
|
||||
if let current = self.backgroundView {
|
||||
backgroundView = current
|
||||
let liquidLensView: LiquidLensView
|
||||
if let current = self.liquidLensView {
|
||||
liquidLensView = current
|
||||
} else {
|
||||
backgroundView = GlassBackgroundView()
|
||||
self.containerNode.view.addSubview(backgroundView)
|
||||
self.containerNode.view.addSubview(self.scrollNode.view)
|
||||
self.backgroundView = backgroundView
|
||||
liquidLensView = LiquidLensView(useBackgroundContainer: false)
|
||||
self.containerNode.view.addSubview(liquidLensView)
|
||||
//self.containerNode.view.addSubview(self.scrollNode.view)
|
||||
self.liquidLensView = liquidLensView
|
||||
|
||||
liquidLensView.contentView.addSubview(self.itemsContainer)
|
||||
liquidLensView.selectedContentView.addSubview(self.selectedItemsContainer)
|
||||
|
||||
self.itemsContainer.clipsToBounds = true
|
||||
self.itemsContainer.layer.cornerRadius = glassPanelHeight * 0.5
|
||||
self.selectedItemsContainer.clipsToBounds = true
|
||||
self.selectedItemsContainer.layer.cornerRadius = glassPanelHeight * 0.5
|
||||
|
||||
let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:)))
|
||||
self.tabSelectionRecognizer = tabSelectionRecognizer
|
||||
liquidLensView.addGestureRecognizer(tabSelectionRecognizer)
|
||||
}
|
||||
let panelSize = CGSize(width: isSelecting ? textPanelWidth : layout.size.width - layout.safeInsets.left - layout.safeInsets.right - panelSideInset * 2.0, height: isSelecting ? textPanelHeight - 11.0 : glassPanelHeight)
|
||||
|
||||
let backgroundViewColor: UIColor
|
||||
if self.presentationData.theme.overallDarkAppearance {
|
||||
backgroundViewColor = self.presentationData.theme.list.modalBlocksBackgroundColor.withAlphaComponent(0.55)
|
||||
} else {
|
||||
backgroundViewColor = self.presentationData.theme.list.plainBackgroundColor.withAlphaComponent(0.75)
|
||||
}
|
||||
|
||||
let backgroundOriginX: CGFloat = isSelecting ? panelSideInset : floorToScreenPixels((layout.size.width - panelSize.width) / 2.0)
|
||||
backgroundView.update(size: panelSize, cornerRadius: isSelecting ? 20.0 : glassPanelHeight * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: backgroundViewColor), transition: ComponentTransition(transition))
|
||||
transition.updatePosition(layer: backgroundView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5))
|
||||
transition.updateBounds(layer: backgroundView.layer, bounds: CGRect(origin: .zero, size: panelSize))
|
||||
liquidLensView.update(size: panelSize, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: panelSize.height), isDark: self.presentationData.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isSelecting, transition: ComponentTransition(transition))
|
||||
|
||||
transition.updatePosition(layer: liquidLensView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5))
|
||||
transition.updateBounds(layer: liquidLensView.layer, bounds: CGRect(origin: .zero, size: panelSize))
|
||||
transition.updateFrame(view: self.itemsContainer, frame: CGRect(origin: .zero, size: panelSize))
|
||||
transition.updateFrame(view: self.selectedItemsContainer, frame: CGRect(origin: .zero, size: panelSize))
|
||||
}
|
||||
|
||||
var containerTransition: ContainedViewLayoutTransition
|
||||
|
|
@ -2002,8 +2144,15 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
let alphaTransition = ContainedViewLayoutTransition.animated(duration: isSelecting ? 0.1 : 0.25, curve: .easeInOut)
|
||||
alphaTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
|
||||
containerTransition.updateTransformScale(node: self.scrollNode, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
|
||||
if let backgroundView = self.backgroundView {
|
||||
containerTransition.updateTransformScale(layer: backgroundView.layer, scale: isAnyButtonVisible ? 0.85 : 1.0)
|
||||
|
||||
alphaTransition.updateAlpha(layer: self.itemsContainer.layer, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
|
||||
containerTransition.updateTransformScale(layer: self.itemsContainer.layer, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
|
||||
|
||||
alphaTransition.updateAlpha(layer: self.selectedItemsContainer.layer, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
|
||||
containerTransition.updateTransformScale(layer: self.selectedItemsContainer.layer, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
|
||||
|
||||
if let liquidLensView = self.liquidLensView {
|
||||
containerTransition.updateTransformScale(layer: liquidLensView.layer, scale: isAnyButtonVisible ? 0.85 : 1.0)
|
||||
}
|
||||
|
||||
if isSelectingUpdated {
|
||||
|
|
@ -2022,7 +2171,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 1.0)
|
||||
|
||||
let componentTransition = ComponentTransition.easeInOut(duration: 0.25)
|
||||
componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
componentTransition.animateBlur(layer: self.itemsContainer.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
|
||||
let blurTransition = ComponentTransition.easeInOut(duration: 0.18)
|
||||
transition.animateTransformScale(node: textInputPanelNode.opaqueActionButtons, from: 0.01)
|
||||
|
|
@ -2054,7 +2203,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 0.0)
|
||||
|
||||
let componentTransition = ComponentTransition.easeInOut(duration: 0.25)
|
||||
componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
componentTransition.animateBlur(layer: self.itemsContainer.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
|
||||
let blurTransition = ComponentTransition.easeInOut(duration: 0.18)
|
||||
transition.animateTransformScale(layer: textInputPanelNode.opaqueActionButtons.layer, from: CGPoint(x: 1.0, y: 1.0), to: CGPoint(x: 0.01, y: 0.01))
|
||||
|
|
@ -2086,8 +2235,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
transition.updateFrameAsPositionAndBounds(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: self.isSelecting ? panelSideInset - defaultPanelSideInset : panelSideInset, y: self.isSelecting ? -11.0 : 0.0), size: CGSize(width: layout.size.width - panelSideInset * 2.0, height: self.buttonSize.height)))
|
||||
}
|
||||
|
||||
self.updateViews(transition: .immediate)
|
||||
|
||||
if let progress = self.loadingProgress {
|
||||
let loadingProgressNode: LoadingProgressNode
|
||||
if let current = self.progressNode {
|
||||
|
|
@ -2183,8 +2330,13 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||
return containerFrame.height
|
||||
}
|
||||
|
||||
func updateItemContainers(contentOffset: CGFloat, transition: ComponentTransition) {
|
||||
let transform = CATransform3DMakeTranslation(-contentOffset, 0.0, 0.0)
|
||||
transition.setSublayerTransform(view: self.itemsContainer, transform: transform)
|
||||
transition.setSublayerTransform(view: self.selectedItemsContainer, transform: transform)
|
||||
}
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateItemContainers(contentOffset: scrollView.contentOffset.x, transition: .immediate)
|
||||
self.updateViews(transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/Utils/DeviceModel",
|
||||
"//submodules/LegacyMediaPickerUI",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import SaveProgressScreen
|
|||
import DeviceModel
|
||||
import LegacyMediaPickerUI
|
||||
import PassKit
|
||||
import AlertComponent
|
||||
|
||||
private final class TonSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
private final class PendingTask {
|
||||
|
|
@ -1258,12 +1259,20 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var completed = false
|
||||
let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler()
|
||||
}
|
||||
})])
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: message,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_OK, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler()
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
|
|
@ -1278,17 +1287,26 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var completed = false
|
||||
let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: message, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(false)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(true)
|
||||
}
|
||||
})])
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: message,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(false)
|
||||
}
|
||||
}),
|
||||
.init(title: presentationData.strings.Common_OK, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
completionHandler(true)
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
|
|
@ -1356,17 +1374,25 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
|
|||
private func presentDownloadConfirmation(fileName: String, proceed: @escaping (Bool) -> Void) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var completed = false
|
||||
let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(false)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_Download_Download, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(true)
|
||||
}
|
||||
})])
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_Cancel, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(false)
|
||||
}
|
||||
}),
|
||||
.init(title: presentationData.strings.WebBrowser_Download_Download, type: .default, action: {
|
||||
if !completed {
|
||||
completed = true
|
||||
proceed(true)
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { byOutsideTap in
|
||||
if byOutsideTap {
|
||||
if !completed {
|
||||
|
|
|
|||
|
|
@ -4535,7 +4535,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||
)
|
||||
interaction.present(alertController, nil)
|
||||
dismissImpl = { [weak alertController] in
|
||||
alertController?.dismissAnimated()
|
||||
alertController?.dismiss()
|
||||
}
|
||||
},
|
||||
isChannelsTabExpanded: recentItems.isChannelsTabExpanded,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public final class MultilineTextWithEntitiesComponent: Component {
|
|||
public let handleSpoilers: Bool
|
||||
public let manualVisibilityControl: Bool
|
||||
public let resetAnimationsOnVisibilityChange: Bool
|
||||
public let enableLooping: Bool
|
||||
public let displaysAsynchronously: Bool
|
||||
public let maxWidth: CGFloat?
|
||||
public let highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)?
|
||||
|
|
@ -71,6 +72,7 @@ public final class MultilineTextWithEntitiesComponent: Component {
|
|||
handleSpoilers: Bool = false,
|
||||
manualVisibilityControl: Bool = false,
|
||||
resetAnimationsOnVisibilityChange: Bool = false,
|
||||
enableLooping: Bool = true,
|
||||
displaysAsynchronously: Bool = true,
|
||||
maxWidth: CGFloat? = nil,
|
||||
highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? = nil,
|
||||
|
|
@ -99,6 +101,7 @@ public final class MultilineTextWithEntitiesComponent: Component {
|
|||
self.handleSpoilers = handleSpoilers
|
||||
self.manualVisibilityControl = manualVisibilityControl
|
||||
self.resetAnimationsOnVisibilityChange = resetAnimationsOnVisibilityChange
|
||||
self.enableLooping = enableLooping
|
||||
self.displaysAsynchronously = displaysAsynchronously
|
||||
self.maxWidth = maxWidth
|
||||
self.tapAction = tapAction
|
||||
|
|
@ -142,6 +145,9 @@ public final class MultilineTextWithEntitiesComponent: Component {
|
|||
if lhs.resetAnimationsOnVisibilityChange != rhs.resetAnimationsOnVisibilityChange {
|
||||
return false
|
||||
}
|
||||
if lhs.enableLooping != rhs.enableLooping {
|
||||
return false
|
||||
}
|
||||
if lhs.displaysAsynchronously != rhs.displaysAsynchronously {
|
||||
return false
|
||||
}
|
||||
|
|
@ -250,6 +256,7 @@ public final class MultilineTextWithEntitiesComponent: Component {
|
|||
self.textNode.tapAttributeAction = component.tapAction
|
||||
self.textNode.longTapAttributeAction = component.longTapAction
|
||||
self.textNode.spoilerColor = component.spoilerColor
|
||||
self.textNode.enableLooping = component.enableLooping
|
||||
|
||||
self.textNode.resetEmojiToFirstFrameAutomatically = component.resetAnimationsOnVisibilityChange
|
||||
|
||||
|
|
|
|||
|
|
@ -974,8 +974,9 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
|
|||
transition.setFrame(view: contentView.tintMaskContainer, frame: contentFrame)
|
||||
}
|
||||
|
||||
let _ = bottomPanelHeight
|
||||
if let contentViewWithBackground = contentView.view.componentView as? PagerContentViewWithBackground {
|
||||
contentViewWithBackground.pagerUpdateBackground(backgroundFrame: CGRect(origin: CGPoint(), size: CGSize(width: backgroundFrame.width, height: availableSize.height)), topPanelHeight: topPanelHeight, bottomPanelHeight: bottomPanelHeight, externalTintMaskContainer: component.externalTintMaskContainer == nil ? nil : contentView.tintMaskContainer, transition: contentTransition)
|
||||
contentViewWithBackground.pagerUpdateBackground(backgroundFrame: CGRect(origin: CGPoint(), size: CGSize(width: backgroundFrame.width, height: availableSize.height)), topPanelHeight: topPanelHeight, bottomPanelHeight: 0.0, externalTintMaskContainer: component.externalTintMaskContainer == nil ? nil : contentView.tintMaskContainer, transition: contentTransition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ public final class SheetComponentEnvironment: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public let sheetComponentTag = GenericComponentViewTag()
|
||||
public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: Component {
|
||||
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,7 @@ public class KeyShortcutsController: UIResponder {
|
|||
private var viewControllerEnumerator: (@escaping (ContainableController) -> Bool) -> Void
|
||||
|
||||
public static var isAvailable: Bool {
|
||||
if #available(iOSApplicationExtension 8.0, iOS 8.0, *), UIDevice.current.userInterfaceIdiom == .pad {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public init(enumerator: @escaping (@escaping (ContainableController) -> Bool) -> Void) {
|
||||
|
|
|
|||
|
|
@ -403,8 +403,8 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
}
|
||||
|
||||
let fieldHeight: CGFloat = 52.0
|
||||
let fieldSpacing: CGFloat = 16.0
|
||||
let buttonHeight: CGFloat = 50.0
|
||||
let fieldSpacing: CGFloat = 10.0
|
||||
let buttonHeight: CGFloat = 52.0
|
||||
let justCreatedCallSeparatorSpacing: CGFloat = 16.0
|
||||
let justCreatedCallTextSpacing: CGFloat = 45.0
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import ComponentFlow
|
|||
import BundleIconComponent
|
||||
import LottieComponent
|
||||
import GlassBarButtonComponent
|
||||
import AlertComponent
|
||||
|
||||
final class MediaPickerInteraction {
|
||||
let downloadManager: AssetDownloadManager
|
||||
|
|
@ -2717,16 +2718,22 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||
} else {
|
||||
text = self.presentationData.strings.Attachment_CancelSelectionAlertText
|
||||
}
|
||||
|
||||
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
|
||||
self?.dismissAllTooltips()
|
||||
completion()
|
||||
})])
|
||||
controller.dismissed = { [weak self] _ in
|
||||
let alertController = AlertScreen(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: text,
|
||||
actions: [
|
||||
.init(title: self.presentationData.strings.Attachment_CancelSelectionAlertNo),
|
||||
.init(title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, type: .default, action: { [weak self] in
|
||||
self?.dismissAllTooltips()
|
||||
completion()
|
||||
}),
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { [weak self] _ in
|
||||
self?.isDismissing = false
|
||||
}
|
||||
self.present(controller, in: .window(.root))
|
||||
self.present(alertController, in: .window(.root))
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/Gifts/TableComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
}
|
||||
})
|
||||
case let .starsMore(theme, title):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandStars()
|
||||
})
|
||||
case let .starsInfo(_, text):
|
||||
|
|
@ -521,14 +521,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
if case let .channel(channel) = peer, case .group = channel.info {
|
||||
isGroup = true
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
|
||||
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
|
||||
}, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
arguments.setItemIdWithRevealedOptions(lhs, rhs)
|
||||
}, removePeer: { id in
|
||||
arguments.removeChannel(id)
|
||||
})
|
||||
case let .channelAdd(theme, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: {
|
||||
arguments.openChannelsSelection()
|
||||
})
|
||||
case let .channelsInfo(_, text):
|
||||
|
|
@ -582,7 +582,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
arguments.openPremiumIntro()
|
||||
})
|
||||
case let .prizeDescription(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showPrizeDescription = value
|
||||
|
|
@ -590,7 +590,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
}
|
||||
})
|
||||
case let .prizeDescriptionText(_, placeholder, value, count):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.prizeDescription = value
|
||||
|
|
@ -616,7 +616,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
text = presentationData.strings.InviteLink_Create_TimeLimitExpiryDateNever
|
||||
}
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
arguments.dismissInput()
|
||||
var focus = false
|
||||
arguments.updateState { state in
|
||||
|
|
@ -677,7 +677,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||
case let .timeInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .winners(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showWinners = value
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import InvisibleInkDustNode
|
|||
import PremiumStarComponent
|
||||
import GlassBarButtonComponent
|
||||
import ButtonComponent
|
||||
import TableComponent
|
||||
import PeerTableCellComponent
|
||||
|
||||
private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -326,7 +328,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||
title: strings.GiftLink_From,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
||||
content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: fromPeer)),
|
||||
action: {
|
||||
if let peer = fromPeer, peer.id != accountContext.account.peerId {
|
||||
component.openPeer(peer)
|
||||
|
|
@ -344,7 +346,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||
title: strings.GiftLink_To,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
||||
content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: toPeer)),
|
||||
action: {
|
||||
if toPeer.id != accountContext.account.peerId {
|
||||
component.openPeer(toPeer)
|
||||
|
|
@ -857,306 +859,6 @@ final class GiftLinkButtonContentComponent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
|
||||
private final class TableComponent: CombinedComponent {
|
||||
class Item: Equatable {
|
||||
public let id: AnyHashable
|
||||
public let title: String
|
||||
public let component: AnyComponent<Empty>
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String, component: AnyComponent<Empty>) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.component = component
|
||||
}
|
||||
|
||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let theme: PresentationTheme
|
||||
private let items: [Item]
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item]) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedBorderImage: (UIImage, PresentationTheme)?
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
public static var body: Body {
|
||||
let leftColumnBackground = Child(Rectangle.self)
|
||||
let verticalBorder = Child(Rectangle.self)
|
||||
let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let outerBorder = Child(Image.self)
|
||||
|
||||
return { context in
|
||||
let verticalPadding: CGFloat = 11.0
|
||||
let horizontalPadding: CGFloat = 12.0
|
||||
let borderWidth: CGFloat = 1.0
|
||||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
||||
var updatedValueChildren: [_UpdatedChildComponent] = []
|
||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = titleChildren[item.id].update(
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: item.title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
updatedTitleChildren.append(titleChild)
|
||||
|
||||
if titleChild.size.width > leftColumnWidth {
|
||||
leftColumnWidth = titleChild.size.width
|
||||
}
|
||||
}
|
||||
|
||||
leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0)
|
||||
let rightColumnWidth = context.availableSize.width - leftColumnWidth
|
||||
|
||||
var i = 0
|
||||
var rowHeights: [Int: CGFloat] = [:]
|
||||
var totalHeight: CGFloat = 0.0
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = updatedTitleChildren[i]
|
||||
let valueChild = valueChildren[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: rightColumnWidth - horizontalPadding * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedValueChildren.append(valueChild)
|
||||
|
||||
let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0)
|
||||
rowHeights[i] = rowHeight
|
||||
totalHeight += rowHeight
|
||||
|
||||
if i < context.component.items.count - 1 {
|
||||
let borderChild = borderChildren[item.id].update(
|
||||
component: AnyComponent(Rectangle(color: borderColor)),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: borderWidth),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedBorderChildren.append(borderChild)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
let leftColumnBackground = leftColumnBackground.update(
|
||||
component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor),
|
||||
availableSize: CGSize(width: leftColumnWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
leftColumnBackground
|
||||
.position(CGPoint(x: leftColumnWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let borderImage: UIImage
|
||||
if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme {
|
||||
borderImage = currentImage
|
||||
} else {
|
||||
let borderRadius: CGFloat = 14.0
|
||||
borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 6.0, height: borderRadius * 2.0 + 6.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil)
|
||||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(borderColor.cgColor)
|
||||
context.setLineWidth(borderWidth)
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius))
|
||||
context.state.cachedBorderImage = (borderImage, context.component.theme)
|
||||
}
|
||||
|
||||
let outerBorder = outerBorder.update(
|
||||
component: Image(image: borderImage),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(outerBorder
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let verticalBorder = verticalBorder.update(
|
||||
component: Rectangle(color: borderColor),
|
||||
availableSize: CGSize(width: borderWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
verticalBorder
|
||||
.position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
i = 0
|
||||
var originY: CGFloat = 0.0
|
||||
for (titleChild, valueChild) in zip(updatedTitleChildren, updatedValueChildren) {
|
||||
let rowHeight = rowHeights[i] ?? 0.0
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
||||
let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + horizontalPadding, y: originY + verticalPadding), size: valueChild.size)
|
||||
|
||||
context.add(titleChild
|
||||
.position(titleFrame.center)
|
||||
)
|
||||
|
||||
context.add(valueChild
|
||||
.position(valueFrame.center)
|
||||
)
|
||||
|
||||
if i < updatedBorderChildren.count {
|
||||
let borderChild = updatedBorderChildren[i]
|
||||
context.add(borderChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
originY += rowHeight
|
||||
i += 1
|
||||
}
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: totalHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerCellComponent: Component {
|
||||
let context: AccountContext
|
||||
let textColor: UIColor
|
||||
let peer: EnginePeer?
|
||||
|
||||
init(context: AccountContext, textColor: UIColor, peer: EnginePeer?) {
|
||||
self.context = context
|
||||
self.textColor = textColor
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerCellComponent, rhs: PeerCellComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor !== rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 13.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PeerCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
self.avatarNode.setPeer(
|
||||
context: component.context,
|
||||
theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme,
|
||||
peer: component.peer,
|
||||
synchronousLoad: true
|
||||
)
|
||||
|
||||
let avatarSize = CGSize(width: 22.0, height: 22.0)
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.peer?.compactDisplayTitle ?? "", font: Font.regular(15.0), textColor: component.textColor, paragraphAlignment: .left))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize)
|
||||
self.avatarNode.frame = avatarFrame
|
||||
|
||||
if let view = self.text.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
transition.setFrame(view: view, frame: textFrame)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class DustComponent: Component {
|
||||
let color: UIColor
|
||||
|
||||
|
|
|
|||
|
|
@ -1679,34 +1679,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.newPerksDisposable = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
ApplicationSpecificNotice.dismissedBusinessBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessLinksBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessIntroBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessChatbotsBadge(accountManager: context.sharedContext.accountManager)
|
||||
).startStrict(next: { [weak self] dismissedBusinessBadge, dismissedBusinessLinksBadge, dismissedBusinessIntroBadge, dismissedBusinessChatbotsBadge in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var newPerks: [String] = []
|
||||
if !dismissedBusinessBadge {
|
||||
newPerks.append(PremiumPerk.business.identifier)
|
||||
}
|
||||
if !dismissedBusinessLinksBadge {
|
||||
newPerks.append(PremiumPerk.businessLinks.identifier)
|
||||
}
|
||||
if !dismissedBusinessIntroBadge {
|
||||
newPerks.append(PremiumPerk.businessIntro.identifier)
|
||||
}
|
||||
if !dismissedBusinessChatbotsBadge {
|
||||
newPerks.append(PremiumPerk.businessChatBots.identifier)
|
||||
}
|
||||
self.newPerks = newPerks
|
||||
self.updated()
|
||||
})
|
||||
|
||||
|
||||
self.adsEnabledDisposable = (context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.AdsEnabled(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] adsEnabled in
|
||||
guard let self else {
|
||||
|
|
@ -2219,7 +2192,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
demoSubject = .todo
|
||||
case .business:
|
||||
demoSubject = .business
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
demoSubject = .doubleLimits
|
||||
}
|
||||
|
|
@ -2418,7 +2390,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
}
|
||||
push(accountContext.sharedContext.makeChatbotSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessIntro:
|
||||
let _ = (accountContext.sharedContext.makeBusinessIntroSetupScreenInitialData(context: accountContext)
|
||||
|> take(1)
|
||||
|
|
@ -2428,7 +2399,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
}
|
||||
push(accountContext.sharedContext.makeBusinessIntroSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessLinks:
|
||||
let _ = (accountContext.sharedContext.makeBusinessLinksSetupScreenInitialData(context: accountContext)
|
||||
|> take(1)
|
||||
|
|
@ -2438,7 +2408,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
}
|
||||
push(accountContext.sharedContext.makeBusinessLinksSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
|
|
@ -2457,13 +2426,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
demoSubject = .businessAwayMessage
|
||||
case .businessChatBots:
|
||||
demoSubject = .businessChatBots
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessIntro:
|
||||
demoSubject = .businessIntro
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessLinks:
|
||||
demoSubject = .businessLinks
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ swift_library(
|
|||
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||
"//submodules/UrlWhitelist:UrlWhitelist",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -4,35 +4,116 @@ import AlertUI
|
|||
import AccountContext
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AlertComponent
|
||||
|
||||
public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController {
|
||||
public func textAlertController(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
forceTheme: PresentationTheme? = nil,
|
||||
title: String?,
|
||||
text: String,
|
||||
actions: [TextAlertAction],
|
||||
actionLayout: TextAlertContentActionLayout = .horizontal,
|
||||
allowInputInset: Bool = true,
|
||||
parseMarkdown: Bool = false,
|
||||
dismissOnOutsideTap: Bool = true,
|
||||
linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil
|
||||
) -> ViewController {
|
||||
return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction)
|
||||
}
|
||||
|
||||
public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController {
|
||||
public func textAlertController(
|
||||
sharedContext: SharedAccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
forceTheme: PresentationTheme? = nil,
|
||||
title: String?,
|
||||
text: String,
|
||||
actions: [TextAlertAction],
|
||||
actionLayout: TextAlertContentActionLayout = .horizontal,
|
||||
allowInputInset: Bool = true,
|
||||
parseMarkdown: Bool = false,
|
||||
dismissOnOutsideTap: Bool = true,
|
||||
linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil
|
||||
) -> ViewController {
|
||||
var presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
||||
if let forceTheme = forceTheme {
|
||||
if let forceTheme {
|
||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: presentationData), themeSignal: (updatedPresentationData?.signal ?? sharedContext.presentationData) |> map {
|
||||
let updatedPresentationDataSignal = (updatedPresentationData?.signal ?? sharedContext.presentationData) |> map {
|
||||
presentationData in
|
||||
var presentationData = presentationData
|
||||
if let forceTheme = forceTheme {
|
||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
return AlertControllerTheme(presentationData: presentationData)
|
||||
}), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction)
|
||||
return presentationData
|
||||
}
|
||||
|
||||
let mappedActions: [AlertScreen.Action] = actions.map { action in
|
||||
let mappedType: AlertScreen.Action.ActionType
|
||||
switch action.type {
|
||||
case .genericAction:
|
||||
mappedType = .generic
|
||||
case .defaultAction:
|
||||
mappedType = .default
|
||||
case .destructiveAction:
|
||||
mappedType = .destructive
|
||||
case .defaultDestructiveAction:
|
||||
mappedType = .defaultDestructive
|
||||
}
|
||||
return AlertScreen.Action(
|
||||
title: action.title,
|
||||
type: mappedType,
|
||||
action: action.action
|
||||
)
|
||||
}
|
||||
|
||||
let controller = AlertScreen(
|
||||
configuration: AlertScreen.Configuration(
|
||||
actionAlignment: actionLayout == .vertical ? .vertical : .default,
|
||||
dismissOnOutsideTap: dismissOnOutsideTap,
|
||||
allowInputInset: allowInputInset
|
||||
),
|
||||
title: title,
|
||||
text: text,
|
||||
actions: mappedActions,
|
||||
updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal)
|
||||
)
|
||||
return controller
|
||||
}
|
||||
|
||||
public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||
public func textAlertController(
|
||||
sharedContext: SharedAccountContext,
|
||||
title: String?,
|
||||
text: String,
|
||||
actions: [TextAlertAction],
|
||||
actionLayout: TextAlertContentActionLayout = .horizontal,
|
||||
allowInputInset: Bool = true,
|
||||
dismissOnOutsideTap: Bool = true
|
||||
) -> AlertController {
|
||||
return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: sharedContext.currentPresentationData.with { $0 }), themeSignal: sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
|
||||
}
|
||||
|
||||
public func richTextAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController {
|
||||
public func richTextAlertController(
|
||||
context: AccountContext,
|
||||
title: NSAttributedString?,
|
||||
text: NSAttributedString,
|
||||
actions: [TextAlertAction],
|
||||
actionLayout: TextAlertContentActionLayout = .horizontal,
|
||||
allowInputInset: Bool = true,
|
||||
dismissAutomatically: Bool = true
|
||||
) -> AlertController {
|
||||
return richTextAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissAutomatically: dismissAutomatically)
|
||||
}
|
||||
|
||||
public func textWithEntitiesAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController {
|
||||
public func textWithEntitiesAlertController(
|
||||
context: AccountContext,
|
||||
title: NSAttributedString?,
|
||||
text: NSAttributedString,
|
||||
actions: [TextAlertAction],
|
||||
actionLayout: TextAlertContentActionLayout = .horizontal,
|
||||
allowInputInset: Bool = true,
|
||||
dismissAutomatically: Bool = true
|
||||
) -> AlertController {
|
||||
return textWithEntitiesAlertController(
|
||||
alertContext: AlertControllerContext(
|
||||
theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import AccountContext
|
|||
import OverlayStatusController
|
||||
import UrlWhitelist
|
||||
import TelegramPresentationData
|
||||
import AlertComponent
|
||||
|
||||
public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url: String, concealed: Bool, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, forceDark: Bool = false, present: @escaping (ViewController) -> Void, openResolved: @escaping (ResolvedUrl) -> Void, progress: Promise<Bool>? = nil, alertDisplayUpdated: ((ViewController?) -> Void)? = nil) -> Disposable {
|
||||
var concealed = concealed
|
||||
|
|
@ -95,8 +96,10 @@ public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url:
|
|||
let alertController = textAlertController(context: context, forceTheme: forceDark ? presentationData.theme : nil, title: nil, text: presentationData.strings.Generic_OpenHiddenLinkAlert(displayUrl).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
|
||||
disposable.set(openImpl())
|
||||
})])
|
||||
alertController.dismissed = { _ in
|
||||
alertDisplayUpdated?(nil)
|
||||
if let alertController = alertController as? AlertScreen {
|
||||
alertController.dismissed = { _ in
|
||||
alertDisplayUpdated?(nil)
|
||||
}
|
||||
}
|
||||
present(alertController)
|
||||
alertDisplayUpdated?(alertController)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ swift_library(
|
|||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
||||
"//submodules/ImageContentAnalysis:ImageContentAnalysis",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -131,6 +131,7 @@ swift_library(
|
|||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/SliderComponent",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import AccountUtils
|
|||
import PremiumUI
|
||||
import PasswordSetupUI
|
||||
import StorageUsageScreen
|
||||
import AlertComponent
|
||||
|
||||
private struct DeleteAccountOptionsArguments {
|
||||
let changePhoneNumber: () -> Void
|
||||
|
|
@ -102,43 +103,43 @@ private enum DeleteAccountOptionsEntry: ItemListNodeEntry, Equatable {
|
|||
let arguments = arguments as! DeleteAccountOptionsArguments
|
||||
switch self {
|
||||
case let .changePhoneNumber(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.changePhoneNumber()
|
||||
})
|
||||
case let .addAccount(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteAddAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteAddAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.addAccount()
|
||||
})
|
||||
case let .changePrivacy(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.security, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.security, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.setupPrivacy()
|
||||
})
|
||||
case let .setTwoStepAuth(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.setupTwoStepAuth()
|
||||
})
|
||||
case let .setPasscode(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.setPasscode()
|
||||
})
|
||||
case let .clearCache(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.dataAndStorage, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.dataAndStorage, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.clearCache()
|
||||
})
|
||||
case let .clearSyncedContacts(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.clearSyncedContacts()
|
||||
})
|
||||
case let .deleteChats(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.deleteChats()
|
||||
})
|
||||
case let .contactSupport(_, title, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.contactSupport()
|
||||
})
|
||||
case let .deleteAccount(_, title):
|
||||
return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.deleteAccount()
|
||||
})
|
||||
}
|
||||
|
|
@ -362,32 +363,36 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo
|
|||
}, dismissInput: {}, contentContext: nil, progress: nil, completion: nil)
|
||||
})
|
||||
}
|
||||
|
||||
let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: {
|
||||
openFaq(resolvedUrlPromise)
|
||||
dismissImpl?()
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
supportPeerDisposable.set((supportPeer.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peerId in
|
||||
guard let peerId = peerId else {
|
||||
return
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer = peer else {
|
||||
let alertController = AlertScreen(
|
||||
context: context,
|
||||
title: nil,
|
||||
text: presentationData.strings.Settings_FAQ_Intro,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Settings_FAQ_Button, action: {
|
||||
openFaq(resolvedUrlPromise)
|
||||
dismissImpl?()
|
||||
}),
|
||||
.init(title: presentationData.strings.Common_OK, type: .default, action: {
|
||||
supportPeerDisposable.set((supportPeer.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peerId in
|
||||
guard let peerId else {
|
||||
return
|
||||
}
|
||||
if let navigationController = navigationController {
|
||||
dismissImpl?()
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
])
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer else {
|
||||
return
|
||||
}
|
||||
if let navigationController = navigationController {
|
||||
dismissImpl?()
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
]
|
||||
)
|
||||
alertController.dismissed = { _ in
|
||||
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_support_cancel")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1406,10 +1406,10 @@ public func privacyAndSecurityController(
|
|||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = textAlertController(
|
||||
context: context, title: emailPattern, text: presentationData.strings.PrivacySettings_LoginEmailAlertText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.PrivacySettings_LoginEmailAlertChange, action: {
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.PrivacySettings_LoginEmailAlertChange, action: {
|
||||
setupEmailImpl?(emailPattern)
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
|
||||
})
|
||||
], actionLayout: .vertical
|
||||
|
|
|
|||
|
|
@ -417,6 +417,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
|
||||
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
|
||||
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
|
||||
dict[-207018934] = { return Api.InputMedia.parse_inputMediaStakeDice($0) }
|
||||
dict[-1979852936] = { return Api.InputMedia.parse_inputMediaStory($0) }
|
||||
dict[-1614454818] = { return Api.InputMedia.parse_inputMediaTodo($0) }
|
||||
dict[58495792] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
|
||||
|
|
@ -660,7 +661,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) }
|
||||
dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
|
||||
dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) }
|
||||
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
|
||||
dict[147581959] = { return Api.MessageMedia.parse_messageMediaDice($0) }
|
||||
dict[1389939929] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
|
||||
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
|
||||
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
||||
|
|
@ -1127,6 +1128,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-302247650] = { return Api.Update.parse_updateDraftMessage($0) }
|
||||
dict[457133559] = { return Api.Update.parse_updateEditChannelMessage($0) }
|
||||
dict[-469536605] = { return Api.Update.parse_updateEditMessage($0) }
|
||||
dict[-73640838] = { return Api.Update.parse_updateEmojiGameInfo($0) }
|
||||
dict[386986326] = { return Api.Update.parse_updateEncryptedChatTyping($0) }
|
||||
dict[956179895] = { return Api.Update.parse_updateEncryptedMessagesRead($0) }
|
||||
dict[-1264392051] = { return Api.Update.parse_updateEncryption($0) }
|
||||
|
|
@ -1417,6 +1419,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) }
|
||||
dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) }
|
||||
dict[-1506535550] = { return Api.messages.DiscussionMessage.parse_discussionMessage($0) }
|
||||
dict[1155883043] = { return Api.messages.EmojiGameInfo.parse_emojiGameDiceInfo($0) }
|
||||
dict[1508266805] = { return Api.messages.EmojiGameInfo.parse_emojiGameUnavailable($0) }
|
||||
dict[-1585592191] = { return Api.messages.EmojiGameOutcome.parse_emojiGameOutcome($0) }
|
||||
dict[-2011186869] = { return Api.messages.EmojiGroups.parse_emojiGroups($0) }
|
||||
dict[1874111879] = { return Api.messages.EmojiGroups.parse_emojiGroupsNotModified($0) }
|
||||
dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) }
|
||||
|
|
@ -2573,6 +2578,10 @@ public extension Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.DiscussionMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.EmojiGameInfo:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.EmojiGameOutcome:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.EmojiGroups:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.ExportedChatInvite:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public extension Api {
|
|||
case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?)
|
||||
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
|
||||
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
|
||||
case inputMediaStakeDice(gameHash: String, tonAmount: Int64, clientSeed: Buffer)
|
||||
case inputMediaStory(peer: Api.InputPeer, id: Int32)
|
||||
case inputMediaTodo(todo: Api.TodoList)
|
||||
case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, videoCover: Api.InputPhoto?, videoTimestamp: Int32?, ttlSeconds: Int32?)
|
||||
|
|
@ -148,6 +149,14 @@ public extension Api {
|
|||
item.serialize(buffer, true)
|
||||
}}
|
||||
break
|
||||
case .inputMediaStakeDice(let gameHash, let tonAmount, let clientSeed):
|
||||
if boxed {
|
||||
buffer.appendInt32(-207018934)
|
||||
}
|
||||
serializeString(gameHash, buffer: buffer, boxed: false)
|
||||
serializeInt64(tonAmount, buffer: buffer, boxed: false)
|
||||
serializeBytes(clientSeed, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMediaStory(let peer, let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1979852936)
|
||||
|
|
@ -245,6 +254,8 @@ public extension Api {
|
|||
return ("inputMediaPhotoExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .inputMediaPoll(let flags, let poll, let correctAnswers, let solution, let solutionEntities):
|
||||
return ("inputMediaPoll", [("flags", flags as Any), ("poll", poll as Any), ("correctAnswers", correctAnswers as Any), ("solution", solution as Any), ("solutionEntities", solutionEntities as Any)])
|
||||
case .inputMediaStakeDice(let gameHash, let tonAmount, let clientSeed):
|
||||
return ("inputMediaStakeDice", [("gameHash", gameHash as Any), ("tonAmount", tonAmount as Any), ("clientSeed", clientSeed as Any)])
|
||||
case .inputMediaStory(let peer, let id):
|
||||
return ("inputMediaStory", [("peer", peer as Any), ("id", id as Any)])
|
||||
case .inputMediaTodo(let todo):
|
||||
|
|
@ -533,6 +544,23 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMediaStakeDice(_ reader: BufferReader) -> InputMedia? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputMedia.inputMediaStakeDice(gameHash: _1!, tonAmount: _2!, clientSeed: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMediaStory(_ reader: BufferReader) -> InputMedia? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -990,113 +1018,3 @@ public extension Api {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputPaymentCredentials: TypeConstructorDescription {
|
||||
case inputPaymentCredentials(flags: Int32, data: Api.DataJSON)
|
||||
case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON)
|
||||
case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON)
|
||||
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(873977640)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
data.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
if boxed {
|
||||
buffer.appendInt32(178373535)
|
||||
}
|
||||
paymentData.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1966921727)
|
||||
}
|
||||
paymentToken.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1056001329)
|
||||
}
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
serializeBytes(tmpPassword, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)])
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)])
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)])
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,113 @@
|
|||
public extension Api {
|
||||
enum InputPaymentCredentials: TypeConstructorDescription {
|
||||
case inputPaymentCredentials(flags: Int32, data: Api.DataJSON)
|
||||
case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON)
|
||||
case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON)
|
||||
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(873977640)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
data.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
if boxed {
|
||||
buffer.appendInt32(178373535)
|
||||
}
|
||||
paymentData.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1966921727)
|
||||
}
|
||||
paymentToken.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1056001329)
|
||||
}
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
serializeBytes(tmpPassword, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)])
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)])
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)])
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputPeer: TypeConstructorDescription {
|
||||
case inputPeerChannel(channelId: Int64, accessHash: Int64)
|
||||
|
|
|
|||
|
|
@ -709,7 +709,7 @@ public extension Api {
|
|||
public extension Api {
|
||||
indirect enum MessageMedia: TypeConstructorDescription {
|
||||
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64)
|
||||
case messageMediaDice(value: Int32, emoticon: String)
|
||||
case messageMediaDice(flags: Int32, value: Int32, emoticon: String, gameOutcome: Api.messages.EmojiGameOutcome?)
|
||||
case messageMediaDocument(flags: Int32, document: Api.Document?, altDocuments: [Api.Document]?, videoCover: Api.Photo?, videoTimestamp: Int32?, ttlSeconds: Int32?)
|
||||
case messageMediaEmpty
|
||||
case messageMediaGame(game: Api.Game)
|
||||
|
|
@ -740,12 +740,14 @@ public extension Api {
|
|||
serializeString(vcard, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageMediaDice(let value, let emoticon):
|
||||
case .messageMediaDice(let flags, let value, let emoticon, let gameOutcome):
|
||||
if boxed {
|
||||
buffer.appendInt32(1065280907)
|
||||
buffer.appendInt32(147581959)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(value, buffer: buffer, boxed: false)
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {gameOutcome!.serialize(buffer, true)}
|
||||
break
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds):
|
||||
if boxed {
|
||||
|
|
@ -930,8 +932,8 @@ public extension Api {
|
|||
switch self {
|
||||
case .messageMediaContact(let phoneNumber, let firstName, let lastName, let vcard, let userId):
|
||||
return ("messageMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any), ("userId", userId as Any)])
|
||||
case .messageMediaDice(let value, let emoticon):
|
||||
return ("messageMediaDice", [("value", value as Any), ("emoticon", emoticon as Any)])
|
||||
case .messageMediaDice(let flags, let value, let emoticon, let gameOutcome):
|
||||
return ("messageMediaDice", [("flags", flags as Any), ("value", value as Any), ("emoticon", emoticon as Any), ("gameOutcome", gameOutcome as Any)])
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds):
|
||||
return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("videoCover", videoCover as Any), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .messageMediaEmpty:
|
||||
|
|
@ -995,12 +997,20 @@ public extension Api {
|
|||
public static func parse_messageMediaDice(_ reader: BufferReader) -> MessageMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Api.messages.EmojiGameOutcome?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameOutcome
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageMedia.messageMediaDice(value: _1!, emoticon: _2!)
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.MessageMedia.messageMediaDice(flags: _1!, value: _2!, emoticon: _3!, gameOutcome: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1247,6 +1247,7 @@ public extension Api {
|
|||
case updateDraftMessage(flags: Int32, peer: Api.Peer, topMsgId: Int32?, savedPeerId: Api.Peer?, draft: Api.DraftMessage)
|
||||
case updateEditChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32)
|
||||
case updateEditMessage(message: Api.Message, pts: Int32, ptsCount: Int32)
|
||||
case updateEmojiGameInfo(info: Api.messages.EmojiGameInfo)
|
||||
case updateEncryptedChatTyping(chatId: Int32)
|
||||
case updateEncryptedMessagesRead(chatId: Int32, maxDate: Int32, date: Int32)
|
||||
case updateEncryption(chat: Api.EncryptedChat, date: Int32)
|
||||
|
|
@ -1887,6 +1888,12 @@ public extension Api {
|
|||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateEmojiGameInfo(let info):
|
||||
if boxed {
|
||||
buffer.appendInt32(-73640838)
|
||||
}
|
||||
info.serialize(buffer, true)
|
||||
break
|
||||
case .updateEncryptedChatTyping(let chatId):
|
||||
if boxed {
|
||||
buffer.appendInt32(386986326)
|
||||
|
|
@ -2774,6 +2781,8 @@ public extension Api {
|
|||
return ("updateEditChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
case .updateEditMessage(let message, let pts, let ptsCount):
|
||||
return ("updateEditMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
case .updateEmojiGameInfo(let info):
|
||||
return ("updateEmojiGameInfo", [("info", info as Any)])
|
||||
case .updateEncryptedChatTyping(let chatId):
|
||||
return ("updateEncryptedChatTyping", [("chatId", chatId as Any)])
|
||||
case .updateEncryptedMessagesRead(let chatId, let maxDate, let date):
|
||||
|
|
@ -4126,6 +4135,19 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateEmojiGameInfo(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.messages.EmojiGameInfo?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameInfo
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.Update.updateEmojiGameInfo(info: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateEncryptedChatTyping(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,120 @@ public extension Api.messages {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum EmojiGameInfo: TypeConstructorDescription {
|
||||
case emojiGameDiceInfo(flags: Int32, gameHash: String, prevStake: Int64, currentStreak: Int32, params: [Int32], playsLeft: Int32?)
|
||||
case emojiGameUnavailable
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .emojiGameDiceInfo(let flags, let gameHash, let prevStake, let currentStreak, let params, let playsLeft):
|
||||
if boxed {
|
||||
buffer.appendInt32(1155883043)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(gameHash, buffer: buffer, boxed: false)
|
||||
serializeInt64(prevStake, buffer: buffer, boxed: false)
|
||||
serializeInt32(currentStreak, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(params.count))
|
||||
for item in params {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(playsLeft!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .emojiGameUnavailable:
|
||||
if boxed {
|
||||
buffer.appendInt32(1508266805)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .emojiGameDiceInfo(let flags, let gameHash, let prevStake, let currentStreak, let params, let playsLeft):
|
||||
return ("emojiGameDiceInfo", [("flags", flags as Any), ("gameHash", gameHash as Any), ("prevStake", prevStake as Any), ("currentStreak", currentStreak as Any), ("params", params as Any), ("playsLeft", playsLeft as Any)])
|
||||
case .emojiGameUnavailable:
|
||||
return ("emojiGameUnavailable", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_emojiGameDiceInfo(_ reader: BufferReader) -> EmojiGameInfo? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
var _6: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.messages.EmojiGameInfo.emojiGameDiceInfo(flags: _1!, gameHash: _2!, prevStake: _3!, currentStreak: _4!, params: _5!, playsLeft: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_emojiGameUnavailable(_ reader: BufferReader) -> EmojiGameInfo? {
|
||||
return Api.messages.EmojiGameInfo.emojiGameUnavailable
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum EmojiGameOutcome: TypeConstructorDescription {
|
||||
case emojiGameOutcome(seed: Buffer, tonAmount: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .emojiGameOutcome(let seed, let tonAmount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1585592191)
|
||||
}
|
||||
serializeBytes(seed, buffer: buffer, boxed: false)
|
||||
serializeInt64(tonAmount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .emojiGameOutcome(let seed, let tonAmount):
|
||||
return ("emojiGameOutcome", [("seed", seed as Any), ("tonAmount", tonAmount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_emojiGameOutcome(_ reader: BufferReader) -> EmojiGameOutcome? {
|
||||
var _1: Buffer?
|
||||
_1 = parseBytes(reader)
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.EmojiGameOutcome.emojiGameOutcome(seed: _1!, tonAmount: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum EmojiGroups: TypeConstructorDescription {
|
||||
case emojiGroups(hash: Int32, groups: [Api.EmojiGroup])
|
||||
|
|
@ -1260,155 +1374,3 @@ public extension Api.messages {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FavedStickers: TypeConstructorDescription {
|
||||
case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document])
|
||||
case favedStickersNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .favedStickers(let hash, let packs, let stickers):
|
||||
if boxed {
|
||||
buffer.appendInt32(750063767)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(packs.count))
|
||||
for item in packs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stickers.count))
|
||||
for item in stickers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .favedStickersNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1634752813)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .favedStickers(let hash, let packs, let stickers):
|
||||
return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)])
|
||||
case .favedStickersNotModified:
|
||||
return ("favedStickersNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.StickerPack]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? {
|
||||
return Api.messages.FavedStickers.favedStickersNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FeaturedStickers: TypeConstructorDescription {
|
||||
case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64])
|
||||
case featuredStickersNotModified(count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .featuredStickers(let flags, let hash, let count, let sets, let unread):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1103615738)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sets.count))
|
||||
for item in sets {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(unread.count))
|
||||
for item in unread {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .featuredStickersNotModified(let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-958657434)
|
||||
}
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .featuredStickers(let flags, let hash, let count, let sets, let unread):
|
||||
return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)])
|
||||
case .featuredStickersNotModified(let count):
|
||||
return ("featuredStickersNotModified", [("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Api.StickerSetCovered]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self)
|
||||
}
|
||||
var _5: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,155 @@
|
|||
public extension Api.messages {
|
||||
enum FavedStickers: TypeConstructorDescription {
|
||||
case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document])
|
||||
case favedStickersNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .favedStickers(let hash, let packs, let stickers):
|
||||
if boxed {
|
||||
buffer.appendInt32(750063767)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(packs.count))
|
||||
for item in packs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stickers.count))
|
||||
for item in stickers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .favedStickersNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1634752813)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .favedStickers(let hash, let packs, let stickers):
|
||||
return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)])
|
||||
case .favedStickersNotModified:
|
||||
return ("favedStickersNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.StickerPack]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? {
|
||||
return Api.messages.FavedStickers.favedStickersNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FeaturedStickers: TypeConstructorDescription {
|
||||
case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64])
|
||||
case featuredStickersNotModified(count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .featuredStickers(let flags, let hash, let count, let sets, let unread):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1103615738)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sets.count))
|
||||
for item in sets {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(unread.count))
|
||||
for item in unread {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .featuredStickersNotModified(let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-958657434)
|
||||
}
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .featuredStickers(let flags, let hash, let count, let sets, let unread):
|
||||
return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)])
|
||||
case .featuredStickersNotModified(let count):
|
||||
return ("featuredStickersNotModified", [("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Api.StickerSetCovered]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self)
|
||||
}
|
||||
var _5: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum ForumTopics: TypeConstructorDescription {
|
||||
case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32)
|
||||
|
|
@ -1490,61 +1642,3 @@ public extension Api.messages {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum SavedGifs: TypeConstructorDescription {
|
||||
case savedGifs(hash: Int64, gifs: [Api.Document])
|
||||
case savedGifsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedGifs(let hash, let gifs):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2069878259)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(gifs.count))
|
||||
for item in gifs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .savedGifsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-402498398)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedGifs(let hash, let gifs):
|
||||
return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)])
|
||||
case .savedGifsNotModified:
|
||||
return ("savedGifsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? {
|
||||
return Api.messages.SavedGifs.savedGifsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,61 @@
|
|||
public extension Api.messages {
|
||||
enum SavedGifs: TypeConstructorDescription {
|
||||
case savedGifs(hash: Int64, gifs: [Api.Document])
|
||||
case savedGifsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedGifs(let hash, let gifs):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2069878259)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(gifs.count))
|
||||
for item in gifs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .savedGifsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-402498398)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedGifs(let hash, let gifs):
|
||||
return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)])
|
||||
case .savedGifsNotModified:
|
||||
return ("savedGifsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? {
|
||||
return Api.messages.SavedGifs.savedGifsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum SavedReactionTags: TypeConstructorDescription {
|
||||
case savedReactionTags(tags: [Api.SavedReactionTag], hash: Int64)
|
||||
|
|
@ -1436,179 +1494,3 @@ public extension Api.payments {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum PaymentReceipt: TypeConstructorDescription {
|
||||
case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User])
|
||||
case paymentReceiptStars(flags: Int32, date: Int32, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, currency: String, totalAmount: Int64, transactionId: String, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1891958275)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeInt64(providerId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(totalAmount, buffer: buffer, boxed: false)
|
||||
serializeString(credentialsTitle, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-625215430)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(totalAmount, buffer: buffer, boxed: false)
|
||||
serializeString(transactionId, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users):
|
||||
return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)])
|
||||
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users):
|
||||
return ("paymentReceiptStars", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("transactionId", transactionId as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: String?
|
||||
_6 = parseString(reader)
|
||||
var _7: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _8: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _9: Api.PaymentRequestedInfo?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
|
||||
} }
|
||||
var _10: Api.ShippingOption?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_10 = Api.parse(reader, signature: signature) as? Api.ShippingOption
|
||||
} }
|
||||
var _11: Int64?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() }
|
||||
var _12: String?
|
||||
_12 = parseString(reader)
|
||||
var _13: Int64?
|
||||
_13 = reader.readInt64()
|
||||
var _14: String?
|
||||
_14 = parseString(reader)
|
||||
var _15: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
||||
let _c12 = _12 != nil
|
||||
let _c13 = _13 != nil
|
||||
let _c14 = _14 != nil
|
||||
let _c15 = _15 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
|
||||
return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_paymentReceiptStars(_ reader: BufferReader) -> PaymentReceipt? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _7: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _8: String?
|
||||
_8 = parseString(reader)
|
||||
var _9: Int64?
|
||||
_9 = reader.readInt64()
|
||||
var _10: String?
|
||||
_10 = parseString(reader)
|
||||
var _11: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.payments.PaymentReceipt.paymentReceiptStars(flags: _1!, date: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, currency: _8!, totalAmount: _9!, transactionId: _10!, users: _11!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,179 @@
|
|||
public extension Api.payments {
|
||||
enum PaymentReceipt: TypeConstructorDescription {
|
||||
case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User])
|
||||
case paymentReceiptStars(flags: Int32, date: Int32, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, currency: String, totalAmount: Int64, transactionId: String, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1891958275)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeInt64(providerId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(totalAmount, buffer: buffer, boxed: false)
|
||||
serializeString(credentialsTitle, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-625215430)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(totalAmount, buffer: buffer, boxed: false)
|
||||
serializeString(transactionId, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users):
|
||||
return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)])
|
||||
case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users):
|
||||
return ("paymentReceiptStars", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("transactionId", transactionId as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: String?
|
||||
_6 = parseString(reader)
|
||||
var _7: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _8: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _9: Api.PaymentRequestedInfo?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
|
||||
} }
|
||||
var _10: Api.ShippingOption?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_10 = Api.parse(reader, signature: signature) as? Api.ShippingOption
|
||||
} }
|
||||
var _11: Int64?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() }
|
||||
var _12: String?
|
||||
_12 = parseString(reader)
|
||||
var _13: Int64?
|
||||
_13 = reader.readInt64()
|
||||
var _14: String?
|
||||
_14 = parseString(reader)
|
||||
var _15: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
||||
let _c12 = _12 != nil
|
||||
let _c13 = _13 != nil
|
||||
let _c14 = _14 != nil
|
||||
let _c15 = _15 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
|
||||
return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_paymentReceiptStars(_ reader: BufferReader) -> PaymentReceipt? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _7: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _8: String?
|
||||
_8 = parseString(reader)
|
||||
var _9: Int64?
|
||||
_9 = reader.readInt64()
|
||||
var _10: String?
|
||||
_10 = parseString(reader)
|
||||
var _11: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.payments.PaymentReceipt.paymentReceiptStars(flags: _1!, date: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, currency: _8!, totalAmount: _9!, transactionId: _10!, users: _11!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
indirect enum PaymentResult: TypeConstructorDescription {
|
||||
case paymentResult(updates: Api.Updates)
|
||||
|
|
@ -1568,113 +1744,3 @@ public extension Api.phone {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.phone {
|
||||
enum JoinAsPeers: TypeConstructorDescription {
|
||||
case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .joinAsPeers(let peers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1343921601)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .joinAsPeers(let peers, let chats, let users):
|
||||
return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? {
|
||||
var _1: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.phone {
|
||||
enum PhoneCall: TypeConstructorDescription {
|
||||
case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .phoneCall(let phoneCall, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-326966976)
|
||||
}
|
||||
phoneCall.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .phoneCall(let phoneCall, let users):
|
||||
return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? {
|
||||
var _1: Api.PhoneCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.PhoneCall
|
||||
}
|
||||
var _2: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,113 @@
|
|||
public extension Api.phone {
|
||||
enum JoinAsPeers: TypeConstructorDescription {
|
||||
case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .joinAsPeers(let peers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1343921601)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .joinAsPeers(let peers, let chats, let users):
|
||||
return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? {
|
||||
var _1: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.phone {
|
||||
enum PhoneCall: TypeConstructorDescription {
|
||||
case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .phoneCall(let phoneCall, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-326966976)
|
||||
}
|
||||
phoneCall.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .phoneCall(let phoneCall, let users):
|
||||
return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? {
|
||||
var _1: Api.PhoneCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.PhoneCall
|
||||
}
|
||||
var _2: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.photos {
|
||||
enum Photo: TypeConstructorDescription {
|
||||
case photo(photo: Api.Photo, users: [Api.User])
|
||||
|
|
@ -1384,141 +1494,3 @@ public extension Api.stories {
|
|||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum PeerStories: TypeConstructorDescription {
|
||||
case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .peerStories(let stories, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-890861720)
|
||||
}
|
||||
stories.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .peerStories(let stories, let chats, let users):
|
||||
return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? {
|
||||
var _1: Api.PeerStories?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.PeerStories
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum Stories: TypeConstructorDescription {
|
||||
case stories(flags: Int32, count: Int32, stories: [Api.StoryItem], pinnedToTop: [Int32]?, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1673780490)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stories.count))
|
||||
for item in stories {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(pinnedToTop!.count))
|
||||
for item in pinnedToTop! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users):
|
||||
return ("stories", [("flags", flags as Any), ("count", count as Any), ("stories", stories as Any), ("pinnedToTop", pinnedToTop as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stories(_ reader: BufferReader) -> Stories? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.StoryItem]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self)
|
||||
}
|
||||
var _4: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.stories.Stories.stories(flags: _1!, count: _2!, stories: _3!, pinnedToTop: _4, chats: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,141 @@
|
|||
public extension Api.stories {
|
||||
enum PeerStories: TypeConstructorDescription {
|
||||
case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .peerStories(let stories, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-890861720)
|
||||
}
|
||||
stories.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .peerStories(let stories, let chats, let users):
|
||||
return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? {
|
||||
var _1: Api.PeerStories?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.PeerStories
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum Stories: TypeConstructorDescription {
|
||||
case stories(flags: Int32, count: Int32, stories: [Api.StoryItem], pinnedToTop: [Int32]?, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1673780490)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stories.count))
|
||||
for item in stories {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(pinnedToTop!.count))
|
||||
for item in pinnedToTop! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users):
|
||||
return ("stories", [("flags", flags as Any), ("count", count as Any), ("stories", stories as Any), ("pinnedToTop", pinnedToTop as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stories(_ reader: BufferReader) -> Stories? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.StoryItem]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self)
|
||||
}
|
||||
var _4: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.stories.Stories.stories(flags: _1!, count: _2!, stories: _3!, pinnedToTop: _4, chats: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.stories {
|
||||
enum StoryReactionsList: TypeConstructorDescription {
|
||||
case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?)
|
||||
|
|
|
|||
|
|
@ -6245,6 +6245,21 @@ public extension Api.functions.messages {
|
|||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getEmojiGameInfo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.EmojiGameInfo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-75592537)
|
||||
|
||||
return (FunctionDescription(name: "messages.getEmojiGameInfo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGameInfo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.EmojiGameInfo?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameInfo
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getEmojiGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.EmojiGroups>) {
|
||||
let buffer = Buffer()
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ enum AccountStateMutationOperation {
|
|||
case UpdateMonoForumNoPaidException(peerId: PeerId, threadId: Int64, isFree: Bool)
|
||||
case UpdateStarGiftAuctionState(giftId: Int64, state: GiftAuctionContext.State.AuctionState)
|
||||
case UpdateStarGiftAuctionMyState(giftId: Int64, state: GiftAuctionContext.State.MyState)
|
||||
case UpdateEmojiGameInfo(info: EmojiGameInfo)
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
|
|
@ -737,9 +738,13 @@ struct AccountMutableState {
|
|||
self.addOperation(.UpdateStarGiftAuctionMyState(giftId: giftId, state: state))
|
||||
}
|
||||
|
||||
mutating func updateEmojiGameInfo(info: EmojiGameInfo) {
|
||||
self.addOperation(.UpdateEmojiGameInfo(info: info))
|
||||
}
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState, .UpdateEmojiGameInfo:
|
||||
break
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
|
|
@ -892,6 +897,7 @@ struct AccountReplayedFinalState {
|
|||
let addedConferenceInvitationMessagesIds: [MessageId]
|
||||
let updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState]
|
||||
let updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState]
|
||||
let updatedEmojiGameInfo: EmojiGameInfo?
|
||||
}
|
||||
|
||||
struct AccountFinalStateEvents {
|
||||
|
|
@ -927,12 +933,13 @@ struct AccountFinalStateEvents {
|
|||
let addedConferenceInvitationMessagesIds: [MessageId]
|
||||
let updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState]
|
||||
let updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState]
|
||||
let updatedEmojiGameInfo: EmojiGameInfo?
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.groupCallMessageUpdates.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedStarsBalance.isEmpty && self.updatedTonBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty && self.addedConferenceInvitationMessagesIds.isEmpty && self.updatedStarGiftAuctionState.isEmpty && self.updatedStarGiftAuctionMyState.isEmpty
|
||||
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.groupCallMessageUpdates.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedStarsBalance.isEmpty && self.updatedTonBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty && self.addedConferenceInvitationMessagesIds.isEmpty && self.updatedStarGiftAuctionState.isEmpty && self.updatedStarGiftAuctionMyState.isEmpty && self.updatedEmojiGameInfo == nil
|
||||
}
|
||||
|
||||
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], groupCallMessageUpdates: [GroupCallMessageUpdate] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedTonBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set(), reportMessageDelivery: Set<MessageId> = Set(), addedConferenceInvitationMessagesIds: [MessageId] = [], updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:], updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:]) {
|
||||
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], groupCallMessageUpdates: [GroupCallMessageUpdate] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedTonBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set(), reportMessageDelivery: Set<MessageId> = Set(), addedConferenceInvitationMessagesIds: [MessageId] = [], updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:], updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:], updatedEmojiGameInfo: EmojiGameInfo? = nil) {
|
||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||
self.addedReactionEvents = addedReactionEvents
|
||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||
|
|
@ -965,6 +972,7 @@ struct AccountFinalStateEvents {
|
|||
self.addedConferenceInvitationMessagesIds = addedConferenceInvitationMessagesIds
|
||||
self.updatedStarGiftAuctionState = updatedStarGiftAuctionState
|
||||
self.updatedStarGiftAuctionMyState = updatedStarGiftAuctionMyState
|
||||
self.updatedEmojiGameInfo = updatedEmojiGameInfo
|
||||
}
|
||||
|
||||
init(state: AccountReplayedFinalState) {
|
||||
|
|
@ -1000,6 +1008,7 @@ struct AccountFinalStateEvents {
|
|||
self.addedConferenceInvitationMessagesIds = state.addedConferenceInvitationMessagesIds
|
||||
self.updatedStarGiftAuctionState = state.updatedStarGiftAuctionState
|
||||
self.updatedStarGiftAuctionMyState = state.updatedStarGiftAuctionMyState
|
||||
self.updatedEmojiGameInfo = state.updatedEmojiGameInfo
|
||||
}
|
||||
|
||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||
|
|
@ -1068,7 +1077,8 @@ struct AccountFinalStateEvents {
|
|||
reportMessageDelivery: reportMessageDelivery,
|
||||
addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds,
|
||||
updatedStarGiftAuctionState: self.updatedStarGiftAuctionState.merging(other.updatedStarGiftAuctionState, uniquingKeysWith: { lhs, _ in lhs }),
|
||||
updatedStarGiftAuctionMyState: self.updatedStarGiftAuctionMyState.merging(other.updatedStarGiftAuctionMyState, uniquingKeysWith: { lhs, _ in lhs })
|
||||
updatedStarGiftAuctionMyState: self.updatedStarGiftAuctionMyState.merging(other.updatedStarGiftAuctionMyState, uniquingKeysWith: { lhs, _ in lhs }),
|
||||
updatedEmojiGameInfo: self.updatedEmojiGameInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -457,8 +457,15 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
|||
}
|
||||
return (TelegramMediaTodo(flags: flags, text: todoText, textEntities: todoEntities, items: list.map(TelegramMediaTodo.Item.init(apiItem:)), completions: todoCompletions), nil, nil, nil, nil, nil)
|
||||
}
|
||||
case let .messageMediaDice(value, emoticon):
|
||||
return (TelegramMediaDice(emoji: emoticon, value: value), nil, nil, nil, nil, nil)
|
||||
case let .messageMediaDice(_, value, emoticon, apiGameOutcome):
|
||||
var gameOutcome: TelegramMediaDice.GameOutcome?
|
||||
switch apiGameOutcome {
|
||||
case let .emojiGameOutcome(seed, tonAmount):
|
||||
gameOutcome = TelegramMediaDice.GameOutcome(seed: seed.makeData(), tonAmount: tonAmount)
|
||||
default:
|
||||
break
|
||||
}
|
||||
return (TelegramMediaDice(emoji: emoticon, value: value, gameOutcome: gameOutcome), nil, nil, nil, nil, nil)
|
||||
case let .messageMediaStory(flags, peerId, id, _):
|
||||
let isMention = (flags & (1 << 1)) != 0
|
||||
return (TelegramMediaStory(storyId: StoryId(peerId: peerId.peerId, id: id), isMention: isMention), nil, nil, nil, nil, nil)
|
||||
|
|
|
|||
|
|
@ -343,8 +343,26 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
|||
let inputTodo = Api.InputMedia.inputMediaTodo(todo: .todoList(flags: flags, title: .textWithEntities(text: todo.text, entities: apiEntitiesFromMessageTextEntities(todo.textEntities, associatedPeers: SimpleDictionary())), list: todo.items.map { $0.apiItem }))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputTodo, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
} else if let dice = media as? TelegramMediaDice {
|
||||
let inputDice = Api.InputMedia.inputMediaDice(emoticon: dice.emoji)
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputDice, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
if let tonAmount = dice.tonAmount {
|
||||
let seedBytes = malloc(32)!
|
||||
let _ = SecRandomCopyBytes(nil, 32, seedBytes.assumingMemoryBound(to: UInt8.self))
|
||||
let clientSeed = MemoryBuffer(memory: seedBytes, capacity: 32, length: 32, freeWhenDone: true)
|
||||
|
||||
return postbox.transaction { transaction -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
let gameInfo = currentEmojiGameInfo(transaction: transaction)
|
||||
if case let .available(info) = gameInfo {
|
||||
let inputStakeDice = Api.InputMedia.inputMediaStakeDice(gameHash: info.gameHash, tonAmount: tonAmount, clientSeed: Buffer(buffer: clientSeed))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputStakeDice, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> castError(PendingMessageUploadError.self)
|
||||
|> switchToLatest
|
||||
} else {
|
||||
let inputDice = Api.InputMedia.inputMediaDice(emoticon: dice.emoji)
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputDice, text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
}
|
||||
} else if let webPage = media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content {
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 2
|
||||
|
|
|
|||
|
|
@ -1906,6 +1906,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||
}
|
||||
case let .updateStarGiftAuctionUserState(giftId, userState):
|
||||
updatedState.updateStarGiftAuctionMyState(giftId: giftId, state: GiftAuctionContext.State.MyState(apiAuctionUserState: userState))
|
||||
case let .updateEmojiGameInfo(info):
|
||||
updatedState.updateEmojiGameInfo(info: EmojiGameInfo(apiEmojiGameInfo: info))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -3629,7 +3631,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
|||
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState, .UpdateEmojiGameInfo:
|
||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
|
|
@ -3772,6 +3774,7 @@ func replayFinalState(
|
|||
var reportMessageDelivery = Set<MessageId>()
|
||||
var updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:]
|
||||
var updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:]
|
||||
var updatedEmojiGameInfo: EmojiGameInfo?
|
||||
|
||||
var holesFromPreviousStateMessageIds: [MessageId] = []
|
||||
var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
|
||||
|
|
@ -5339,6 +5342,8 @@ func replayFinalState(
|
|||
updatedStarGiftAuctionState[giftId] = state
|
||||
case let .UpdateStarGiftAuctionMyState(giftId, state):
|
||||
updatedStarGiftAuctionMyState[giftId] = state
|
||||
case let .UpdateEmojiGameInfo(info):
|
||||
updatedEmojiGameInfo = info
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5889,6 +5894,7 @@ func replayFinalState(
|
|||
reportMessageDelivery: reportMessageDelivery,
|
||||
addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds,
|
||||
updatedStarGiftAuctionState: updatedStarGiftAuctionState,
|
||||
updatedStarGiftAuctionMyState: updatedStarGiftAuctionMyState
|
||||
updatedStarGiftAuctionMyState: updatedStarGiftAuctionMyState,
|
||||
updatedEmojiGameInfo: updatedEmojiGameInfo
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ public final class AccountStateManager {
|
|||
private let appliedQtsPromise = Promise<Int32?>(nil)
|
||||
private let appliedQtsDisposable = MetaDisposable()
|
||||
private let reportMessageDeliveryDisposable = DisposableSet()
|
||||
private let updateEmojiGameInfoDisposable = MetaDisposable()
|
||||
|
||||
let updateConfigRequested: (() -> Void)?
|
||||
let isPremiumUpdated: (() -> Void)?
|
||||
|
|
@ -414,6 +415,7 @@ public final class AccountStateManager {
|
|||
self.appliedMaxMessageIdDisposable.dispose()
|
||||
self.appliedQtsDisposable.dispose()
|
||||
self.reportMessageDeliveryDisposable.dispose()
|
||||
self.updateEmojiGameInfoDisposable.dispose()
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
|
|
@ -1137,6 +1139,11 @@ public final class AccountStateManager {
|
|||
if !events.updatedStarGiftAuctionMyState.isEmpty {
|
||||
strongSelf.notifyUpdatedStarGiftAuctionMyState(events.updatedStarGiftAuctionMyState)
|
||||
}
|
||||
if let updatedEmojiGameInfo = events.updatedEmojiGameInfo {
|
||||
strongSelf.updateEmojiGameInfoDisposable.set(strongSelf.postbox.transaction({ transaction in
|
||||
updateEmojiGameInfo(transaction: transaction, { _ in return updatedEmojiGameInfo })
|
||||
}).start())
|
||||
}
|
||||
if !events.updatedCalls.isEmpty {
|
||||
for call in events.updatedCalls {
|
||||
strongSelf.callSessionManager?.updateSession(call, completion: { _ in })
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ final class AccountTaskManager {
|
|||
tasks.add(managedPeerColorUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedStarGiftsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
||||
tasks.add(managedSavedMusicIdsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
||||
tasks.add(managedEmojiGameUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
|
||||
self.managedTopReactionsDisposable.set(managedTopReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
public enum EmojiGameInfo: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case info
|
||||
}
|
||||
|
||||
public struct Info: Codable, Equatable {
|
||||
public let gameHash: String
|
||||
public let previousStake: Int64
|
||||
public let currentStreak: Int32
|
||||
public let parameters: [Int32]
|
||||
public let playsLeft: Int32?
|
||||
}
|
||||
|
||||
case available(Info)
|
||||
case unavailable
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
|
||||
let type = try container.decode(Int32.self, forKey: .type)
|
||||
switch type {
|
||||
case 1:
|
||||
self = .available(try container.decode(Info.self, forKey: .info))
|
||||
default:
|
||||
self = .unavailable
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
switch self {
|
||||
case let .available(info):
|
||||
try container.encode(Int32(1), forKey: .type)
|
||||
try container.encode(info, forKey: .info)
|
||||
case .unavailable:
|
||||
try container.encode(Int32(0), forKey: .type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension EmojiGameInfo {
|
||||
init(apiEmojiGameInfo: Api.messages.EmojiGameInfo) {
|
||||
switch apiEmojiGameInfo {
|
||||
case let .emojiGameDiceInfo(_, gameHash, prevStake, currentStreak, params, playsLeft):
|
||||
self = .available(Info(gameHash: gameHash, previousStake: prevStake, currentStreak: currentStreak, parameters: params, playsLeft: playsLeft))
|
||||
case .emojiGameUnavailable:
|
||||
self = .unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func currentEmojiGameInfo(transaction: Transaction) -> EmojiGameInfo {
|
||||
if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.emojiGameInfo())?.get(EmojiGameInfo.self) {
|
||||
return entry
|
||||
} else {
|
||||
return .unavailable
|
||||
}
|
||||
}
|
||||
|
||||
func updateEmojiGameInfo(transaction: Transaction, _ f: (EmojiGameInfo) -> EmojiGameInfo) {
|
||||
let current = currentEmojiGameInfo(transaction: transaction)
|
||||
let updated = f(current)
|
||||
if updated != current {
|
||||
transaction.setPreferencesEntry(key: PreferencesKeys.emojiGameInfo(), value: PreferencesEntry(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func updateEmojiGameInfoOnce(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return network.request(Api.functions.messages.getEmojiGameInfo())
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.EmojiGameInfo?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
guard let result else {
|
||||
return .complete()
|
||||
}
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let info = EmojiGameInfo(apiEmojiGameInfo: result)
|
||||
updateEmojiGameInfo(transaction: transaction) { _ in
|
||||
return info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func managedEmojiGameUpdates(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
let poll = Signal<Void, NoError> { subscriber in
|
||||
return updateEmojiGameInfoOnce(postbox: postbox, network: network).start(completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
}
|
||||
return (poll |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 220
|
||||
return 221
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ private enum PreferencesKeyValues: Int32 {
|
|||
case persistentChatInterfaceData = 45
|
||||
case globalPostSearchState = 46
|
||||
case savedMusicIds = 47
|
||||
case emojiGameInfo = 48
|
||||
}
|
||||
|
||||
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
||||
|
|
@ -591,6 +592,12 @@ public struct PreferencesKeys {
|
|||
key.setInt32(0, value: PreferencesKeyValues.savedMusicIds.rawValue)
|
||||
return key
|
||||
}
|
||||
|
||||
public static func emojiGameInfo() -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: PreferencesKeyValues.emojiGameInfo.rawValue)
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
private enum SharedDataKeyValues: Int32 {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,57 @@
|
|||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public final class TelegramMediaDice: Media, Equatable {
|
||||
public struct GameOutcome: Equatable {
|
||||
let seed: Data
|
||||
public let tonAmount: Int64
|
||||
}
|
||||
|
||||
public let emoji: String
|
||||
public let tonAmount: Int64?
|
||||
public let value: Int32?
|
||||
public let gameOutcome: GameOutcome?
|
||||
|
||||
public let id: MediaId? = nil
|
||||
public let peerIds: [PeerId] = []
|
||||
|
||||
public init(emoji: String, value: Int32? = nil) {
|
||||
public init(emoji: String, tonAmount: Int64? = nil, value: Int32? = nil, gameOutcome: GameOutcome? = nil) {
|
||||
self.emoji = emoji
|
||||
self.tonAmount = tonAmount
|
||||
self.value = value
|
||||
self.gameOutcome = gameOutcome
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.emoji = decoder.decodeStringForKey("e", orElse: "🎲")
|
||||
self.tonAmount = decoder.decodeOptionalInt64ForKey("ta")
|
||||
self.value = decoder.decodeOptionalInt32ForKey("v")
|
||||
if let seed = decoder.decodeDataForKey("gos"), let tonAmount = decoder.decodeOptionalInt64ForKey("goa") {
|
||||
self.gameOutcome = GameOutcome(seed: seed, tonAmount: tonAmount)
|
||||
} else {
|
||||
self.gameOutcome = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeString(self.emoji, forKey: "e")
|
||||
if let tonAmount = self.tonAmount {
|
||||
encoder.encodeInt64(tonAmount, forKey: "ta")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ta")
|
||||
}
|
||||
if let value = self.value {
|
||||
encoder.encodeInt32(value, forKey: "v")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "v")
|
||||
}
|
||||
if let gameOutcome = self.gameOutcome {
|
||||
encoder.encodeData(gameOutcome.seed, forKey: "gos")
|
||||
encoder.encodeInt64(gameOutcome.tonAmount, forKey: "goa")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "gos")
|
||||
encoder.encodeNil(forKey: "goa")
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaDice, rhs: TelegramMediaDice) -> Bool {
|
||||
|
|
@ -35,9 +63,15 @@ public final class TelegramMediaDice: Media, Equatable {
|
|||
if self.emoji != other.emoji {
|
||||
return false
|
||||
}
|
||||
if self.tonAmount != other.tonAmount {
|
||||
return false
|
||||
}
|
||||
if self.value != other.value {
|
||||
return false
|
||||
}
|
||||
if self.gameOutcome != other.gameOutcome {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -571,5 +571,26 @@ public extension TelegramEngine.EngineData.Item {
|
|||
return value
|
||||
}
|
||||
}
|
||||
|
||||
public struct EmojiGame: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = EmojiGameInfo
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .preferences(keys: Set([PreferencesKeys.emojiGameInfo()]))
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? PreferencesView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
guard let emojiGameInfo = view.values[PreferencesKeys.emojiGameInfo()]?.get(EmojiGameInfo.self) else {
|
||||
return .unavailable
|
||||
}
|
||||
return emojiGameInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,12 +193,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||
case incomingVideoMessagePlayOnceTip = 62
|
||||
case outgoingVideoMessagePlayOnceTip = 63
|
||||
case savedMessageTagLabelSuggestion = 65
|
||||
case dismissedBusinessBadge = 68
|
||||
case monetizationIntroDismissed = 70
|
||||
case businessBotMessageTooltip = 71
|
||||
case dismissedBusinessIntroBadge = 72
|
||||
case dismissedBusinessLinksBadge = 73
|
||||
case dismissedBusinessChatbotsBadge = 74
|
||||
case captionAboveMediaTooltip = 75
|
||||
case channelSendGiftTooltip = 76
|
||||
case starGiftWearTips = 77
|
||||
|
|
@ -519,10 +515,6 @@ private struct ApplicationSpecificNoticeKeys {
|
|||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.savedMessageTagLabelSuggestion.key)
|
||||
}
|
||||
|
||||
static func dismissedBusinessBadge() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessBadge.key)
|
||||
}
|
||||
|
||||
static func dismissedBirthdayPremiumGiftTip(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: dismissedBirthdayPremiumGiftTipNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
|
|
@ -543,18 +535,6 @@ private struct ApplicationSpecificNoticeKeys {
|
|||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.businessBotMessageTooltip.key)
|
||||
}
|
||||
|
||||
static func dismissedBusinessIntroBadge() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessIntroBadge.key)
|
||||
}
|
||||
|
||||
static func dismissedBusinessLinksBadge() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessLinksBadge.key)
|
||||
}
|
||||
|
||||
static func dismissedBusinessChatbotsBadge() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessChatbotsBadge.key)
|
||||
}
|
||||
|
||||
static func captionAboveMediaTooltip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.captionAboveMediaTooltip.key)
|
||||
}
|
||||
|
|
@ -2154,27 +2134,6 @@ public struct ApplicationSpecificNotice {
|
|||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
|
||||
public static func setDismissedBusinessBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessBadge(), entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public static func dismissedBusinessBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessBadge())
|
||||
|> map { view -> Bool in
|
||||
if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
}
|
||||
|
||||
public static func dismissedBirthdayPremiumGiftTip(accountManager: AccountManager<TelegramAccountManagerTypes>, peerId: PeerId) -> Signal<Int32?, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBirthdayPremiumGiftTip(peerId: peerId))
|
||||
|
|
@ -2286,69 +2245,6 @@ public struct ApplicationSpecificNotice {
|
|||
}
|
||||
}
|
||||
|
||||
public static func setDismissedBusinessLinksBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessLinksBadge(), entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public static func dismissedBusinessLinksBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessLinksBadge())
|
||||
|> map { view -> Bool in
|
||||
if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
}
|
||||
|
||||
public static func setDismissedBusinessIntroBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessIntroBadge(), entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public static func dismissedBusinessIntroBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessIntroBadge())
|
||||
|> map { view -> Bool in
|
||||
if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
}
|
||||
|
||||
public static func setDismissedBusinessChatbotsBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(ApplicationSpecificBoolNotice()) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessChatbotsBadge(), entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public static func dismissedBusinessChatbotsBadge(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessChatbotsBadge())
|
||||
|> map { view -> Bool in
|
||||
if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
}
|
||||
|
||||
public static func getCaptionAboveMediaTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.captionAboveMediaTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public final class PermissionContentNode: ASDisplayNode {
|
|||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.isAccessibilityElement = true
|
||||
|
||||
self.actionButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), height: 52.0, cornerRadius: 9.0, isShimmering: true)
|
||||
self.actionButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 52.0, cornerRadius: 26.0, isShimmering: true)
|
||||
|
||||
self.footerNode = ImmediateTextNode()
|
||||
self.footerNode.textAlignment = .center
|
||||
|
|
|
|||
|
|
@ -36,15 +36,17 @@ public final class PermissionController: ViewController {
|
|||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.splashScreen = splashScreen
|
||||
|
||||
let navigationBarPresentationData: NavigationBarPresentationData
|
||||
if splashScreen {
|
||||
navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))
|
||||
} else {
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
|
||||
}
|
||||
// let navigationBarPresentationData: NavigationBarPresentationData
|
||||
// if splashScreen {
|
||||
// navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, style: .glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))
|
||||
// } else {
|
||||
let navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)
|
||||
// }
|
||||
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
|
||||
self._hasGlassStyle = true
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.updateThemeAndStrings()
|
||||
|
|
@ -86,15 +88,15 @@ public final class PermissionController: ViewController {
|
|||
private func updateThemeAndStrings() {
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
let navigationBarPresentationData: NavigationBarPresentationData
|
||||
if self.splashScreen {
|
||||
navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))
|
||||
} else {
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
|
||||
}
|
||||
// let navigationBarPresentationData: NavigationBarPresentationData
|
||||
// if self.splashScreen {
|
||||
// navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))
|
||||
// } else {
|
||||
let navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)
|
||||
// }
|
||||
|
||||
self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate)
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil)
|
||||
//self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil)
|
||||
if self.navigationItem.rightBarButtonItem != nil {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Permissions_Skip, style: .plain, target: self, action: #selector(PermissionController.nextPressed))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1764,6 +1764,17 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
resultTitleString = strings.Conversation_StoryExpiredMentionTextOutgoing(compactPeerName)
|
||||
}
|
||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||
} else if let dice = media as? TelegramMediaDice, let gameOutcome = dice.gameOutcome {
|
||||
if let value = dice.value, value > 1 {
|
||||
//TODO:localize
|
||||
let value = formatTonAmountText(gameOutcome.tonAmount, dateTimeFormat: dateTimeFormat)
|
||||
let attributedText = NSMutableAttributedString(string: "You won $\(value)", font: titleFont, textColor: primaryTextColor)
|
||||
if let range = attributedText.string.range(of: "$") {
|
||||
attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: true)), range: NSRange(range, in: attributedText.string))
|
||||
attributedText.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedText.string))
|
||||
}
|
||||
attributedString = attributedText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -508,6 +508,8 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode",
|
||||
"//submodules/TelegramUI/Components/Settings/PasskeysScreen",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftDemoScreen",
|
||||
"//submodules/TelegramUI/Components/EmojiGameStakeScreen",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
"//build-system:ios_sim_arm64": [],
|
||||
|
|
|
|||
|
|
@ -12,9 +12,17 @@ swift_library(
|
|||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/Components/ViewControllerComponent",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/Components/ActivityIndicatorComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
import GlassBackgroundComponent
|
||||
import ActivityIndicatorComponent
|
||||
|
||||
private let titleFont = Font.medium(17.0)
|
||||
private let boldTitleFont = Font.semibold(17.0)
|
||||
|
||||
final class AlertActionComponent: Component {
|
||||
typealias EnvironmentType = AlertComponentEnvironment
|
||||
|
||||
static let actionHeight: CGFloat = 48.0
|
||||
|
||||
struct Theme: Equatable {
|
||||
enum Font {
|
||||
case regular
|
||||
case bold
|
||||
}
|
||||
|
||||
let background: UIColor
|
||||
let foreground: UIColor
|
||||
let secondary: UIColor
|
||||
let font: Font
|
||||
}
|
||||
|
||||
let theme: Theme
|
||||
let title: String
|
||||
let isHighlighted: Bool
|
||||
let progress: Signal<Bool, NoError>
|
||||
|
||||
init(
|
||||
theme: Theme,
|
||||
title: String,
|
||||
isHighlighted: Bool,
|
||||
progress: Signal<Bool, NoError>
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.isHighlighted = isHighlighted
|
||||
self.progress = progress
|
||||
}
|
||||
|
||||
static func ==(lhs: AlertActionComponent, rhs: AlertActionComponent) -> Bool {
|
||||
if lhs.theme != rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.isHighlighted != rhs.isHighlighted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = UIView()
|
||||
private let title = ComponentView<Empty>()
|
||||
private var activity: ComponentView<Empty>?
|
||||
|
||||
private var component: AlertActionComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
private var progressDisposable: Disposable?
|
||||
private var hasProgress = false
|
||||
|
||||
private var isUpdating = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundView.clipsToBounds = true
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(component: AlertActionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
if self.component == nil {
|
||||
self.progressDisposable = (component.progress
|
||||
|> deliverOnMainQueue).start(next: { [weak self] hasProgress in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.hasProgress = hasProgress
|
||||
if !self.isUpdating {
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.25))
|
||||
}
|
||||
})
|
||||
}
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let attributedString = NSMutableAttributedString(string: component.title, font: component.theme.font == .bold ? boldTitleFont : titleFont, textColor: .white, paragraphAlignment: .center)
|
||||
if let range = attributedString.string.range(of: "$") {
|
||||
attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string))
|
||||
attributedString.addAttribute(.foregroundColor, value: UIColor.white, range: NSRange(range, in: attributedString.string))
|
||||
attributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedString.string))
|
||||
}
|
||||
|
||||
let titlePadding: CGFloat = 16.0
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(attributedString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1,
|
||||
tintColor: component.theme.foreground
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - titlePadding * 2.0, height: availableSize.height)
|
||||
)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
titleView.bounds = CGRect(origin: .zero, size: titleSize)
|
||||
transition.setAlpha(view: titleView, alpha: self.hasProgress ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if self.hasProgress {
|
||||
let activity: ComponentView<Empty>
|
||||
if let current = self.activity {
|
||||
activity = current
|
||||
} else {
|
||||
activity = ComponentView()
|
||||
self.activity = activity
|
||||
}
|
||||
let activitySize = activity.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ActivityIndicatorComponent(color: component.theme.secondary)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let activityView = activity.view {
|
||||
if activityView.superview == nil {
|
||||
self.addSubview(activityView)
|
||||
transition.animateAlpha(view: activityView, from: 0.0, to: 1.0)
|
||||
}
|
||||
activityView.bounds = CGRect(origin: .zero, size: activitySize)
|
||||
}
|
||||
} else if let activity = self.activity {
|
||||
self.activity = nil
|
||||
if let activityView = activity.view {
|
||||
transition.setAlpha(view: activityView, alpha: 0.0, completion: { _ in
|
||||
activityView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
transition.setBackgroundColor(view: self.backgroundView, color: component.theme.background)
|
||||
transition.setAlpha(view: self.backgroundView, alpha: component.isHighlighted ? 0.35 : 1.0)
|
||||
self.backgroundView.layer.cornerRadius = availableSize.height * 0.5
|
||||
|
||||
return CGSize(width: titleSize.width + titlePadding * 2.0, height: availableSize.height)
|
||||
}
|
||||
|
||||
func applySize(size: CGSize, transition: ComponentTransition) {
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
if let titleView = self.title.view {
|
||||
let titleSize = titleView.bounds.size
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
transition.setFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
|
||||
if let activityView = self.activity?.view {
|
||||
let activitySize = activityView.bounds.size
|
||||
let activityFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - activitySize.width) / 2.0), y: floorToScreenPixels((size.height - activitySize.height) / 2.0)), size: activitySize)
|
||||
transition.setFrame(view: activityView, frame: activityFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,218 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
import Markdown
|
||||
import TextFormat
|
||||
|
||||
private let titleFont = Font.bold(17.0)
|
||||
private let defaultTextFont = Font.regular(15.0)
|
||||
private let defaultBoldTextFont = Font.semibold(15.0)
|
||||
private let smallTextFont = Font.regular(14.0)
|
||||
private let smallBoldTextFont = Font.semibold(14.0)
|
||||
|
||||
public final class AlertTitleComponent: Component {
|
||||
public typealias EnvironmentType = AlertComponentEnvironment
|
||||
|
||||
let title: String
|
||||
|
||||
public init(
|
||||
title: String
|
||||
) {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
public static func ==(lhs: AlertTitleComponent, rhs: AlertTitleComponent) -> Bool {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
private var component: AlertTitleComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
func update(component: AlertTitleComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environment = environment[AlertComponentEnvironment.self]
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: component.title,
|
||||
font: titleFont,
|
||||
textColor: environment.theme.actionSheet.primaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let titleFrame = CGRect(origin: .zero, size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
transition.setFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
return CGSize(width: availableSize.width, height: titleSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class AlertTextComponent: Component {
|
||||
public typealias EnvironmentType = AlertComponentEnvironment
|
||||
|
||||
public enum Content: Equatable {
|
||||
case plain(String)
|
||||
case attributed(NSAttributedString)
|
||||
}
|
||||
|
||||
public enum Color {
|
||||
case primary
|
||||
case secondary
|
||||
case destructive
|
||||
}
|
||||
|
||||
public enum TextSize {
|
||||
case `default`
|
||||
case small
|
||||
}
|
||||
|
||||
let content: Content
|
||||
let color: Color
|
||||
let textSize: TextSize
|
||||
let action: ([NSAttributedString.Key: Any]) -> Void
|
||||
|
||||
public init(
|
||||
content: Content,
|
||||
color: Color = .primary,
|
||||
textSize: TextSize = .default,
|
||||
action: @escaping ([NSAttributedString.Key: Any]) -> Void = { _ in }
|
||||
) {
|
||||
self.content = content
|
||||
self.color = color
|
||||
self.textSize = textSize
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func ==(lhs: AlertTextComponent, rhs: AlertTextComponent) -> Bool {
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
if lhs.textSize != rhs.textSize {
|
||||
return false
|
||||
}
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: AlertTextComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
func update(component: AlertTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environment = environment[AlertComponentEnvironment.self]
|
||||
|
||||
let textColor: UIColor
|
||||
switch component.color {
|
||||
case .primary:
|
||||
textColor = environment.theme.actionSheet.primaryTextColor
|
||||
case .secondary:
|
||||
textColor = environment.theme.actionSheet.secondaryTextColor
|
||||
case .destructive:
|
||||
textColor = environment.theme.actionSheet.destructiveActionTextColor
|
||||
}
|
||||
let linkColor = environment.theme.actionSheet.controlAccentColor
|
||||
|
||||
let textFont: UIFont
|
||||
let boldTextFont: UIFont
|
||||
switch component.textSize {
|
||||
case .default:
|
||||
textFont = defaultTextFont
|
||||
boldTextFont = defaultBoldTextFont
|
||||
case .small:
|
||||
textFont = smallTextFont
|
||||
boldTextFont = smallBoldTextFont
|
||||
}
|
||||
|
||||
var finalText: NSAttributedString
|
||||
switch component.content {
|
||||
case let .plain(text):
|
||||
let markdownAttributes = MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: linkColor),
|
||||
linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
}
|
||||
)
|
||||
finalText = parseMarkdownIntoAttributedString(text, attributes: markdownAttributes)
|
||||
case let .attributed(attributedText):
|
||||
finalText = attributedText
|
||||
}
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(finalText),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1,
|
||||
highlightColor: linkColor.withAlphaComponent(0.2),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { attributes, _ in
|
||||
component.action(attributes)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let textFrame = CGRect(origin: .zero, size: textSize)
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
self.addSubview(textView)
|
||||
}
|
||||
transition.setFrame(view: textView, frame: textFrame)
|
||||
}
|
||||
return CGSize(width: availableSize.width, height: textSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import AsyncDisplayKit
|
||||
|
||||
private let alertWidth: CGFloat = 270.0
|
||||
|
||||
public enum ComponentAlertActionType {
|
||||
case genericAction
|
||||
case defaultAction
|
||||
case destructiveAction
|
||||
case defaultDestructiveAction
|
||||
}
|
||||
|
||||
public struct ComponentAlertAction {
|
||||
public let type: ComponentAlertActionType
|
||||
public let title: String
|
||||
public let action: () -> Void
|
||||
|
||||
public init(type: ComponentAlertActionType, title: String, action: @escaping () -> Void) {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
}
|
||||
|
||||
public final class ComponentAlertContentActionNode: HighlightableButtonNode {
|
||||
private var theme: AlertControllerTheme
|
||||
public var action: ComponentAlertAction {
|
||||
didSet {
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
public var highlightedUpdated: (Bool) -> Void = { _ in }
|
||||
|
||||
public init(theme: AlertControllerTheme, action: ComponentAlertAction) {
|
||||
self.theme = theme
|
||||
self.action = action
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.alpha = 0.0
|
||||
|
||||
super.init()
|
||||
|
||||
self.titleNode.maximumNumberOfLines = 2
|
||||
|
||||
self.highligthedChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(value, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
}
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.pointerInteraction = PointerInteraction(node: self, style: .hover, willEnter: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(true, animated: false)
|
||||
}
|
||||
}, willExit: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(false, animated: false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func performAction() {
|
||||
if self.actionEnabled {
|
||||
self.action.action()
|
||||
}
|
||||
}
|
||||
|
||||
public func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
self.highlightedUpdated(highlighted)
|
||||
if highlighted {
|
||||
if self.backgroundNode.supernode == nil {
|
||||
self.insertSubnode(self.backgroundNode, at: 0)
|
||||
}
|
||||
self.backgroundNode.alpha = 1.0
|
||||
} else {
|
||||
if animated {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
self.backgroundNode.alpha = 0.0
|
||||
})
|
||||
} else {
|
||||
self.backgroundNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
public var actionEnabled: Bool = true {
|
||||
didSet {
|
||||
self.isUserInteractionEnabled = self.actionEnabled
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.theme = theme
|
||||
self.backgroundNode.backgroundColor = theme.highlightedItemColor
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
var font = Font.regular(theme.baseFontSize)
|
||||
var color: UIColor
|
||||
switch self.action.type {
|
||||
case .defaultAction, .genericAction:
|
||||
color = self.actionEnabled ? self.theme.accentColor : self.theme.disabledColor
|
||||
case .destructiveAction, .defaultDestructiveAction:
|
||||
color = self.actionEnabled ? self.theme.destructiveColor : self.theme.disabledColor
|
||||
}
|
||||
switch self.action.type {
|
||||
case .defaultAction, .defaultDestructiveAction:
|
||||
font = Font.semibold(theme.baseFontSize)
|
||||
case .destructiveAction, .genericAction:
|
||||
break
|
||||
}
|
||||
self.setAttributedTitle(NSAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center), for: [])
|
||||
self.accessibilityLabel = self.action.title
|
||||
self.accessibilityTraits = [.button]
|
||||
}
|
||||
|
||||
@objc func pressed() {
|
||||
self.action.action()
|
||||
}
|
||||
|
||||
override public func layout() {
|
||||
super.layout()
|
||||
|
||||
self.backgroundNode.frame = self.bounds
|
||||
}
|
||||
}
|
||||
|
||||
public enum ComponentAlertContentActionLayout {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
public final class ComponentAlertContentNode: AlertContentNode {
|
||||
private var theme: AlertControllerTheme
|
||||
private let actionLayout: ComponentAlertContentActionLayout
|
||||
|
||||
private let content: AnyComponent<Empty>
|
||||
private let contentView = ComponentView<Empty>()
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [ComponentAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private let _dismissOnOutsideTap: Bool
|
||||
override public var dismissOnOutsideTap: Bool {
|
||||
return self._dismissOnOutsideTap
|
||||
}
|
||||
|
||||
private var highlightedItemIndex: Int? = nil
|
||||
|
||||
public init(theme: AlertControllerTheme, content: AnyComponent<Empty>, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout, dismissOnOutsideTap: Bool) {
|
||||
self.theme = theme
|
||||
self.actionLayout = actionLayout
|
||||
self._dismissOnOutsideTap = dismissOnOutsideTap
|
||||
self.content = content
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isUserInteractionEnabled = false
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
|
||||
self.actionNodes = actions.map { action -> ComponentAlertContentActionNode in
|
||||
return ComponentAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
var i = 0
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
|
||||
let index = i
|
||||
actionNode.highlightedUpdated = { [weak self] highlighted in
|
||||
if highlighted {
|
||||
self?.highlightedItemIndex = index
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
}
|
||||
|
||||
func setHighlightedItemIndex(_ index: Int?, update: Bool = false) {
|
||||
self.highlightedItemIndex = index
|
||||
|
||||
if update {
|
||||
var i = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if i == index {
|
||||
actionNode.setHighlighted(true, animated: false)
|
||||
} else {
|
||||
actionNode.setHighlighted(false, animated: false)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func decreaseHighlightedIndex() {
|
||||
let currentHighlightedIndex = self.highlightedItemIndex ?? 0
|
||||
|
||||
self.setHighlightedItemIndex(max(0, currentHighlightedIndex - 1), update: true)
|
||||
}
|
||||
|
||||
override public func increaseHighlightedIndex() {
|
||||
let currentHighlightedIndex = self.highlightedItemIndex ?? -1
|
||||
|
||||
self.setHighlightedItemIndex(min(self.actionNodes.count - 1, currentHighlightedIndex + 1), update: true)
|
||||
}
|
||||
|
||||
override public func performHighlightedAction() {
|
||||
guard let highlightedItemIndex = self.highlightedItemIndex else {
|
||||
return
|
||||
}
|
||||
|
||||
var i = 0
|
||||
for itemNode in self.actionNodes {
|
||||
if i == highlightedItemIndex {
|
||||
itemNode.performAction()
|
||||
return
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.theme = theme
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
self.validLayout = size
|
||||
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
var size = size
|
||||
size.width = min(size.width, alertWidth)
|
||||
|
||||
let contentSize = self.contentView.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: self.content,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - insets.left - insets.right, height: 10000.0)
|
||||
)
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
var effectiveActionLayout = self.actionLayout
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||
effectiveActionLayout = .vertical
|
||||
}
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||
case .vertical:
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
}
|
||||
|
||||
let resultSize: CGSize
|
||||
|
||||
var actionsHeight: CGFloat = 0.0
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionsHeight = actionButtonHeight
|
||||
case .vertical:
|
||||
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
}
|
||||
|
||||
let contentWidth = alertWidth - insets.left - insets.right
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - contentSize.width) / 2.0), y: insets.top), size: contentSize)
|
||||
if let contentComponentView = self.contentView.view {
|
||||
if contentComponentView.superview == nil {
|
||||
self.view.insertSubview(contentComponentView, belowSubview: self.actionNodesSeparator.view)
|
||||
transition.updateFrame(view: contentComponentView, frame: contentFrame)
|
||||
}
|
||||
}
|
||||
|
||||
resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: contentSize.height + actionsHeight + insets.top + insets.bottom)
|
||||
|
||||
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||
case .vertical:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
if nodeIndex == self.actionNodes.count - 1 {
|
||||
currentActionWidth = resultSize.width - actionOffset
|
||||
} else {
|
||||
currentActionWidth = actionWidth
|
||||
}
|
||||
case .vertical:
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += currentActionWidth
|
||||
case .vertical:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
|
||||
public func componentAlertController(theme: AlertControllerTheme, content: AnyComponent<Empty>, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout = .horizontal, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = AlertController(theme: theme, contentNode: ComponentAlertContentNode(theme: theme, content: content, actions: actions.map { action in
|
||||
return ComponentAlertAction(type: action.type, title: action.title, action: {
|
||||
dismissImpl?()
|
||||
action.action()
|
||||
})
|
||||
}, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap))
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
@ -95,6 +95,8 @@ swift_library(
|
|||
"//submodules/TelegramCallsUI",
|
||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
|
||||
"//submodules/TelegramUI/Components/LiquidLens",
|
||||
"//submodules/TelegramUI/Components/TabSelectionRecognizer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -2097,7 +2097,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
if !isSticker, case .none = component.cameraState.recording, component.cameraState.isStreaming == .none && !state.isTransitioning && hasAllRequiredAccess && component.cameraState.collageProgress < 1.0 - .ulpOfOne {
|
||||
let availableModeControlSize: CGSize
|
||||
if isTablet {
|
||||
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
|
||||
availableModeControlSize = CGSize(width: floor(panelWidth), height: 120.0)
|
||||
} else {
|
||||
availableModeControlSize = availableSize
|
||||
}
|
||||
|
|
@ -2131,7 +2131,6 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||
modeControlPosition = CGPoint(x: availableSize.width / 2.0, y: availableSize.height - environment.safeInsets.bottom + modeControl.size.height / 2.0 + controlsBottomInset + 16.0)
|
||||
}
|
||||
context.add(modeControl
|
||||
.clipsToBounds(true)
|
||||
.position(modeControlPosition)
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import ComponentFlow
|
|||
import MultilineTextComponent
|
||||
import TelegramPresentationData
|
||||
import GlassBackgroundComponent
|
||||
import LiquidLens
|
||||
import TabSelectionRecognizer
|
||||
|
||||
extension CameraMode {
|
||||
func title(strings: PresentationStrings) -> String {
|
||||
|
|
@ -70,28 +72,17 @@ final class ModeComponent: Component {
|
|||
|
||||
final class View: UIView, ComponentTaggedView {
|
||||
private var component: ModeComponent?
|
||||
private var state: EmptyComponentState?
|
||||
|
||||
final class ItemView: HighlightTrackingButton {
|
||||
var pressed: () -> Void = {
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.isExclusiveTouch = true
|
||||
|
||||
self.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
func update(isTablet: Bool, value: String, selected: Bool, tintColor: UIColor) -> CGSize {
|
||||
let accentColor: UIColor
|
||||
let normalColor: UIColor
|
||||
|
|
@ -113,9 +104,15 @@ final class ModeComponent: Component {
|
|||
}
|
||||
|
||||
private var backgroundView = UIView()
|
||||
private var glassContainerView = GlassBackgroundContainerView()
|
||||
private var selectionView = GlassBackgroundView()
|
||||
private var itemViews: [Int32: ItemView] = [:]
|
||||
private var backgroundContainer = GlassBackgroundContainerView()
|
||||
|
||||
private let liquidLensView: LiquidLensView
|
||||
|
||||
private var itemViews: [AnyHashable: ItemView] = [:]
|
||||
private var selectedItemViews: [AnyHashable: ItemView] = [:]
|
||||
|
||||
private var tabSelectionRecognizer: TabSelectionRecognizer?
|
||||
private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)?
|
||||
|
||||
public func matches(tag: Any) -> Bool {
|
||||
if let component = self.component, let componentTag = component.tag {
|
||||
|
|
@ -128,16 +125,23 @@ final class ModeComponent: Component {
|
|||
}
|
||||
|
||||
init() {
|
||||
self.liquidLensView = LiquidLensView(useBackgroundContainer: false)
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.backgroundView.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.11)
|
||||
self.backgroundView.layer.cornerRadius = 24.0
|
||||
|
||||
|
||||
self.layer.allowsGroupOpacity = true
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.addSubview(self.glassContainerView)
|
||||
self.glassContainerView.contentView.addSubview(self.selectionView)
|
||||
self.backgroundView.addSubview(self.backgroundContainer)
|
||||
|
||||
self.backgroundContainer.contentView.addSubview(self.liquidLensView)
|
||||
|
||||
let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:)))
|
||||
self.tabSelectionRecognizer = tabSelectionRecognizer
|
||||
self.liquidLensView.addGestureRecognizer(tabSelectionRecognizer)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
|
@ -162,14 +166,69 @@ final class ModeComponent: Component {
|
|||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return self.backgroundView.frame.contains(point)
|
||||
}
|
||||
|
||||
func update(component: ModeComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let isTablet = component.isTablet
|
||||
let updatedMode = component.updatedMode
|
||||
private func item(at point: CGPoint) -> AnyHashable? {
|
||||
var closestItem: (AnyHashable, CGFloat)?
|
||||
for (id, itemView) in self.itemViews {
|
||||
if itemView.frame.contains(point) {
|
||||
return id
|
||||
} else {
|
||||
let distance = abs(point.x - itemView.center.x)
|
||||
if let closestItemValue = closestItem {
|
||||
if closestItemValue.1 > distance {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
} else {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
}
|
||||
}
|
||||
return closestItem?.0
|
||||
}
|
||||
|
||||
@objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
let location = recognizer.location(in: self.liquidLensView.contentView)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] {
|
||||
let startX = itemView.frame.minX - 4.0
|
||||
self.selectionGestureState = (startX, startX, itemId)
|
||||
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
|
||||
}
|
||||
case .changed:
|
||||
if var selectionGestureState = self.selectionGestureState {
|
||||
selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x
|
||||
if let itemId = self.item(at: location) {
|
||||
selectionGestureState.itemId = itemId
|
||||
}
|
||||
self.selectionGestureState = selectionGestureState
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
self.selectionGestureState = nil
|
||||
if case .ended = recognizer.state {
|
||||
guard let item = component.availableModes.first(where: { AnyHashable($0.rawValue) == selectionGestureState.itemId }) else {
|
||||
return
|
||||
}
|
||||
component.updatedMode(item)
|
||||
}
|
||||
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: ModeComponent, availableSize: CGSize, state: EmptyComponentState, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let isTablet = component.isTablet
|
||||
|
||||
self.glassContainerView.isHidden = component.isTablet
|
||||
self.backgroundView.backgroundColor = component.isTablet ? .clear : UIColor(rgb: 0xffffff, alpha: 0.11)
|
||||
|
||||
let inset: CGFloat = 23.0
|
||||
|
|
@ -180,25 +239,34 @@ final class ModeComponent: Component {
|
|||
var selectedCenter = itemFrame.minX
|
||||
var selectedFrame = itemFrame
|
||||
|
||||
var validKeys: Set<Int32> = Set()
|
||||
var validKeys: Set<AnyHashable> = Set()
|
||||
for mode in component.availableModes.reversed() {
|
||||
let id = mode.rawValue
|
||||
validKeys.insert(id)
|
||||
|
||||
let itemView: ItemView
|
||||
if let current = self.itemViews[id] {
|
||||
let selectedItemView: ItemView
|
||||
if let current = self.itemViews[id], let currentSelected = self.selectedItemViews[id] {
|
||||
itemView = current
|
||||
selectedItemView = currentSelected
|
||||
} else {
|
||||
itemView = ItemView()
|
||||
self.backgroundView.addSubview(itemView)
|
||||
itemView.isUserInteractionEnabled = false
|
||||
self.itemViews[id] = itemView
|
||||
}
|
||||
itemView.pressed = {
|
||||
updatedMode(mode)
|
||||
self.liquidLensView.contentView.addSubview(itemView)
|
||||
|
||||
selectedItemView = ItemView()
|
||||
selectedItemView.isUserInteractionEnabled = false
|
||||
self.selectedItemViews[id] = selectedItemView
|
||||
self.liquidLensView.selectedContentView.addSubview(selectedItemView)
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: mode == component.currentMode, tintColor: component.tintColor)
|
||||
let itemSize = itemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: false, tintColor: component.tintColor)
|
||||
itemView.bounds = CGRect(origin: .zero, size: itemSize)
|
||||
|
||||
let _ = selectedItemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: true, tintColor: component.tintColor)
|
||||
selectedItemView.bounds = CGRect(origin: .zero, size: itemSize)
|
||||
|
||||
itemFrame = CGRect(origin: itemFrame.origin, size: itemSize)
|
||||
|
||||
if mode == component.currentMode {
|
||||
|
|
@ -207,12 +275,14 @@ final class ModeComponent: Component {
|
|||
|
||||
if isTablet {
|
||||
itemView.center = CGPoint(x: availableSize.width / 2.0, y: itemFrame.midY)
|
||||
selectedItemView.center = itemView.center
|
||||
if mode == component.currentMode {
|
||||
selectedCenter = itemFrame.midY
|
||||
}
|
||||
itemFrame = itemFrame.offsetBy(dx: 0.0, dy: tabletButtonSize.height + spacing)
|
||||
} else {
|
||||
itemView.center = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
|
||||
selectedItemView.center = itemView.center
|
||||
if mode == component.currentMode {
|
||||
selectedCenter = itemFrame.midX
|
||||
}
|
||||
|
|
@ -221,7 +291,7 @@ final class ModeComponent: Component {
|
|||
i += 1
|
||||
}
|
||||
|
||||
var removeKeys: [Int32] = []
|
||||
var removeKeys: [AnyHashable] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validKeys.contains(id) {
|
||||
removeKeys.append(id)
|
||||
|
|
@ -248,12 +318,19 @@ final class ModeComponent: Component {
|
|||
}
|
||||
|
||||
let containerFrame = CGRect(origin: .zero, size: self.backgroundView.frame.size)
|
||||
transition.setFrame(view: self.glassContainerView, frame: containerFrame)
|
||||
transition.setFrame(view: self.backgroundContainer, frame: containerFrame)
|
||||
|
||||
let selectionFrame = selectedFrame.insetBy(dx: -20.0, dy: 3.0)
|
||||
self.glassContainerView.update(size: containerFrame.size, isDark: true, transition: .immediate)
|
||||
self.selectionView.update(size: selectionFrame.size, cornerRadius: selectionFrame.height * 0.5, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0xffffff, alpha: 0.16)), transition: transition)
|
||||
transition.setFrame(view: self.selectionView, frame: selectionFrame)
|
||||
let selectionFrame = selectedFrame.insetBy(dx: -23.0, dy: 3.0)
|
||||
let lensSelection: (x: CGFloat, width: CGFloat)
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
lensSelection = (selectionGestureState.currentX, selectionFrame.width)
|
||||
} else {
|
||||
lensSelection = (selectionFrame.minX, selectionFrame.width)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: containerFrame.size))
|
||||
self.liquidLensView.update(size: containerFrame.size, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: selectionFrame.height), isDark: true, isLifted: self.selectionGestureState != nil, isCollapsed: false, transition: transition)
|
||||
self.backgroundContainer.update(size: containerFrame.size, isDark: true, transition: .immediate)
|
||||
|
||||
return size
|
||||
}
|
||||
|
|
@ -264,7 +341,7 @@ final class ModeComponent: Component {
|
|||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
return view.update(component: self, availableSize: availableSize, state: state, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
if node.iconNode == nil {
|
||||
let iconNode = ASImageNode()
|
||||
iconNode.contentMode = .center
|
||||
iconNode.isUserInteractionEnabled = false
|
||||
node.iconNode = iconNode
|
||||
node.addSubnode(iconNode)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ swift_library(
|
|||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/LocalMediaResources",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/ChatPresentationInterfaceState",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import ManagedDiceAnimationNode
|
|||
import MessageHaptics
|
||||
import ChatMessageTransitionNode
|
||||
import ChatMessageSuggestedPostInfoNode
|
||||
import TelegramStringFormatting
|
||||
|
||||
private let nameFont = Font.medium(14.0)
|
||||
private let inlineBotPrefixFont = Font.regular(14.0)
|
||||
|
|
@ -99,6 +100,11 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
||||
private var swipeToReplyFeedback: HapticFeedback?
|
||||
|
||||
private let labelNode: TextNodeWithEntities
|
||||
private var labelBackgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let labelBackgroundMaskNode: ASImageNode
|
||||
private var cachedMaskLabelBackgroundImage: (CGPoint, UIImage, [CGRect])?
|
||||
|
||||
private var selectionNode: ChatMessageSelectionNode?
|
||||
private var deliveryFailedNode: ChatMessageDeliveryFailedNode?
|
||||
private var shareButtonNode: ChatMessageShareButton?
|
||||
|
|
@ -161,6 +167,12 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
self.textNode.textNode.displaysAsynchronously = false
|
||||
self.textNode.textNode.isUserInteractionEnabled = false
|
||||
|
||||
self.labelNode = TextNodeWithEntities()
|
||||
self.labelNode.textNode.isUserInteractionEnabled = false
|
||||
self.labelNode.textNode.displaysAsynchronously = false
|
||||
|
||||
self.labelBackgroundMaskNode = ASImageNode()
|
||||
|
||||
super.init(rotated: rotated)
|
||||
|
||||
self.containerNode.shouldBegin = { [weak self] location in
|
||||
|
|
@ -469,9 +481,22 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
} else if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode {
|
||||
if let value = telegramDice.value {
|
||||
let wasRolling = diceNode.isRolling
|
||||
diceNode.setState(value == 0 ? .rolling : .value(value, true))
|
||||
|
||||
if wasRolling && !diceNode.isRolling {
|
||||
Queue.mainQueue().after(3.0, {
|
||||
self.labelNode.textNode.alpha = 1.0
|
||||
self.labelBackgroundNode?.alpha = 1.0
|
||||
self.labelNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
self.labelBackgroundNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
diceNode.setState(.rolling)
|
||||
|
||||
self.labelNode.textNode.alpha = 0.0
|
||||
self.labelBackgroundNode?.alpha = 0.0
|
||||
}
|
||||
} else if self.telegramFile == nil && self.telegramDice == nil {
|
||||
let (emoji, fitz) = item.message.text.basicEmoji
|
||||
|
|
@ -816,6 +841,9 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode)
|
||||
let reactionButtonsLayout = ChatMessageReactionButtonsNode.asyncLayout(self.reactionButtonsNode)
|
||||
|
||||
let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode)
|
||||
let cachedMaskLabelBackgroundImage = self.cachedMaskLabelBackgroundImage
|
||||
|
||||
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
|
||||
|
||||
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
|
||||
|
|
@ -850,6 +878,44 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
let avatarInset: CGFloat
|
||||
var hasAvatar = false
|
||||
|
||||
let labelAttributedText = universalServiceMessageString(presentationData: (item.presentationData.theme.theme, item.presentationData.theme.wallpaper), strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: EngineMessage(item.message), accountPeerId: item.context.account.peerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true)
|
||||
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var labelRects = labelLayout.linesRects()
|
||||
if labelRects.count > 1 {
|
||||
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
||||
for i in 0 ..< sortedIndices.count {
|
||||
let index = sortedIndices[i]
|
||||
for j in -1 ... 1 {
|
||||
if j != 0 && index + j >= 0 && index + j < sortedIndices.count {
|
||||
if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 {
|
||||
labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width)
|
||||
labelRects[index].size.width = labelRects[index + j].size.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0 ..< labelRects.count {
|
||||
labelRects[i] = labelRects[i].insetBy(dx: -7.0, dy: floor((labelRects[i].height - 22.0) / 2.0))
|
||||
labelRects[i].size.height = 22.0
|
||||
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
|
||||
}
|
||||
|
||||
let backgroundMaskImage: (CGPoint, UIImage)?
|
||||
var backgroundMaskUpdated = false
|
||||
if labelLayout.size.height > 0.0 {
|
||||
if let (currentOffset, currentImage, currentRects) = cachedMaskLabelBackgroundImage, currentRects == labelRects {
|
||||
backgroundMaskImage = (currentOffset, currentImage)
|
||||
} else {
|
||||
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 11.0, outerRadius: 11.0, rects: labelRects, useModernPathCalculation: false)
|
||||
backgroundMaskUpdated = true
|
||||
}
|
||||
} else {
|
||||
backgroundMaskImage = nil
|
||||
}
|
||||
|
||||
switch item.chatLocation {
|
||||
case let .peer(peerId):
|
||||
if peerId != item.context.account.peerId {
|
||||
|
|
@ -1493,6 +1559,51 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
let _ = labelApply(TextNodeWithEntities.Arguments(
|
||||
context: item.context,
|
||||
cache: item.controllerInteraction.presentationContext.animationCache,
|
||||
renderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||
placeholderColor: item.presentationData.theme.theme.chat.message.freeform.withWallpaper.reactionInactiveBackground,
|
||||
attemptSynchronous: synchronousLoads
|
||||
))
|
||||
let labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - labelLayout.size.width) / 2.0), y: 2.0), size: labelLayout.size)
|
||||
strongSelf.labelNode.textNode.frame = labelFrame
|
||||
if strongSelf.labelNode.textNode.supernode == nil, labelLayout.size.height > 0.0 {
|
||||
strongSelf.addSubnode(strongSelf.labelNode.textNode)
|
||||
}
|
||||
|
||||
let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
|
||||
if let (offset, image) = backgroundMaskImage {
|
||||
if strongSelf.labelBackgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
backgroundNode.alpha = strongSelf.labelNode.textNode.alpha
|
||||
strongSelf.labelBackgroundNode = backgroundNode
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
if backgroundMaskUpdated, let backgroundNode = strongSelf.labelBackgroundNode {
|
||||
if labelRects.count == 1 {
|
||||
backgroundNode.clipsToBounds = true
|
||||
backgroundNode.cornerRadius = labelRects[0].height / 2.0
|
||||
backgroundNode.view.mask = nil
|
||||
} else {
|
||||
backgroundNode.clipsToBounds = false
|
||||
backgroundNode.cornerRadius = 0.0
|
||||
backgroundNode.view.mask = strongSelf.labelBackgroundMaskNode.view
|
||||
}
|
||||
}
|
||||
|
||||
if let backgroundNode = strongSelf.labelBackgroundNode {
|
||||
backgroundNode.layer.frame = CGRect(origin: CGPoint(x: baseBackgroundFrame.minX + offset.x, y: baseBackgroundFrame.minY + offset.y), size: image.size)
|
||||
}
|
||||
strongSelf.labelBackgroundMaskNode.image = image
|
||||
strongSelf.labelBackgroundMaskNode.frame = CGRect(origin: CGPoint(), size: image.size)
|
||||
|
||||
strongSelf.cachedMaskLabelBackgroundImage = (offset, image, labelRects)
|
||||
}
|
||||
|
||||
var updatedImageFrame: CGRect
|
||||
var contextContentFrame: CGRect
|
||||
if let _ = emojiString {
|
||||
|
|
|
|||
|
|
@ -455,7 +455,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
switch action.action {
|
||||
case let .giftPremium(_, _, daysValue, _, _, giftText, giftEntities):
|
||||
months = max(3, Int32(round(Float(daysValue) / 30.0)))
|
||||
if months == 12 {
|
||||
if daysValue < 30 {
|
||||
title = item.presentationData.strings.Notification_PremiumGift_DaysTitle(daysValue)
|
||||
} else if months == 12 {
|
||||
title = item.presentationData.strings.Notification_PremiumGift_YearsTitle(1)
|
||||
} else {
|
||||
title = item.presentationData.strings.Notification_PremiumGift_MonthsTitle(months)
|
||||
|
|
@ -513,7 +515,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
title = item.presentationData.strings.Notification_StarsGiveaway_Title
|
||||
let starsString = item.presentationData.strings.Notification_StarsGiveaway_Subtitle_Stars(Int32(clamping: count)).replacingOccurrences(of: " ", with: "\u{00A0}")
|
||||
text = item.presentationData.strings.Notification_StarsGiveaway_Subtitle(peerName, starsString).string
|
||||
case let .giftCode(_, fromGiveaway, unclaimed, channelId, monthsValue, _, _, _, _, giftText, giftEntities):
|
||||
case let .giftCode(_, fromGiveaway, unclaimed, channelId, daysValue, _, _, _, _, giftText, giftEntities):
|
||||
let monthsValue = max(3, Int32(round(Float(daysValue) / 30.0)))
|
||||
if channelId == nil {
|
||||
months = monthsValue
|
||||
if months == 12 {
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ public struct InteractiveEmojiConfiguration {
|
|||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> InteractiveEmojiConfiguration {
|
||||
if let data = appConfiguration.data, let emojis = data["emojies_send_dice"] as? [String] {
|
||||
if let data = appConfiguration.data, var emojis = data["emojies_send_dice"] as? [String] {
|
||||
var successParameters: [String: InteractiveEmojiSuccessParameters] = [:]
|
||||
if let success = data["emojies_send_dice_success"] as? [String: [String: Double]] {
|
||||
for (key, dict) in success {
|
||||
|
|
@ -109,6 +109,11 @@ public struct InteractiveEmojiConfiguration {
|
|||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
if !emojis.contains("🎲") {
|
||||
emojis.append("🎲")
|
||||
}
|
||||
#endif
|
||||
return InteractiveEmojiConfiguration(emojis: emojis, successParameters: successParameters)
|
||||
} else {
|
||||
return .defaultValue
|
||||
|
|
@ -126,6 +131,10 @@ public final class ManagedDiceAnimationNode: ManagedAnimationNode {
|
|||
private let configuration = Promise<InteractiveEmojiConfiguration?>()
|
||||
private let emojis = Promise<[TelegramMediaFile]>()
|
||||
|
||||
public var isRolling: Bool {
|
||||
return self.diceState == .rolling
|
||||
}
|
||||
|
||||
public var success: (() -> Void)?
|
||||
|
||||
public init(context: AccountContext, emoji: String) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import LegacyMessageInputPanelInputView
|
|||
import AttachmentTextInputPanelNode
|
||||
import GlassBackgroundComponent
|
||||
|
||||
private let keyboardCornerRadius: CGFloat = 30.0
|
||||
|
||||
public final class EmptyInputView: UIView, UIInputViewAudioFeedback {
|
||||
public var enableInputClicksWhenVisible: Bool {
|
||||
return true
|
||||
|
|
@ -490,7 +492,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
|
||||
self.clippingView = UIView()
|
||||
self.clippingView.clipsToBounds = true
|
||||
self.clippingView.layer.cornerRadius = 20.0
|
||||
self.clippingView.layer.cornerRadius = keyboardCornerRadius
|
||||
|
||||
self.entityKeyboardView = ComponentHostView<Empty>()
|
||||
|
||||
|
|
@ -1904,8 +1906,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
theme: interfaceState.theme,
|
||||
strings: interfaceState.strings,
|
||||
isContentInFocus: isVisible,
|
||||
containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: keyboardBottomInset, right: rightInset),
|
||||
topPanelInsets: UIEdgeInsets(),
|
||||
containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -42.0 : 0.0, left: leftInset, bottom: keyboardBottomInset, right: rightInset),
|
||||
topPanelInsets: UIEdgeInsets(top: 0.0, left: 5.0, bottom: 0.0, right: 5.0),
|
||||
emojiContent: emojiContent,
|
||||
stickerContent: stickerContent,
|
||||
maskContent: nil,
|
||||
|
|
@ -2030,16 +2032,16 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
backgroundFrame.size.height += 32.0
|
||||
|
||||
if backgroundChromeView.image == nil {
|
||||
backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 20.0 * 2.0, height: 20.0 * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear)
|
||||
backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: keyboardCornerRadius * 2.0, height: keyboardCornerRadius * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear)
|
||||
}
|
||||
if backgroundTintView.image == nil {
|
||||
backgroundTintView.image = generateStretchableFilledCircleImage(diameter: 20.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
backgroundTintView.image = generateStretchableFilledCircleImage(diameter: keyboardCornerRadius * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
backgroundTintView.tintColor = interfaceState.theme.chat.inputMediaPanel.backgroundColor
|
||||
|
||||
transition.updateFrame(view: backgroundView, frame: backgroundFrame)
|
||||
backgroundView.updateColor(color: .clear, forceKeepBlur: true, transition: .immediate)
|
||||
backgroundView.update(size: backgroundFrame.size, cornerRadius: 20.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition)
|
||||
backgroundView.update(size: backgroundFrame.size, cornerRadius: keyboardCornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition)
|
||||
|
||||
transition.updateFrame(view: backgroundChromeView, frame: backgroundFrame.insetBy(dx: -1.0, dy: 0.0))
|
||||
|
||||
|
|
|
|||
55
submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD
Normal file
55
submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "EmojiGameStakeScreen",
|
||||
module_name = "EmojiGameStakeScreen",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ViewControllerComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/TelegramUI/Components/ScrollComponent",
|
||||
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/PasswordSetupUI",
|
||||
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
||||
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
|
||||
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
||||
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/Components/MultilineTextWithEntitiesComponent",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -53,6 +53,9 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/BatchVideoRendering",
|
||||
"//submodules/TelegramUI/Components/GifVideoLayer",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/LiquidLens",
|
||||
"//submodules/TelegramUI/Components/TabSelectionRecognizer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -419,13 +419,13 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
|||
|
||||
context.setFillColor(color.withMultipliedAlpha(0.2).cgColor)
|
||||
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 21.0).cgPath)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 30.0).cgPath)
|
||||
context.fillPath()
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
let plusSize = CGSize(width: 3.5, height: 28.0)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
let plusSize = CGSize(width: 4.0, height: 27.0)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
|
|
@ -437,7 +437,7 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
|||
let components = string.components(separatedBy: "\n")
|
||||
for component in components {
|
||||
context.saveGState()
|
||||
let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(17.0), NSAttributedString.Key.foregroundColor: color])
|
||||
let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(16.0), NSAttributedString.Key.foregroundColor: color])
|
||||
|
||||
let line = CTLineCreateWithAttributedString(attributedString)
|
||||
let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds)
|
||||
|
|
|
|||
|
|
@ -4026,7 +4026,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||
private func updateTopPanelSeparator(transition: ComponentTransition) {
|
||||
if let topPanelSeparator = self.topPanelSeparator {
|
||||
var offset = self.scrollView.contentOffset.y
|
||||
let startOffset: CGFloat = 40.0 - self.topPanelHeight
|
||||
let startOffset: CGFloat = 46.0 - self.topPanelHeight
|
||||
let endOffset: CGFloat = startOffset + 10.0
|
||||
|
||||
offset = min(max(offset, startOffset), endOffset)
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let _ = component.maskContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
|
||||
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "masks", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Chat/Input/Media/EntityInputSettingsIcon",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
maskContent.inputInteractionHolder.inputInteraction?.openStickerSettings?()
|
||||
}
|
||||
|
|
@ -417,7 +417,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Media Editor/AddImage",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
addImage()
|
||||
}
|
||||
|
|
@ -537,7 +537,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let _ = component.stickerContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
|
||||
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Chat/Input/Media/EntityInputSettingsIcon",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
stickerContent.inputInteractionHolder.inputInteraction?.openStickerSettings?()
|
||||
}
|
||||
|
|
@ -546,7 +546,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Media Editor/AddImage",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
addImage()
|
||||
}
|
||||
|
|
@ -649,7 +649,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let _ = deleteBackwards {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Chat/Input/Media/EntityInputGlobeIcon",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: { [weak self] in
|
||||
guard let strongSelf = self, let component = strongSelf.component else {
|
||||
return
|
||||
|
|
@ -660,7 +660,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
} else if let addImage = component.emojiContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Media Editor/AddImage",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
addImage()
|
||||
}
|
||||
|
|
@ -671,7 +671,7 @@ public final class EntityKeyboardComponent: Component {
|
|||
if let _ = deleteBackwards {
|
||||
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton(
|
||||
icon: "Chat/Input/Media/EntityInputClearIcon",
|
||||
color: component.theme.chat.inputPanel.inputControlColor,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
deleteBackwards?()
|
||||
AudioServicesPlaySystemSound(1155)
|
||||
|
|
@ -721,7 +721,8 @@ public final class EntityKeyboardComponent: Component {
|
|||
topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent(
|
||||
theme: component.theme,
|
||||
overflowHeight: component.hiddenInputHeight,
|
||||
topInset: component.externalTopPanelContainer == nil ? 6.0 : 0.0,
|
||||
topInset: component.externalTopPanelContainer == nil ? 8.0 : 0.0,
|
||||
height: component.externalTopPanelContainer == nil ? 40.0 : 34.0,
|
||||
displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground
|
||||
)),
|
||||
externalTopPanelContainer: component.externalTopPanelContainer,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Foundation
|
|||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import PagerComponent
|
||||
import ComponentDisplayAdapters
|
||||
import BundleIconComponent
|
||||
|
|
@ -10,18 +11,18 @@ import AppBundle
|
|||
|
||||
final class EntityKeyboardBottomPanelButton: Component {
|
||||
let icon: String
|
||||
let color: UIColor
|
||||
let theme: PresentationTheme
|
||||
let action: () -> Void
|
||||
let holdAction: (() -> Void)?
|
||||
|
||||
init(
|
||||
icon: String,
|
||||
color: UIColor,
|
||||
theme: PresentationTheme,
|
||||
action: @escaping () -> Void,
|
||||
holdAction: (() -> Void)? = nil
|
||||
) {
|
||||
self.icon = icon
|
||||
self.color = color
|
||||
self.theme = theme
|
||||
self.action = action
|
||||
self.holdAction = holdAction
|
||||
}
|
||||
|
|
@ -30,7 +31,7 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
if lhs.icon != rhs.icon {
|
||||
return false
|
||||
}
|
||||
if lhs.color != rhs.color {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if (lhs.holdAction == nil) != (rhs.holdAction == nil) {
|
||||
|
|
@ -39,7 +40,9 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
return true
|
||||
}
|
||||
|
||||
final class View: HighlightTrackingButton {
|
||||
final class View: UIView {
|
||||
private let backgroundView: GlassBackgroundView
|
||||
let buttonView: HighlightTrackingButton
|
||||
let iconView: GlassBackgroundView.ContentImageView
|
||||
let tintMaskContainer: UIView
|
||||
|
||||
|
|
@ -48,15 +51,19 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
|
||||
var component: EntityKeyboardBottomPanelButton?
|
||||
|
||||
private var currentIsHighlighted: Bool = false {
|
||||
didSet {
|
||||
if self.currentIsHighlighted != oldValue {
|
||||
self.updateAlpha(transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
// private var currentIsHighlighted: Bool = false {
|
||||
// didSet {
|
||||
// if self.currentIsHighlighted != oldValue {
|
||||
// self.updateAlpha(transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
|
||||
self.buttonView = HighlightTrackingButton()
|
||||
|
||||
self.iconView = GlassBackgroundView.ContentImageView()
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
|
||||
|
|
@ -65,9 +72,11 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.iconView)
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.contentView.addSubview(self.iconView)
|
||||
self.backgroundView.contentView.addSubview(self.buttonView)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.buttonView.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -86,65 +95,65 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
}
|
||||
}
|
||||
|
||||
override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
self.currentIsHighlighted = true
|
||||
|
||||
self.holdActionTriggerred = false
|
||||
|
||||
if self.component?.holdAction != nil {
|
||||
self.holdActionTriggerred = true
|
||||
self.component?.action()
|
||||
|
||||
self.holdActionTimer?.invalidate()
|
||||
let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.holdActionTimer?.invalidate()
|
||||
strongSelf.component?.holdAction?()
|
||||
strongSelf.beginExecuteHoldActionTimer()
|
||||
})
|
||||
self.holdActionTimer = holdActionTimer
|
||||
RunLoop.main.add(holdActionTimer, forMode: .common)
|
||||
}
|
||||
|
||||
return super.beginTracking(touch, with: event)
|
||||
}
|
||||
// override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
// self.currentIsHighlighted = true
|
||||
//
|
||||
// self.holdActionTriggerred = false
|
||||
//
|
||||
// if self.component?.holdAction != nil {
|
||||
// self.holdActionTriggerred = true
|
||||
// self.component?.action()
|
||||
//
|
||||
// self.holdActionTimer?.invalidate()
|
||||
// let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.holdActionTimer?.invalidate()
|
||||
// strongSelf.component?.holdAction?()
|
||||
// strongSelf.beginExecuteHoldActionTimer()
|
||||
// })
|
||||
// self.holdActionTimer = holdActionTimer
|
||||
// RunLoop.main.add(holdActionTimer, forMode: .common)
|
||||
// }
|
||||
//
|
||||
// return super.beginTracking(touch, with: event)
|
||||
// }
|
||||
|
||||
private func beginExecuteHoldActionTimer() {
|
||||
self.holdActionTimer?.invalidate()
|
||||
let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.component?.holdAction?()
|
||||
})
|
||||
self.holdActionTimer = holdActionTimer
|
||||
RunLoop.main.add(holdActionTimer, forMode: .common)
|
||||
}
|
||||
// private func beginExecuteHoldActionTimer() {
|
||||
// self.holdActionTimer?.invalidate()
|
||||
// let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.component?.holdAction?()
|
||||
// })
|
||||
// self.holdActionTimer = holdActionTimer
|
||||
// RunLoop.main.add(holdActionTimer, forMode: .common)
|
||||
// }
|
||||
|
||||
override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||
self.currentIsHighlighted = false
|
||||
|
||||
self.holdActionTimer?.invalidate()
|
||||
self.holdActionTimer = nil
|
||||
|
||||
super.endTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override public func cancelTracking(with event: UIEvent?) {
|
||||
self.currentIsHighlighted = false
|
||||
|
||||
self.holdActionTimer?.invalidate()
|
||||
self.holdActionTimer = nil
|
||||
|
||||
super.cancelTracking(with: event)
|
||||
}
|
||||
// override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||
// self.currentIsHighlighted = false
|
||||
//
|
||||
// self.holdActionTimer?.invalidate()
|
||||
// self.holdActionTimer = nil
|
||||
//
|
||||
// super.endTracking(touch, with: event)
|
||||
// }
|
||||
//
|
||||
// override public func cancelTracking(with event: UIEvent?) {
|
||||
// self.currentIsHighlighted = false
|
||||
//
|
||||
// self.holdActionTimer?.invalidate()
|
||||
// self.holdActionTimer = nil
|
||||
//
|
||||
// super.cancelTracking(with: event)
|
||||
// }
|
||||
|
||||
private func updateAlpha(transition: ComponentTransition) {
|
||||
let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0
|
||||
transition.setAlpha(view: self.iconView, alpha: alpha)
|
||||
}
|
||||
// private func updateAlpha(transition: ComponentTransition) {
|
||||
// let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0
|
||||
// transition.setAlpha(view: self.iconView, alpha: alpha)
|
||||
// }
|
||||
|
||||
func update(component: EntityKeyboardBottomPanelButton, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
if self.component?.icon != component.icon {
|
||||
|
|
@ -153,15 +162,21 @@ final class EntityKeyboardBottomPanelButton: Component {
|
|||
|
||||
self.component = component
|
||||
|
||||
self.iconView.tintColor = component.color
|
||||
self.iconView.tintColor = component.theme.chat.inputPanel.panelControlColor
|
||||
|
||||
let size = CGSize(width: 38.0, height: 38.0)
|
||||
let size = CGSize(width: 40.0, height: 40.0)
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)
|
||||
self.iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
let tintColor: GlassBackgroundView.TintColor = .init(kind: .panel, color: component.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: tintColor, isInteractive: true, transition: transition)
|
||||
|
||||
self.buttonView.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import TelegramCore
|
|||
import ComponentDisplayAdapters
|
||||
import BundleIconComponent
|
||||
import GlassBackgroundComponent
|
||||
import EdgeEffect
|
||||
import LiquidLens
|
||||
import TabSelectionRecognizer
|
||||
|
||||
private final class BottomPanelIconComponent: Component {
|
||||
let title: String
|
||||
|
|
@ -57,22 +60,15 @@ private final class BottomPanelIconComponent: Component {
|
|||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentView)
|
||||
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.component?.action()
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: BottomPanelIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
if self.component?.title != component.title {
|
||||
let text = NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: .white)
|
||||
let text = NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: .white)
|
||||
let textBounds = text.boundingRect(with: CGSize(width: 120.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||
self.contentView.image = generateImage(CGSize(width: ceil(textBounds.width), height: ceil(textBounds.height)), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
|
@ -89,19 +85,9 @@ private final class BottomPanelIconComponent: Component {
|
|||
let textSize = self.contentView.image?.size ?? CGSize()
|
||||
let size = CGSize(width: textSize.width + textInset * 2.0, height: 28.0)
|
||||
|
||||
let color = component.theme.chat.inputPanel.inputControlColor
|
||||
self.contentView.tintColor = component.theme.chat.inputPanel.panelControlColor
|
||||
|
||||
if self.contentView.tintColor != color {
|
||||
if !transition.animation.isImmediate {
|
||||
UIView.animate(withDuration: 0.15, delay: 0.0, options: [], animations: {
|
||||
self.contentView.tintColor = color
|
||||
}, completion: nil)
|
||||
} else {
|
||||
self.contentView.tintColor = color
|
||||
}
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0 - 1.0), size: textSize))
|
||||
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0), size: textSize))
|
||||
|
||||
return size
|
||||
}
|
||||
|
|
@ -163,17 +149,27 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
private var leftAccessoryButton: AccessoryButtonView?
|
||||
private var rightAccessoryButton: AccessoryButtonView?
|
||||
|
||||
private var iconViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var highlightedIconBackgroundView: UIView
|
||||
private var highlightedTintIconBackgroundView: UIView
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
private let backgroundContainer: GlassBackgroundContainerView
|
||||
private let liquidLensView: LiquidLensView
|
||||
|
||||
private var itemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var selectedItemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
|
||||
private var tabSelectionRecognizer: TabSelectionRecognizer?
|
||||
private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)?
|
||||
|
||||
let tintContentMask: UIView
|
||||
|
||||
private var component: EntityKeyboardBottomPanelComponent?
|
||||
private var state: EmptyComponentState?
|
||||
private var environment: PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.tintContentMask = UIView()
|
||||
|
||||
self.edgeEffectView = EdgeEffectView()
|
||||
|
||||
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true, customBlurRadius: 10.0)
|
||||
|
||||
self.separatorView = UIView()
|
||||
|
|
@ -182,51 +178,102 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
self.tintSeparatorView.isUserInteractionEnabled = false
|
||||
self.tintSeparatorView.backgroundColor = UIColor(white: 0.0, alpha: 0.7)
|
||||
|
||||
self.tintContentMask.addSubview(self.tintSeparatorView)
|
||||
|
||||
self.highlightedIconBackgroundView = UIView()
|
||||
self.highlightedIconBackgroundView.isUserInteractionEnabled = false
|
||||
self.highlightedIconBackgroundView.layer.cornerRadius = 10.0
|
||||
self.highlightedIconBackgroundView.clipsToBounds = true
|
||||
|
||||
self.highlightedTintIconBackgroundView = UIView()
|
||||
self.highlightedTintIconBackgroundView.isUserInteractionEnabled = false
|
||||
self.highlightedTintIconBackgroundView.layer.cornerRadius = 10.0
|
||||
self.highlightedTintIconBackgroundView.clipsToBounds = true
|
||||
self.highlightedTintIconBackgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.1)
|
||||
|
||||
self.tintContentMask.addSubview(self.highlightedTintIconBackgroundView)
|
||||
|
||||
self.backgroundContainer = GlassBackgroundContainerView()
|
||||
self.liquidLensView = LiquidLensView(useBackgroundContainer: false)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addSubview(self.highlightedIconBackgroundView)
|
||||
self.addSubview(self.separatorView)
|
||||
self.addSubview(self.edgeEffectView)
|
||||
|
||||
self.addSubview(self.backgroundContainer)
|
||||
self.backgroundContainer.contentView.addSubview(self.liquidLensView)
|
||||
|
||||
let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:)))
|
||||
self.tabSelectionRecognizer = tabSelectionRecognizer
|
||||
self.liquidLensView.addGestureRecognizer(tabSelectionRecognizer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func item(at point: CGPoint) -> AnyHashable? {
|
||||
var closestItem: (AnyHashable, CGFloat)?
|
||||
for (id, itemView) in self.itemViews {
|
||||
if itemView.frame.contains(point) {
|
||||
return id
|
||||
} else {
|
||||
let distance = abs(point.x - itemView.center.x)
|
||||
if let closestItemValue = closestItem {
|
||||
if closestItemValue.1 > distance {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
} else {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
}
|
||||
}
|
||||
return closestItem?.0
|
||||
}
|
||||
|
||||
@objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) {
|
||||
guard let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
let location = recognizer.location(in: self.liquidLensView.contentView)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] {
|
||||
let startX = itemView.frame.minX - 4.0
|
||||
self.selectionGestureState = (startX, startX, itemId)
|
||||
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
|
||||
}
|
||||
case .changed:
|
||||
if var selectionGestureState = self.selectionGestureState {
|
||||
selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x
|
||||
if let itemId = self.item(at: location) {
|
||||
selectionGestureState.itemId = itemId
|
||||
}
|
||||
self.selectionGestureState = selectionGestureState
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
self.selectionGestureState = nil
|
||||
if case .ended = recognizer.state {
|
||||
guard let item = environment.contentIcons.first(where: { $0.id == selectionGestureState.itemId }) else {
|
||||
return
|
||||
}
|
||||
environment.navigateToContentId(item.id)
|
||||
}
|
||||
self.state?.updated(transition: .spring(duration: 0.4), isLocal: true)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: EntityKeyboardBottomPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
if self.component?.theme !== component.theme {
|
||||
self.separatorView.backgroundColor = component.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5)
|
||||
|
||||
self.backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(1.0), transition: .immediate)
|
||||
self.highlightedIconBackgroundView.backgroundColor = component.theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor
|
||||
}
|
||||
|
||||
let intrinsicHeight: CGFloat = 34.0
|
||||
let height = intrinsicHeight + component.containerInsets.bottom
|
||||
let height = intrinsicHeight + component.containerInsets.bottom + 20.0
|
||||
|
||||
let accessoryButtonOffset: CGFloat
|
||||
if component.containerInsets.bottom > 0.0 {
|
||||
accessoryButtonOffset = 2.0
|
||||
accessoryButtonOffset = 0.0
|
||||
} else {
|
||||
accessoryButtonOffset = -2.0
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
let panelEnvironment = environment[PagerComponentPanelEnvironment<EntityKeyboardTopContainerPanelEnvironment>.self].value
|
||||
self.environment = panelEnvironment
|
||||
let activeContentId = panelEnvironment.activeContentId
|
||||
|
||||
var leftAccessoryButtonComponent: AnyComponentWithIdentity<Empty>?
|
||||
|
|
@ -257,7 +304,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
environment: {},
|
||||
containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight)
|
||||
)
|
||||
let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 2.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize)
|
||||
let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 18.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize)
|
||||
leftAccessoryButtonTransition.setFrame(view: leftAccessoryButton.view, frame: leftAccessoryButtonFrame)
|
||||
if let leftAccessoryButtonView = leftAccessoryButton.view.componentView as? PagerTopPanelView {
|
||||
if leftAccessoryButtonView.tintContentMask.superview == nil {
|
||||
|
|
@ -328,7 +375,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight)
|
||||
)
|
||||
|
||||
let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 2.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize)
|
||||
let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 18.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize)
|
||||
rightAccessoryButtonTransition.setFrame(view: rightAccessoryButton.view, frame: rightAccessoryButtonFrame)
|
||||
if let rightAccessoryButtonView = rightAccessoryButton.view.componentView as? PagerTopPanelView {
|
||||
if rightAccessoryButtonView.tintContentMask.superview == nil {
|
||||
|
|
@ -374,23 +421,32 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
var iconInfos: [AnyHashable: (size: CGSize, transition: ComponentTransition)] = [:]
|
||||
|
||||
var iconTotalSize = CGSize()
|
||||
let iconSpacing: CGFloat = 4.0
|
||||
let iconSpacing: CGFloat = 0.0
|
||||
|
||||
let navigateToContentId = panelEnvironment.navigateToContentId
|
||||
|
||||
var lensSelection: (x: CGFloat, width: CGFloat) = (0.0, 0.0)
|
||||
|
||||
if panelEnvironment.contentIcons.count > 1 {
|
||||
for icon in panelEnvironment.contentIcons {
|
||||
validIconIds.append(icon.id)
|
||||
|
||||
var iconTransition = transition
|
||||
let iconView: ComponentHostView<Empty>
|
||||
if let current = self.iconViews[icon.id] {
|
||||
let selectedIconView: ComponentHostView<Empty>
|
||||
if let current = self.itemViews[icon.id], let currentSelected = self.selectedItemViews[icon.id] {
|
||||
iconView = current
|
||||
selectedIconView = currentSelected
|
||||
} else {
|
||||
iconTransition = .immediate
|
||||
iconView = ComponentHostView<Empty>()
|
||||
self.iconViews[icon.id] = iconView
|
||||
self.addSubview(iconView)
|
||||
iconView.isUserInteractionEnabled = false
|
||||
selectedIconView = ComponentHostView<Empty>()
|
||||
selectedIconView.isUserInteractionEnabled = false
|
||||
self.itemViews[icon.id] = iconView
|
||||
self.selectedItemViews[icon.id] = selectedIconView
|
||||
self.liquidLensView.contentView.addSubview(iconView)
|
||||
self.liquidLensView.selectedContentView.addSubview(selectedIconView)
|
||||
}
|
||||
|
||||
let iconSize = iconView.update(
|
||||
|
|
@ -407,65 +463,70 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
containerSize: CGSize(width: 28.0, height: 28.0)
|
||||
)
|
||||
|
||||
let _ = selectedIconView.update(
|
||||
transition: iconTransition,
|
||||
component: AnyComponent(BottomPanelIconComponent(
|
||||
title: icon.title,
|
||||
isHighlighted: icon.id == activeContentId,
|
||||
theme: component.theme,
|
||||
action: {
|
||||
navigateToContentId(icon.id)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 28.0, height: 28.0)
|
||||
)
|
||||
|
||||
iconInfos[icon.id] = (size: iconSize, transition: iconTransition)
|
||||
|
||||
if !iconTotalSize.width.isZero {
|
||||
iconTotalSize.width += iconSpacing
|
||||
iconTotalSize.width += iconSpacing - 8.0
|
||||
}
|
||||
iconTotalSize.width += iconSize.width
|
||||
iconTotalSize.height = max(iconTotalSize.height, iconSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
var nextIconOrigin = CGPoint(x: floor((availableSize.width - iconTotalSize.width) / 2.0), y: floor((intrinsicHeight - iconTotalSize.height) / 2.0))
|
||||
if component.containerInsets.bottom > 0.0 {
|
||||
nextIconOrigin.y += 3.0
|
||||
}
|
||||
|
||||
let tabsSize = CGSize(width: iconTotalSize.width, height: 40.0)
|
||||
var nextIconOrigin = CGPoint(x: floor((tabsSize.width - iconTotalSize.width) / 2.0), y: floor((tabsSize.height - iconTotalSize.height) / 2.0))
|
||||
|
||||
transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: .zero, size: availableSize))
|
||||
self.backgroundContainer.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition)
|
||||
|
||||
if panelEnvironment.contentIcons.count > 1 {
|
||||
for icon in panelEnvironment.contentIcons {
|
||||
guard let iconInfo = iconInfos[icon.id], let iconView = self.iconViews[icon.id] else {
|
||||
guard let iconInfo = iconInfos[icon.id], let iconView = self.itemViews[icon.id], let selectedIconView = self.selectedItemViews[icon.id] else {
|
||||
continue
|
||||
}
|
||||
|
||||
let iconFrame = CGRect(origin: nextIconOrigin, size: iconInfo.size)
|
||||
iconInfo.transition.setFrame(view: iconView, frame: iconFrame, completion: nil)
|
||||
|
||||
if let iconView = iconView.componentView as? BottomPanelIconComponent.View {
|
||||
if iconView.tintMaskContainer.superview == nil {
|
||||
self.tintContentMask.addSubview(iconView.tintMaskContainer)
|
||||
}
|
||||
iconInfo.transition.setFrame(view: iconView.tintMaskContainer, frame: iconFrame, completion: nil)
|
||||
}
|
||||
iconInfo.transition.setFrame(view: selectedIconView, frame: iconFrame, completion: nil)
|
||||
|
||||
if let activeContentId = activeContentId, activeContentId == icon.id {
|
||||
self.highlightedIconBackgroundView.isHidden = false
|
||||
self.highlightedTintIconBackgroundView.isHidden = false
|
||||
transition.setFrame(view: self.highlightedIconBackgroundView, frame: iconFrame)
|
||||
transition.setFrame(view: self.highlightedTintIconBackgroundView, frame: iconFrame)
|
||||
|
||||
let cornerRadius: CGFloat = min(iconFrame.width, iconFrame.height) / 2.0
|
||||
transition.setCornerRadius(layer: self.highlightedIconBackgroundView.layer, cornerRadius: cornerRadius)
|
||||
transition.setCornerRadius(layer: self.highlightedTintIconBackgroundView.layer, cornerRadius: cornerRadius)
|
||||
lensSelection = (iconFrame.origin.x, iconFrame.width)
|
||||
}
|
||||
|
||||
nextIconOrigin.x += iconInfo.size.width + iconSpacing
|
||||
nextIconOrigin.x += iconInfo.size.width + iconSpacing - 8.0
|
||||
}
|
||||
}
|
||||
|
||||
if activeContentId == nil {
|
||||
self.highlightedIconBackgroundView.isHidden = true
|
||||
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
lensSelection = (selectionGestureState.currentX, lensSelection.width)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - tabsSize.width) / 2.0), y: 0.0), size: tabsSize))
|
||||
self.liquidLensView.update(size: tabsSize, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: tabsSize.height), isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: activeContentId == nil, transition: transition)
|
||||
|
||||
var removedIconViewIds: [AnyHashable] = []
|
||||
for (id, iconView) in self.iconViews {
|
||||
for (id, iconView) in self.itemViews {
|
||||
if !validIconIds.contains(id) {
|
||||
removedIconViewIds.append(id)
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
for id in removedIconViewIds {
|
||||
self.iconViews.removeValue(forKey: id)
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.separatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
|
||||
|
|
@ -473,8 +534,11 @@ final class EntityKeyboardBottomPanelComponent: Component {
|
|||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: height)))
|
||||
//self.backgroundView.update(size: CGSize(width: availableSize.width, height: height), transition: transition.containedViewLayoutTransition)
|
||||
|
||||
self.component = component
|
||||
|
||||
let edgeEffectHeight: CGFloat = 80.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: component.theme.chat.inputMediaPanel.backgroundColor.withMultipliedAlpha(0.8), rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: transition)
|
||||
|
||||
return CGSize(width: availableSize.width, height: height)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,18 @@ import Postbox
|
|||
|
||||
public final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
|
||||
let isContentInFocus: Bool
|
||||
let height: CGFloat
|
||||
let visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)>
|
||||
let isExpandedUpdated: (Bool, ComponentTransition) -> Void
|
||||
|
||||
init(
|
||||
isContentInFocus: Bool,
|
||||
height: CGFloat,
|
||||
visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)>,
|
||||
isExpandedUpdated: @escaping (Bool, ComponentTransition) -> Void
|
||||
) {
|
||||
self.isContentInFocus = isContentInFocus
|
||||
self.height = height
|
||||
self.visibilityFractionUpdated = visibilityFractionUpdated
|
||||
self.isExpandedUpdated = isExpandedUpdated
|
||||
}
|
||||
|
|
@ -26,6 +29,9 @@ public final class EntityKeyboardTopContainerPanelEnvironment: Equatable {
|
|||
if lhs.isContentInFocus != rhs.isContentInFocus {
|
||||
return false
|
||||
}
|
||||
if lhs.height != rhs.height {
|
||||
return false
|
||||
}
|
||||
if lhs.visibilityFractionUpdated !== rhs.visibilityFractionUpdated {
|
||||
return false
|
||||
}
|
||||
|
|
@ -39,17 +45,20 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
|
|||
let theme: PresentationTheme
|
||||
let overflowHeight: CGFloat
|
||||
let topInset: CGFloat
|
||||
let height: CGFloat
|
||||
let displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
overflowHeight: CGFloat,
|
||||
topInset: CGFloat,
|
||||
height: CGFloat,
|
||||
displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground
|
||||
) {
|
||||
self.theme = theme
|
||||
self.overflowHeight = overflowHeight
|
||||
self.topInset = topInset
|
||||
self.height = height
|
||||
self.displayBackground = displayBackground
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +69,9 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
|
|||
if lhs.overflowHeight != rhs.overflowHeight {
|
||||
return false
|
||||
}
|
||||
if lhs.height != rhs.height {
|
||||
return false
|
||||
}
|
||||
if lhs.topInset != rhs.topInset {
|
||||
return false
|
||||
}
|
||||
|
|
@ -105,7 +117,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
|
|||
}
|
||||
|
||||
func update(component: EntityKeyboardTopContainerPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
let intrinsicHeight: CGFloat = 34.0
|
||||
let intrinsicHeight: CGFloat = component.height
|
||||
let height = intrinsicHeight + component.topInset
|
||||
|
||||
let panelEnvironment = environment[PagerComponentPanelEnvironment.self].value
|
||||
|
|
@ -165,6 +177,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component {
|
|||
environment: {
|
||||
EntityKeyboardTopContainerPanelEnvironment(
|
||||
isContentInFocus: panelEnvironment.isContentInFocus,
|
||||
height: intrinsicHeight,
|
||||
visibilityFractionUpdated: panelView.visibilityFractionUpdated,
|
||||
isExpandedUpdated: { [weak self] isExpanded, transition in
|
||||
guard let strongSelf = self else {
|
||||
|
|
|
|||
|
|
@ -2009,7 +2009,7 @@ public final class EntityKeyboardTopPanelComponent: Component {
|
|||
let panelEnvironment = environment[EntityKeyboardTopContainerPanelEnvironment.self].value
|
||||
self.environment = panelEnvironment
|
||||
|
||||
let isExpanded = availableSize.height > 34.0
|
||||
let isExpanded = availableSize.height > panelEnvironment.height
|
||||
let wasExpanded = self.isExpanded
|
||||
self.isExpanded = isExpanded
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ public final class GiftCompositionComponent: Component {
|
|||
public fileprivate(set) var previewSymbol: StarGift.UniqueGift.Attribute?
|
||||
|
||||
public init() {
|
||||
self.previewPatternColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,238 +3,237 @@ import UIKit
|
|||
import SwiftSignalKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import AvatarNode
|
||||
import Markdown
|
||||
import AlertComponent
|
||||
|
||||
private final class GiftAuctionTransferAlertContentNode: AlertContentNode {
|
||||
private let strings: PresentationStrings
|
||||
private let title: String
|
||||
private let text: String
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let textNode: ASTextNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let secondAvatarNode: AvatarNode
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, fromPeer: EnginePeer, toPeer: EnginePeer, title: String, text: String, actions: [TextAlertAction]) {
|
||||
self.strings = strings
|
||||
self.title = title
|
||||
self.text = text
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.displayWithoutProcessing = true
|
||||
|
||||
self.secondAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isLayerBacked = true
|
||||
|
||||
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||
return TextAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.addSubnode(self.secondAvatarNode)
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
|
||||
self.avatarNode.setPeer(context: context, theme: ptheme, peer: fromPeer)
|
||||
self.secondAvatarNode.setPeer(context: context, theme: ptheme, peer: toPeer)
|
||||
}
|
||||
|
||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor)
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 270.0)
|
||||
|
||||
self.validLayout = size
|
||||
|
||||
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||
|
||||
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.avatarNode.updateSize(size: avatarSize)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||
|
||||
if let arrowImage = self.arrowNode.image {
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
}
|
||||
|
||||
let secondAvatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize)
|
||||
transition.updateFrame(node: self.secondAvatarNode, frame: secondAvatarFrame)
|
||||
|
||||
origin.y += avatarSize.height + 10.0
|
||||
//private final class GiftAuctionTransferAlertContentNode: AlertContentNode {
|
||||
// private let strings: PresentationStrings
|
||||
// private let title: String
|
||||
// private let text: String
|
||||
//
|
||||
// private let titleNode: ASTextNode
|
||||
// private let textNode: ASTextNode
|
||||
// private let avatarNode: AvatarNode
|
||||
// private let arrowNode: ASImageNode
|
||||
// private let secondAvatarNode: AvatarNode
|
||||
//
|
||||
// private let actionNodesSeparator: ASDisplayNode
|
||||
// private let actionNodes: [TextAlertContentActionNode]
|
||||
// private let actionVerticalSeparators: [ASDisplayNode]
|
||||
//
|
||||
// private var validLayout: CGSize?
|
||||
//
|
||||
// override var dismissOnOutsideTap: Bool {
|
||||
// return self.isUserInteractionEnabled
|
||||
// }
|
||||
//
|
||||
// init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, fromPeer: EnginePeer, toPeer: EnginePeer, title: String, text: String, actions: [TextAlertAction]) {
|
||||
// self.strings = strings
|
||||
// self.title = title
|
||||
// self.text = text
|
||||
//
|
||||
// self.titleNode = ASTextNode()
|
||||
// self.titleNode.maximumNumberOfLines = 0
|
||||
//
|
||||
// self.textNode = ASTextNode()
|
||||
// self.textNode.maximumNumberOfLines = 0
|
||||
//
|
||||
// self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
//
|
||||
// self.arrowNode = ASImageNode()
|
||||
// self.arrowNode.displaysAsynchronously = false
|
||||
// self.arrowNode.displayWithoutProcessing = true
|
||||
//
|
||||
// self.secondAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
//
|
||||
// self.actionNodesSeparator = ASDisplayNode()
|
||||
// self.actionNodesSeparator.isLayerBacked = true
|
||||
//
|
||||
// self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||
// return TextAlertContentActionNode(theme: theme, action: action)
|
||||
// }
|
||||
//
|
||||
// var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
// if actions.count > 1 {
|
||||
// for _ in 0 ..< actions.count - 1 {
|
||||
// let separatorNode = ASDisplayNode()
|
||||
// separatorNode.isLayerBacked = true
|
||||
// actionVerticalSeparators.append(separatorNode)
|
||||
// }
|
||||
// }
|
||||
// self.actionVerticalSeparators = actionVerticalSeparators
|
||||
//
|
||||
// super.init()
|
||||
//
|
||||
// self.addSubnode(self.titleNode)
|
||||
// self.addSubnode(self.textNode)
|
||||
// self.addSubnode(self.avatarNode)
|
||||
// self.addSubnode(self.arrowNode)
|
||||
// self.addSubnode(self.secondAvatarNode)
|
||||
//
|
||||
// self.addSubnode(self.actionNodesSeparator)
|
||||
//
|
||||
// for actionNode in self.actionNodes {
|
||||
// self.addSubnode(actionNode)
|
||||
// }
|
||||
//
|
||||
// for separatorNode in self.actionVerticalSeparators {
|
||||
// self.addSubnode(separatorNode)
|
||||
// }
|
||||
//
|
||||
// self.updateTheme(theme)
|
||||
//
|
||||
// self.avatarNode.setPeer(context: context, theme: ptheme, peer: fromPeer)
|
||||
// self.secondAvatarNode.setPeer(context: context, theme: ptheme, peer: toPeer)
|
||||
// }
|
||||
//
|
||||
// override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
// self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
// self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||
// body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
// bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||
// link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
// linkAttribute: { url in
|
||||
// return ("URL", url)
|
||||
// }
|
||||
// ), textAlignment: .center)
|
||||
// self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor)
|
||||
//
|
||||
// self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
// for actionNode in self.actionNodes {
|
||||
// actionNode.updateTheme(theme)
|
||||
// }
|
||||
// for separatorNode in self.actionVerticalSeparators {
|
||||
// separatorNode.backgroundColor = theme.separatorColor
|
||||
// }
|
||||
//
|
||||
// if let size = self.validLayout {
|
||||
// _ = self.updateLayout(size: size, transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
// var size = size
|
||||
// size.width = min(size.width, 270.0)
|
||||
//
|
||||
// self.validLayout = size
|
||||
//
|
||||
// var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||
//
|
||||
// let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||
// self.avatarNode.updateSize(size: avatarSize)
|
||||
//
|
||||
// let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||
// transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||
//
|
||||
// if let arrowImage = self.arrowNode.image {
|
||||
// let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
// transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
// }
|
||||
//
|
||||
// let secondAvatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize)
|
||||
// transition.updateFrame(node: self.secondAvatarNode, frame: secondAvatarFrame)
|
||||
//
|
||||
// origin.y += avatarSize.height + 10.0
|
||||
//
|
||||
// let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
// transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
// origin.y += titleSize.height + 4.0
|
||||
//
|
||||
// let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
// transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||
// origin.y += textSize.height + 10.0
|
||||
//
|
||||
// let actionButtonHeight: CGFloat = 44.0
|
||||
// var minActionsWidth: CGFloat = 0.0
|
||||
// let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
// let actionTitleInsets: CGFloat = 8.0
|
||||
//
|
||||
// var effectiveActionLayout = TextAlertContentActionLayout.horizontal
|
||||
// for actionNode in self.actionNodes {
|
||||
// let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
// if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||
// effectiveActionLayout = .vertical
|
||||
// }
|
||||
// switch effectiveActionLayout {
|
||||
// case .horizontal:
|
||||
// minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||
// case .vertical:
|
||||
// minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
//
|
||||
// let contentWidth = max(size.width, minActionsWidth)
|
||||
//
|
||||
// var actionsHeight: CGFloat = 0.0
|
||||
// switch effectiveActionLayout {
|
||||
// case .horizontal:
|
||||
// actionsHeight = actionButtonHeight
|
||||
// case .vertical:
|
||||
// actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
// }
|
||||
//
|
||||
// let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom)
|
||||
// transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
//
|
||||
// var actionOffset: CGFloat = 0.0
|
||||
// let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||
// var separatorIndex = -1
|
||||
// var nodeIndex = 0
|
||||
// for actionNode in self.actionNodes {
|
||||
// if separatorIndex >= 0 {
|
||||
// let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
// switch effectiveActionLayout {
|
||||
// case .horizontal:
|
||||
// transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||
// case .vertical:
|
||||
// transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
// }
|
||||
// }
|
||||
// separatorIndex += 1
|
||||
//
|
||||
// let currentActionWidth: CGFloat
|
||||
// switch effectiveActionLayout {
|
||||
// case .horizontal:
|
||||
// if nodeIndex == self.actionNodes.count - 1 {
|
||||
// currentActionWidth = resultSize.width - actionOffset
|
||||
// } else {
|
||||
// currentActionWidth = actionWidth
|
||||
// }
|
||||
// case .vertical:
|
||||
// currentActionWidth = resultSize.width
|
||||
// }
|
||||
//
|
||||
// let actionNodeFrame: CGRect
|
||||
// switch effectiveActionLayout {
|
||||
// case .horizontal:
|
||||
// actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
// actionOffset += currentActionWidth
|
||||
// case .vertical:
|
||||
// actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
// actionOffset += actionButtonHeight
|
||||
// }
|
||||
//
|
||||
// transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
//
|
||||
// nodeIndex += 1
|
||||
// }
|
||||
//
|
||||
// return resultSize
|
||||
// }
|
||||
//}
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
origin.y += titleSize.height + 4.0
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||
origin.y += textSize.height + 10.0
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
var effectiveActionLayout = TextAlertContentActionLayout.horizontal
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||
effectiveActionLayout = .vertical
|
||||
}
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||
case .vertical:
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
}
|
||||
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
let contentWidth = max(size.width, minActionsWidth)
|
||||
|
||||
var actionsHeight: CGFloat = 0.0
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionsHeight = actionButtonHeight
|
||||
case .vertical:
|
||||
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom)
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||
case .vertical:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
if nodeIndex == self.actionNodes.count - 1 {
|
||||
currentActionWidth = resultSize.width - actionOffset
|
||||
} else {
|
||||
currentActionWidth = actionWidth
|
||||
}
|
||||
case .vertical:
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += currentActionWidth
|
||||
case .vertical:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
|
||||
func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer, toPeer: EnginePeer, commit: @escaping () -> Void) -> AlertController {
|
||||
func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer, toPeer: EnginePeer, commit: @escaping () -> Void) -> AlertScreen {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
|
||||
let title = strings.Gift_AuctionTransfer_Title
|
||||
let text: String
|
||||
if fromPeer.id == context.account.peerId {
|
||||
text = strings.Gift_AuctionTransfer_TextFromYourself(toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
|
|
@ -243,25 +242,74 @@ func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer
|
|||
} else {
|
||||
text = strings.Gift_AuctionTransfer_Text(fromPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
|
||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
||||
// content.append(AnyComponentWithIdentity(
|
||||
// id: "header",
|
||||
// component: AnyComponent(
|
||||
// AlertTransferHeaderComponent(
|
||||
// fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent(
|
||||
// AvatarComponent(
|
||||
// context: context,
|
||||
// theme: presentationData.theme,
|
||||
// peer: fromPeer
|
||||
// )
|
||||
// )),
|
||||
// toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent(
|
||||
// AvatarComponent(
|
||||
// context: context,
|
||||
// theme: presentationData.theme,
|
||||
// peer: toPeer
|
||||
// )
|
||||
// ))
|
||||
// )
|
||||
// )
|
||||
// ))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
AlertTitleComponent(title: title)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(text))
|
||||
)
|
||||
))
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: context,
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false),
|
||||
content: content,
|
||||
actions: [
|
||||
.init(title: strings.Gift_AuctionTransfer_Change, type: .default, action: {
|
||||
commit()
|
||||
}),
|
||||
.init(title: strings.Common_Cancel)
|
||||
]
|
||||
)
|
||||
return alertController
|
||||
|
||||
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
var contentNode: GiftAuctionTransferAlertContentNode?
|
||||
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?(true)
|
||||
}), TextAlertAction(type: .defaultAction, title: strings.Gift_AuctionTransfer_Change, action: {
|
||||
dismissImpl?(true)
|
||||
commit()
|
||||
})]
|
||||
|
||||
contentNode = GiftAuctionTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, fromPeer: fromPeer, toPeer: toPeer, title: strings.Gift_AuctionTransfer_Title, text: text, actions: actions)
|
||||
|
||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!)
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
} else {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
return controller
|
||||
// var dismissImpl: ((Bool) -> Void)?
|
||||
// var contentNode: GiftAuctionTransferAlertContentNode?
|
||||
// let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
// dismissImpl?(true)
|
||||
// }), TextAlertAction(type: .defaultAction, title: strings.Gift_AuctionTransfer_Change, action: {
|
||||
// dismissImpl?(true)
|
||||
// commit()
|
||||
// })]
|
||||
//
|
||||
// contentNode = GiftAuctionTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, fromPeer: fromPeer, toPeer: toPeer, title: strings.Gift_AuctionTransfer_Title, text: text, actions: actions)
|
||||
//
|
||||
// let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!)
|
||||
// dismissImpl = { [weak controller] animated in
|
||||
// if animated {
|
||||
// controller?.dismissAnimated()
|
||||
// } else {
|
||||
// controller?.dismiss()
|
||||
// }
|
||||
// }
|
||||
// return controller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -991,7 +991,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||
controller.present(alertController, in: .current)
|
||||
|
||||
dismissAlertImpl = { [weak alertController] in
|
||||
alertController?.dismissAnimated()
|
||||
alertController?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/SegmentControlComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftRemainingCountComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/InfoParagraphComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/TableComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import TelegramStringFormatting
|
|||
import GlassBarButtonComponent
|
||||
import GiftItemComponent
|
||||
import EdgeEffect
|
||||
import TableComponent
|
||||
import PeerTableCellComponent
|
||||
|
||||
private final class GiftAuctionAcquiredScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -373,7 +375,7 @@ private final class GiftAuctionAcquiredScreenComponent: Component {
|
|||
title: environment.strings.Gift_Acquired_Recipient,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
|
|||
if self.backgroundHandleView.image == nil {
|
||||
self.backgroundHandleView.image = generateStretchableFilledCircleImage(diameter: 5.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
self.backgroundHandleView.tintColor = environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(environment.theme.overallDarkAppearance ? 0.2 : 0.07)
|
||||
self.backgroundHandleView.tintColor = theme.list.itemPrimaryTextColor.withMultipliedAlpha(theme.overallDarkAppearance ? 0.2 : 0.07)
|
||||
let backgroundHandleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - 36.0) * 0.5), y: 5.0), size: CGSize(width: 36.0, height: 5.0))
|
||||
if self.backgroundHandleView.superview == nil {
|
||||
self.navigationBarContainer.addSubview(self.backgroundHandleView)
|
||||
|
|
@ -383,13 +383,13 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
|
|||
transition: .immediate,
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
tintColor: theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
|
|
@ -413,14 +413,13 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
|
|||
let containerInset: CGFloat = environment.statusBarHeight + 10.0
|
||||
contentHeight += environment.safeInsets.bottom
|
||||
|
||||
var initialContentHeight = contentHeight
|
||||
let clippingY: CGFloat
|
||||
|
||||
let titleText: String = environment.strings.Gift_ActiveAuctions_Title(Int32(self.auctionStates.count))
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleText, font: Font.semibold(17.0), textColor: environment.theme.list.itemPrimaryTextColor))
|
||||
text: .plain(NSAttributedString(string: titleText, font: Font.semibold(17.0), textColor: theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -434,12 +433,12 @@ private final class GiftAuctionActiveBidsScreenComponent: Component {
|
|||
transition.setFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
|
||||
initialContentHeight = contentHeight
|
||||
let initialContentHeight = contentHeight
|
||||
|
||||
let edgeEffectHeight: CGFloat = 80.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: CGSize(width: fillingSize, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.topEdgeEffectView, frame: edgeEffectFrame)
|
||||
self.topEdgeEffectView.update(content: environment.theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition)
|
||||
self.topEdgeEffectView.update(content: theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition)
|
||||
if self.topEdgeEffectView.superview == nil {
|
||||
self.navigationBarContainer.insertSubview(self.topEdgeEffectView, at: 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import ButtonComponent
|
|||
import UndoUI
|
||||
import LottieComponent
|
||||
import AnimatedTextComponent
|
||||
import TableComponent
|
||||
|
||||
private final class GiftAuctionViewSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -1075,7 +1076,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
guard let state, let attributes = state.giftUpgradeAttributes else {
|
||||
return
|
||||
}
|
||||
let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsPreviewScreen(context: component.context, gift: .generic(gift), attributes: attributes)
|
||||
let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsScreen(context: component.context, gift: .generic(gift), attributes: attributes, selectedAttributes: nil, focusedAttribute: nil)
|
||||
environment.controller()?.push(variantsController)
|
||||
}, animateScale: false),
|
||||
availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height),
|
||||
|
|
|
|||
|
|
@ -3,478 +3,29 @@ import UIKit
|
|||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import AvatarNode
|
||||
import Markdown
|
||||
import GiftItemComponent
|
||||
import ChatMessagePaymentAlertController
|
||||
import ActivityIndicator
|
||||
import TooltipUI
|
||||
import MultilineTextComponent
|
||||
import BalancedTextComponent
|
||||
import BundleIconComponent
|
||||
import TelegramStringFormatting
|
||||
|
||||
private final class GiftOfferAlertContentNode: AlertContentNode {
|
||||
private let context: AccountContext
|
||||
private let strings: PresentationStrings
|
||||
private var presentationTheme: PresentationTheme
|
||||
private let title: String
|
||||
private let text: String
|
||||
private let amount: CurrencyAmount
|
||||
private let gift: StarGift.UniqueGift
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let giftView = ComponentView<Empty>()
|
||||
private let textNode: ASTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let tableView = ComponentView<Empty>()
|
||||
private let valueDelta = ComponentView<Empty>()
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
private let symbolButtonTag = GenericComponentViewTag()
|
||||
|
||||
fileprivate var getController: () -> ViewController? = { return nil}
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var activityIndicator: ActivityIndicator?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
var inProgress = false {
|
||||
didSet {
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: AlertControllerTheme,
|
||||
ptheme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
gift: StarGift.UniqueGift,
|
||||
peer: EnginePeer,
|
||||
title: String,
|
||||
text: String,
|
||||
amount: CurrencyAmount,
|
||||
actions: [TextAlertAction]
|
||||
) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.presentationTheme = ptheme
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.amount = amount
|
||||
self.gift = gift
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.displayWithoutProcessing = true
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isLayerBacked = true
|
||||
|
||||
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||
return TextAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
|
||||
self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer)
|
||||
}
|
||||
|
||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor)
|
||||
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor.withAlphaComponent(0.9))
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func dismissAllTooltips() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
controller.window?.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
})
|
||||
controller.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func showAttributeInfo(tag: Any, text: String) {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
guard let sourceView = self.tableView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
controller.present(tooltipController, in: .current)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 310.0)
|
||||
|
||||
let strings = self.strings
|
||||
|
||||
self.validLayout = size
|
||||
|
||||
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||
|
||||
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.avatarNode.updateSize(size: avatarSize)
|
||||
|
||||
let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||
|
||||
let _ = self.giftView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationTheme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: self.gift, price: nil),
|
||||
mode: .thumbnail
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: avatarSize
|
||||
)
|
||||
if let view = self.giftView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = giftFrame
|
||||
}
|
||||
|
||||
if let arrowImage = self.arrowNode.image {
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
}
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize)
|
||||
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||
|
||||
origin.y += avatarSize.height + 17.0
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
origin.y += titleSize.height + 5.0
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||
origin.y += textSize.height + 10.0
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
let contentWidth = max(size.width, minActionsWidth)
|
||||
|
||||
let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
|
||||
let tableFont = Font.regular(15.0)
|
||||
let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor
|
||||
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
.model, .pattern, .backdrop, .originalInfo
|
||||
]
|
||||
|
||||
var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:]
|
||||
for attribute in self.gift.attributes {
|
||||
attributeMap[attribute.attributeType] = attribute
|
||||
}
|
||||
|
||||
for type in order {
|
||||
if let attribute = attributeMap[type] {
|
||||
let id: String?
|
||||
let title: String?
|
||||
let value: NSAttributedString
|
||||
let percentage: Float?
|
||||
let tag: AnyObject?
|
||||
|
||||
switch attribute {
|
||||
case let .model(name, _, rarity):
|
||||
id = "model"
|
||||
title = strings.Gift_Unique_Model
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.modelButtonTag
|
||||
case let .backdrop(name, _, _, _, _, _, rarity):
|
||||
id = "backdrop"
|
||||
title = strings.Gift_Unique_Backdrop
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.backdropButtonTag
|
||||
case let .pattern(name, _, rarity):
|
||||
id = "pattern"
|
||||
title = strings.Gift_Unique_Symbol
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.symbolButtonTag
|
||||
case .originalInfo:
|
||||
continue
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(value))
|
||||
)
|
||||
)
|
||||
)
|
||||
if let percentage, let tag {
|
||||
items.append(AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: self.context,
|
||||
text: formatPercentage(percentage),
|
||||
color: self.presentationTheme.list.itemAccentColor
|
||||
)),
|
||||
action: { [weak self] in
|
||||
self?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
}
|
||||
let itemComponent = AnyComponent(
|
||||
HStack(items, spacing: 4.0)
|
||||
)
|
||||
|
||||
tableItems.append(.init(
|
||||
id: id,
|
||||
title: title,
|
||||
hasBackground: false,
|
||||
component: itemComponent
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let tableSize = self.tableView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
TableComponent(
|
||||
theme: self.presentationTheme,
|
||||
items: tableItems,
|
||||
semiTransparent: true
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: contentWidth - 32.0, height: size.height)
|
||||
)
|
||||
let tableFrame = CGRect(origin: CGPoint(x: 16.0, y: avatarSize.height + titleSize.height + textSize.height + 60.0), size: tableSize)
|
||||
if let view = self.tableView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = tableFrame
|
||||
}
|
||||
|
||||
var valueDeltaHeight: CGFloat = 0.0
|
||||
if let valueAmount = self.gift.valueUsdAmount {
|
||||
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let usdRate: Double
|
||||
switch self.amount.currency {
|
||||
case .stars:
|
||||
usdRate = Double(resaleConfiguration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
case .ton:
|
||||
usdRate = Double(resaleConfiguration.tonUsdRate) / 1000.0 / 1000000.0
|
||||
}
|
||||
let offerUsdValue = Double(self.amount.amount.value) * usdRate
|
||||
let giftUsdValue = Double(valueAmount) / 100.0
|
||||
|
||||
let fraction = giftUsdValue / offerUsdValue
|
||||
let percentage = Int(fraction * 100) - 100
|
||||
|
||||
if percentage > 20 {
|
||||
let textColor = self.presentationTheme.list.itemDestructiveColor
|
||||
let markdownAttributes = MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
)
|
||||
let valueDeltaSize = self.valueDelta.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BalancedTextComponent(
|
||||
text: .markdown(text: strings.Chat_GiftPurchaseOffer_AcceptConfirmation_BadValue("\(percentage)%").string, attributes: markdownAttributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: contentWidth - 32.0, height: size.height)
|
||||
)
|
||||
let valueDeltaFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - valueDeltaSize.width) / 2.0), y: avatarSize.height + titleSize.height + textSize.height + 73.0 + tableSize.height), size: valueDeltaSize)
|
||||
if let view = self.valueDelta.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = valueDeltaFrame
|
||||
}
|
||||
valueDeltaHeight += valueDeltaSize.height + 10.0
|
||||
}
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + valueDeltaHeight + 40.0 + insets.top + insets.bottom)
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
do {
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
do {
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
do {
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let activityIndicator: ActivityIndicator
|
||||
if let current = self.activityIndicator {
|
||||
activityIndicator = current
|
||||
} else {
|
||||
activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false))
|
||||
self.addSubnode(activityIndicator)
|
||||
}
|
||||
|
||||
if let actionNode = self.actionNodes.first {
|
||||
actionNode.isUserInteractionEnabled = false
|
||||
actionNode.isHidden = false
|
||||
|
||||
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
||||
transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||
}
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
import AlertComponent
|
||||
import TableComponent
|
||||
|
||||
public func giftOfferAlertController(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||
gift: StarGift.UniqueGift,
|
||||
peer: EnginePeer,
|
||||
amount: CurrencyAmount,
|
||||
commit: @escaping () -> Void
|
||||
) -> AlertController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
) -> ViewController {
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
|
||||
let title = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Title
|
||||
|
|
@ -502,28 +53,367 @@ public func giftOfferAlertController(
|
|||
let giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
let text = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text(giftTitle, peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), priceString, finalPriceString).string
|
||||
|
||||
var contentNode: GiftOfferAlertContentNode?
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { [weak contentNode] in
|
||||
contentNode?.inProgress = true
|
||||
commit()
|
||||
dismissImpl?(true)
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?(true)
|
||||
})]
|
||||
let tableFont = Font.regular(15.0)
|
||||
let tableTextColor = presentationData.theme.list.itemPrimaryTextColor
|
||||
|
||||
contentNode = GiftOfferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, amount: amount, actions: actions)
|
||||
let modelButtonTag = GenericComponentViewTag()
|
||||
let backdropButtonTag = GenericComponentViewTag()
|
||||
let symbolButtonTag = GenericComponentViewTag()
|
||||
var showAttributeInfoImpl: ((Any, String) -> Void)?
|
||||
|
||||
let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: nil, chatPeerId: context.account.peerId, showBalance: false)
|
||||
contentNode?.getController = { [weak controller] in
|
||||
return controller
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
.model, .pattern, .backdrop, .originalInfo
|
||||
]
|
||||
|
||||
var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:]
|
||||
for attribute in gift.attributes {
|
||||
attributeMap[attribute.attributeType] = attribute
|
||||
}
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
} else {
|
||||
controller?.dismiss()
|
||||
|
||||
for type in order {
|
||||
if let attribute = attributeMap[type] {
|
||||
let id: String?
|
||||
let title: String?
|
||||
let value: NSAttributedString
|
||||
let percentage: Float?
|
||||
let tag: AnyObject?
|
||||
|
||||
switch attribute {
|
||||
case let .model(name, _, rarity):
|
||||
id = "model"
|
||||
title = strings.Gift_Unique_Model
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = modelButtonTag
|
||||
case let .backdrop(name, _, _, _, _, _, rarity):
|
||||
id = "backdrop"
|
||||
title = strings.Gift_Unique_Backdrop
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = backdropButtonTag
|
||||
case let .pattern(name, _, rarity):
|
||||
id = "pattern"
|
||||
title = strings.Gift_Unique_Symbol
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = symbolButtonTag
|
||||
case .originalInfo:
|
||||
continue
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(value))
|
||||
)
|
||||
)
|
||||
)
|
||||
if let percentage, let tag {
|
||||
items.append(AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: context,
|
||||
text: formatPercentage(percentage),
|
||||
color: presentationData.theme.list.itemAccentColor
|
||||
)),
|
||||
action: {
|
||||
showAttributeInfoImpl?(tag, strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
}
|
||||
let itemComponent = AnyComponent(
|
||||
HStack(items, spacing: 4.0)
|
||||
)
|
||||
|
||||
tableItems.append(.init(
|
||||
id: id,
|
||||
title: title,
|
||||
hasBackground: false,
|
||||
component: itemComponent
|
||||
))
|
||||
}
|
||||
}
|
||||
return controller
|
||||
|
||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "header",
|
||||
component: AnyComponent(
|
||||
AlertTransferHeaderComponent(
|
||||
fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: context,
|
||||
theme: presentationData.theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: gift, price: nil),
|
||||
mode: .thumbnail
|
||||
)
|
||||
)),
|
||||
toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent(
|
||||
AvatarComponent(
|
||||
context: context,
|
||||
theme: presentationData.theme,
|
||||
peer: peer
|
||||
)
|
||||
))
|
||||
)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
AlertTitleComponent(title: title)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(text))
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "table",
|
||||
component: AnyComponent(
|
||||
AlertTableComponent(items: tableItems)
|
||||
)
|
||||
))
|
||||
|
||||
if let valueAmount = gift.valueUsdAmount {
|
||||
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let usdRate: Double
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
usdRate = Double(resaleConfiguration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
case .ton:
|
||||
usdRate = Double(resaleConfiguration.tonUsdRate) / 1000.0 / 1000000.0
|
||||
}
|
||||
let offerUsdValue = Double(amount.amount.value) * usdRate
|
||||
let giftUsdValue = Double(valueAmount) / 100.0
|
||||
|
||||
let fraction = giftUsdValue / offerUsdValue
|
||||
let percentage = Int(fraction * 100) - 100
|
||||
|
||||
if percentage > 20 {
|
||||
let warningText = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_BadValue("\(percentage)%").string
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "warning",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(warningText), color: .destructive, textSize: .small)
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let updatedPresentationDataSignal = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
let alertController = AlertScreen(
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false),
|
||||
content: content,
|
||||
actions: [
|
||||
.init(title: buttonText, type: .default, action: {
|
||||
commit()
|
||||
}),
|
||||
.init(title: strings.Common_Cancel)
|
||||
],
|
||||
updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal)
|
||||
)
|
||||
|
||||
var dismissAllTooltipsImpl: (() -> Void)?
|
||||
showAttributeInfoImpl = { [weak alertController] tag, text in
|
||||
dismissAllTooltipsImpl?()
|
||||
guard let alertController, let sourceView = alertController.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: alertController.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
alertController.present(tooltipController, in: .current)
|
||||
}
|
||||
dismissAllTooltipsImpl = { [weak alertController] in
|
||||
guard let alertController else {
|
||||
return
|
||||
}
|
||||
alertController.window?.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
})
|
||||
alertController.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return alertController
|
||||
}
|
||||
|
||||
final class AlertTransferHeaderComponent: Component {
|
||||
typealias EnvironmentType = AlertComponentEnvironment
|
||||
|
||||
let fromComponent: AnyComponentWithIdentity<Empty>
|
||||
let toComponent: AnyComponentWithIdentity<Empty>
|
||||
|
||||
public init(
|
||||
fromComponent: AnyComponentWithIdentity<Empty>,
|
||||
toComponent: AnyComponentWithIdentity<Empty>
|
||||
) {
|
||||
self.fromComponent = fromComponent
|
||||
self.toComponent = toComponent
|
||||
}
|
||||
|
||||
public static func ==(lhs: AlertTransferHeaderComponent, rhs: AlertTransferHeaderComponent) -> Bool {
|
||||
if lhs.fromComponent != rhs.fromComponent {
|
||||
return false
|
||||
}
|
||||
if lhs.toComponent != rhs.toComponent {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let from = ComponentView<Empty>()
|
||||
private let to = ComponentView<Empty>()
|
||||
private let arrow = ComponentView<Empty>()
|
||||
|
||||
private var component: AlertTransferHeaderComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
func update(component: AlertTransferHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environment = environment[AlertComponentEnvironment.self]
|
||||
|
||||
let size = CGSize(width: 148.0, height: 60.0)
|
||||
let sideInset = floorToScreenPixels((availableSize.width - size.width) / 2.0)
|
||||
|
||||
let fromSize = self.from.update(
|
||||
transition: transition,
|
||||
component: component.fromComponent.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 60.0, height: 60.0)
|
||||
)
|
||||
let fromFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: fromSize)
|
||||
if let fromView = self.from.view {
|
||||
if fromView.superview == nil {
|
||||
self.addSubview(fromView)
|
||||
}
|
||||
transition.setFrame(view: fromView, frame: fromFrame)
|
||||
}
|
||||
|
||||
|
||||
let arrowSize = self.arrow.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
BundleIconComponent(name: "Peer Info/AlertArrow", tintColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.2))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - arrowSize.width) / 2.0), y: floorToScreenPixels((size.height - arrowSize.height) / 2.0)), size: arrowSize)
|
||||
if let arrowView = self.arrow.view {
|
||||
if arrowView.superview == nil {
|
||||
self.addSubview(arrowView)
|
||||
}
|
||||
transition.setFrame(view: arrowView, frame: arrowFrame)
|
||||
}
|
||||
|
||||
let toSize = self.to.update(
|
||||
transition: transition,
|
||||
component: component.toComponent.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 60.0, height: 60.0)
|
||||
)
|
||||
let toFrame = CGRect(origin: CGPoint(x: availableSize.width - toSize.width - sideInset, y: 0.0), size: toSize)
|
||||
if let toView = self.to.view {
|
||||
if toView.superview == nil {
|
||||
self.addSubview(toView)
|
||||
}
|
||||
transition.setFrame(view: toView, frame: toFrame)
|
||||
}
|
||||
|
||||
return CGSize(width: availableSize.width, height: size.height + 11.0)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
final class AlertTableComponent: Component {
|
||||
typealias EnvironmentType = AlertComponentEnvironment
|
||||
|
||||
let items: [TableComponent.Item]
|
||||
|
||||
public init(
|
||||
items: [TableComponent.Item]
|
||||
) {
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: AlertTableComponent, rhs: AlertTableComponent) -> Bool {
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let table = ComponentView<Empty>()
|
||||
|
||||
private var component: AlertTableComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
func update(component: AlertTableComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environment = environment[AlertComponentEnvironment.self]
|
||||
|
||||
let tableSize = self.table.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
TableComponent(
|
||||
theme: environment.theme,
|
||||
items: component.items,
|
||||
semiTransparent: true
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width + 20.0, height: availableSize.height)
|
||||
)
|
||||
let tableFrame = CGRect(origin: CGPoint(x: -10.0, y: 5.0), size: tableSize)
|
||||
if let tableView = self.table.view {
|
||||
if tableView.superview == nil {
|
||||
self.addSubview(tableView)
|
||||
}
|
||||
transition.setFrame(view: tableView, frame: tableFrame)
|
||||
}
|
||||
return CGSize(width: availableSize.width, height: tableSize.height + 10.0)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import ActivityIndicator
|
|||
import MultilineTextWithEntitiesComponent
|
||||
import TelegramStringFormatting
|
||||
import TextFormat
|
||||
import AlertComponent
|
||||
|
||||
private final class GiftRemoveInfoAlertContentNode: AlertContentNode {
|
||||
private let context: AccountContext
|
||||
|
|
|
|||
|
|
@ -6,421 +6,15 @@ import ComponentFlow
|
|||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import AvatarNode
|
||||
import Markdown
|
||||
import GiftItemComponent
|
||||
import ChatMessagePaymentAlertController
|
||||
import ActivityIndicator
|
||||
import TooltipUI
|
||||
import MultilineTextComponent
|
||||
import TelegramStringFormatting
|
||||
|
||||
private final class GiftTransferAlertContentNode: AlertContentNode {
|
||||
private let context: AccountContext
|
||||
private let strings: PresentationStrings
|
||||
private var presentationTheme: PresentationTheme
|
||||
private let title: String
|
||||
private let text: String
|
||||
private let gift: StarGift.UniqueGift
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let giftView = ComponentView<Empty>()
|
||||
private let textNode: ASTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let tableView = ComponentView<Empty>()
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
private let symbolButtonTag = GenericComponentViewTag()
|
||||
|
||||
fileprivate var getController: () -> ViewController? = { return nil}
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var activityIndicator: ActivityIndicator?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
var inProgress = false {
|
||||
didSet {
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: AlertControllerTheme,
|
||||
ptheme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
gift: StarGift.UniqueGift,
|
||||
peer: EnginePeer,
|
||||
title: String,
|
||||
text: String,
|
||||
actions: [TextAlertAction]
|
||||
) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.presentationTheme = ptheme
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.gift = gift
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.displayWithoutProcessing = true
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isLayerBacked = true
|
||||
|
||||
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||
return TextAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
|
||||
self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer)
|
||||
}
|
||||
|
||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor)
|
||||
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
), textAlignment: .center)
|
||||
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor.withAlphaComponent(0.9))
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func dismissAllTooltips() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
controller.window?.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
})
|
||||
controller.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func showAttributeInfo(tag: Any, text: String) {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
guard let sourceView = self.tableView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
controller.present(tooltipController, in: .current)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 310.0)
|
||||
|
||||
let strings = self.strings
|
||||
|
||||
self.validLayout = size
|
||||
|
||||
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||
|
||||
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.avatarNode.updateSize(size: avatarSize)
|
||||
|
||||
let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize)
|
||||
|
||||
let _ = self.giftView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationTheme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: self.gift, price: nil),
|
||||
mode: .thumbnail
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: avatarSize
|
||||
)
|
||||
if let view = self.giftView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = giftFrame
|
||||
}
|
||||
|
||||
if let arrowImage = self.arrowNode.image {
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
}
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize)
|
||||
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||
|
||||
origin.y += avatarSize.height + 17.0
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
origin.y += titleSize.height + 5.0
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||
origin.y += textSize.height + 10.0
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
let contentWidth = max(size.width, minActionsWidth)
|
||||
|
||||
let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
|
||||
let tableFont = Font.regular(15.0)
|
||||
let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor
|
||||
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
.model, .pattern, .backdrop, .originalInfo
|
||||
]
|
||||
|
||||
var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:]
|
||||
for attribute in self.gift.attributes {
|
||||
attributeMap[attribute.attributeType] = attribute
|
||||
}
|
||||
|
||||
for type in order {
|
||||
if let attribute = attributeMap[type] {
|
||||
let id: String?
|
||||
let title: String?
|
||||
let value: NSAttributedString
|
||||
let percentage: Float?
|
||||
let tag: AnyObject?
|
||||
|
||||
switch attribute {
|
||||
case let .model(name, _, rarity):
|
||||
id = "model"
|
||||
title = strings.Gift_Unique_Model
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.modelButtonTag
|
||||
case let .backdrop(name, _, _, _, _, _, rarity):
|
||||
id = "backdrop"
|
||||
title = strings.Gift_Unique_Backdrop
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.backdropButtonTag
|
||||
case let .pattern(name, _, rarity):
|
||||
id = "pattern"
|
||||
title = strings.Gift_Unique_Symbol
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = self.symbolButtonTag
|
||||
case .originalInfo:
|
||||
continue
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(value))
|
||||
)
|
||||
)
|
||||
)
|
||||
if let percentage, let tag {
|
||||
items.append(AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: self.context,
|
||||
text: formatPercentage(percentage),
|
||||
color: self.presentationTheme.list.itemAccentColor
|
||||
)),
|
||||
action: { [weak self] in
|
||||
self?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
}
|
||||
let itemComponent = AnyComponent(
|
||||
HStack(items, spacing: 4.0)
|
||||
)
|
||||
|
||||
tableItems.append(.init(
|
||||
id: id,
|
||||
title: title,
|
||||
hasBackground: false,
|
||||
component: itemComponent
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if let valueAmount = self.gift.valueAmount, let valueCurrency = self.gift.valueCurrency {
|
||||
tableItems.append(.init(
|
||||
id: "fiatValue",
|
||||
title: strings.Gift_Unique_Value,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0)
|
||||
))
|
||||
}
|
||||
|
||||
let tableSize = self.tableView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
TableComponent(
|
||||
theme: self.presentationTheme,
|
||||
items: tableItems
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: contentWidth - 32.0, height: size.height)
|
||||
)
|
||||
let tableFrame = CGRect(origin: CGPoint(x: 16.0, y: avatarSize.height + titleSize.height + textSize.height + 60.0), size: tableSize)
|
||||
if let view = self.tableView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = tableFrame
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + 40.0 + insets.top + insets.bottom)
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
do {
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
do {
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
do {
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
if self.inProgress {
|
||||
let activityIndicator: ActivityIndicator
|
||||
if let current = self.activityIndicator {
|
||||
activityIndicator = current
|
||||
} else {
|
||||
activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false))
|
||||
self.addSubnode(activityIndicator)
|
||||
}
|
||||
|
||||
if let actionNode = self.actionNodes.first {
|
||||
actionNode.isUserInteractionEnabled = false
|
||||
actionNode.isHidden = false
|
||||
|
||||
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
||||
transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||
}
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
import AlertComponent
|
||||
import TableComponent
|
||||
|
||||
public func giftTransferAlertController(
|
||||
context: AccountContext,
|
||||
|
|
@ -429,7 +23,7 @@ public func giftTransferAlertController(
|
|||
transferStars: Int64,
|
||||
navigationController: NavigationController?,
|
||||
commit: @escaping () -> Void
|
||||
) -> AlertController {
|
||||
) -> AlertScreen {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
|
||||
|
|
@ -444,27 +38,176 @@ public func giftTransferAlertController(
|
|||
buttonText = strings.Gift_Transfer_Confirmation_TransferFree
|
||||
}
|
||||
|
||||
var contentNode: GiftTransferAlertContentNode?
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { [weak contentNode] in
|
||||
contentNode?.inProgress = true
|
||||
commit()
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?(true)
|
||||
})]
|
||||
let tableFont = Font.regular(15.0)
|
||||
let tableTextColor = presentationData.theme.list.itemPrimaryTextColor
|
||||
|
||||
contentNode = GiftTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions)
|
||||
let modelButtonTag = GenericComponentViewTag()
|
||||
let backdropButtonTag = GenericComponentViewTag()
|
||||
let symbolButtonTag = GenericComponentViewTag()
|
||||
var showAttributeInfoImpl: ((Any, String) -> Void)?
|
||||
|
||||
let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: navigationController, chatPeerId: context.account.peerId, showBalance: transferStars > 0)
|
||||
contentNode?.getController = { [weak controller] in
|
||||
return controller
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
.model, .pattern, .backdrop, .originalInfo
|
||||
]
|
||||
|
||||
var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:]
|
||||
for attribute in gift.attributes {
|
||||
attributeMap[attribute.attributeType] = attribute
|
||||
}
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
} else {
|
||||
controller?.dismiss()
|
||||
|
||||
for type in order {
|
||||
if let attribute = attributeMap[type] {
|
||||
let id: String?
|
||||
let title: String?
|
||||
let value: NSAttributedString
|
||||
let percentage: Float?
|
||||
let tag: AnyObject?
|
||||
|
||||
switch attribute {
|
||||
case let .model(name, _, rarity):
|
||||
id = "model"
|
||||
title = strings.Gift_Unique_Model
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = modelButtonTag
|
||||
case let .backdrop(name, _, _, _, _, _, rarity):
|
||||
id = "backdrop"
|
||||
title = strings.Gift_Unique_Backdrop
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = backdropButtonTag
|
||||
case let .pattern(name, _, rarity):
|
||||
id = "pattern"
|
||||
title = strings.Gift_Unique_Symbol
|
||||
value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)
|
||||
percentage = Float(rarity) * 0.1
|
||||
tag = symbolButtonTag
|
||||
case .originalInfo:
|
||||
continue
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(value))
|
||||
)
|
||||
)
|
||||
)
|
||||
if let percentage, let tag {
|
||||
items.append(AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: context,
|
||||
text: formatPercentage(percentage),
|
||||
color: presentationData.theme.list.itemAccentColor
|
||||
)),
|
||||
action: {
|
||||
showAttributeInfoImpl?(tag, strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
}
|
||||
let itemComponent = AnyComponent(
|
||||
HStack(items, spacing: 4.0)
|
||||
)
|
||||
|
||||
tableItems.append(.init(
|
||||
id: id,
|
||||
title: title,
|
||||
hasBackground: false,
|
||||
component: itemComponent
|
||||
))
|
||||
}
|
||||
}
|
||||
return controller
|
||||
|
||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "header",
|
||||
component: AnyComponent(
|
||||
AlertTransferHeaderComponent(
|
||||
fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: context,
|
||||
theme: presentationData.theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: gift, price: nil),
|
||||
mode: .thumbnail
|
||||
)
|
||||
)),
|
||||
toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent(
|
||||
AvatarComponent(
|
||||
context: context,
|
||||
theme: presentationData.theme,
|
||||
peer: peer
|
||||
)
|
||||
))
|
||||
)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
AlertTitleComponent(title: title)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(text))
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "table",
|
||||
component: AnyComponent(
|
||||
AlertTableComponent(items: tableItems)
|
||||
)
|
||||
))
|
||||
|
||||
let alertController = AlertScreen(
|
||||
context: context,
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false),
|
||||
content: content,
|
||||
actions: [
|
||||
.init(title: buttonText, type: .default, action: {
|
||||
commit()
|
||||
}),
|
||||
.init(title: strings.Common_Cancel)
|
||||
]
|
||||
)
|
||||
|
||||
var dismissAllTooltipsImpl: (() -> Void)?
|
||||
showAttributeInfoImpl = { [weak alertController] tag, text in
|
||||
dismissAllTooltipsImpl?()
|
||||
guard let alertController, let sourceView = alertController.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: alertController.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
alertController.present(tooltipController, in: .current)
|
||||
}
|
||||
dismissAllTooltipsImpl = { [weak alertController] in
|
||||
guard let alertController else {
|
||||
return
|
||||
}
|
||||
alertController.window?.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
})
|
||||
alertController.forEachController({ controller in
|
||||
if let controller = controller as? TooltipScreen {
|
||||
controller.dismiss(inPlace: false)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return alertController
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import LottieComponent
|
|||
import ProfileLevelRatingBarComponent
|
||||
import TextFormat
|
||||
import TelegramStringFormatting
|
||||
import TableComponent
|
||||
|
||||
private final class GiftUpgradeCostScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
|
|||
|
|
@ -24,24 +24,30 @@ import SegmentControlComponent
|
|||
import GiftAnimationComponent
|
||||
import GlassBackgroundComponent
|
||||
|
||||
private final class GiftUpgradePreviewScreenComponent: Component {
|
||||
private final class GiftUpgradeVariantsScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let gift: StarGift
|
||||
let attributes: [StarGift.UniqueGift.Attribute]
|
||||
let selectedAttributes: [StarGift.UniqueGift.Attribute]?
|
||||
let focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
gift: StarGift,
|
||||
attributes: [StarGift.UniqueGift.Attribute]
|
||||
attributes: [StarGift.UniqueGift.Attribute],
|
||||
selectedAttributes: [StarGift.UniqueGift.Attribute]?,
|
||||
focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
) {
|
||||
self.context = context
|
||||
self.gift = gift
|
||||
self.attributes = attributes
|
||||
self.selectedAttributes = selectedAttributes
|
||||
self.focusedAttribute = focusedAttribute
|
||||
}
|
||||
|
||||
static func ==(lhs: GiftUpgradePreviewScreenComponent, rhs: GiftUpgradePreviewScreenComponent) -> Bool {
|
||||
static func ==(lhs: GiftUpgradeVariantsScreenComponent, rhs: GiftUpgradeVariantsScreenComponent) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +133,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
|
||||
private var ignoreScrolling: Bool = false
|
||||
|
||||
private var component: GiftUpgradePreviewScreenComponent?
|
||||
private var component: GiftUpgradeVariantsScreenComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
private var isUpdating: Bool = false
|
||||
private var environment: ViewControllerComponentContainer.Environment?
|
||||
|
|
@ -426,7 +432,11 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
rarity = rarityValue
|
||||
modelAttribute = attribute
|
||||
|
||||
isSelected = self.selectedModel == attribute
|
||||
if case let .model(_, selectedFile, _) = self.selectedModel {
|
||||
isSelected = file.fileId == selectedFile.fileId
|
||||
} else {
|
||||
isSelected = false
|
||||
}
|
||||
}
|
||||
case let .backdrop(name, id, _, _, _, _, rarityValue):
|
||||
itemId += "\(id)"
|
||||
|
|
@ -435,7 +445,11 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
rarity = rarityValue
|
||||
backdropAttribute = attribute
|
||||
|
||||
isSelected = self.selectedBackdrop == attribute
|
||||
if case let .backdrop(_, selectedId, _, _, _, _, _) = self.selectedBackdrop {
|
||||
isSelected = id == selectedId
|
||||
} else {
|
||||
isSelected = false
|
||||
}
|
||||
}
|
||||
case let .pattern(name, file, rarityValue):
|
||||
itemId += "\(file.fileId.id)"
|
||||
|
|
@ -444,7 +458,11 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
rarity = rarityValue
|
||||
symbolAttribute = attribute
|
||||
|
||||
isSelected = self.selectedSymbol == attribute
|
||||
if case let .pattern(_, selectedFile, _) = self.selectedSymbol {
|
||||
isSelected = file.fileId == selectedFile.fileId
|
||||
} else {
|
||||
isSelected = false
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
|
@ -557,7 +575,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
func update(component: GiftUpgradePreviewScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
func update(component: GiftUpgradeVariantsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
|
|
@ -626,6 +644,34 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
}).shuffled().prefix(15))
|
||||
self.previewSymbols = randomSymbols
|
||||
|
||||
if let selectedAttributes = component.selectedAttributes {
|
||||
self.isPlaying = false
|
||||
for attribute in selectedAttributes {
|
||||
switch attribute {
|
||||
case .model:
|
||||
self.selectedModel = attribute
|
||||
case .pattern:
|
||||
self.selectedSymbol = attribute
|
||||
case .backdrop:
|
||||
self.selectedBackdrop = attribute
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if let focusedAttribute = component.focusedAttribute {
|
||||
switch focusedAttribute {
|
||||
case .model:
|
||||
self.selectedSection = .models
|
||||
case .pattern:
|
||||
self.selectedSection = .symbols
|
||||
case .backdrop:
|
||||
self.selectedSection = .backdrops
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.updateEffectiveGifts(attributes: component.attributes)
|
||||
}
|
||||
|
||||
|
|
@ -826,6 +872,16 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
|
||||
contentHeight += 16.0
|
||||
|
||||
let selectedId: AnyHashable
|
||||
switch self.selectedSection {
|
||||
case .models:
|
||||
selectedId = AnyHashable(SelectedSection.models)
|
||||
case .backdrops:
|
||||
selectedId = AnyHashable(SelectedSection.backdrops)
|
||||
case .symbols:
|
||||
selectedId = AnyHashable(SelectedSection.symbols)
|
||||
}
|
||||
|
||||
let segmentedSize = self.segmentControl.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(SegmentControlComponent(
|
||||
|
|
@ -835,7 +891,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.backdrops), title: environment.strings.Gift_Variants_Backdrops),
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.symbols), title: environment.strings.Gift_Variants_Symbols)
|
||||
],
|
||||
selectedId: "models",
|
||||
selectedId: selectedId,
|
||||
action: { [weak self] id in
|
||||
guard let self, let component = self.component, let id = id.base as? SelectedSection else {
|
||||
return
|
||||
|
|
@ -1074,7 +1130,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
public class GiftUpgradePreviewScreen: ViewControllerComponentContainer {
|
||||
public class GiftUpgradeVariantsScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
|
||||
private var didPlayAppearAnimation: Bool = false
|
||||
|
|
@ -1083,14 +1139,18 @@ public class GiftUpgradePreviewScreen: ViewControllerComponentContainer {
|
|||
public init(
|
||||
context: AccountContext,
|
||||
gift: StarGift,
|
||||
attributes: [StarGift.UniqueGift.Attribute]
|
||||
attributes: [StarGift.UniqueGift.Attribute],
|
||||
selectedAttributes: [StarGift.UniqueGift.Attribute]?,
|
||||
focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
super.init(context: context, component: GiftUpgradePreviewScreenComponent(
|
||||
super.init(context: context, component: GiftUpgradeVariantsScreenComponent(
|
||||
context: context,
|
||||
gift: gift,
|
||||
attributes: attributes
|
||||
attributes: attributes,
|
||||
selectedAttributes: selectedAttributes,
|
||||
focusedAttribute: focusedAttribute
|
||||
), navigationBarAppearance: .none, theme: .default)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
|
@ -1114,7 +1174,7 @@ public class GiftUpgradePreviewScreen: ViewControllerComponentContainer {
|
|||
if !self.didPlayAppearAnimation {
|
||||
self.didPlayAppearAnimation = true
|
||||
|
||||
if let componentView = self.node.hostView.componentView as? GiftUpgradePreviewScreenComponent.View {
|
||||
if let componentView = self.node.hostView.componentView as? GiftUpgradeVariantsScreenComponent.View {
|
||||
componentView.animateIn()
|
||||
}
|
||||
}
|
||||
|
|
@ -1124,7 +1184,7 @@ public class GiftUpgradePreviewScreen: ViewControllerComponentContainer {
|
|||
if !self.isDismissed {
|
||||
self.isDismissed = true
|
||||
|
||||
if let componentView = self.node.hostView.componentView as? GiftUpgradePreviewScreenComponent.View {
|
||||
if let componentView = self.node.hostView.componentView as? GiftUpgradeVariantsScreenComponent.View {
|
||||
componentView.animateOut(completion: { [weak self] in
|
||||
completion?()
|
||||
self?.dismiss(animated: false)
|
||||
|
|
@ -25,6 +25,7 @@ import GiftAnimationComponent
|
|||
import ContextUI
|
||||
import GiftItemComponent
|
||||
import GlassBarButtonComponent
|
||||
import TableComponent
|
||||
|
||||
private final class GiftValueSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ import ProfileLevelRatingBarComponent
|
|||
import AnimatedTextComponent
|
||||
import InfoParagraphComponent
|
||||
import ChatMessagePaymentAlertController
|
||||
import TableComponent
|
||||
import PeerTableCellComponent
|
||||
|
||||
private final class GiftViewSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -121,6 +123,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
let levelsDisposable = MetaDisposable()
|
||||
var nextGiftToUpgrade: ProfileGiftsContext.State.StarGift?
|
||||
|
||||
var giftVariantsDisposable = MetaDisposable()
|
||||
|
||||
var buyForm: BotPaymentForm?
|
||||
var buyFormDisposable: Disposable?
|
||||
var buyDisposable: Disposable?
|
||||
|
|
@ -375,6 +379,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
self.buyDisposable?.dispose()
|
||||
self.levelsDisposable.dispose()
|
||||
self.starsTopUpOptionsDisposable?.dispose()
|
||||
self.giftVariantsDisposable.dispose()
|
||||
}
|
||||
|
||||
func openPeer(_ peer: EnginePeer, gifts: Bool = false, dismiss: Bool = true) {
|
||||
|
|
@ -1351,6 +1356,33 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
func openUpgradeVariants(attribute: StarGift.UniqueGift.Attribute? = nil) {
|
||||
guard let controller = self.getController() as? GiftViewScreen, let arguments = self.subject.arguments else {
|
||||
return
|
||||
}
|
||||
var selectedAttributes: [StarGift.UniqueGift.Attribute]?
|
||||
if case let .unique(uniqueGift) = arguments.gift {
|
||||
selectedAttributes = uniqueGift.attributes
|
||||
}
|
||||
|
||||
self.giftVariantsDisposable.set((self.context.engine.payments.getStarGiftUpgradeAttributes(giftId: arguments.gift.giftId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] attributes in
|
||||
guard let self, let attributes else {
|
||||
return
|
||||
}
|
||||
let variantsController = self.context.sharedContext.makeGiftUpgradeVariantsScreen(
|
||||
context: self.context,
|
||||
gift: arguments.gift,
|
||||
attributes: attributes,
|
||||
selectedAttributes: selectedAttributes,
|
||||
focusedAttribute: attribute
|
||||
)
|
||||
controller.push(variantsController)
|
||||
}))
|
||||
}
|
||||
|
||||
func showAttributeInfo(tag: Any, text: String) {
|
||||
guard let controller = self.getController() as? GiftViewScreen else {
|
||||
return
|
||||
|
|
@ -2514,7 +2546,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
let upgradeNextButton = Child(PlainButtonComponent.self)
|
||||
|
||||
let upgradeTitle = Child(MultilineTextComponent.self)
|
||||
let upgradeDescription = Child(BalancedTextComponent.self)
|
||||
let upgradeDescription = Child(PlainButtonComponent.self)
|
||||
let upgradePerks = Child(List<Empty>.self)
|
||||
let upgradeKeepName = Child(PlainButtonComponent.self)
|
||||
let upgradePriceButton = Child(PlainButtonComponent.self)
|
||||
|
|
@ -2721,10 +2753,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
}
|
||||
headerSubject = .unique(state.justUpgraded ? state.upgradePreview?.attributes : nil, uniqueGift)
|
||||
} else if state.inUpgradePreview, let attributes = state.upgradePreview?.attributes {
|
||||
headerHeight = 258.0
|
||||
headerHeight = 246.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if case let .upgradePreview(attributes, _) = component.subject {
|
||||
headerHeight = 258.0
|
||||
headerHeight = 246.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if case let .wearPreview(_, attributes) = component.subject, let attributes {
|
||||
headerHeight = 200.0
|
||||
|
|
@ -2978,9 +3010,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
originY += 16.0
|
||||
} else if showUpgradePreview {
|
||||
let title: String
|
||||
let description: String
|
||||
//let description: String
|
||||
let uniqueText: String
|
||||
let transferableText: String
|
||||
//let transferableText: String
|
||||
let tradableText: String
|
||||
if !incoming, case let .profileGift(peerId, _) = subject, let peer = state.peerMap[peerId] {
|
||||
var peerName = peer.compactDisplayTitle
|
||||
|
|
@ -2988,9 +3020,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
peerName = "\(peerName.prefix(22))…"
|
||||
}
|
||||
title = environment.strings.Gift_Upgrade_GiftTitle
|
||||
description = environment.strings.Gift_Upgrade_GiftDescription(peerName).string
|
||||
// description = environment.strings.Gift_Upgrade_GiftDescription(peerName).string
|
||||
uniqueText = strings.Gift_Upgrade_Unique_GiftDescription(peerName).string
|
||||
transferableText = strings.Gift_Upgrade_Transferable_GiftDescription(peerName).string
|
||||
// transferableText = strings.Gift_Upgrade_Transferable_GiftDescription(peerName).string
|
||||
tradableText = strings.Gift_Upgrade_Tradable_GiftDescription(peerName).string
|
||||
} else if case let .upgradePreview(_, peerName) = component.subject {
|
||||
var peerName = peerName
|
||||
|
|
@ -2998,15 +3030,15 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
peerName = "\(peerName.prefix(22))…"
|
||||
}
|
||||
title = environment.strings.Gift_Upgrade_IncludeTitle
|
||||
description = environment.strings.Gift_Upgrade_IncludeDescription(peerName).string
|
||||
// description = environment.strings.Gift_Upgrade_IncludeDescription(peerName).string
|
||||
uniqueText = strings.Gift_Upgrade_Unique_IncludeDescription
|
||||
transferableText = strings.Gift_Upgrade_Transferable_IncludeDescription
|
||||
// transferableText = strings.Gift_Upgrade_Transferable_IncludeDescription
|
||||
tradableText = strings.Gift_Upgrade_Tradable_IncludeDescription
|
||||
} else {
|
||||
title = environment.strings.Gift_Upgrade_Title
|
||||
description = environment.strings.Gift_Upgrade_Description
|
||||
// description = environment.strings.Gift_Upgrade_Description
|
||||
uniqueText = strings.Gift_Upgrade_Unique_Description
|
||||
transferableText = strings.Gift_Upgrade_Transferable_Description
|
||||
// transferableText = strings.Gift_Upgrade_Transferable_Description
|
||||
tradableText = strings.Gift_Upgrade_Tradable_Description
|
||||
}
|
||||
|
||||
|
|
@ -3024,39 +3056,109 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
let upgradeDescription = upgradeDescription.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: description,
|
||||
font: Font.regular(13.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 5,
|
||||
lineSpacing: 0.2,
|
||||
tintColor: vibrantColor
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 6.0
|
||||
let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height
|
||||
|
||||
headerComponents.append({
|
||||
context.add(upgradeTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0)))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
if case let .generic(gift) = component.subject.arguments?.gift, let upgradePreview = state.upgradePreview {
|
||||
var variant1: GiftItemComponent.Subject = .starGift(gift: gift, price: "")
|
||||
var variant2: GiftItemComponent.Subject = .starGift(gift: gift, price: "")
|
||||
var variant3: GiftItemComponent.Subject = .starGift(gift: gift, price: "")
|
||||
|
||||
var i = 0
|
||||
for attribute in upgradePreview.attributes {
|
||||
if case .model = attribute {
|
||||
switch i {
|
||||
case 0:
|
||||
variant1 = .preview(attributes: [attribute], rarity: 0)
|
||||
case 1:
|
||||
variant2 = .preview(attributes: [attribute], rarity: 0)
|
||||
case 2:
|
||||
variant3 = .preview(attributes: [attribute], rarity: 0)
|
||||
default:
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
//TODO:localize
|
||||
var buttonColor: UIColor = UIColor.white.withAlphaComponent(0.16)
|
||||
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
|
||||
buttonColor = previewPatternColor
|
||||
}
|
||||
let upgradeDescription = upgradeDescription.update(
|
||||
component: PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
ZStack([
|
||||
AnyComponentWithIdentity(id: "background", component: AnyComponent(
|
||||
FilledRoundedRectangleComponent(color: buttonColor, cornerRadius: .minEdge, smoothCorners: false)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(HStack([
|
||||
AnyComponentWithIdentity(id: "icon1", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: variant1,
|
||||
isPlaceholder: false,
|
||||
mode: .tableIcon
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "icon2", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: variant2,
|
||||
isPlaceholder: false,
|
||||
mode: .tableIcon
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "icon3", component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: nil,
|
||||
subject: variant3,
|
||||
isPlaceholder: false,
|
||||
mode: .tableIcon
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "text", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "View all variants", font: Font.semibold(13.0), textColor: .white)))
|
||||
)),
|
||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
||||
BundleIconComponent(name: "Item List/InlineTextRightArrow", tintColor: .white)
|
||||
))
|
||||
], spacing: 3.0)))
|
||||
])
|
||||
),
|
||||
action: { [weak state] in
|
||||
state?.openUpgradeVariants()
|
||||
},
|
||||
animateScale: false
|
||||
),
|
||||
availableSize: CGSize(width: 190.0, height: 24.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 6.0
|
||||
let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height
|
||||
|
||||
context.add(upgradeDescription
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0)))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
})
|
||||
headerComponents.append({
|
||||
context.add(upgradeTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(194.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0)))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
|
||||
context.add(upgradeDescription
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(198.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0)))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
})
|
||||
}
|
||||
originY += 24.0
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
|
|
@ -3078,20 +3180,20 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "transferable",
|
||||
component: AnyComponent(InfoParagraphComponent(
|
||||
title: strings.Gift_Upgrade_Transferable_Title,
|
||||
titleColor: textColor,
|
||||
text: transferableText,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Transferable",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
// items.append(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "transferable",
|
||||
// component: AnyComponent(InfoParagraphComponent(
|
||||
// title: strings.Gift_Upgrade_Transferable_Title,
|
||||
// titleColor: textColor,
|
||||
// text: transferableText,
|
||||
// textColor: secondaryTextColor,
|
||||
// accentColor: linkColor,
|
||||
// iconName: "Premium/Collectible/Transferable",
|
||||
// iconColor: linkColor
|
||||
// ))
|
||||
// )
|
||||
// )
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "tradable",
|
||||
|
|
@ -3106,6 +3208,21 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
))
|
||||
)
|
||||
)
|
||||
//TODO:localize
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "wearable",
|
||||
component: AnyComponent(InfoParagraphComponent(
|
||||
title: "Wearable",
|
||||
titleColor: textColor,
|
||||
text: "Display gifts on your page and set them as profile covers or statuses.",
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Transferable",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
let perksSideInset = sideInset + 16.0
|
||||
let upgradePerks = upgradePerks.update(
|
||||
|
|
@ -3598,7 +3715,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
id: AnyHashable(0),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -3631,7 +3748,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
} else {
|
||||
ownerComponent = AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -3682,7 +3799,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
title: strings.Gift_Unique_Telegram,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -3712,7 +3829,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
id: AnyHashable(0),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -3742,7 +3859,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
} else {
|
||||
fromComponent = AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -3767,7 +3884,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
id: "from_anon",
|
||||
title: strings.Gift_View_From,
|
||||
component: AnyComponent(
|
||||
PeerCellComponent(
|
||||
PeerTableCellComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
|
|
@ -4151,7 +4268,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
color: theme.list.itemAccentColor
|
||||
)),
|
||||
action: { [weak state] in
|
||||
state?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
state?.openUpgradeVariants(attribute: attribute)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
|
|
@ -5784,115 +5901,6 @@ func formatPercentage(_ value: Float) -> String {
|
|||
return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%"
|
||||
}
|
||||
|
||||
final class PeerCellComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let peer: EnginePeer?
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer?) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerCellComponent, rhs: PeerCellComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PeerCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let avatarSize = CGSize(width: 22.0, height: 22.0)
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
var peerName: String
|
||||
let avatarOverride: AvatarNodeImageOverride?
|
||||
if let peerValue = component.peer {
|
||||
peerName = peerValue.compactDisplayTitle
|
||||
if peerName.count > 40 {
|
||||
peerName = "\(peerName.prefix(40))…"
|
||||
}
|
||||
avatarOverride = nil
|
||||
} else {
|
||||
peerName = component.strings.Gift_View_HiddenName
|
||||
avatarOverride = .anonymousSavedMessagesIcon(isColored: true)
|
||||
}
|
||||
|
||||
let avatarNaturalSize = CGSize(width: 40.0, height: 40.0)
|
||||
self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer, overrideImage: avatarOverride)
|
||||
self.avatarNode.bounds = CGRect(origin: .zero, size: avatarNaturalSize)
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: peerName, font: Font.regular(15.0), textColor: component.peer != nil ? component.theme.list.itemAccentColor : component.theme.list.itemPrimaryTextColor, paragraphAlignment: .left))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize)
|
||||
self.avatarNode.frame = avatarFrame
|
||||
|
||||
if let view = self.text.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
transition.setFrame(view: view, frame: textFrame)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class HeaderContentComponent: Component {
|
||||
let attributedText: NSAttributedString
|
||||
|
||||
|
|
@ -6250,6 +6258,10 @@ final class AvatarComponent: Component {
|
|||
}
|
||||
|
||||
func update(component: AvatarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
if self.component == nil {
|
||||
self.avatarNode.font = avatarPlaceholderFont(size: 42.0 * floor(availableSize.width / 100.0))
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "PeerTableCellComponent",
|
||||
module_name = "PeerTableCellComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AvatarNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import ComponentFlow
|
||||
import Display
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
import AvatarNode
|
||||
import AccountContext
|
||||
|
||||
public final class PeerTableCellComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let peer: EnginePeer?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
peer: EnginePeer?
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
public static func ==(lhs: PeerTableCellComponent, rhs: PeerTableCellComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerTableCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 12.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PeerTableCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let avatarSize = CGSize(width: 22.0, height: 22.0)
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
var peerName: String
|
||||
let avatarOverride: AvatarNodeImageOverride?
|
||||
if let peerValue = component.peer {
|
||||
peerName = peerValue.compactDisplayTitle
|
||||
if peerName.count > 40 {
|
||||
peerName = "\(peerName.prefix(40))…"
|
||||
}
|
||||
avatarOverride = nil
|
||||
} else {
|
||||
peerName = component.strings.Gift_View_HiddenName
|
||||
avatarOverride = .anonymousSavedMessagesIcon(isColored: true)
|
||||
}
|
||||
|
||||
let avatarNaturalSize = CGSize(width: 40.0, height: 40.0)
|
||||
self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer, overrideImage: avatarOverride)
|
||||
self.avatarNode.bounds = CGRect(origin: .zero, size: avatarNaturalSize)
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: peerName, font: Font.regular(15.0), textColor: component.peer != nil ? component.theme.list.itemAccentColor : component.theme.list.itemPrimaryTextColor, paragraphAlignment: .left))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize)
|
||||
self.avatarNode.frame = avatarFrame
|
||||
|
||||
if let view = self.text.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
transition.setFrame(view: view, frame: textFrame)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
23
submodules/TelegramUI/Components/Gifts/TableComponent/BUILD
Normal file
23
submodules/TelegramUI/Components/Gifts/TableComponent/BUILD
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "TableComponent",
|
||||
module_name = "TableComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -5,9 +5,9 @@ import Display
|
|||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
|
||||
final class TableComponent: CombinedComponent {
|
||||
class Item: Equatable {
|
||||
enum TitleFont {
|
||||
public final class TableComponent: CombinedComponent {
|
||||
public class Item: Equatable {
|
||||
public enum TitleFont {
|
||||
case regular
|
||||
case bold
|
||||
}
|
||||
|
|
@ -19,7 +19,14 @@ final class TableComponent: CombinedComponent {
|
|||
public let component: AnyComponent<Empty>
|
||||
public let insets: UIEdgeInsets?
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String?, titleFont: TitleFont = .regular, hasBackground: Bool = false, component: AnyComponent<Empty>, insets: UIEdgeInsets? = nil) {
|
||||
public init<IdType: Hashable>(
|
||||
id: IdType,
|
||||
title: String?,
|
||||
titleFont: TitleFont = .regular,
|
||||
hasBackground: Bool = false,
|
||||
component: AnyComponent<Empty>,
|
||||
insets: UIEdgeInsets? = nil
|
||||
) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.titleFont = titleFont
|
||||
|
|
@ -74,13 +81,13 @@ final class TableComponent: CombinedComponent {
|
|||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
public final class State: ComponentState {
|
||||
var cachedLastBackgroundImage: (UIImage, PresentationTheme)?
|
||||
var cachedLeftColumnImage: (UIImage, PresentationTheme)?
|
||||
var cachedBorderImage: (UIImage, PresentationTheme)?
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
public func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
|
|
@ -98,11 +105,15 @@ final class TableComponent: CombinedComponent {
|
|||
let horizontalPadding: CGFloat = 12.0
|
||||
let borderWidth: CGFloat = 1.0
|
||||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
var secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
|
||||
let borderColor: UIColor
|
||||
let secondaryBackgroundColor: UIColor
|
||||
if context.component.semiTransparent {
|
||||
secondaryBackgroundColor = borderColor.withMultipliedAlpha(0.5)
|
||||
borderColor = context.component.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1)
|
||||
secondaryBackgroundColor = context.component.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.05)
|
||||
} else {
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
|
||||
}
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
|
@ -66,17 +66,17 @@ public final class LiquidLensView: UIView {
|
|||
|
||||
private struct Params: Equatable {
|
||||
var size: CGSize
|
||||
var selectionX: CGFloat
|
||||
var selectionWidth: CGFloat
|
||||
var selectionOrigin: CGPoint
|
||||
var selectionSize: CGSize
|
||||
var inset: CGFloat
|
||||
var isDark: Bool
|
||||
var isLifted: Bool
|
||||
var isCollapsed: Bool
|
||||
|
||||
init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) {
|
||||
init(size: CGSize, selectionOrigin: CGPoint, selectionSize: CGSize, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) {
|
||||
self.size = size
|
||||
self.selectionX = selectionX
|
||||
self.selectionWidth = selectionWidth
|
||||
self.selectionOrigin = selectionOrigin
|
||||
self.selectionSize = selectionSize
|
||||
self.inset = inset
|
||||
self.isLifted = isLifted
|
||||
self.isDark = isDark
|
||||
|
|
@ -121,12 +121,12 @@ public final class LiquidLensView: UIView {
|
|||
|
||||
private var liftedDisplayLink: SharedDisplayLinkDriver.Link?
|
||||
|
||||
public var selectionX: CGFloat? {
|
||||
return self.params?.selectionX
|
||||
public var selectionOrigin: CGPoint? {
|
||||
return self.params?.selectionOrigin
|
||||
}
|
||||
|
||||
public var selectionWidth: CGFloat? {
|
||||
return self.params?.selectionWidth
|
||||
public var selectionSize: CGSize? {
|
||||
return self.params?.selectionSize
|
||||
}
|
||||
|
||||
public private(set) var isAnimating: Bool = false {
|
||||
|
|
@ -286,8 +286,8 @@ public final class LiquidLensView: UIView {
|
|||
lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: view)
|
||||
}
|
||||
|
||||
public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) {
|
||||
let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, inset: inset, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed)
|
||||
public func update(size: CGSize, selectionOrigin: CGPoint, selectionSize: CGSize, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) {
|
||||
let params = Params(size: size, selectionOrigin: selectionOrigin, selectionSize: selectionSize, inset: inset, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed)
|
||||
if self.params == params {
|
||||
return
|
||||
}
|
||||
|
|
@ -444,7 +444,7 @@ public final class LiquidLensView: UIView {
|
|||
transition.setCornerRadius(layer: self.liftedContainerView.layer, cornerRadius: params.size.height * 0.5)
|
||||
}
|
||||
|
||||
let baseLensFrame = CGRect(origin: CGPoint(x: params.selectionX, y: 0.0), size: CGSize(width: params.selectionWidth, height: params.size.height))
|
||||
let baseLensFrame = CGRect(origin: CGPoint(x: params.selectionOrigin.x, y: 0.0), size: CGSize(width: params.selectionSize.width, height: params.size.height))
|
||||
self.updateLens(params: LensParams(baseFrame: baseLensFrame, inset: params.inset, isLifted: params.isLifted), transition: transition)
|
||||
|
||||
if let legacyContentMaskView = self.legacyContentMaskView {
|
||||
|
|
|
|||
|
|
@ -1170,6 +1170,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
|||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: environment.theme.list.itemCheckColors.fillColor,
|
||||
foreground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
|
||||
|
|
@ -1186,7 +1187,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - bottomPanelButtonInsets.left - bottomPanelButtonInsets.right, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - bottomPanelButtonInsets.left - bottomPanelButtonInsets.right, height: 52.0)
|
||||
)
|
||||
|
||||
let bottomPanelHeight: CGFloat = bottomPanelButtonInsets.top + bottomPanelButtonSize.height + bottomPanelButtonInsets.bottom + bottomPanelTextSize.height + 8.0 + environment.safeInsets.bottom
|
||||
|
|
|
|||
|
|
@ -1130,11 +1130,11 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
|||
)),
|
||||
background: AnyComponent(FilledRoundedRectangleComponent(
|
||||
color: environment.theme.list.itemInputField.backgroundColor,
|
||||
cornerRadius: .value(8.0),
|
||||
cornerRadius: .minEdge,
|
||||
smoothCorners: true
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
minSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
minSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 52.0),
|
||||
contentInsets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0),
|
||||
action: { [weak self] in
|
||||
guard let self, case let .active(active) = self.currentMode else {
|
||||
|
|
@ -1148,7 +1148,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
|||
animateContents: false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 52.0)
|
||||
)
|
||||
let linkTextFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - linkTextSize.width) * 0.5), y: contentHeight), size: linkTextSize)
|
||||
if let linkTextView = self.linkText.view {
|
||||
|
|
@ -1170,6 +1170,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
|||
actionButtonTitle = environment.strings.AffiliateProgram_ActionCopyLink
|
||||
}
|
||||
|
||||
let buttonSideInset: CGFloat = 30.0
|
||||
let actionButtonSize = self.actionButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
|
|
@ -1208,7 +1209,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 30.0 * 2.0, height: 52.0)
|
||||
containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 52.0)
|
||||
)
|
||||
|
||||
let bottomTextSize = self.bottomText.update(
|
||||
|
|
@ -1238,7 +1239,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
|||
let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelHeight), size: CGSize(width: availableSize.width, height: bottomPanelHeight))
|
||||
transition.setFrame(view: self.bottomPanelContainer, frame: bottomPanelFrame)
|
||||
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: actionButtonSize)
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: 0.0), size: actionButtonSize)
|
||||
if let actionButtonView = self.actionButton.view {
|
||||
if actionButtonView.superview == nil {
|
||||
self.bottomPanelContainer.addSubview(actionButtonView)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue